@astrosheep/keiyaku 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -8
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
- package/build/src/akuma/abort.d.ts +2 -0
- package/build/src/akuma/abort.js +50 -0
- package/build/src/akuma/akuma.d.ts +50 -33
- package/build/src/akuma/akuma.js +175 -127
- package/build/src/akuma/archetype.d.ts +14 -5
- package/build/src/akuma/archetype.js +85 -39
- package/build/src/akuma/body.d.ts +3 -5
- package/build/src/akuma/body.js +490 -183
- package/build/src/akuma/coordinate.d.ts +9 -0
- package/build/src/akuma/coordinate.js +22 -0
- package/build/src/akuma/heart/facts.d.ts +88 -45
- package/build/src/akuma/heart/facts.js +19 -8
- package/build/src/akuma/heart/index.d.ts +64 -55
- package/build/src/akuma/heart/index.js +172 -252
- package/build/src/akuma/heart/rows.d.ts +38 -83
- package/build/src/akuma/heart/rows.js +117 -155
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +61 -22
- package/build/src/akuma/heart/soul.d.ts +8 -0
- package/build/src/akuma/heart/soul.js +136 -0
- package/build/src/akuma/heart/storage.d.ts +47 -0
- package/build/src/akuma/heart/storage.js +224 -0
- package/build/src/akuma/heart/tells.d.ts +12 -0
- package/build/src/akuma/heart/tells.js +79 -0
- package/build/src/akuma/heart/timeline.d.ts +15 -0
- package/build/src/akuma/heart/timeline.js +86 -0
- package/build/src/akuma/identity.d.ts +2 -2
- package/build/src/akuma/identity.js +9 -9
- package/build/src/akuma/index.d.ts +10 -3
- package/build/src/akuma/index.js +7 -1
- package/build/src/akuma/projection.d.ts +176 -0
- package/build/src/akuma/projection.js +256 -0
- package/build/src/akuma/provider-recipe.d.ts +24 -0
- package/build/src/akuma/provider-recipe.js +100 -0
- package/build/src/akuma/provider.d.ts +67 -13
- package/build/src/akuma/provider.js +172 -105
- package/build/src/akuma/providers/acp/core.d.ts +19 -0
- package/build/src/akuma/providers/acp/core.js +159 -0
- package/build/src/akuma/providers/acp/events.d.ts +20 -0
- package/build/src/akuma/providers/acp/events.js +73 -0
- package/build/src/akuma/providers/acp/index.d.ts +12 -0
- package/build/src/akuma/providers/acp/index.js +94 -0
- package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
- package/build/src/akuma/providers/claude/events.js +238 -0
- package/build/src/akuma/providers/claude/index.d.ts +47 -0
- package/build/src/akuma/providers/claude/index.js +279 -0
- package/build/src/akuma/providers/claude/input.d.ts +13 -0
- package/build/src/akuma/providers/claude/input.js +109 -0
- package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
- package/build/src/akuma/providers/codex-app-server/events.js +15 -12
- package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
- package/build/src/akuma/providers/codex-app-server/index.js +77 -33
- package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
- package/build/src/akuma/providers/grok-build/index.js +70 -0
- package/build/src/akuma/providers/index.d.ts +5 -3
- package/build/src/akuma/providers/index.js +35 -56
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
- package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
- package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
- package/build/src/akuma/providers/pi/events.d.ts +33 -0
- package/build/src/akuma/providers/pi/events.js +166 -0
- package/build/src/akuma/providers/pi/index.d.ts +11 -0
- package/build/src/akuma/providers/pi/index.js +191 -0
- package/build/src/akuma/publication.d.ts +3 -3
- package/build/src/akuma/publication.js +28 -40
- package/build/src/akuma/requests.d.ts +9 -6
- package/build/src/akuma/requests.js +95 -65
- package/build/src/alias/index.d.ts +4 -3
- package/build/src/alias/index.js +12 -13
- package/build/src/body/decode.js +7 -1
- package/build/src/body/region.d.ts +2 -0
- package/build/src/body/region.js +77 -7
- package/build/src/body/render.js +2 -1
- package/build/src/body/shape.js +7 -1
- package/build/src/body/verification.js +19 -4
- package/build/src/cli/accepted.d.ts +18 -12
- package/build/src/cli/accepted.js +73 -34
- package/build/src/cli/actor.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
- package/build/src/cli/commands/akuma-invoke.js +101 -52
- package/build/src/cli/commands/akuma.d.ts +27 -9
- package/build/src/cli/commands/akuma.js +151 -80
- package/build/src/cli/commands/contract.d.ts +155 -0
- package/build/src/cli/commands/contract.js +20 -0
- package/build/src/cli/commands/install.js +2 -0
- package/build/src/cli/commands/task-invoke.d.ts +19 -4
- package/build/src/cli/commands/task-invoke.js +65 -17
- package/build/src/cli/commands/task-query.d.ts +5 -0
- package/build/src/cli/commands/task-query.js +247 -0
- package/build/src/cli/commands/task.d.ts +3 -1
- package/build/src/cli/commands/task.js +52 -14
- package/build/src/cli/coordinates.d.ts +18 -0
- package/build/src/cli/coordinates.js +120 -0
- package/build/src/cli/draft.d.ts +8 -0
- package/build/src/cli/draft.js +96 -0
- package/build/src/cli/invoke.d.ts +9 -3
- package/build/src/cli/invoke.js +287 -108
- package/build/src/cli/main.js +54 -7
- package/build/src/cli/parse.d.ts +21 -117
- package/build/src/cli/parse.js +79 -84
- package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
- package/build/src/cli/render/akuma-tool-command.js +144 -0
- package/build/src/cli/render/akuma-tool.d.ts +4 -2
- package/build/src/cli/render/akuma-tool.js +35 -5
- package/build/src/cli/render/akuma.d.ts +3 -1
- package/build/src/cli/render/akuma.js +247 -212
- package/build/src/cli/render/audit.d.ts +3 -0
- package/build/src/cli/render/audit.js +104 -0
- package/build/src/cli/render/catalog.d.ts +2 -0
- package/build/src/cli/render/catalog.js +16 -0
- package/build/src/cli/render/contract.d.ts +3 -3
- package/build/src/cli/render/contract.js +200 -54
- package/build/src/cli/render/kanshi.js +272 -129
- package/build/src/cli/render/receipt.d.ts +19 -0
- package/build/src/cli/render/receipt.js +106 -0
- package/build/src/cli/render/refusal.d.ts +16 -2
- package/build/src/cli/render/refusal.js +88 -3
- package/build/src/cli/render/region.d.ts +2 -0
- package/build/src/cli/render/region.js +17 -0
- package/build/src/cli/render/task.d.ts +2 -1
- package/build/src/cli/render/task.js +223 -60
- package/build/src/cli/render/terminal.d.ts +20 -0
- package/build/src/cli/render/terminal.js +121 -0
- package/build/src/cli/render/text.js +11 -3
- package/build/src/cli/result.d.ts +126 -19
- package/build/src/cli/usage.d.ts +1 -0
- package/build/src/cli/usage.js +3 -0
- package/build/src/contract-worktree.d.ts +44 -0
- package/build/src/contract-worktree.js +251 -0
- package/build/src/coordination/durable-file.d.ts +5 -1
- package/build/src/coordination/durable-file.js +57 -10
- package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
- package/build/src/coordination/sqlite-transaction-lock.js +22 -5
- package/build/src/core/facts/codec.js +23 -4
- package/build/src/core/facts/eligibility.d.ts +1 -10
- package/build/src/core/facts/eligibility.js +0 -49
- package/build/src/core/facts/fold.js +1 -6
- package/build/src/core/facts/gate.d.ts +1 -0
- package/build/src/core/facts/gate.js +1 -0
- package/build/src/core/facts/observation.d.ts +3 -0
- package/build/src/core/facts/observation.js +12 -0
- package/build/src/core/facts/types.d.ts +10 -3
- package/build/src/core/subject.js +2 -2
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +3 -18
- package/build/src/core/verbs/bind.js +2 -16
- package/build/src/core/verbs/deliver.d.ts +1 -1
- package/build/src/core/verbs/deliver.js +14 -4
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +7 -9
- package/build/src/dispatch/index.d.ts +5 -3
- package/build/src/dispatch/index.js +30 -21
- package/build/src/duration.d.ts +10 -0
- package/build/src/duration.js +22 -0
- package/build/src/git/admission.d.ts +2 -2
- package/build/src/git/admission.js +33 -23
- package/build/src/git/hooks.d.ts +15 -0
- package/build/src/git/hooks.js +70 -18
- package/build/src/git/identity.d.ts +5 -1
- package/build/src/git/identity.js +23 -4
- package/build/src/git/integration.d.ts +48 -0
- package/build/src/git/integration.js +250 -0
- package/build/src/git/observe.d.ts +29 -15
- package/build/src/git/observe.js +185 -76
- package/build/src/git/read-observation.d.ts +41 -0
- package/build/src/git/read-observation.js +361 -0
- package/build/src/git/reconcile.d.ts +17 -5
- package/build/src/git/reconcile.js +305 -109
- package/build/src/git/repository.d.ts +41 -18
- package/build/src/git/repository.js +220 -114
- package/build/src/git/scratch.d.ts +17 -0
- package/build/src/git/scratch.js +73 -0
- package/build/src/git/target-placement.d.ts +92 -0
- package/build/src/git/target-placement.js +375 -0
- package/build/src/git/tender.d.ts +49 -0
- package/build/src/git/tender.js +63 -0
- package/build/src/git/terminal-seal.d.ts +24 -0
- package/build/src/git/terminal-seal.js +89 -0
- package/build/src/git/tree.d.ts +2 -2
- package/build/src/git/tree.js +4 -0
- package/build/src/git/workspace.d.ts +53 -0
- package/build/src/git/workspace.js +139 -0
- package/build/src/identity/selector.d.ts +6 -0
- package/build/src/identity/selector.js +19 -0
- package/build/src/index.d.ts +4 -2
- package/build/src/index.js +2 -1
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +7 -3
- package/build/src/kanshi/read.js +245 -45
- package/build/src/kanshi/report.d.ts +81 -6
- package/build/src/kanshi/select.d.ts +5 -0
- package/build/src/kanshi/select.js +33 -1
- package/build/src/library/address.d.ts +57 -0
- package/build/src/library/address.js +165 -0
- package/build/src/library/akuma-creation.d.ts +16 -7
- package/build/src/library/akuma-creation.js +96 -22
- package/build/src/library/audit.d.ts +18 -0
- package/build/src/library/audit.js +44 -0
- package/build/src/library/bind.d.ts +21 -0
- package/build/src/library/bind.js +56 -0
- package/build/src/library/catalog.d.ts +58 -0
- package/build/src/library/catalog.js +69 -0
- package/build/src/library/configuration.d.ts +6 -15
- package/build/src/library/configuration.js +14 -17
- package/build/src/library/contract.d.ts +23 -49
- package/build/src/library/contract.js +171 -185
- package/build/src/library/delivery.d.ts +24 -0
- package/build/src/library/delivery.js +21 -0
- package/build/src/library/fleet.d.ts +65 -0
- package/build/src/library/fleet.js +153 -0
- package/build/src/library/input.d.ts +3 -0
- package/build/src/library/input.js +19 -0
- package/build/src/library/keiyaku.d.ts +18 -3
- package/build/src/library/keiyaku.js +12 -1
- package/build/src/library/mutation.d.ts +39 -0
- package/build/src/library/mutation.js +37 -0
- package/build/src/library/reconcile.d.ts +29 -0
- package/build/src/library/reconcile.js +179 -0
- package/build/src/library/refusal.d.ts +21 -0
- package/build/src/library/refusal.js +29 -0
- package/build/src/library/region.d.ts +14 -1
- package/build/src/library/region.js +18 -4
- package/build/src/library/repo.d.ts +4 -13
- package/build/src/library/repo.js +14 -24
- package/build/src/protocol/attempt.d.ts +16 -3
- package/build/src/protocol/attempt.js +41 -10
- package/build/src/protocol/bind.d.ts +9 -2
- package/build/src/protocol/bind.js +48 -40
- package/build/src/protocol/intent.d.ts +57 -20
- package/build/src/protocol/intent.js +55 -79
- package/build/src/protocol/operations.d.ts +152 -43
- package/build/src/protocol/operations.js +482 -166
- package/build/src/protocol/outcome.d.ts +14 -3
- package/build/src/protocol/outcome.js +11 -3
- package/build/src/protocol/placement.d.ts +41 -0
- package/build/src/protocol/placement.js +147 -0
- package/build/src/protocol/read/documents.d.ts +2 -2
- package/build/src/protocol/read/documents.js +4 -4
- package/build/src/protocol/read/status.d.ts +18 -5
- package/build/src/protocol/read/status.js +73 -13
- package/build/src/protocol/run.d.ts +49 -7
- package/build/src/protocol/run.js +53 -25
- package/build/src/runtime/proc/line-rpc.d.ts +4 -8
- package/build/src/runtime/proc/line-rpc.js +22 -28
- package/build/src/runtime/proc/run.d.ts +23 -21
- package/build/src/runtime/proc/run.js +123 -89
- package/build/src/runtime/proc/stdio.d.ts +18 -0
- package/build/src/runtime/proc/stdio.js +58 -0
- package/build/src/settings.d.ts +7 -1
- package/build/src/settings.js +32 -17
- package/build/src/settlement/fence.d.ts +5 -0
- package/build/src/settlement/fence.js +14 -0
- package/build/src/settlement/holder.d.ts +35 -4
- package/build/src/settlement/holder.js +127 -28
- package/build/src/settlement/settle.d.ts +9 -1
- package/build/src/settlement/settle.js +74 -37
- package/build/src/task/board.d.ts +23 -5
- package/build/src/task/board.js +17 -10
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +18 -10
- package/build/src/task/context.d.ts +3 -3
- package/build/src/task/context.js +15 -20
- package/build/src/task/document.d.ts +2 -1
- package/build/src/task/document.js +15 -5
- package/build/src/task/index.d.ts +27 -15
- package/build/src/task/index.js +57 -35
- package/build/src/task/operations.d.ts +38 -13
- package/build/src/task/operations.js +80 -46
- package/build/src/task/query.d.ts +67 -0
- package/build/src/task/query.js +279 -0
- package/build/src/task/store.d.ts +5 -7
- package/build/src/task/store.js +43 -28
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/execution.d.ts +51 -0
- package/build/src/verification/execution.js +108 -0
- package/build/src/workspace-place.d.ts +41 -0
- package/build/src/workspace-place.js +386 -0
- package/build/src/world.d.ts +24 -0
- package/build/src/world.js +136 -0
- package/package.json +8 -4
- package/build/src/akuma/activity.d.ts +0 -66
- package/build/src/akuma/activity.js +0 -139
- package/build/src/akuma/providers/claude.js +0 -305
- package/build/src/context-root.d.ts +0 -4
- package/build/src/context-root.js +0 -15
- package/build/src/git/delivery.d.ts +0 -20
- package/build/src/git/delivery.js +0 -109
- package/build/src/git/verification.d.ts +0 -14
- package/build/src/git/verification.js +0 -56
- package/build/src/protocol/read/audit.d.ts +0 -25
- package/build/src/protocol/read/audit.js +0 -39
- package/build/src/verification/producer.d.ts +0 -23
- package/build/src/verification/producer.js +0 -54
|
@@ -28,7 +28,29 @@ function turnId(result) {
|
|
|
28
28
|
throw new Error("codex app-server did not return a turn id");
|
|
29
29
|
return id;
|
|
30
30
|
}
|
|
31
|
+
async function steerTurn(server, state, tell) {
|
|
32
|
+
if (state.settled)
|
|
33
|
+
return { kind: "turn-ended" };
|
|
34
|
+
if (state.threadId === undefined || state.turnId === undefined)
|
|
35
|
+
throw new Error("codex app-server turn is not admitted");
|
|
36
|
+
const request = server.request("turn/steer", {
|
|
37
|
+
threadId: state.threadId,
|
|
38
|
+
expectedTurnId: state.turnId,
|
|
39
|
+
input: [{ type: "text", text: tell.text }],
|
|
40
|
+
clientUserMessageId: tell.id,
|
|
41
|
+
});
|
|
42
|
+
const response = codexObject(await request);
|
|
43
|
+
const accepted = codexText(response?.turnId);
|
|
44
|
+
if (accepted === undefined)
|
|
45
|
+
throw new Error("codex app-server steer did not return a turn id");
|
|
46
|
+
if (accepted !== state.turnId)
|
|
47
|
+
throw new Error("codex app-server steer acknowledged a different turn");
|
|
48
|
+
return { kind: "accepted", fence: `${accepted}:${tell.id}` };
|
|
49
|
+
}
|
|
31
50
|
function sandbox(cwd, options, requests) {
|
|
51
|
+
if (options.readonly === true) {
|
|
52
|
+
return { type: "readOnly", networkAccess: options.network === "enabled" };
|
|
53
|
+
}
|
|
32
54
|
return {
|
|
33
55
|
type: "workspaceWrite",
|
|
34
56
|
writableRoots: [cwd, ...(requests === undefined ? [] : [requests.dir])],
|
|
@@ -46,16 +68,20 @@ async function admitTurn(server, input, state, events, config) {
|
|
|
46
68
|
...(input.options.systemPrompt === undefined || input.options.systemPrompt.length === 0
|
|
47
69
|
? {} : { developerInstructions: input.options.systemPrompt }),
|
|
48
70
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
if (input.session.kind === "fresh") {
|
|
72
|
+
state.threadId = threadId(await server.request("thread/start", threadParams));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
state.threadId = threadId(await server.request("thread/resume", {
|
|
76
|
+
threadId: input.session.coordinate.sessionId,
|
|
53
77
|
...threadParams,
|
|
54
|
-
}), input.session.sessionId);
|
|
78
|
+
}), input.session.coordinate.sessionId);
|
|
79
|
+
}
|
|
55
80
|
events.emit({ type: "session", coordinate: { sessionId: state.threadId } });
|
|
56
81
|
state.turnId = turnId(await server.request("turn/start", {
|
|
57
82
|
threadId: state.threadId,
|
|
58
|
-
input: [{ type: "text", text: input.
|
|
83
|
+
input: [{ type: "text", text: [input.body, ...input.launchTells.map((tell) => tell.text)]
|
|
84
|
+
.filter((part) => part.length > 0).join("\n\n") }],
|
|
59
85
|
...(input.options.model === undefined ? {} : { model: input.options.model }),
|
|
60
86
|
...(input.options.effort === undefined ? {} : { effort: input.options.effort }),
|
|
61
87
|
approvalPolicy: "never",
|
|
@@ -63,6 +89,8 @@ async function admitTurn(server, input, state, events, config) {
|
|
|
63
89
|
}));
|
|
64
90
|
}
|
|
65
91
|
async function forkCodex(execution, input) {
|
|
92
|
+
if (!("sessionId" in input.session))
|
|
93
|
+
throw new Error("Codex app-server fork requires sessionId");
|
|
66
94
|
const server = new LineRpcProcess({
|
|
67
95
|
argv: [execution.executable ?? "codex", "app-server", "--listen", "stdio://"],
|
|
68
96
|
cwd: input.cwd,
|
|
@@ -82,22 +110,15 @@ async function forkCodex(execution, input) {
|
|
|
82
110
|
await server.close();
|
|
83
111
|
}
|
|
84
112
|
}
|
|
85
|
-
async function abortTurn(server, state,
|
|
86
|
-
if (!state.settled
|
|
87
|
-
|
|
88
|
-
await server.request("turn/interrupt", { threadId: state.threadId, turnId: state.turnId });
|
|
89
|
-
await completion;
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
finish({ kind: "failed", diagnostic: diagnostic(error) });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else if (!state.settled) {
|
|
96
|
-
finish({ kind: "failed", diagnostic: "codex app-server aborted before turn admission" });
|
|
97
|
-
}
|
|
113
|
+
async function abortTurn(server, state, settle) {
|
|
114
|
+
if (!state.settled)
|
|
115
|
+
settle({ kind: "failed", diagnostic: "codex app-server interrupted" });
|
|
98
116
|
await server.close(true);
|
|
99
117
|
}
|
|
100
118
|
async function startCodex(execution, input) {
|
|
119
|
+
const signal = input.signal ?? new AbortController().signal;
|
|
120
|
+
if (input.session.kind === "resume" && !("sessionId" in input.session.coordinate))
|
|
121
|
+
throw new Error("Codex app-server resume requires sessionId");
|
|
101
122
|
const events = new AgentEventChannel();
|
|
102
123
|
const server = new LineRpcProcess({
|
|
103
124
|
argv: [execution.executable ?? "codex", "app-server", "--listen", "stdio://"],
|
|
@@ -108,16 +129,28 @@ async function startCodex(execution, input) {
|
|
|
108
129
|
...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
|
|
109
130
|
} }),
|
|
110
131
|
});
|
|
111
|
-
const state = {
|
|
132
|
+
const state = { settled: false, tools: new Map() };
|
|
112
133
|
let settle;
|
|
113
134
|
const completion = new Promise((resolve) => { settle = resolve; });
|
|
135
|
+
let terminal;
|
|
114
136
|
const finish = (result) => {
|
|
115
|
-
if (
|
|
137
|
+
if (terminal !== undefined)
|
|
116
138
|
return;
|
|
139
|
+
terminal = result;
|
|
117
140
|
state.settled = true;
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
void server.endInputAndDrain().then(() => {
|
|
142
|
+
events.end();
|
|
143
|
+
settle(result);
|
|
144
|
+
}, () => {
|
|
145
|
+
events.end();
|
|
146
|
+
settle(result);
|
|
147
|
+
});
|
|
120
148
|
};
|
|
149
|
+
server.onNotification((notification) => {
|
|
150
|
+
const result = codexNotificationResult(notification, state, events);
|
|
151
|
+
if (result !== undefined)
|
|
152
|
+
finish(result);
|
|
153
|
+
});
|
|
121
154
|
server.onExit(({ code, signal, stderr }) => finish({
|
|
122
155
|
kind: "failed",
|
|
123
156
|
diagnostic: stderr || `codex app-server exited before completion (${code ?? signal ?? "unknown"})`,
|
|
@@ -126,21 +159,30 @@ async function startCodex(execution, input) {
|
|
|
126
159
|
kind: "failed",
|
|
127
160
|
diagnostic: `codex app-server made forbidden interactive request ${request.method}`,
|
|
128
161
|
}));
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (result !== undefined)
|
|
132
|
-
finish(result);
|
|
133
|
-
});
|
|
162
|
+
const abortSetup = () => { void server.close(true); };
|
|
163
|
+
signal.addEventListener("abort", abortSetup, { once: true });
|
|
134
164
|
try {
|
|
165
|
+
signal.throwIfAborted();
|
|
135
166
|
await admitTurn(server, input, state, events, execution.config);
|
|
136
167
|
}
|
|
137
168
|
catch (error) {
|
|
169
|
+
if (signal.aborted) {
|
|
170
|
+
await server.close(true);
|
|
171
|
+
throw signal.reason;
|
|
172
|
+
}
|
|
138
173
|
finish({ kind: "failed", diagnostic: diagnostic(error) });
|
|
139
174
|
}
|
|
175
|
+
finally {
|
|
176
|
+
signal.removeEventListener("abort", abortSetup);
|
|
177
|
+
}
|
|
178
|
+
if (state.turnId === undefined)
|
|
179
|
+
throw new Error("codex app-server did not admit a turn");
|
|
140
180
|
return {
|
|
181
|
+
admission: { fence: state.turnId },
|
|
141
182
|
events,
|
|
142
183
|
completion,
|
|
143
|
-
abort: () => abortTurn(server, state,
|
|
184
|
+
abort: () => abortTurn(server, state, finish),
|
|
185
|
+
tell: (tell) => steerTurn(server, state, tell),
|
|
144
186
|
};
|
|
145
187
|
}
|
|
146
188
|
export function createCodexAppServerProvider(input = "codex") {
|
|
@@ -150,13 +192,15 @@ export function createCodexAppServerProvider(input = "codex") {
|
|
|
150
192
|
return {
|
|
151
193
|
confinement: ({ cwd }) => ({ kind: "declared", writableRoots: [cwd] }),
|
|
152
194
|
admitOptions(options) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
195
|
+
return {
|
|
196
|
+
kind: "admitted",
|
|
197
|
+
options: Object.freeze({ ...options }),
|
|
198
|
+
...(options.readonly === undefined ? {} : { readonly: { enforcement: "native" } }),
|
|
199
|
+
};
|
|
157
200
|
},
|
|
158
201
|
fork: (input) => forkCodex(execution, input),
|
|
159
202
|
start: (input) => startCodex(execution, input),
|
|
203
|
+
resume: (input) => startCodex(execution, input),
|
|
160
204
|
};
|
|
161
205
|
}
|
|
162
206
|
export const codexAppServerProvider = createCodexAppServerProvider();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderAdapter } from "../../provider.js";
|
|
2
|
+
import type { ProviderExecution } from "../../provider-recipe.js";
|
|
3
|
+
import { type AcpDependencies } from "../acp/core.js";
|
|
4
|
+
export declare function createGrokBuildProvider(execution: ProviderExecution, dependencies?: AcpDependencies): ProviderAdapter;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { startAcpSession } from "../acp/core.js";
|
|
2
|
+
const INTERJECT_METHOD = "x.ai/interject";
|
|
3
|
+
function optionAdmission(options) {
|
|
4
|
+
if (options.network !== undefined) {
|
|
5
|
+
return { kind: "refused", diagnostic: "Grok Build does not support the network option" };
|
|
6
|
+
}
|
|
7
|
+
if (options.systemPrompt !== undefined && options.systemPrompt.length > 0) {
|
|
8
|
+
return { kind: "refused", diagnostic: "Grok Build does not support the systemPrompt option" };
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
kind: "admitted",
|
|
12
|
+
options,
|
|
13
|
+
...(options.readonly === undefined ? {} : {
|
|
14
|
+
readonly: {
|
|
15
|
+
enforcement: "none",
|
|
16
|
+
diagnostic: "Grok Build cannot remove task-surface mutation capabilities",
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function argv(execution, options) {
|
|
22
|
+
if (execution.executable === undefined)
|
|
23
|
+
throw new Error("Grok Build provider execution requires executable");
|
|
24
|
+
const values = [execution.executable, "agent", "--always-approve"];
|
|
25
|
+
if (options.model !== undefined)
|
|
26
|
+
values.push("--model", options.model);
|
|
27
|
+
if (options.effort !== undefined)
|
|
28
|
+
values.push("--reasoning-effort", options.effort);
|
|
29
|
+
values.push("stdio");
|
|
30
|
+
return values;
|
|
31
|
+
}
|
|
32
|
+
function withInterject(live) {
|
|
33
|
+
return {
|
|
34
|
+
...live.session,
|
|
35
|
+
tell: async (tell) => {
|
|
36
|
+
if (!live.open())
|
|
37
|
+
return { kind: "turn-ended" };
|
|
38
|
+
const response = await live.agent.request(INTERJECT_METHOD, {
|
|
39
|
+
sessionId: live.sessionId,
|
|
40
|
+
text: tell.text,
|
|
41
|
+
interjectionId: tell.id,
|
|
42
|
+
});
|
|
43
|
+
if (!live.open())
|
|
44
|
+
return { kind: "turn-ended" };
|
|
45
|
+
if (response === null || typeof response !== "object" || response.status !== "queued") {
|
|
46
|
+
throw new Error("Grok Build interject did not return queued");
|
|
47
|
+
}
|
|
48
|
+
return { kind: "accepted", fence: tell.id };
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function createGrokBuildProvider(execution, dependencies = {}) {
|
|
53
|
+
if (execution.executable === undefined)
|
|
54
|
+
throw new TypeError("Grok Build provider execution requires executable");
|
|
55
|
+
if (execution.config !== undefined)
|
|
56
|
+
throw new TypeError("Grok Build provider does not support execution config");
|
|
57
|
+
const drive = async (input) => {
|
|
58
|
+
const launch = {
|
|
59
|
+
argv: argv(execution, input.options),
|
|
60
|
+
...(execution.env === undefined ? {} : { env: execution.env }),
|
|
61
|
+
};
|
|
62
|
+
return withInterject(await startAcpSession(launch, input, dependencies));
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
confinement: () => ({ kind: "unconfined" }),
|
|
66
|
+
admitOptions: optionAdmission,
|
|
67
|
+
start: async (input) => await drive(input),
|
|
68
|
+
resume: async (input) => await drive(input),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ProviderAdapter } from "../provider.js";
|
|
2
|
-
import type
|
|
3
|
-
export declare function
|
|
4
|
-
|
|
2
|
+
import { type ProviderExecution } from "../provider-recipe.js";
|
|
3
|
+
export declare function resolveProviderExecution(input: unknown): Readonly<{
|
|
4
|
+
execution: ProviderExecution;
|
|
5
|
+
adapter: ProviderAdapter;
|
|
6
|
+
}>;
|
|
@@ -1,65 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeProviderRecipe } from "../provider-recipe.js";
|
|
2
|
+
import { createAcpProvider, decodeAcpConfig } from "./acp/index.js";
|
|
3
|
+
import { claudeProvider, createClaudeProvider } from "./claude/index.js";
|
|
2
4
|
import { createCodexAppServerProvider } from "./codex-app-server/index.js";
|
|
5
|
+
import { createGrokBuildProvider } from "./grok-build/index.js";
|
|
6
|
+
import { createOpencodeProvider } from "./opencode-sdk/index.js";
|
|
7
|
+
import { createPiProvider } from "./pi/index.js";
|
|
3
8
|
const PROVIDERS = {
|
|
4
9
|
claude: claudeProvider,
|
|
5
10
|
};
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
function snapshot(value) {
|
|
11
|
-
if (Array.isArray(value))
|
|
12
|
-
return Object.freeze(value.map(snapshot));
|
|
13
|
-
const object = record(value);
|
|
14
|
-
if (object === null)
|
|
15
|
-
return value;
|
|
16
|
-
return Object.freeze(Object.fromEntries(Object.entries(object).map(([key, item]) => [key, snapshot(item)])));
|
|
17
|
-
}
|
|
18
|
-
function optionalText(value, field) {
|
|
19
|
-
const selected = value[field];
|
|
20
|
-
if (selected === undefined)
|
|
21
|
-
return undefined;
|
|
22
|
-
if (typeof selected !== "string" || selected.trim().length === 0) {
|
|
23
|
-
throw new TypeError(`provider execution ${field} must be a nonblank string`);
|
|
24
|
-
}
|
|
25
|
-
return selected;
|
|
26
|
-
}
|
|
27
|
-
export function decodeProviderExecution(input) {
|
|
28
|
-
const value = record(input);
|
|
29
|
-
if (value === null)
|
|
30
|
-
throw new TypeError("provider execution must be an object");
|
|
31
|
-
const allowed = ["config", "env", "executable", "kind", "name"];
|
|
32
|
-
const unknown = Object.keys(value).find((key) => !allowed.includes(key));
|
|
33
|
-
if (unknown !== undefined)
|
|
34
|
-
throw new TypeError(`provider execution has unknown field ${unknown}`);
|
|
35
|
-
if (typeof value.name !== "string" || value.name.trim().length === 0) {
|
|
36
|
-
throw new TypeError("provider execution name must be a nonblank string");
|
|
37
|
-
}
|
|
38
|
-
if (value.kind !== "claude-agent-sdk" && value.kind !== "codex-app-server") {
|
|
39
|
-
throw new TypeError("provider execution has unknown kind");
|
|
40
|
-
}
|
|
41
|
-
const executable = optionalText(value, "executable");
|
|
42
|
-
const config = value.config === undefined ? undefined : record(value.config);
|
|
43
|
-
if (config === null)
|
|
44
|
-
throw new TypeError("provider execution config must be an object");
|
|
45
|
-
if (value.kind === "claude-agent-sdk" && config !== undefined) {
|
|
11
|
+
function providerConfig(execution) {
|
|
12
|
+
const { config, kind } = execution;
|
|
13
|
+
if (kind === "claude-agent-sdk" && config !== undefined)
|
|
46
14
|
throw new TypeError("provider execution config is unsupported by claude-agent-sdk");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw new TypeError("provider execution env must contain only string values");
|
|
53
|
-
}
|
|
54
|
-
return Object.freeze({
|
|
55
|
-
name: value.name,
|
|
56
|
-
kind: value.kind,
|
|
57
|
-
...(executable === undefined ? {} : { executable }),
|
|
58
|
-
...(config === undefined ? {} : { config: snapshot(config) }),
|
|
59
|
-
...(env === undefined ? {} : { env: Object.freeze({ ...env }) }),
|
|
60
|
-
});
|
|
15
|
+
if (kind === "opencode-sdk" && config !== undefined)
|
|
16
|
+
throw new TypeError("provider execution config is unsupported by opencode-sdk");
|
|
17
|
+
if (kind === "acp")
|
|
18
|
+
return decodeAcpConfig(config);
|
|
19
|
+
return config;
|
|
61
20
|
}
|
|
62
|
-
|
|
21
|
+
function adapterFor(execution) {
|
|
22
|
+
if (execution.kind === "acp")
|
|
23
|
+
return createAcpProvider(execution);
|
|
63
24
|
if (execution.kind === "claude-agent-sdk") {
|
|
64
25
|
return execution.executable === undefined && execution.env === undefined
|
|
65
26
|
? PROVIDERS.claude
|
|
@@ -67,5 +28,23 @@ export function providerNamed(execution) {
|
|
|
67
28
|
}
|
|
68
29
|
if (execution.kind === "codex-app-server")
|
|
69
30
|
return createCodexAppServerProvider(execution);
|
|
31
|
+
if (execution.kind === "grok-build")
|
|
32
|
+
return createGrokBuildProvider(execution);
|
|
33
|
+
if (execution.kind === "opencode-sdk")
|
|
34
|
+
return createOpencodeProvider(execution);
|
|
35
|
+
if (execution.kind === "pi")
|
|
36
|
+
return createPiProvider(execution);
|
|
70
37
|
throw new TypeError(`unknown Akuma provider kind ${execution.kind}`);
|
|
71
38
|
}
|
|
39
|
+
export function resolveProviderExecution(input) {
|
|
40
|
+
const decoded = decodeProviderRecipe(input);
|
|
41
|
+
const config = providerConfig(decoded);
|
|
42
|
+
const execution = Object.freeze({
|
|
43
|
+
name: decoded.name,
|
|
44
|
+
kind: decoded.kind,
|
|
45
|
+
...(decoded.executable === undefined ? {} : { executable: decoded.executable }),
|
|
46
|
+
...(config === undefined ? {} : { config }),
|
|
47
|
+
...(decoded.env === undefined ? {} : { env: decoded.env }),
|
|
48
|
+
});
|
|
49
|
+
return Object.freeze({ execution, adapter: adapterFor(execution) });
|
|
50
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type AgentEvent, type ToolCall } from "../../provider.js";
|
|
2
|
+
type Emitter = {
|
|
3
|
+
emit(event: AgentEvent): void;
|
|
4
|
+
};
|
|
5
|
+
type ObservedTool = Readonly<{
|
|
6
|
+
name: string;
|
|
7
|
+
call: ToolCall;
|
|
8
|
+
}>;
|
|
9
|
+
type State = {
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
assistantParts: Set<string>;
|
|
12
|
+
reasoningParts: Set<string>;
|
|
13
|
+
tools: Map<string, ObservedTool>;
|
|
14
|
+
completedTools: Set<string>;
|
|
15
|
+
failure?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const OPENCODE_EVENT_DISPOSITIONS: {
|
|
18
|
+
readonly "server.instance.disposed": "dropped";
|
|
19
|
+
readonly "installation.updated": "dropped";
|
|
20
|
+
readonly "installation.update-available": "dropped";
|
|
21
|
+
readonly "lsp.client.diagnostics": "dropped";
|
|
22
|
+
readonly "lsp.updated": "dropped";
|
|
23
|
+
readonly "message.updated": "mapped";
|
|
24
|
+
readonly "message.removed": "dropped";
|
|
25
|
+
readonly "message.part.updated": "mapped";
|
|
26
|
+
readonly "message.part.removed": "dropped";
|
|
27
|
+
readonly "permission.updated": "dropped";
|
|
28
|
+
readonly "permission.replied": "dropped";
|
|
29
|
+
readonly "session.status": "dropped";
|
|
30
|
+
readonly "session.idle": "dropped";
|
|
31
|
+
readonly "session.compacted": "dropped";
|
|
32
|
+
readonly "file.edited": "dropped";
|
|
33
|
+
readonly "todo.updated": "mapped";
|
|
34
|
+
readonly "command.executed": "dropped";
|
|
35
|
+
readonly "session.created": "dropped";
|
|
36
|
+
readonly "session.updated": "dropped";
|
|
37
|
+
readonly "session.deleted": "dropped";
|
|
38
|
+
readonly "session.diff": "dropped";
|
|
39
|
+
readonly "session.error": "mapped";
|
|
40
|
+
readonly "file.watcher.updated": "dropped";
|
|
41
|
+
readonly "vcs.branch.updated": "dropped";
|
|
42
|
+
readonly "tui.prompt.append": "dropped";
|
|
43
|
+
readonly "tui.command.execute": "dropped";
|
|
44
|
+
readonly "tui.toast.show": "dropped";
|
|
45
|
+
readonly "pty.created": "dropped";
|
|
46
|
+
readonly "pty.updated": "dropped";
|
|
47
|
+
readonly "pty.exited": "dropped";
|
|
48
|
+
readonly "pty.deleted": "dropped";
|
|
49
|
+
readonly "server.connected": "dropped";
|
|
50
|
+
};
|
|
51
|
+
export declare function mapEvent(value: unknown, events: Emitter, state: State): void;
|
|
52
|
+
export type EventState = State;
|
|
53
|
+
export declare function createEventState(sessionId?: string): State;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { noteEvent } from "../../provider.js";
|
|
2
|
+
export const OPENCODE_EVENT_DISPOSITIONS = {
|
|
3
|
+
"server.instance.disposed": "dropped",
|
|
4
|
+
"installation.updated": "dropped",
|
|
5
|
+
"installation.update-available": "dropped",
|
|
6
|
+
"lsp.client.diagnostics": "dropped",
|
|
7
|
+
"lsp.updated": "dropped",
|
|
8
|
+
"message.updated": "mapped",
|
|
9
|
+
"message.removed": "dropped",
|
|
10
|
+
"message.part.updated": "mapped",
|
|
11
|
+
"message.part.removed": "dropped",
|
|
12
|
+
"permission.updated": "dropped",
|
|
13
|
+
"permission.replied": "dropped",
|
|
14
|
+
"session.status": "dropped",
|
|
15
|
+
"session.idle": "dropped",
|
|
16
|
+
"session.compacted": "dropped",
|
|
17
|
+
"file.edited": "dropped",
|
|
18
|
+
"todo.updated": "mapped",
|
|
19
|
+
"command.executed": "dropped",
|
|
20
|
+
"session.created": "dropped",
|
|
21
|
+
"session.updated": "dropped",
|
|
22
|
+
"session.deleted": "dropped",
|
|
23
|
+
"session.diff": "dropped",
|
|
24
|
+
"session.error": "mapped",
|
|
25
|
+
"file.watcher.updated": "dropped",
|
|
26
|
+
"vcs.branch.updated": "dropped",
|
|
27
|
+
"tui.prompt.append": "dropped",
|
|
28
|
+
"tui.command.execute": "dropped",
|
|
29
|
+
"tui.toast.show": "dropped",
|
|
30
|
+
"pty.created": "dropped",
|
|
31
|
+
"pty.updated": "dropped",
|
|
32
|
+
"pty.exited": "dropped",
|
|
33
|
+
"pty.deleted": "dropped",
|
|
34
|
+
"server.connected": "dropped",
|
|
35
|
+
};
|
|
36
|
+
// OpenCode may emit these server-side metadata events before the generated
|
|
37
|
+
// SDK union catches up. They are all non-narrative and therefore remain drop
|
|
38
|
+
// dispositions; genuinely new execution events still use the unknown arm.
|
|
39
|
+
const RUNTIME_DROPPED_EVENT_KINDS = new Set([
|
|
40
|
+
"plugin.added",
|
|
41
|
+
"catalog.updated",
|
|
42
|
+
"reference.updated",
|
|
43
|
+
"integration.updated",
|
|
44
|
+
"server.heartbeat",
|
|
45
|
+
"message.part.delta",
|
|
46
|
+
]);
|
|
47
|
+
function object(value) {
|
|
48
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
49
|
+
? value : undefined;
|
|
50
|
+
}
|
|
51
|
+
function text(value) {
|
|
52
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
53
|
+
}
|
|
54
|
+
function diagnostic(value) {
|
|
55
|
+
const data = object(value);
|
|
56
|
+
const nested = object(data?.data);
|
|
57
|
+
return text(nested?.message) ?? text(data?.message) ?? "OpenCode session failed";
|
|
58
|
+
}
|
|
59
|
+
function positiveLine(value) {
|
|
60
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 ? value : undefined;
|
|
61
|
+
}
|
|
62
|
+
function runCall(name, value) {
|
|
63
|
+
return name === "bash" || name === "shell"
|
|
64
|
+
? { kind: "run", command: text(value?.command) ?? name }
|
|
65
|
+
: undefined;
|
|
66
|
+
}
|
|
67
|
+
function readCall(name, value) {
|
|
68
|
+
if (name !== "read")
|
|
69
|
+
return undefined;
|
|
70
|
+
const path = text(value?.filePath) ?? text(value?.path);
|
|
71
|
+
if (path === undefined)
|
|
72
|
+
return undefined;
|
|
73
|
+
const offset = positiveLine(value?.offset);
|
|
74
|
+
const limit = positiveLine(value?.limit);
|
|
75
|
+
return {
|
|
76
|
+
kind: "read",
|
|
77
|
+
path,
|
|
78
|
+
...(offset === undefined ? {} : { offset }),
|
|
79
|
+
...(limit === undefined ? {} : { limit }),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function searchCall(name, value) {
|
|
83
|
+
if (name !== "grep" && name !== "glob" && name !== "search")
|
|
84
|
+
return undefined;
|
|
85
|
+
const query = text(value?.pattern) ?? text(value?.query);
|
|
86
|
+
if (query === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
const path = text(value?.path) ?? text(value?.filePath);
|
|
89
|
+
const glob = text(value?.glob);
|
|
90
|
+
const scope = name === "glob" ? "files" : "content";
|
|
91
|
+
return {
|
|
92
|
+
kind: "search",
|
|
93
|
+
query,
|
|
94
|
+
scope,
|
|
95
|
+
...(path === undefined ? {} : { path }),
|
|
96
|
+
...(scope === "content" && glob !== undefined ? { glob } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function callFor(name, input) {
|
|
100
|
+
const value = object(input);
|
|
101
|
+
const lower = name.toLowerCase();
|
|
102
|
+
if (lower === "bash" || lower === "shell")
|
|
103
|
+
return runCall(lower, value);
|
|
104
|
+
if (lower === "read")
|
|
105
|
+
return readCall(lower, value);
|
|
106
|
+
if (lower === "grep" || lower === "glob" || lower === "search")
|
|
107
|
+
return searchCall(lower, value);
|
|
108
|
+
return { kind: "other", display: name };
|
|
109
|
+
}
|
|
110
|
+
function belongs(part, state) {
|
|
111
|
+
return state.sessionId === undefined || part.sessionID === state.sessionId;
|
|
112
|
+
}
|
|
113
|
+
function mapTextPart(part, events, state, thought) {
|
|
114
|
+
const id = text(part.id);
|
|
115
|
+
const time = object(part.time);
|
|
116
|
+
const content = text(part.text);
|
|
117
|
+
const seen = thought ? state.reasoningParts : state.assistantParts;
|
|
118
|
+
if (!belongs(part, state) || id === undefined || content === undefined || time?.end === undefined || seen.has(id))
|
|
119
|
+
return;
|
|
120
|
+
seen.add(id);
|
|
121
|
+
if (thought)
|
|
122
|
+
events.emit({ type: "thought", text: content });
|
|
123
|
+
else
|
|
124
|
+
events.emit({ type: "assistant", text: content });
|
|
125
|
+
}
|
|
126
|
+
function resultFor(state, failed) {
|
|
127
|
+
const exit = object(state.metadata)?.exit;
|
|
128
|
+
const exitCode = typeof exit === "number" && Number.isInteger(exit) ? exit : undefined;
|
|
129
|
+
if (failed) {
|
|
130
|
+
return { status: "error", message: text(state.error) ?? "OpenCode tool failed", ...(exitCode === undefined ? {} : { exitCode }) };
|
|
131
|
+
}
|
|
132
|
+
return { status: exitCode !== undefined && exitCode !== 0 ? "error" : "ok", ...(exitCode === undefined ? {} : { exitCode }) };
|
|
133
|
+
}
|
|
134
|
+
function mapToolPart(part, events, state) {
|
|
135
|
+
if (!belongs(part, state))
|
|
136
|
+
return;
|
|
137
|
+
const id = text(part.callID);
|
|
138
|
+
const name = text(part.tool);
|
|
139
|
+
const toolState = object(part.state);
|
|
140
|
+
if (id === undefined || name === undefined || toolState === undefined)
|
|
141
|
+
return;
|
|
142
|
+
if ((toolState.status === "pending" || toolState.status === "running")
|
|
143
|
+
&& !state.tools.has(id) && !state.completedTools.has(id)) {
|
|
144
|
+
const call = callFor(name, toolState.input);
|
|
145
|
+
if (call === undefined)
|
|
146
|
+
return;
|
|
147
|
+
const observed = { name, call };
|
|
148
|
+
state.tools.set(id, observed);
|
|
149
|
+
events.emit({ type: "tool", phase: "started", id, ...observed });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if ((toolState.status !== "completed" && toolState.status !== "error") || state.completedTools.has(id))
|
|
153
|
+
return;
|
|
154
|
+
const started = state.tools.get(id);
|
|
155
|
+
const call = started?.call ?? callFor(name, toolState.input);
|
|
156
|
+
if (call === undefined)
|
|
157
|
+
return;
|
|
158
|
+
const observed = started ?? { name, call };
|
|
159
|
+
if (!state.tools.has(id)) {
|
|
160
|
+
state.tools.set(id, observed);
|
|
161
|
+
events.emit({ type: "tool", phase: "started", id, ...observed });
|
|
162
|
+
}
|
|
163
|
+
state.completedTools.add(id);
|
|
164
|
+
events.emit({
|
|
165
|
+
type: "tool",
|
|
166
|
+
phase: "completed",
|
|
167
|
+
id,
|
|
168
|
+
...observed,
|
|
169
|
+
result: resultFor(toolState, toolState.status === "error"),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function mapPart(part, events, state) {
|
|
173
|
+
if (part.type === "text")
|
|
174
|
+
return mapTextPart(part, events, state, false);
|
|
175
|
+
if (part.type === "reasoning")
|
|
176
|
+
return mapTextPart(part, events, state, true);
|
|
177
|
+
if (part.type === "tool")
|
|
178
|
+
mapToolPart(part, events, state);
|
|
179
|
+
}
|
|
180
|
+
function mapSessionError(properties, events, state) {
|
|
181
|
+
if (state.sessionId !== undefined && properties.sessionID !== state.sessionId)
|
|
182
|
+
return;
|
|
183
|
+
state.failure = diagnostic(properties.error);
|
|
184
|
+
events.emit(noteEvent(state.failure));
|
|
185
|
+
}
|
|
186
|
+
function mapTodo(properties, events, state) {
|
|
187
|
+
if (properties.sessionID !== state.sessionId || !Array.isArray(properties.todos))
|
|
188
|
+
return;
|
|
189
|
+
const summary = properties.todos.flatMap((value) => {
|
|
190
|
+
const todo = object(value);
|
|
191
|
+
const content = text(todo?.content);
|
|
192
|
+
return content === undefined ? [] : [`${text(todo?.status) ?? "todo"}: ${content}`];
|
|
193
|
+
}).join("; ");
|
|
194
|
+
if (summary.length > 0)
|
|
195
|
+
events.emit(noteEvent(summary));
|
|
196
|
+
}
|
|
197
|
+
function ignoredEvent(type) {
|
|
198
|
+
return typeof type === "string"
|
|
199
|
+
&& (OPENCODE_EVENT_DISPOSITIONS[type] === "dropped" || RUNTIME_DROPPED_EVENT_KINDS.has(type));
|
|
200
|
+
}
|
|
201
|
+
export function mapEvent(value, events, state) {
|
|
202
|
+
const event = object(value);
|
|
203
|
+
if (event === undefined)
|
|
204
|
+
return;
|
|
205
|
+
const properties = object(event.properties) ?? {};
|
|
206
|
+
if (event.type === "message.part.updated") {
|
|
207
|
+
const part = object(properties.part);
|
|
208
|
+
if (part !== undefined)
|
|
209
|
+
mapPart(part, events, state);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (event.type === "message.updated") {
|
|
213
|
+
const info = object(properties.info);
|
|
214
|
+
if (info !== undefined && info.sessionID === state.sessionId && info.role === "assistant" && info.error !== undefined) {
|
|
215
|
+
state.failure = diagnostic(info.error);
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (event.type === "session.error")
|
|
220
|
+
return mapSessionError(properties, events, state);
|
|
221
|
+
if (event.type === "todo.updated")
|
|
222
|
+
return mapTodo(properties, events, state);
|
|
223
|
+
if (ignoredEvent(event.type))
|
|
224
|
+
return;
|
|
225
|
+
events.emit({ type: "unknown", kind: typeof event.type === "string" ? event.type : "unknown" });
|
|
226
|
+
}
|
|
227
|
+
export function createEventState(sessionId) {
|
|
228
|
+
return {
|
|
229
|
+
...(sessionId === undefined ? {} : { sessionId }),
|
|
230
|
+
assistantParts: new Set(),
|
|
231
|
+
reasoningParts: new Set(),
|
|
232
|
+
tools: new Map(),
|
|
233
|
+
completedTools: new Set(),
|
|
234
|
+
};
|
|
235
|
+
}
|