Closes D-5 / D-6 (sqlite-vec dylib bundling) carry-forwards from the 2026-05-04 audit by adopting jkrukowski/SQLiteVec as a SPM dependency. The package's CSQLiteVec target compiles sqlite3.c + sqlite-vec.c into one statically-linked C target with `core_vec_init()` registering vec0 as an auto-extension. All sqlite3_* calls in Replay's SQLiteConnection and Memory's MemoryStore resolve to this bundled SQLite (vec0 always available) instead of Apple's stripped /usr/lib/libsqlite3.dylib. The retired hand-rolled approach (dlsym pathway + JARVIS_VEC0_STUB_PATH env-flag + Bundle.module placeholder text file) never worked on macOS because Apple stripped sqlite3_load_extension from the system libsqlite3. Memory init has been silently dormant since Phase 7 (2026-04 onward), which is what made B-02 (history threading) appear permanently broken to the user during dogfooding even after the48ad8d6tactical patch landed. Live verification 2026-05-11: fresh launch with empty jarvis.db creates schema, persists turn rows across submits, B-02 multi-turn context resolves correctly ("what breed is Toby?" → "Golden Retriever" after prior turn established Toby is a Golden Retriever), and the new get_memory_stats tool reports `turnsCount=6, vecVersion=v0.1.6, sqliteVersion=3.51.0`. Changes: - packages/Replay/Package.swift + SQLiteVec SPM dependency (from: "0.0.14") + CSQLiteVec product wired into Replay target - packages/Replay/Sources/Replay/SQLiteConnection.swift - import SQLite3 + @_exported import CSQLiteVec All sqlite3_* call sites unchanged — linker now resolves to our bundled sqlite3 with vec0 statically linked in. - packages/Memory/Package.swift + SQLiteVec SPM dependency (named product so MemoryStore can import CSQLiteVec directly for the core_vec_init() one-shot call) - resources: PLACEHOLDER.txt copy (defunct vec0.dylib stub slot) - packages/Memory/Sources/Memory/MemoryStore.swift - import SQLite3 → import CSQLiteVec + _vecAutoExtensionRegistered static-let one-shot: calls CSQLiteVec.core_vec_init() which registers vec0 via sqlite3_auto_extension on the first MemoryStore construction. Idempotent (static-let initializer fires exactly once). - loadVecExtension(on:) dlsym/JARVIS_VEC0_STUB_PATH/Bundle.module pathway (retired; ~70 lines removed) Updated docstring to reflect the new hard-fail boot policy. - packages/Memory/Sources/Memory/Resources/PLACEHOLDER.txt — deleted - App/AppDelegate.swift installMemory() Reverses Track-D D-1 (75a10be, 2026-05-04): MemoryStore.init failures are now FATAL via Logger.critical + TCCAlertService.presentHardBlock + NSApp.terminate. The graceful-degradation path that kept the app booting with memoryStore=nil hid the entire B-02/B-04/B-05 silent- failure bug class. Aligns with the 2026-05-07 API-first pivot thesis: "no silent failures". `store` is now non-optional throughout installMemory; downstream callers no longer need `if let store = store`. - packages/MCP/Sources/MCP/InProcess/GetMemoryStatsTool.swift (new) + MemoryStatsDispatching protocol + MemoryStats Codable struct (turnsCount, factsTotal, factsActive, sessionsCount, dbFileBytes, vecVersion, sqliteVersion, lastTurnAtUnixMs, lastFactAtUnixMs) + GetMemoryStatsTool conforming to InProcessTool - App/MCP/InProcessMemoryAdapters.swift + MemoryStatsStoreAdapter — bridges the protocol to MemoryStore's public queryRowCount / querySingleString seams and FileManager for the on-disk file size. Read-only, no confirmation. - App/AppDelegate.swift installSelfKnowledgeTools() + Registers GetMemoryStatsTool with MemoryStatsStoreAdapter. - packages/Memory/Tests/MemoryTests/* Retired the JARVIS_VEC0_STUB_PATH XCTSkipUnless guards from 9 tests (vec0 is always available now — no need to env-gate). Rewrote MemoryStoreTests to drop the now-impossible "init throws when vec missing" case and assert facts_vec virtual table creation + vec_version() readback. Added seedTurnRow helper to MemoryStoreApplyOpTests so the now-enforced `facts.source_turn_id REFERENCES turns(id)` foreign key is honored in tests that previously passed when FK enforcement was dormant. Test summary: Memory full suite 91 tests, 10 skipped (env-gated real- Ollama tests), 0 failures. App target builds clean. Substrate verified end-to-end via live launch (vec_version: v0.1.6 logged at boot; get_memory_stats returns actual schema row counts). Pending D-7 carry-forward: ollama pull nomic-embed-text (~270MB) for embedder; user already has qwen2.5-coder:32b-instruct-q8_0 which is a higher-quality variant of the project's hardcoded qwen2.5-coder:32b — small ModelID config delta to follow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MCP
Tool dispatch + child-process spawning. Two library products: JarvisMCP (the dispatcher chain) and JarvisChildSpawn (the FD/env-hardened spawn primitive).
Key public types
JarvisMCP
| Type | Purpose |
|---|---|
MCPClient (actor) |
Registry of MCPServerHandle actors; per-server restart mutex; tool→server lookup |
MCPServerHandle (actor) |
One running helper process; restart task slot; tools/list cache |
ToolRegistry |
Plain-data registry of ToolMetadata (name, server, schema, requiresConfirmation) |
MCPToolDispatcher |
Inner dispatcher (Plan 05-04) — direct call routing |
ConfirmingToolDispatcher |
Outer dispatcher (Plan 05-05) — gates requiresConfirmation: true through broker |
ConfirmationBroker / ConfirmationPresenter |
Awaitable confirmation channel + UI presenter |
ConfirmationOutcome |
.approved / .denied / .timeout |
SanitizeForModel |
Wraps tool results in UntrustedWrapper + applies 8 KB cap |
InProcessTool (protocol) |
Contract for in-process tools (no helper spawn) |
InProcessToolRegistry |
Separate registry for in-process tools |
SearchMemoryTool / ForgetFactTool |
Memory in-process tools (Track D-2). Gated on store + search availability in AppDelegate |
SearchConversationTool |
In-process scaffold (code-present; not yet registered in App/) |
ListAudioDevicesTool / GetActiveAudioRouteTool / GetSelfStateTool / ListCameraDevicesTool |
Phase 10 / Wave-1 self-knowledge tools (74b9ac2). All read-only, no confirmation. App-side dispatcher adapters live in App/MCP/InProcessSelfStateAdapters.swift |
JarvisChildSpawn
| Type | Purpose |
|---|---|
ChildSpawnGate |
Spawn primitive — FD_CLOEXEC on every long-lived parent FD; minimal PATH=/usr/bin:/bin env |
Depends on
AgentCore, Logging. External: modelcontextprotocol/swift-sdk v0.12.0, swift-log.
Used by
App/MCP/MCPRuntimeWiring.swift, App/AppDelegate, packages/Vision (uses JarvisChildSpawn), packages/Memory (in-process tool adapters wired via App/MCP/InProcessMemoryAdapters.swift), App/MCP/InProcessSelfStateAdapters.swift (Phase 10 self-knowledge dispatchers), packages/Harness.
Key invariants / contracts
requiresConfirmation: trueforrun_applescriptat TWO sites (MCPRuntimeWiring.swift+ the harness adapter).scripts/check-applescript-confirmation.shgrep-gates.- Per-server restart mutex. Concurrent callers awaiting a crashed helper share one in-flight restart, never stampede.
ChildSpawnGateenforcesFD_CLOEXECon every long-lived parent FD (replay log, SQLite WAL).- Minimal child env.
PATH=/usr/bin:/binonly. No parent env inheritance. SanitizeForModelrunsUntrustedWrapperon every tool result before the bytes reach the model. Pre-sanitize bytes still go to replay (SEC-07).- Tool result content capped at 8 KB.
ToolResultPacker(inAgentCore) is invoked before model emit.
Tests
85 XCTest. Crash/restart fixtures via MockHelper (a real fixture binary that responds to start/list/call/crash signals), confirmation broker tests, sanitize round-trips, tool-cap-recovery scenarios.
Notable files
Sources/MCP/MCPClient.swift— top-level facadeSources/MCP/ConfirmingToolDispatcher.swift— confirmation-gating outerSources/MCP/SanitizeForModel.swift— UntrustedWrapper + capSources/JarvisChildSpawn/ChildSpawnGate.swift— FD_CLOEXEC gateSources/MCP/InProcess/SearchMemoryTool.swift— in-process memory toolSources/MCP/InProcess/{ListAudioDevicesTool,GetActiveAudioRouteTool,GetSelfStateTool,ListCameraDevicesTool}.swift— Phase 10 / Wave-1 self-knowledge tools