Merge branch 'dev' into canary
This commit is contained in:
+25
-25
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"originHash" : "997519e7bf6b3bb52f6f766bab10b0bf950c6beb140aa897dc50ba77eaaa5801",
|
||||
"originHash" : "fa70224ebd92e3bfa55fbdf5bbea5d2a51a1f6402e805a443a323509fbc905ed",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swift-server/async-http-client.git",
|
||||
"state" : {
|
||||
"revision" : "7744c2a035c68ec14726c709f031835e3e30bde1",
|
||||
"version" : "1.34.0"
|
||||
"revision" : "4603a8036d921ea999fadb742931546c341f4bd7",
|
||||
"version" : "1.35.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -15,8 +15,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/groue/GRDB.swift.git",
|
||||
"state" : {
|
||||
"revision" : "9ed8c8457e00ff9c7aedb3bf213f20a2cfdf509e",
|
||||
"version" : "7.11.0"
|
||||
"revision" : "b83108d10f42680d78f23fe4d4d80fc88dab3212",
|
||||
"version" : "7.11.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24,8 +24,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift-2.git",
|
||||
"state" : {
|
||||
"revision" : "21fe69ab7ce0e87ac089534733c52f037e74a3eb",
|
||||
"version" : "2.4.1"
|
||||
"revision" : "28cdd63ef88583ddc67d7bb179eab46fab465ce9",
|
||||
"version" : "2.4.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -33,8 +33,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift-nio-transport.git",
|
||||
"state" : {
|
||||
"revision" : "e7d463749f9037b047dcd6da4e633718ddc432b8",
|
||||
"version" : "2.8.0"
|
||||
"revision" : "2ca31f06658ed288a2560e23ad649acbb3d6b3a3",
|
||||
"version" : "2.9.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -42,8 +42,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "b05885fa9bdd88f1eab2e7162f1ee81340b0da33",
|
||||
"version" : "2.4.0"
|
||||
"revision" : "176c5a434fd76f6f479848d1a8f7d44967534168",
|
||||
"version" : "2.4.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -51,8 +51,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
||||
"state" : {
|
||||
"revision" : "d46d456107feacc80711b21847b82b07bd9fb46e",
|
||||
"version" : "2.9.3"
|
||||
"revision" : "b6496a74a087257ef5e6da1c5b29a447a60f5bd7",
|
||||
"version" : "2.9.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -87,8 +87,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-async-algorithms.git",
|
||||
"state" : {
|
||||
"revision" : "d0b4a06d0f173a2f3be27d3ea21b3c3aa18db440",
|
||||
"version" : "1.1.4"
|
||||
"revision" : "3da39bbc4e687d4192af7c9cf4eab805745a0b9c",
|
||||
"version" : "1.1.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -105,8 +105,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-certificates.git",
|
||||
"state" : {
|
||||
"revision" : "bde8ca32a096825dfce37467137c903418c1893d",
|
||||
"version" : "1.19.1"
|
||||
"revision" : "89fbc3714264cce8db8e4ec51b64e01c3e28c6c5",
|
||||
"version" : "1.19.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -168,8 +168,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log.git",
|
||||
"state" : {
|
||||
"revision" : "92448c359f00ebe36ae97d3bd9086f13c7692b5a",
|
||||
"version" : "1.13.2"
|
||||
"revision" : "a878e7f8f46cfc0e1125e565b5c08e7d5272dc9a",
|
||||
"version" : "1.14.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -177,8 +177,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio.git",
|
||||
"state" : {
|
||||
"revision" : "77b84ac2cd2ac9e4ac67d19f045fd5b434f56967",
|
||||
"version" : "2.101.0"
|
||||
"revision" : "cd3e1152083706d77b223fb29110e590efcc70c0",
|
||||
"version" : "2.101.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -186,8 +186,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-extras.git",
|
||||
"state" : {
|
||||
"revision" : "d2eeec0339074034f11a040a74aa2a341a2c4506",
|
||||
"version" : "1.34.1"
|
||||
"revision" : "88a51340f59cf181ebde888bd1b749296b3ec029",
|
||||
"version" : "1.34.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -258,8 +258,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system.git",
|
||||
"state" : {
|
||||
"revision" : "7502b711c92a17741fa625d722b0ccbd595d8ed1",
|
||||
"version" : "1.7.2"
|
||||
"revision" : "b5544ba79a70a0cb3563e75bf26dc198d6b40ed3",
|
||||
"version" : "1.7.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -640,6 +640,12 @@ final class HostConnection {
|
||||
case .macPairResolved(let deviceID, _):
|
||||
// Answered on this Mac or another device (or timed out) — dismiss the prompt.
|
||||
callbacks.macPairResolved(deviceID)
|
||||
case .intelligenceRequest, .credentialNeeded, .credentialUpdate:
|
||||
// Antimatter runner verbs (docs/ANTIMATTER_RUNNER.md §5–6): a runner host delegating
|
||||
// intelligence work or asking for / mirroring sealed credentials. Inert here until the
|
||||
// phone-side executor/vault land — and a host only sends these to clients that
|
||||
// advertised the matching `WireClientCapabilities`, which this app doesn't yet.
|
||||
break
|
||||
case .wireError(let error):
|
||||
if error.code == .channelMismatch {
|
||||
connectivity = .failed(error.message)
|
||||
|
||||
@@ -1672,10 +1672,13 @@ final class RemoteStore: ObservableObject {
|
||||
// Never originated from here (connection-internal, or handled by dedicated loops).
|
||||
// `requestPairingCode`/`cancelPairingCode` are sent straight to the chosen host by
|
||||
// `requestPairingCode()`/`cancelPairingCode()`, not through this owner-routing switch.
|
||||
// The runner verbs (intelligence results, credential mesh — ANTIMATTER_RUNNER §5–6) will
|
||||
// ride their own executor/vault loops when the phone side lands; nothing routes them here.
|
||||
case .hello, .ping, .listPeers, .addressUpdate, .meshRoster,
|
||||
.registerLiveActivity, .endLiveActivity, .registerPushToStartToken, .setForeground,
|
||||
.transferOffer, .transferChunk, .transferCommit, .transferCancel, .fetchTranscript,
|
||||
.requestPairingCode, .cancelPairingCode, .respondMacPair:
|
||||
.requestPairingCode, .cancelPairingCode, .respondMacPair,
|
||||
.intelligenceResult, .credentialManifest, .credentialProvision:
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1862,7 +1865,9 @@ final class RemoteStore: ObservableObject {
|
||||
.transferOffer, .transferChunk, .transferCommit, .transferCancel,
|
||||
// "Add a device" mint is handled directly against a live host, not via demoHandle;
|
||||
// the demo path short-circuits in `requestPairingCode()` with a stand-in code.
|
||||
.requestPairingCode, .cancelPairingCode, .respondMacPair:
|
||||
.requestPairingCode, .cancelPairingCode, .respondMacPair,
|
||||
// Antimatter runner verbs (ANTIMATTER_RUNNER §5–6) — demo has no runner host.
|
||||
.intelligenceResult, .credentialManifest, .credentialProvision:
|
||||
break // passive / already handled by the seeded fixtures (demo has no mesh peers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,10 @@ enum SubagentRunState {
|
||||
|
||||
init(group: ToolGroup) {
|
||||
if !group.finished { self = .running }
|
||||
else if group.isError { self = .failed }
|
||||
// A `Task` subagent surfaces failure as an error result. A `nucleic_subagent` worker
|
||||
// instead returns a *non-error* result whose JSON envelope carries `{"ok": false}` (or
|
||||
// `{"denied": true}`) in-band, so read that too — otherwise a failed worker reads "Done".
|
||||
else if group.isError || Subagent.workerFailed(group) { self = .failed }
|
||||
else { self = .done }
|
||||
}
|
||||
|
||||
@@ -234,9 +237,14 @@ enum Subagent {
|
||||
return (raw?.isEmpty == false) ? raw : nil
|
||||
}
|
||||
|
||||
/// The one-line task the parent handed the subagent (`description`), falling back to the
|
||||
/// full `prompt`, then a generic label.
|
||||
/// The one-line task the parent handed the subagent: a `nucleic_subagent` worker's `task`
|
||||
/// label, or a `Task` subagent's `description`, falling back to the full `prompt`, then a
|
||||
/// generic label.
|
||||
static func taskLabel(_ group: ToolGroup) -> String {
|
||||
if let task = group.input["task"]?.stringValue?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines), !task.isEmpty {
|
||||
return task
|
||||
}
|
||||
if let description = group.input["description"]?.stringValue?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines), !description.isEmpty {
|
||||
return description
|
||||
@@ -258,9 +266,48 @@ enum Subagent {
|
||||
/// The subagent's returned report, or nil while it's still working / reported nothing.
|
||||
static func report(_ group: ToolGroup) -> String? {
|
||||
guard let result = group.result else { return nil }
|
||||
// A `nucleic_subagent` worker returns a JSON envelope — surface its actual `output` (or
|
||||
// failure `message`) rather than a one-line gist of the raw `{ok,session_id,…}` object. A
|
||||
// `Task` subagent returns prose, which falls through to the gist unchanged.
|
||||
if let worker = workerText(result.resultText), !worker.isEmpty { return worker }
|
||||
let text = result.compactSummary
|
||||
return text.isEmpty ? nil : text
|
||||
}
|
||||
|
||||
/// Whether a `nucleic_subagent` worker result reports failure — a `{"ok": false}` completion
|
||||
/// or a `{"denied": true}` refusal, both delivered as non-error tool results. False for any
|
||||
/// other tool (a `Task` subagent, whose failure is an error result handled separately).
|
||||
static func workerFailed(_ group: ToolGroup) -> Bool {
|
||||
guard let result = group.result,
|
||||
let envelope = workerEnvelope(result.resultText) else { return false }
|
||||
if let denied = envelope["denied"] as? Bool, denied { return true }
|
||||
if let ok = envelope["ok"] as? Bool { return !ok }
|
||||
return false
|
||||
}
|
||||
|
||||
/// The human-facing text of a `nucleic_subagent` worker envelope — the worker's `output` on
|
||||
/// success, or its `message` on failure/denial — or nil when `text` isn't such an envelope
|
||||
/// (so a `Task` subagent's prose report falls through unchanged).
|
||||
private static func workerText(_ text: String) -> String? {
|
||||
guard let envelope = workerEnvelope(text) else { return nil }
|
||||
if let denied = envelope["denied"] as? Bool, denied {
|
||||
return envelope["message"] as? String ?? ""
|
||||
}
|
||||
guard let ok = envelope["ok"] as? Bool else { return nil }
|
||||
return ok ? (envelope["output"] as? String ?? "")
|
||||
: (envelope["message"] as? String ?? "")
|
||||
}
|
||||
|
||||
/// Parses a `nucleic_subagent` result string into its JSON envelope, gated on the envelope's
|
||||
/// signature keys (`ok` / `denied`) so an arbitrary JSON-shaped tool result isn't mistaken for
|
||||
/// one. Nil for anything that isn't a worker envelope.
|
||||
private static func workerEnvelope(_ text: String) -> [String: Any]? {
|
||||
guard let data = text.data(using: .utf8),
|
||||
let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
object["ok"] != nil || object["denied"] != nil
|
||||
else { return nil }
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
/// A small leading status glyph for a subagent — a gold spinner while it works, a green check or
|
||||
@@ -531,7 +578,7 @@ enum ToolGlyph {
|
||||
case "Edit", "Write", "MultiEdit", "NotebookEdit": return "pencil"
|
||||
case "Grep", "Glob", "Search": return "magnifyingglass"
|
||||
case "WebFetch", "WebSearch": return "globe"
|
||||
case "Task", "Agent": return "person.2"
|
||||
case "Task", "Agent", ToolGroup.orchestraSubagentToolName: return "person.2"
|
||||
case "TodoWrite": return "checklist"
|
||||
case "AskUserQuestion": return "questionmark.bubble"
|
||||
case HostCommandSummary.hostExecToolName: return "desktopcomputer"
|
||||
|
||||
@@ -76,8 +76,16 @@ struct ToolGroup: Equatable {
|
||||
|
||||
struct FilePatch: Equatable { let path: String; let change: FileChange.ChangeKind }
|
||||
|
||||
/// Subagent orchestration (`Task`/`Agent`) gets the gold card treatment, like the Mac.
|
||||
var isOrchestration: Bool { name == "Task" || name == "Agent" }
|
||||
/// Nucleic's own Orchestra worker spawn — the qualified `nucleic_subagent` MCP wire name.
|
||||
/// Unlike a `Task` subagent, a worker runs as its own observed session, so it carries no
|
||||
/// nested `children` here; its card shows the final output it returned instead.
|
||||
static let orchestraSubagentToolName = "mcp__nucleic__nucleic_subagent"
|
||||
|
||||
/// Subagent orchestration — a `Task`/`Agent` spawn or a `nucleic_subagent` worker — gets the
|
||||
/// gold card treatment, like the Mac.
|
||||
var isOrchestration: Bool {
|
||||
name == "Task" || name == "Agent" || name == Self.orchestraSubagentToolName
|
||||
}
|
||||
var isAskUserQuestion: Bool { name == "AskUserQuestion" }
|
||||
|
||||
/// The literal shell command a `Bash` call ran, if any (nil for every other tool). Used to
|
||||
|
||||
Reference in New Issue
Block a user