fix(blocker-int-1): replace NoopBusGateway with real MCPBusGatewayAdapter
ConfirmingToolDispatcher emits tool-call events through `BusGateway`. The prior `NoopBusGateway` (AppDelegate.swift:503 + dead struct at :1894-1904) silently dropped every emission, so the HUD's chat-panel ToolCallCard never rendered a Running / Completed transition for any tool dispatch. This is the canonical "test mock and prod stub diverge in the same direction" pattern called out in `.planning/audit-2026-05-03/tests-audit.md` §6: SpyBus in MCPRuntimeWiringTests counted invocations; production shipped with empty bodies — both satisfied the protocol; only the latter was load-bearing in production. The replacement `MCPBusGatewayAdapter` (App/MCPBusGatewayAdapter.swift): - Conforms to `BusGateway`, forwards to `OutboundBatcher.flushAndSend` with `.toolCallStart` / `.toolCallEnd` envelopes. - `updateArgsPreview` re-emits `.toolCallStart` with the same id; the HUD's `upsertToolCall` (webview/packages/hud/src/bus/client.ts) patches the existing card by id, transitioning awaiting-approval → running with the post-approval sanitized args. - `String → UUID` mapping via SHA256-derived RFC 4122 v5-shape UUID, so `emitToolCallStart(id="toolu_01X")` and `emitToolCallEnd(id="toolu_01X")` produce IDENTICAL UUIDs for HUD reconciliation across start/update/end. The dispatcher protocol uses `String` (CR-03 in ConfirmingToolDispatcher.swift — Anthropic toolu_01ABCD… / Ollama free-form, never UUID); the bus protocol uses `UUID` (Phase 2 closed schema). Bridge is one-way; the audit-trail correlation by String id already lives at ReplayingToolResultObserver. - Late binding via `@Sendable () async -> OutboundBatcher?` resolver: the batcher is built in installAgent (step 13), AFTER mcpInstallTask captures the gateway in step 10. The closure resolves it at emit-time; if the batcher is nil (early window before installAgent finishes, or shutdown), emissions drop silently — the replay log path still records the call so audit isn't lost. Tests: App/Tests/AppTests/MCPBusGatewayAdapterTests.swift (+5): - stableUUID determinism (same input → same UUID) - stableUUID distinguishes different ids - emitToolCallStart reaches the sink with right name/argsPreview/id - emitToolCallEnd id matches the start id (reconciliation invariant) - updateArgsPreview re-emits .toolCallStart with same id, updated args - nil batcher drops events silently (no trap) The recording sink + OutboundBatcher path the test exercises is the same path production uses end-to-end through to WebviewBridge.send. The remaining seam (sink → JS-side store) is already covered by RealWKWebViewIntegrationTests + webview/hud streaming.test.tsx D5/D6/D7. App build clean. Bus tests 58/58 (was 57/58 before F-A2-01 fix). MCP tests 85/85 unchanged. All 5 boundary gates green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
6f6617e67b
commit
ad93dac665
4 files changed
+281
-15
No files matched your search
+10
-15
@@ -500,7 +500,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
let bundleURL = Bundle.main.bundleURL
|
||||
mcpInstallTask = Task { @MainActor [weak self] in
|
||||
guard let self = self, let channel = self.orchToReplayChannel else { return }
|
||||
let busAdapter = NoopBusGateway() // CR-02: orchestrator wiring (later plan) replaces with real bus adapter.
|
||||
// BLOCKER-INT-1 fix: real BusGateway that forwards tool-call
|
||||
// events to the HUD via OutboundBatcher. The batcher is
|
||||
// constructed later (step 13 in installAgent) so the adapter
|
||||
// resolves it lazily via a MainActor-isolated closure. Until
|
||||
// the batcher exists, emissions are dropped (the
|
||||
// ReplayingToolResultObserver still records the call so the
|
||||
// audit trail isn't lost).
|
||||
let busAdapter = MCPBusGatewayAdapter(resolveBatcher: { [weak self] in
|
||||
await MainActor.run { [weak self] in self?.outboundBatcher }
|
||||
})
|
||||
do {
|
||||
let runtime = try await MCPRuntimeWiring.build(
|
||||
bundleURL: bundleURL,
|
||||
@@ -1889,20 +1898,6 @@ private final class BridgeNavigationDelegate: NSObject, WKNavigationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Bus gateway placeholder
|
||||
|
||||
/// CR-02 (REVIEW 05): pre-orchestrator no-op BusGateway. Phase 6 / 7
|
||||
/// orchestrator wiring replaces this with a real adapter that translates
|
||||
/// the dispatcher's bus events into `BusOutbound.toolCallStart` /
|
||||
/// `toolCallEnd` cases on the WebviewBridge. Until then, the dispatcher
|
||||
/// chain still composes (BusGateway must be non-nil for some call sites)
|
||||
/// but emits into a sink that drops events on the floor.
|
||||
struct NoopBusGateway: BusGateway {
|
||||
func emitToolCallStart(toolUseId: String, name: String, argsPreview: String) async {}
|
||||
func updateArgsPreview(toolUseId: String, name: String, argsPreview: String) async {}
|
||||
func emitToolCallEnd(toolUseId: String, name: String, ok: Bool, previewOrError: String) async {}
|
||||
}
|
||||
|
||||
// MARK: - Voice subsystem adapters (Plan 06-05)
|
||||
//
|
||||
// These thin adapters bridge the Voice package protocol seams to App-target types.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import Bus // OutboundBatcher, BusOutbound
|
||||
import JarvisMCP // BusGateway
|
||||
|
||||
/// BLOCKER-INT-1 fix: real `BusGateway` implementation that forwards
|
||||
/// `ConfirmingToolDispatcher`'s tool-call events to the HUD via
|
||||
/// `OutboundBatcher`. Replaces the prior `NoopBusGateway` in `AppDelegate.swift`
|
||||
/// (line ~503) which silently dropped every tool-call card.
|
||||
///
|
||||
/// Wire mapping:
|
||||
/// - `emitToolCallStart(toolUseId:name:argsPreview:)` →
|
||||
/// `BusOutbound.toolCallStart(id: stableUUID(toolUseId), name:, argsPreview:)`
|
||||
/// - `updateArgsPreview(toolUseId:name:argsPreview:)` →
|
||||
/// second `.toolCallStart` with the SAME id; the HUD's `upsertToolCall`
|
||||
/// reconciler patches the existing card with the post-approval
|
||||
/// `argsPreview`. Drift the audit calls out in `streaming.test.tsx D5b`
|
||||
/// is preserved (whitespace-insensitive parser on JS side).
|
||||
/// - `emitToolCallEnd(toolUseId:name:ok:previewOrError:)` →
|
||||
/// `BusOutbound.toolCallEnd(id: stableUUID(toolUseId), ok:, previewOrError:)`
|
||||
///
|
||||
/// `BusOutbound.toolCallStart/End` use `UUID` for `id` (Phase 2 closed schema);
|
||||
/// the dispatcher's protocol uses `String` (Anthropic `toolu_01ABCD…` /
|
||||
/// Ollama free-form, never UUID — see CR-03 in
|
||||
/// `ConfirmingToolDispatcher.swift`). We bridge with a deterministic SHA256
|
||||
/// → UUID derivation so:
|
||||
/// 1. `emitToolCallStart(id="toolu_01X")` and `emitToolCallEnd(id="toolu_01X")`
|
||||
/// produce the IDENTICAL `UUID`, allowing the HUD to reconcile by id.
|
||||
/// 2. The mapping is one-way (we never decode UUID back to original id);
|
||||
/// the audit-trail correlation across bus / replay / orchestrator
|
||||
/// already lives at the String layer in `ReplayingToolResultObserver`.
|
||||
/// 3. Two distinct dispatcher String ids hash to two distinct UUIDs with
|
||||
/// negligible collision probability (SHA256 truncated to 128 bits).
|
||||
///
|
||||
/// Late binding: at the time `mcpInstallTask` captures the gateway (step 10
|
||||
/// of `applicationWillFinishLaunching`), `OutboundBatcher` is not yet
|
||||
/// constructed — `installAgent` (step 13) builds it. The gateway uses a
|
||||
/// `@Sendable` resolver closure so `OutboundBatcher?` is read at emit-time,
|
||||
/// not capture-time. Until the batcher exists, emissions are dropped (the
|
||||
/// observer-side replay log still records the call).
|
||||
public actor MCPBusGatewayAdapter: BusGateway {
|
||||
private let resolveBatcher: @Sendable () async -> OutboundBatcher?
|
||||
|
||||
/// `resolveBatcher` returns the live `OutboundBatcher` (or nil if not
|
||||
/// yet constructed / app shutting down). Production path: closure
|
||||
/// hops to MainActor and reads `appDelegate.outboundBatcher`.
|
||||
public init(resolveBatcher: @escaping @Sendable () async -> OutboundBatcher?) {
|
||||
self.resolveBatcher = resolveBatcher
|
||||
}
|
||||
|
||||
// MARK: BusGateway
|
||||
|
||||
public func emitToolCallStart(toolUseId: String, name: String, argsPreview: String) async {
|
||||
guard let batcher = await resolveBatcher() else { return }
|
||||
let uuid = Self.stableUUID(from: toolUseId)
|
||||
try? await batcher.flushAndSend(.toolCallStart(id: uuid, name: name, argsPreview: argsPreview))
|
||||
}
|
||||
|
||||
public func updateArgsPreview(toolUseId: String, name: String, argsPreview: String) async {
|
||||
guard let batcher = await resolveBatcher() else { return }
|
||||
let uuid = Self.stableUUID(from: toolUseId)
|
||||
// Re-emit toolCallStart with same id; HUD upserts the args field
|
||||
// by id (see webview/packages/hud/src/bus/client.ts case
|
||||
// 'toolCallStart' → `store.upsertToolCall(msg.id, …)`).
|
||||
try? await batcher.flushAndSend(.toolCallStart(id: uuid, name: name, argsPreview: argsPreview))
|
||||
}
|
||||
|
||||
public func emitToolCallEnd(toolUseId: String, name: String, ok: Bool, previewOrError: String) async {
|
||||
guard let batcher = await resolveBatcher() else { return }
|
||||
let uuid = Self.stableUUID(from: toolUseId)
|
||||
try? await batcher.flushAndSend(.toolCallEnd(id: uuid, ok: ok, previewOrError: previewOrError))
|
||||
}
|
||||
|
||||
// MARK: - Stable id derivation
|
||||
|
||||
/// Deterministic `String → UUID` derivation: SHA256 of UTF-8 bytes, take
|
||||
/// the first 16 bytes, set the version (5) and variant (RFC 4122)
|
||||
/// bits per RFC 4122 §4.4 so the resulting UUID is well-formed. The
|
||||
/// hash is the load-bearing property — RFC compliance is incidental
|
||||
/// but makes the values valid `UUID(uuidString:)` round-trips.
|
||||
static func stableUUID(from toolUseId: String) -> UUID {
|
||||
let digest = SHA256.hash(data: Data(toolUseId.utf8))
|
||||
var bytes = [UInt8](digest.prefix(16))
|
||||
// RFC 4122 §4.4: set version (top nibble of byte 6) to 5
|
||||
bytes[6] = (bytes[6] & 0x0F) | 0x50
|
||||
// RFC 4122 §4.4: set variant (top two bits of byte 8) to 10
|
||||
bytes[8] = (bytes[8] & 0x3F) | 0x80
|
||||
let tuple = (
|
||||
bytes[0], bytes[1], bytes[2], bytes[3],
|
||||
bytes[4], bytes[5], bytes[6], bytes[7],
|
||||
bytes[8], bytes[9], bytes[10], bytes[11],
|
||||
bytes[12], bytes[13], bytes[14], bytes[15]
|
||||
)
|
||||
return UUID(uuid: tuple)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// MCPBusGatewayAdapterTests.swift
|
||||
//
|
||||
// BLOCKER-INT-1 closure proof: assert that `MCPBusGatewayAdapter` (the
|
||||
// real BusGateway production code; replaces NoopBusGateway at
|
||||
// AppDelegate.swift:503) forwards tool-call events through `OutboundBatcher`
|
||||
// to its `Sink` so the HUD's chat-panel ToolCallCard receives the wire
|
||||
// `BusOutbound.toolCallStart` / `.toolCallEnd` envelopes the JS-side
|
||||
// reconciler in `webview/packages/hud/src/bus/client.ts` consumes.
|
||||
//
|
||||
// The previous `NoopBusGateway` was the canonical "test mock and prod
|
||||
// stub diverge in the same direction" pattern called out in
|
||||
// `.planning/audit-2026-05-03/tests-audit.md` §6: SpyBus in
|
||||
// MCPRuntimeWiringTests.swift counted invocations + production used
|
||||
// NoopBusGateway with empty bodies. Both satisfied the protocol; only
|
||||
// the latter shipped — and silently dropped every tool-call card.
|
||||
//
|
||||
// These tests assert the production-side behavior of the replacement:
|
||||
// the events DO reach the sink, with stable ids that allow the HUD to
|
||||
// reconcile start / update / end by id.
|
||||
|
||||
import XCTest
|
||||
import Foundation
|
||||
import Bus
|
||||
@testable import Jarvis // MCPBusGatewayAdapter is internal to the App target
|
||||
|
||||
@MainActor
|
||||
final class MCPBusGatewayAdapterTests: XCTestCase {
|
||||
|
||||
/// Recording `OutboundBatcher.Sink` — captures every `BusOutbound`
|
||||
/// envelope that reaches the JS-call boundary. `@MainActor` matches
|
||||
/// the protocol's `sendRaw` isolation.
|
||||
final class RecordingSink: OutboundBatcher.Sink, @unchecked Sendable {
|
||||
var sent: [BusOutbound] = []
|
||||
@MainActor func sendRaw(_ msg: BusOutbound) async throws {
|
||||
sent.append(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Stable id derivation
|
||||
|
||||
/// `stableUUID(from:)` is deterministic — same input string always
|
||||
/// produces the same UUID. Load-bearing for HUD reconciliation:
|
||||
/// `emitToolCallStart(id="toolu_X")` followed by
|
||||
/// `emitToolCallEnd(id="toolu_X")` MUST produce matching UUIDs so
|
||||
/// the JS-side `upsertToolCall` patches the existing card rather
|
||||
/// than orphaning the start emission.
|
||||
func test_stableUUID_isDeterministic() {
|
||||
let a = MCPBusGatewayAdapter.stableUUID(from: "toolu_01ABCDEFGH")
|
||||
let b = MCPBusGatewayAdapter.stableUUID(from: "toolu_01ABCDEFGH")
|
||||
XCTAssertEqual(a, b, "same input must produce same UUID across calls")
|
||||
}
|
||||
|
||||
/// Distinct dispatcher String ids must produce distinct UUIDs so two
|
||||
/// concurrent tool calls don't collide in the HUD.
|
||||
func test_stableUUID_distinguishesDifferentIds() {
|
||||
let a = MCPBusGatewayAdapter.stableUUID(from: "toolu_01ABC")
|
||||
let b = MCPBusGatewayAdapter.stableUUID(from: "toolu_01XYZ")
|
||||
XCTAssertNotEqual(a, b)
|
||||
}
|
||||
|
||||
// MARK: - Event flow: gateway → batcher → sink
|
||||
|
||||
/// `emitToolCallStart` produces exactly one `.toolCallStart` envelope at
|
||||
/// the sink, carrying the deterministic UUID + the name + argsPreview.
|
||||
/// This is the base case BLOCKER-INT-1 was breaking: NoopBusGateway
|
||||
/// dropped this entirely.
|
||||
func test_emitToolCallStart_reachesSink() async throws {
|
||||
let sink = RecordingSink()
|
||||
let batcher = OutboundBatcher(sink: sink, windowMillis: 5)
|
||||
let adapter = MCPBusGatewayAdapter(resolveBatcher: { batcher })
|
||||
|
||||
await adapter.emitToolCallStart(
|
||||
toolUseId: "toolu_01TEST",
|
||||
name: "run_applescript",
|
||||
argsPreview: "{\"awaitingApproval\":true}"
|
||||
)
|
||||
|
||||
XCTAssertEqual(sink.sent.count, 1, "exactly one envelope at the sink")
|
||||
guard case let .toolCallStart(id, name, argsPreview) = sink.sent[0] else {
|
||||
XCTFail("expected .toolCallStart, got \(sink.sent[0])")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(id, MCPBusGatewayAdapter.stableUUID(from: "toolu_01TEST"))
|
||||
XCTAssertEqual(name, "run_applescript")
|
||||
XCTAssertEqual(argsPreview, "{\"awaitingApproval\":true}")
|
||||
}
|
||||
|
||||
/// `emitToolCallEnd` produces a `.toolCallEnd` whose UUID matches the
|
||||
/// UUID `emitToolCallStart` would have produced for the same dispatcher
|
||||
/// String id. HUD reconciliation depends on this equality.
|
||||
func test_emitToolCallEnd_idMatchesStart() async throws {
|
||||
let sink = RecordingSink()
|
||||
let batcher = OutboundBatcher(sink: sink, windowMillis: 5)
|
||||
let adapter = MCPBusGatewayAdapter(resolveBatcher: { batcher })
|
||||
|
||||
await adapter.emitToolCallStart(toolUseId: "toolu_01ID", name: "get_time", argsPreview: "{}")
|
||||
await adapter.emitToolCallEnd(
|
||||
toolUseId: "toolu_01ID",
|
||||
name: "get_time",
|
||||
ok: true,
|
||||
previewOrError: "\"2026-05-04T00:00:00Z\""
|
||||
)
|
||||
|
||||
XCTAssertEqual(sink.sent.count, 2)
|
||||
guard
|
||||
case let .toolCallStart(startId, _, _) = sink.sent[0],
|
||||
case let .toolCallEnd(endId, ok, previewOrError) = sink.sent[1]
|
||||
else {
|
||||
XCTFail("expected toolCallStart followed by toolCallEnd")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(startId, endId, "HUD reconciliation requires matching id across start/end")
|
||||
XCTAssertTrue(ok)
|
||||
XCTAssertEqual(previewOrError, "\"2026-05-04T00:00:00Z\"")
|
||||
}
|
||||
|
||||
/// `updateArgsPreview` re-emits a `.toolCallStart` carrying the
|
||||
/// post-approval sanitized args. The HUD's `upsertToolCall` patches
|
||||
/// the existing card by id (see streaming.test.tsx D5 / D5b in
|
||||
/// `webview/packages/hud/`), transitioning awaiting-approval → running
|
||||
/// while the args render the real post-approval payload.
|
||||
func test_updateArgsPreview_reEmitsStartWithSameId() async throws {
|
||||
let sink = RecordingSink()
|
||||
let batcher = OutboundBatcher(sink: sink, windowMillis: 5)
|
||||
let adapter = MCPBusGatewayAdapter(resolveBatcher: { batcher })
|
||||
|
||||
await adapter.emitToolCallStart(
|
||||
toolUseId: "toolu_01APPLE",
|
||||
name: "run_applescript",
|
||||
argsPreview: "{\"awaitingApproval\":true}"
|
||||
)
|
||||
await adapter.updateArgsPreview(
|
||||
toolUseId: "toolu_01APPLE",
|
||||
name: "run_applescript",
|
||||
argsPreview: "{\"script\":\"return 1\"}"
|
||||
)
|
||||
|
||||
XCTAssertEqual(sink.sent.count, 2)
|
||||
guard
|
||||
case let .toolCallStart(id1, _, args1) = sink.sent[0],
|
||||
case let .toolCallStart(id2, _, args2) = sink.sent[1]
|
||||
else {
|
||||
XCTFail("expected two .toolCallStart emissions")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(id1, id2, "update must carry same id so HUD patches the existing card")
|
||||
XCTAssertEqual(args1, "{\"awaitingApproval\":true}")
|
||||
XCTAssertEqual(args2, "{\"script\":\"return 1\"}")
|
||||
}
|
||||
|
||||
/// Late binding: when the resolver returns `nil` (batcher not yet
|
||||
/// constructed in installAgent step 13), the adapter drops the
|
||||
/// emission silently rather than crashing. The audit-trail
|
||||
/// correlation lives in `ReplayingToolResultObserver` (separate path).
|
||||
func test_nilBatcher_dropsEventSilently() async {
|
||||
let adapter = MCPBusGatewayAdapter(resolveBatcher: { nil })
|
||||
// No assertion fails — the call must not throw or trap.
|
||||
await adapter.emitToolCallStart(toolUseId: "toolu_01EARLY", name: "x", argsPreview: "{}")
|
||||
await adapter.updateArgsPreview(toolUseId: "toolu_01EARLY", name: "x", argsPreview: "{}")
|
||||
await adapter.emitToolCallEnd(
|
||||
toolUseId: "toolu_01EARLY",
|
||||
name: "x",
|
||||
ok: false,
|
||||
previewOrError: "early"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
66B4B219EF4EC3EBDE5D1A4B /* AppDelegateWiringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 909BFE54FAE9CA6B878D5625 /* AppDelegateWiringTests.swift */; };
|
||||
6A2D9DE01C3C53094A820DDF /* JarvisEntitlementProbeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36AC239C99B913A4767C0EE8 /* JarvisEntitlementProbeTests.swift */; };
|
||||
6AD395C3C083197537B81713 /* AgentCore in Frameworks */ = {isa = PBXBuildFile; productRef = 73CDBE39012C4ECD47291B0E /* AgentCore */; };
|
||||
70D09A556654E11B140AFBD4 /* MCPBusGatewayAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6639284829C6DB4F69ED47F7 /* MCPBusGatewayAdapterTests.swift */; };
|
||||
7270A6A54B0DC1510EB9C56A /* WizardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A33A1960A6404CD790036C46 /* WizardView.swift */; };
|
||||
781E23F05F4EBBD3D3CB8E3F /* AppleScriptRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F80410112AAA66BF53BBC28 /* AppleScriptRunner.swift */; };
|
||||
78AA6D0286502DC0F1F4304D /* HudState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF7206DB98EC02734975536 /* HudState.swift */; };
|
||||
@@ -76,6 +77,7 @@
|
||||
C9F3B34208084769106C4B07 /* HudStateCoordinatorBusWiringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46CB8B7693CEA625D7331C3E /* HudStateCoordinatorBusWiringTests.swift */; };
|
||||
CCA51A998B128C9C03ED0662 /* AppDelegateFrameAttachAdapters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB47EBED48DF63272C6D77F /* AppDelegateFrameAttachAdapters.swift */; };
|
||||
CD22CB0656265A2623BA7FD0 /* AnthropicKeyValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39DD53A4E2CCED58C85B63FC /* AnthropicKeyValidatorTests.swift */; };
|
||||
CD2C5779101FE0384611ECCC /* MCPBusGatewayAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C42ACA7552D565D9F198399 /* MCPBusGatewayAdapter.swift */; };
|
||||
CFBEF67D687A1227C9C2A6BD /* HUDBannerCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DF77D7944DD23250A9AA97 /* HUDBannerCoordinatorTests.swift */; };
|
||||
CFFC4AB8D77907C477B1E947 /* JarvisMCP in Frameworks */ = {isa = PBXBuildFile; productRef = DE970D2B3CA1AB0097A120D3 /* JarvisMCP */; };
|
||||
D0F74EFBDD6B9FDD0FC15A80 /* JarvisMCP in Frameworks */ = {isa = PBXBuildFile; productRef = 0AA20E2847670C06A4E77816 /* JarvisMCP */; };
|
||||
@@ -148,6 +150,7 @@
|
||||
2468510EF34D9D8D56F5E3B9 /* MenuBarIconControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIconControllerTests.swift; sourceTree = "<group>"; };
|
||||
2857325F1F38B436FFA8AD8B /* mcp-time.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "mcp-time.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
288436CCFD821C16F6C5995E /* Vision */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Vision; path = packages/Vision; sourceTree = SOURCE_ROOT; };
|
||||
2C42ACA7552D565D9F198399 /* MCPBusGatewayAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCPBusGatewayAdapter.swift; sourceTree = "<group>"; };
|
||||
2E9C7A0B14B3AE5C9100CACF /* Config */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Config; path = packages/Config; sourceTree = SOURCE_ROOT; };
|
||||
3527AF52C1E81BAC4EF70766 /* JarvisHUDPanelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JarvisHUDPanelTests.swift; sourceTree = "<group>"; };
|
||||
3577EF13419964F207DF4A37 /* VoiceOrchestratorAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceOrchestratorAdapterTests.swift; sourceTree = "<group>"; };
|
||||
@@ -171,6 +174,7 @@
|
||||
604976A6CA41878D67AF7B71 /* CAAnimationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAAnimationFactory.swift; sourceTree = "<group>"; };
|
||||
639B5F162E847B0CFC679E30 /* Bus */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Bus; path = packages/Bus; sourceTree = SOURCE_ROOT; };
|
||||
6569DC91C2ACEB0E1396E1B8 /* HudStateBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HudStateBridge.swift; sourceTree = "<group>"; };
|
||||
6639284829C6DB4F69ED47F7 /* MCPBusGatewayAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCPBusGatewayAdapterTests.swift; sourceTree = "<group>"; };
|
||||
6784683F2262D8AFA439668D /* JarvisAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JarvisAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
718CFD37BC47D00448B86C8D /* VoiceTTSAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceTTSAdapter.swift; sourceTree = "<group>"; };
|
||||
74FC22D7D34AB5E6061959D7 /* WizardStageHotkeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardStageHotkeyView.swift; sourceTree = "<group>"; };
|
||||
@@ -297,6 +301,7 @@
|
||||
C1517F151F3BA96803143692 /* AppDelegate.swift */,
|
||||
467AE2D19D58325D227E2F89 /* Assets.xcassets */,
|
||||
90D238B5CF4BD07EB47F84F0 /* JarvisApp.swift */,
|
||||
2C42ACA7552D565D9F198399 /* MCPBusGatewayAdapter.swift */,
|
||||
F76520C565169D7547EE9608 /* HUD */,
|
||||
EE585F62F46C659EDFC48DE7 /* MCP */,
|
||||
35E7CF3C8F80086AD447480E /* MenuBar */,
|
||||
@@ -358,6 +363,7 @@
|
||||
8B7E85856B679CE0788D38DB /* HudStateCoordinatorTests.swift */,
|
||||
510B6A716A10A8B7CEEC1D3F /* HudStateEnumTests.swift */,
|
||||
3527AF52C1E81BAC4EF70766 /* JarvisHUDPanelTests.swift */,
|
||||
6639284829C6DB4F69ED47F7 /* MCPBusGatewayAdapterTests.swift */,
|
||||
0B5B16BDAC02919861AA4B0B /* MCPRuntimeWiringTests.swift */,
|
||||
2468510EF34D9D8D56F5E3B9 /* MenuBarIconControllerTests.swift */,
|
||||
3577EF13419964F207DF4A37 /* VoiceOrchestratorAdapterTests.swift */,
|
||||
@@ -1258,6 +1264,7 @@
|
||||
8E4590089A7955545E2146D3 /* HudStateCoordinatorTests.swift in Sources */,
|
||||
BBD8BBC82E0D3C57E0DB4ED0 /* HudStateEnumTests.swift in Sources */,
|
||||
461A5FACC5A3C2025FB0A233 /* JarvisHUDPanelTests.swift in Sources */,
|
||||
70D09A556654E11B140AFBD4 /* MCPBusGatewayAdapterTests.swift in Sources */,
|
||||
5AD556F5095852328E72BE36 /* MCPRuntimeWiringTests.swift in Sources */,
|
||||
5B5175F8D07766B3394DF19C /* MenuBarIconControllerTests.swift in Sources */,
|
||||
B5416071D18E6C124365A8EC /* VoiceOrchestratorAdapterTests.swift in Sources */,
|
||||
@@ -1299,6 +1306,7 @@
|
||||
896984D88C412595C0F6F97E /* HudStateIntent.swift in Sources */,
|
||||
526BDF4D6BC5714809B2E937 /* JarvisApp.swift in Sources */,
|
||||
BEFC509502C7940A21001F9F /* JarvisHUDPanel.swift in Sources */,
|
||||
CD2C5779101FE0384611ECCC /* MCPBusGatewayAdapter.swift in Sources */,
|
||||
8EF7109838AD91E95506BD1B /* MCPRuntimeWiring.swift in Sources */,
|
||||
A20E24D0AB73FFBBE4AB9B9B /* MenuBarContextMenu.swift in Sources */,
|
||||
9933987292A1035BB048D0B9 /* MenuBarIconController.swift in Sources */,
|
||||
|
||||
Reference in new issue
Block a user