@astrosheep/keiyaku 4.0.0 → 4.0.1
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 +2 -9
- 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 +11 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +9 -6
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +94 -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 +162 -45
- package/build/src/akuma/akuma.d.ts +33 -25
- package/build/src/akuma/akuma.js +93 -61
- package/build/src/akuma/archetype.d.ts +8 -0
- package/build/src/akuma/archetype.js +28 -9
- package/build/src/akuma/body.js +196 -84
- 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 +67 -15
- package/build/src/akuma/heart/facts.js +3 -3
- package/build/src/akuma/heart/index.d.ts +41 -29
- package/build/src/akuma/heart/index.js +72 -193
- package/build/src/akuma/heart/rows.d.ts +30 -53
- package/build/src/akuma/heart/rows.js +91 -111
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +55 -8
- package/build/src/akuma/heart/storage.d.ts +33 -0
- package/build/src/akuma/heart/storage.js +164 -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/index.d.ts +4 -2
- package/build/src/akuma/index.js +1 -1
- package/build/src/akuma/projection.d.ts +97 -0
- package/build/src/akuma/projection.js +145 -0
- package/build/src/akuma/provider.d.ts +57 -11
- package/build/src/akuma/provider.js +57 -37
- package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
- package/build/src/akuma/providers/claude/events.js +185 -0
- package/build/src/akuma/providers/claude/index.d.ts +46 -0
- package/build/src/akuma/providers/claude/index.js +265 -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 +12 -12
- package/build/src/akuma/providers/codex-app-server/index.d.ts +13 -11
- package/build/src/akuma/providers/codex-app-server/index.js +73 -22
- package/build/src/akuma/providers/index.js +22 -8
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +48 -0
- package/build/src/akuma/providers/opencode-sdk/events.js +116 -0
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +32 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +203 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +41 -0
- package/build/src/akuma/providers/opencode-sdk/session.js +62 -0
- package/build/src/akuma/providers/pi/events.d.ts +33 -0
- package/build/src/akuma/providers/pi/events.js +128 -0
- package/build/src/akuma/providers/pi/index.d.ts +11 -0
- package/build/src/akuma/providers/pi/index.js +162 -0
- package/build/src/akuma/requests.d.ts +1 -0
- package/build/src/akuma/requests.js +5 -2
- package/build/src/alias/index.d.ts +4 -3
- package/build/src/alias/index.js +2 -3
- 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 +2 -0
- package/build/src/cli/accepted.js +2 -1
- package/build/src/cli/commands/akuma-invoke.d.ts +42 -18
- package/build/src/cli/commands/akuma-invoke.js +94 -50
- package/build/src/cli/commands/akuma.d.ts +15 -7
- package/build/src/cli/commands/akuma.js +94 -56
- package/build/src/cli/commands/contract.d.ts +142 -0
- package/build/src/cli/commands/contract.js +14 -0
- package/build/src/cli/commands/install.js +2 -0
- package/build/src/cli/commands/task-invoke.d.ts +3 -1
- package/build/src/cli/commands/task-invoke.js +27 -2
- package/build/src/cli/commands/task.js +2 -2
- package/build/src/cli/invoke.d.ts +6 -1
- package/build/src/cli/invoke.js +241 -43
- package/build/src/cli/main.js +20 -1
- package/build/src/cli/parse.d.ts +13 -116
- package/build/src/cli/parse.js +51 -78
- package/build/src/cli/render/akuma-tool.d.ts +2 -0
- package/build/src/cli/render/akuma-tool.js +6 -2
- package/build/src/cli/render/akuma.d.ts +2 -1
- package/build/src/cli/render/akuma.js +157 -217
- 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 +1 -1
- package/build/src/cli/render/contract.js +38 -5
- package/build/src/cli/render/kanshi.js +53 -5
- package/build/src/cli/render/refusal.js +17 -0
- package/build/src/cli/render/terminal.d.ts +13 -0
- package/build/src/cli/render/terminal.js +75 -0
- package/build/src/cli/render/text.js +5 -0
- package/build/src/cli/result.d.ts +15 -3
- package/build/src/contract-worktree.d.ts +43 -0
- package/build/src/contract-worktree.js +213 -0
- package/build/src/coordination/durable-file.d.ts +3 -0
- package/build/src/coordination/durable-file.js +44 -1
- 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/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.js +12 -16
- package/build/src/core/verbs/bind.js +2 -16
- package/build/src/core/verbs/deliver.js +20 -4
- package/build/src/core/verbs/placement.js +3 -8
- package/build/src/dispatch/index.d.ts +3 -1
- package/build/src/dispatch/index.js +14 -5
- 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 +26 -16
- package/build/src/git/hooks.d.ts +15 -0
- package/build/src/git/hooks.js +57 -5
- package/build/src/git/identity.d.ts +5 -1
- package/build/src/git/identity.js +23 -4
- package/build/src/git/integration.d.ts +49 -0
- package/build/src/git/integration.js +214 -0
- package/build/src/git/observe.d.ts +25 -13
- package/build/src/git/observe.js +175 -72
- 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 +15 -5
- package/build/src/git/reconcile.js +238 -85
- package/build/src/git/repository.d.ts +19 -4
- package/build/src/git/repository.js +77 -43
- package/build/src/git/scratch.d.ts +13 -0
- package/build/src/git/scratch.js +66 -0
- package/build/src/git/target-placement.d.ts +56 -0
- package/build/src/git/target-placement.js +255 -0
- package/build/src/git/tender.d.ts +48 -0
- package/build/src/git/tender.js +82 -0
- package/build/src/git/terminal-seal.d.ts +24 -0
- package/build/src/git/terminal-seal.js +87 -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 +21 -0
- package/build/src/git/workspace.js +104 -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 +1 -1
- package/build/src/kanshi/read.d.ts +5 -2
- package/build/src/kanshi/read.js +154 -45
- package/build/src/kanshi/report.d.ts +31 -6
- package/build/src/kanshi/select.js +1 -1
- package/build/src/library/address.d.ts +48 -0
- package/build/src/library/address.js +134 -0
- package/build/src/library/akuma-creation.d.ts +10 -6
- package/build/src/library/akuma-creation.js +12 -11
- 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 +72 -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 +21 -46
- package/build/src/library/contract.js +193 -183
- package/build/src/library/delivery.d.ts +23 -0
- package/build/src/library/delivery.js +21 -0
- package/build/src/library/fleet.d.ts +65 -0
- package/build/src/library/fleet.js +189 -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 +16 -2
- package/build/src/library/keiyaku.js +11 -1
- package/build/src/library/mutation.d.ts +38 -0
- package/build/src/library/mutation.js +39 -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 +2 -1
- package/build/src/library/region.js +3 -3
- package/build/src/library/repo.d.ts +4 -1
- package/build/src/library/repo.js +31 -16
- package/build/src/protocol/attempt.d.ts +16 -3
- package/build/src/protocol/attempt.js +39 -8
- package/build/src/protocol/bind.d.ts +9 -2
- package/build/src/protocol/bind.js +47 -39
- package/build/src/protocol/intent.d.ts +42 -18
- package/build/src/protocol/intent.js +36 -75
- package/build/src/protocol/operations.d.ts +93 -35
- package/build/src/protocol/operations.js +235 -123
- package/build/src/protocol/outcome.d.ts +7 -2
- package/build/src/protocol/outcome.js +9 -3
- package/build/src/protocol/placement.d.ts +30 -0
- package/build/src/protocol/placement.js +114 -0
- package/build/src/protocol/read/audit.d.ts +9 -3
- package/build/src/protocol/read/audit.js +25 -9
- 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 +11 -5
- package/build/src/protocol/read/status.js +28 -15
- 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 +1 -0
- package/build/src/runtime/proc/line-rpc.js +21 -0
- package/build/src/runtime/proc/run.d.ts +18 -2
- package/build/src/runtime/proc/run.js +34 -8
- package/build/src/settings.d.ts +6 -0
- package/build/src/settings.js +29 -14
- package/build/src/settlement/fence.d.ts +9 -0
- package/build/src/settlement/fence.js +23 -0
- package/build/src/settlement/holder.d.ts +25 -4
- package/build/src/settlement/holder.js +102 -28
- package/build/src/settlement/settle.d.ts +8 -0
- package/build/src/settlement/settle.js +102 -28
- package/build/src/task/board.d.ts +20 -2
- package/build/src/task/board.js +11 -1
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +2 -2
- package/build/src/task/context.js +2 -7
- package/build/src/task/index.d.ts +9 -9
- package/build/src/task/index.js +15 -12
- package/build/src/task/operations.d.ts +31 -12
- package/build/src/task/operations.js +8 -4
- package/build/src/task/store.d.ts +4 -6
- package/build/src/task/store.js +3 -3
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/execution.d.ts +49 -0
- package/build/src/verification/execution.js +104 -0
- package/build/src/world.d.ts +13 -0
- package/build/src/world.js +103 -0
- package/package.json +7 -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/verification/producer.d.ts +0 -23
- package/build/src/verification/producer.js +0 -54
|
@@ -2,6 +2,14 @@ import { LineRpcProcess } from "../../../runtime/proc/line-rpc.js";
|
|
|
2
2
|
import { AgentEventChannel, AKUMA_REQUESTS_ENV, } from "../../provider.js";
|
|
3
3
|
import { codexNotificationResult, codexObject, codexText, } from "./events.js";
|
|
4
4
|
export { CODEX_ITEM_DISPOSITIONS, CODEX_NOTIFICATION_DISPOSITIONS } from "./events.js";
|
|
5
|
+
const INTERRUPT_TIMEOUT_MS = 1_000;
|
|
6
|
+
function interrupt(server, threadId, turnId) {
|
|
7
|
+
const request = server.request("turn/interrupt", { threadId, turnId });
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const timer = setTimeout(() => reject(new Error("codex app-server interrupt timed out")), INTERRUPT_TIMEOUT_MS);
|
|
10
|
+
void request.then((value) => { clearTimeout(timer); resolve(value); }, (error) => { clearTimeout(timer); reject(error); });
|
|
11
|
+
});
|
|
12
|
+
}
|
|
5
13
|
function diagnostic(error) {
|
|
6
14
|
return error instanceof Error ? error.message : String(error);
|
|
7
15
|
}
|
|
@@ -28,6 +36,25 @@ function turnId(result) {
|
|
|
28
36
|
throw new Error("codex app-server did not return a turn id");
|
|
29
37
|
return id;
|
|
30
38
|
}
|
|
39
|
+
async function steerTurn(server, state, tell) {
|
|
40
|
+
if (state.settled)
|
|
41
|
+
return { kind: "turn-ended" };
|
|
42
|
+
if (state.threadId === undefined || state.turnId === undefined)
|
|
43
|
+
throw new Error("codex app-server turn is not admitted");
|
|
44
|
+
const request = server.request("turn/steer", {
|
|
45
|
+
threadId: state.threadId,
|
|
46
|
+
expectedTurnId: state.turnId,
|
|
47
|
+
input: [{ type: "text", text: tell.text }],
|
|
48
|
+
clientUserMessageId: tell.id,
|
|
49
|
+
});
|
|
50
|
+
const response = codexObject(await request);
|
|
51
|
+
const accepted = codexText(response?.turnId);
|
|
52
|
+
if (accepted === undefined)
|
|
53
|
+
throw new Error("codex app-server steer did not return a turn id");
|
|
54
|
+
if (accepted !== state.turnId)
|
|
55
|
+
throw new Error("codex app-server steer acknowledged a different turn");
|
|
56
|
+
return { kind: "accepted", fence: `${accepted}:${tell.id}` };
|
|
57
|
+
}
|
|
31
58
|
function sandbox(cwd, options, requests) {
|
|
32
59
|
return {
|
|
33
60
|
type: "workspaceWrite",
|
|
@@ -46,16 +73,20 @@ async function admitTurn(server, input, state, events, config) {
|
|
|
46
73
|
...(input.options.systemPrompt === undefined || input.options.systemPrompt.length === 0
|
|
47
74
|
? {} : { developerInstructions: input.options.systemPrompt }),
|
|
48
75
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
if (input.session.kind === "fresh") {
|
|
77
|
+
state.threadId = threadId(await server.request("thread/start", threadParams));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
state.threadId = threadId(await server.request("thread/resume", {
|
|
81
|
+
threadId: input.session.coordinate.sessionId,
|
|
53
82
|
...threadParams,
|
|
54
|
-
}), input.session.sessionId);
|
|
83
|
+
}), input.session.coordinate.sessionId);
|
|
84
|
+
}
|
|
55
85
|
events.emit({ type: "session", coordinate: { sessionId: state.threadId } });
|
|
56
86
|
state.turnId = turnId(await server.request("turn/start", {
|
|
57
87
|
threadId: state.threadId,
|
|
58
|
-
input: [{ type: "text", text: input.
|
|
88
|
+
input: [{ type: "text", text: [input.body, ...input.launchTells.map((tell) => tell.text)]
|
|
89
|
+
.filter((part) => part.length > 0).join("\n\n") }],
|
|
59
90
|
...(input.options.model === undefined ? {} : { model: input.options.model }),
|
|
60
91
|
...(input.options.effort === undefined ? {} : { effort: input.options.effort }),
|
|
61
92
|
approvalPolicy: "never",
|
|
@@ -63,6 +94,8 @@ async function admitTurn(server, input, state, events, config) {
|
|
|
63
94
|
}));
|
|
64
95
|
}
|
|
65
96
|
async function forkCodex(execution, input) {
|
|
97
|
+
if (!("sessionId" in input.session))
|
|
98
|
+
throw new Error("Codex app-server fork requires sessionId");
|
|
66
99
|
const server = new LineRpcProcess({
|
|
67
100
|
argv: [execution.executable ?? "codex", "app-server", "--listen", "stdio://"],
|
|
68
101
|
cwd: input.cwd,
|
|
@@ -82,22 +115,28 @@ async function forkCodex(execution, input) {
|
|
|
82
115
|
await server.close();
|
|
83
116
|
}
|
|
84
117
|
}
|
|
85
|
-
async function abortTurn(server, state, completion,
|
|
86
|
-
if (
|
|
118
|
+
async function abortTurn(server, state, completion, settle) {
|
|
119
|
+
if (state.settled) {
|
|
120
|
+
await completion;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (state.threadId !== undefined && state.turnId !== undefined) {
|
|
87
124
|
try {
|
|
88
|
-
await server
|
|
125
|
+
await interrupt(server, state.threadId, state.turnId);
|
|
89
126
|
await completion;
|
|
90
127
|
}
|
|
91
128
|
catch (error) {
|
|
92
|
-
|
|
129
|
+
settle({ kind: "failed", diagnostic: diagnostic(error) });
|
|
93
130
|
}
|
|
94
131
|
}
|
|
95
|
-
else
|
|
96
|
-
|
|
132
|
+
else {
|
|
133
|
+
settle({ kind: "failed", diagnostic: "codex app-server aborted before turn admission" });
|
|
97
134
|
}
|
|
98
|
-
await
|
|
135
|
+
await completion;
|
|
99
136
|
}
|
|
100
137
|
async function startCodex(execution, input) {
|
|
138
|
+
if (input.session.kind === "resume" && !("sessionId" in input.session.coordinate))
|
|
139
|
+
throw new Error("Codex app-server resume requires sessionId");
|
|
101
140
|
const events = new AgentEventChannel();
|
|
102
141
|
const server = new LineRpcProcess({
|
|
103
142
|
argv: [execution.executable ?? "codex", "app-server", "--listen", "stdio://"],
|
|
@@ -108,16 +147,28 @@ async function startCodex(execution, input) {
|
|
|
108
147
|
...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
|
|
109
148
|
} }),
|
|
110
149
|
});
|
|
111
|
-
const state = {
|
|
150
|
+
const state = { settled: false, tools: new Map() };
|
|
112
151
|
let settle;
|
|
113
152
|
const completion = new Promise((resolve) => { settle = resolve; });
|
|
153
|
+
let terminal;
|
|
114
154
|
const finish = (result) => {
|
|
115
|
-
if (
|
|
155
|
+
if (terminal !== undefined)
|
|
116
156
|
return;
|
|
157
|
+
terminal = result;
|
|
117
158
|
state.settled = true;
|
|
118
|
-
|
|
119
|
-
|
|
159
|
+
void server.endInputAndDrain().then(() => {
|
|
160
|
+
events.end();
|
|
161
|
+
settle(result);
|
|
162
|
+
}, () => {
|
|
163
|
+
events.end();
|
|
164
|
+
settle(result);
|
|
165
|
+
});
|
|
120
166
|
};
|
|
167
|
+
server.onNotification((notification) => {
|
|
168
|
+
const result = codexNotificationResult(notification, state, events);
|
|
169
|
+
if (result !== undefined)
|
|
170
|
+
finish(result);
|
|
171
|
+
});
|
|
121
172
|
server.onExit(({ code, signal, stderr }) => finish({
|
|
122
173
|
kind: "failed",
|
|
123
174
|
diagnostic: stderr || `codex app-server exited before completion (${code ?? signal ?? "unknown"})`,
|
|
@@ -126,21 +177,20 @@ async function startCodex(execution, input) {
|
|
|
126
177
|
kind: "failed",
|
|
127
178
|
diagnostic: `codex app-server made forbidden interactive request ${request.method}`,
|
|
128
179
|
}));
|
|
129
|
-
server.onNotification((notification) => {
|
|
130
|
-
const result = codexNotificationResult(notification, state, events);
|
|
131
|
-
if (result !== undefined)
|
|
132
|
-
finish(result);
|
|
133
|
-
});
|
|
134
180
|
try {
|
|
135
181
|
await admitTurn(server, input, state, events, execution.config);
|
|
136
182
|
}
|
|
137
183
|
catch (error) {
|
|
138
184
|
finish({ kind: "failed", diagnostic: diagnostic(error) });
|
|
139
185
|
}
|
|
186
|
+
if (state.turnId === undefined)
|
|
187
|
+
throw new Error("codex app-server did not admit a turn");
|
|
140
188
|
return {
|
|
189
|
+
admission: { fence: state.turnId },
|
|
141
190
|
events,
|
|
142
191
|
completion,
|
|
143
192
|
abort: () => abortTurn(server, state, completion, finish),
|
|
193
|
+
tell: (tell) => steerTurn(server, state, tell),
|
|
144
194
|
};
|
|
145
195
|
}
|
|
146
196
|
export function createCodexAppServerProvider(input = "codex") {
|
|
@@ -157,6 +207,7 @@ export function createCodexAppServerProvider(input = "codex") {
|
|
|
157
207
|
},
|
|
158
208
|
fork: (input) => forkCodex(execution, input),
|
|
159
209
|
start: (input) => startCodex(execution, input),
|
|
210
|
+
resume: (input) => startCodex(execution, input),
|
|
160
211
|
};
|
|
161
212
|
}
|
|
162
213
|
export const codexAppServerProvider = createCodexAppServerProvider();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { claudeProvider, createClaudeProvider } from "./claude.js";
|
|
1
|
+
import { claudeProvider, createClaudeProvider } from "./claude/index.js";
|
|
2
2
|
import { createCodexAppServerProvider } from "./codex-app-server/index.js";
|
|
3
|
+
import { createOpencodeProvider } from "./opencode-sdk/index.js";
|
|
4
|
+
import { createPiProvider } from "./pi/index.js";
|
|
3
5
|
const PROVIDERS = {
|
|
4
6
|
claude: claudeProvider,
|
|
5
7
|
};
|
|
@@ -24,6 +26,19 @@ function optionalText(value, field) {
|
|
|
24
26
|
}
|
|
25
27
|
return selected;
|
|
26
28
|
}
|
|
29
|
+
function providerKind(value) {
|
|
30
|
+
return value === "claude-agent-sdk" || value === "codex-app-server" || value === "opencode-sdk" || value === "pi";
|
|
31
|
+
}
|
|
32
|
+
function providerConfig(value, kind) {
|
|
33
|
+
const config = value.config === undefined ? undefined : record(value.config);
|
|
34
|
+
if (config === null)
|
|
35
|
+
throw new TypeError("provider execution config must be an object");
|
|
36
|
+
if (kind === "claude-agent-sdk" && config !== undefined)
|
|
37
|
+
throw new TypeError("provider execution config is unsupported by claude-agent-sdk");
|
|
38
|
+
if (kind === "opencode-sdk" && config !== undefined)
|
|
39
|
+
throw new TypeError("provider execution config is unsupported by opencode-sdk");
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
27
42
|
export function decodeProviderExecution(input) {
|
|
28
43
|
const value = record(input);
|
|
29
44
|
if (value === null)
|
|
@@ -35,16 +50,11 @@ export function decodeProviderExecution(input) {
|
|
|
35
50
|
if (typeof value.name !== "string" || value.name.trim().length === 0) {
|
|
36
51
|
throw new TypeError("provider execution name must be a nonblank string");
|
|
37
52
|
}
|
|
38
|
-
if (value.kind
|
|
53
|
+
if (!providerKind(value.kind)) {
|
|
39
54
|
throw new TypeError("provider execution has unknown kind");
|
|
40
55
|
}
|
|
41
56
|
const executable = optionalText(value, "executable");
|
|
42
|
-
const config = value
|
|
43
|
-
if (config === null)
|
|
44
|
-
throw new TypeError("provider execution config must be an object");
|
|
45
|
-
if (value.kind === "claude-agent-sdk" && config !== undefined) {
|
|
46
|
-
throw new TypeError("provider execution config is unsupported by claude-agent-sdk");
|
|
47
|
-
}
|
|
57
|
+
const config = providerConfig(value, value.kind);
|
|
48
58
|
const env = value.env === undefined ? undefined : record(value.env);
|
|
49
59
|
if (env === null)
|
|
50
60
|
throw new TypeError("provider execution env must be an object");
|
|
@@ -67,5 +77,9 @@ export function providerNamed(execution) {
|
|
|
67
77
|
}
|
|
68
78
|
if (execution.kind === "codex-app-server")
|
|
69
79
|
return createCodexAppServerProvider(execution);
|
|
80
|
+
if (execution.kind === "opencode-sdk")
|
|
81
|
+
return createOpencodeProvider(execution);
|
|
82
|
+
if (execution.kind === "pi")
|
|
83
|
+
return createPiProvider(execution);
|
|
70
84
|
throw new TypeError(`unknown Akuma provider kind ${execution.kind}`);
|
|
71
85
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { AgentEvent, ToolCall } from "../../provider.js";
|
|
2
|
+
type Emitter = {
|
|
3
|
+
emit(event: AgentEvent): void;
|
|
4
|
+
};
|
|
5
|
+
type ToolState = Readonly<{
|
|
6
|
+
name: string;
|
|
7
|
+
call: ToolCall;
|
|
8
|
+
}>;
|
|
9
|
+
type State = {
|
|
10
|
+
tools: Map<string, ToolState>;
|
|
11
|
+
answer: string[];
|
|
12
|
+
seen: Set<string>;
|
|
13
|
+
failure?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const OPENCODE_EVENT_DISPOSITIONS: {
|
|
16
|
+
readonly "session.next.agent.switched": "dropped";
|
|
17
|
+
readonly "session.next.model.switched": "dropped";
|
|
18
|
+
readonly "session.next.moved": "dropped";
|
|
19
|
+
readonly "session.next.prompted": "dropped";
|
|
20
|
+
readonly "session.next.prompt.admitted": "dropped";
|
|
21
|
+
readonly "session.next.context.updated": "dropped";
|
|
22
|
+
readonly "session.next.synthetic": "dropped";
|
|
23
|
+
readonly "session.next.shell.started": "dropped";
|
|
24
|
+
readonly "session.next.shell.ended": "dropped";
|
|
25
|
+
readonly "session.next.step.started": "dropped";
|
|
26
|
+
readonly "session.next.step.ended": "dropped";
|
|
27
|
+
readonly "session.next.step.failed": "mapped";
|
|
28
|
+
readonly "session.next.text.started": "dropped";
|
|
29
|
+
readonly "session.next.text.ended": "mapped";
|
|
30
|
+
readonly "session.next.tool.input.started": "dropped";
|
|
31
|
+
readonly "session.next.tool.input.ended": "dropped";
|
|
32
|
+
readonly "session.next.tool.called": "mapped";
|
|
33
|
+
readonly "session.next.tool.progress": "dropped";
|
|
34
|
+
readonly "session.next.tool.success": "mapped";
|
|
35
|
+
readonly "session.next.tool.failed": "mapped";
|
|
36
|
+
readonly "session.next.reasoning.started": "dropped";
|
|
37
|
+
readonly "session.next.reasoning.ended": "mapped";
|
|
38
|
+
readonly "session.next.retried": "mapped";
|
|
39
|
+
readonly "session.next.compaction.started": "dropped";
|
|
40
|
+
readonly "session.next.compaction.ended": "dropped";
|
|
41
|
+
readonly "session.next.revert.staged": "dropped";
|
|
42
|
+
readonly "session.next.revert.cleared": "dropped";
|
|
43
|
+
readonly "session.next.revert.committed": "dropped";
|
|
44
|
+
};
|
|
45
|
+
export declare function mapEvent(value: unknown, events: Emitter, state: State): void;
|
|
46
|
+
export type EventState = State;
|
|
47
|
+
export declare function createEventState(): State;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export const OPENCODE_EVENT_DISPOSITIONS = {
|
|
2
|
+
"session.next.agent.switched": "dropped",
|
|
3
|
+
"session.next.model.switched": "dropped",
|
|
4
|
+
"session.next.moved": "dropped",
|
|
5
|
+
"session.next.prompted": "dropped",
|
|
6
|
+
"session.next.prompt.admitted": "dropped",
|
|
7
|
+
"session.next.context.updated": "dropped",
|
|
8
|
+
"session.next.synthetic": "dropped",
|
|
9
|
+
"session.next.shell.started": "dropped",
|
|
10
|
+
"session.next.shell.ended": "dropped",
|
|
11
|
+
"session.next.step.started": "dropped",
|
|
12
|
+
"session.next.step.ended": "dropped",
|
|
13
|
+
"session.next.step.failed": "mapped",
|
|
14
|
+
"session.next.text.started": "dropped",
|
|
15
|
+
"session.next.text.ended": "mapped",
|
|
16
|
+
"session.next.tool.input.started": "dropped",
|
|
17
|
+
"session.next.tool.input.ended": "dropped",
|
|
18
|
+
"session.next.tool.called": "mapped",
|
|
19
|
+
"session.next.tool.progress": "dropped",
|
|
20
|
+
"session.next.tool.success": "mapped",
|
|
21
|
+
"session.next.tool.failed": "mapped",
|
|
22
|
+
"session.next.reasoning.started": "dropped",
|
|
23
|
+
"session.next.reasoning.ended": "mapped",
|
|
24
|
+
"session.next.retried": "mapped",
|
|
25
|
+
"session.next.compaction.started": "dropped",
|
|
26
|
+
"session.next.compaction.ended": "dropped",
|
|
27
|
+
"session.next.revert.staged": "dropped",
|
|
28
|
+
"session.next.revert.cleared": "dropped",
|
|
29
|
+
"session.next.revert.committed": "dropped",
|
|
30
|
+
};
|
|
31
|
+
function object(value) {
|
|
32
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
33
|
+
}
|
|
34
|
+
function text(value) { return typeof value === "string" && value.length > 0 ? value : undefined; }
|
|
35
|
+
function diagnostic(value) { const v = object(value); return text(v?.message) ?? text(v?.error) ?? "OpenCode session failed"; }
|
|
36
|
+
function callFor(name, input) {
|
|
37
|
+
const value = object(input);
|
|
38
|
+
if (name === "shell")
|
|
39
|
+
return { kind: "run", command: text(value?.command) ?? "shell" };
|
|
40
|
+
if (name === "read")
|
|
41
|
+
return { kind: "read", path: text(value?.path) ?? "file" };
|
|
42
|
+
if (name === "grep" || name === "search")
|
|
43
|
+
return { kind: "search", query: text(value?.query) ?? "search" };
|
|
44
|
+
return { kind: "other", display: name };
|
|
45
|
+
}
|
|
46
|
+
function resultFor(value, failed) {
|
|
47
|
+
return { status: failed ? "error" : "ok", ...(failed ? { message: diagnostic(value) } : {}) };
|
|
48
|
+
}
|
|
49
|
+
export function mapEvent(value, events, state) {
|
|
50
|
+
const event = object(value);
|
|
51
|
+
const eventId = text(event?.id);
|
|
52
|
+
if (eventId !== undefined && state.seen.has(eventId))
|
|
53
|
+
return;
|
|
54
|
+
if (eventId !== undefined)
|
|
55
|
+
state.seen.add(eventId);
|
|
56
|
+
const kind = text(event?.type) ?? "unknown";
|
|
57
|
+
const data = object(event?.data) ?? event ?? {};
|
|
58
|
+
switch (kind) {
|
|
59
|
+
case "session.next.text.ended": {
|
|
60
|
+
const value = text(data.text);
|
|
61
|
+
if (value !== undefined) {
|
|
62
|
+
state.answer.push(value);
|
|
63
|
+
events.emit({ type: "assistant", text: value });
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
case "session.next.reasoning.ended": {
|
|
68
|
+
const value = text(data.text);
|
|
69
|
+
if (value !== undefined)
|
|
70
|
+
events.emit({ type: "thought", text: value });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case "session.next.tool.called": {
|
|
74
|
+
const id = text(data.callID);
|
|
75
|
+
if (id === undefined) {
|
|
76
|
+
events.emit({ type: "unknown", kind: `${kind}/missing-id` });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const observed = { name: text(data.tool) ?? "tool", call: callFor(text(data.tool) ?? "tool", data.input) };
|
|
80
|
+
state.tools.set(id, observed);
|
|
81
|
+
events.emit({ type: "tool", phase: "started", id, ...observed });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
case "session.next.tool.success":
|
|
85
|
+
case "session.next.tool.failed": {
|
|
86
|
+
const id = text(data.callID);
|
|
87
|
+
if (id === undefined) {
|
|
88
|
+
events.emit({ type: "unknown", kind: `${kind}/missing-id` });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const started = state.tools.get(id);
|
|
92
|
+
const failed = kind.endsWith("failed");
|
|
93
|
+
if (started === undefined) {
|
|
94
|
+
events.emit({ type: "unknown", kind: `${kind}/orphan` });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
state.tools.delete(id);
|
|
98
|
+
events.emit({ type: "tool", phase: "completed", id, ...started, result: resultFor(data.error, failed) });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
case "session.next.step.failed":
|
|
102
|
+
state.failure = diagnostic(data.error);
|
|
103
|
+
events.emit({ type: "note", text: state.failure });
|
|
104
|
+
return;
|
|
105
|
+
case "session.next.retried":
|
|
106
|
+
events.emit({ type: "note", text: "Retrying" });
|
|
107
|
+
return;
|
|
108
|
+
default: {
|
|
109
|
+
const disposition = OPENCODE_EVENT_DISPOSITIONS[kind];
|
|
110
|
+
if (disposition === "dropped")
|
|
111
|
+
return;
|
|
112
|
+
events.emit({ type: "unknown", kind });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export function createEventState() { return { tools: new Map(), answer: [], seen: new Set() }; }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ProviderAdapter, type ProviderOptions, type Session } from "../../provider.js";
|
|
2
|
+
import type { ProviderExecution, ResumeCoordinate } from "../../heart/index.js";
|
|
3
|
+
import { type OpencodeSdkLoader } from "./session.js";
|
|
4
|
+
export type OpencodeProviderTestOptions = Readonly<{
|
|
5
|
+
loader?: OpencodeSdkLoader;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function createOpencodeProvider(input?: ProviderExecution | OpencodeProviderTestOptions): ProviderAdapter;
|
|
8
|
+
export declare const opencodeProvider: Readonly<{
|
|
9
|
+
confinement(input: Readonly<{
|
|
10
|
+
cwd: string;
|
|
11
|
+
options: ProviderOptions;
|
|
12
|
+
}>): import("../../heart/facts.js").Confinement;
|
|
13
|
+
admitOptions(options: ProviderOptions): import("../../provider.js").ProviderOptionAdmission;
|
|
14
|
+
fork?(input: Readonly<{
|
|
15
|
+
session: ResumeCoordinate;
|
|
16
|
+
at: string;
|
|
17
|
+
cwd: string;
|
|
18
|
+
}>): Promise<Readonly<{
|
|
19
|
+
session: ResumeCoordinate;
|
|
20
|
+
}>>;
|
|
21
|
+
start(input: import("../../provider.js").DriveInput & Readonly<{
|
|
22
|
+
session: Readonly<{
|
|
23
|
+
kind: "fresh";
|
|
24
|
+
}>;
|
|
25
|
+
}>): Promise<Session>;
|
|
26
|
+
resume?(input: import("../../provider.js").DriveInput & Readonly<{
|
|
27
|
+
session: Readonly<{
|
|
28
|
+
kind: "resume";
|
|
29
|
+
coordinate: ResumeCoordinate;
|
|
30
|
+
}>;
|
|
31
|
+
}>): Promise<Session>;
|
|
32
|
+
}>;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { AgentEventChannel } from "../../provider.js";
|
|
2
|
+
import { createEventState, mapEvent } from "./events.js";
|
|
3
|
+
import { coordinate, loadOpencode, OPENCODE_SDK_PROVIDER, parseModel } from "./session.js";
|
|
4
|
+
function diagnostic(error) { return error instanceof Error ? error.message : String(error); }
|
|
5
|
+
function opencodeSessionId(coordinate) {
|
|
6
|
+
if (!("sessionId" in coordinate) || coordinate.sessionId === undefined) {
|
|
7
|
+
throw new Error("OpenCode resume requires sessionId");
|
|
8
|
+
}
|
|
9
|
+
return coordinate.sessionId;
|
|
10
|
+
}
|
|
11
|
+
function admit(options) {
|
|
12
|
+
if (options.access !== undefined)
|
|
13
|
+
throw new Error("OpenCode does not support explicit access");
|
|
14
|
+
if (options.network !== undefined)
|
|
15
|
+
throw new Error("OpenCode does not support explicit network");
|
|
16
|
+
if (options.systemPrompt !== undefined && options.systemPrompt.length > 0) {
|
|
17
|
+
throw new Error("OpenCode V2 does not expose an enforceable system prompt option");
|
|
18
|
+
}
|
|
19
|
+
if (options.effort !== undefined && options.model === undefined) {
|
|
20
|
+
throw new Error("OpenCode effort requires an explicit model");
|
|
21
|
+
}
|
|
22
|
+
if (options.model !== undefined)
|
|
23
|
+
parseModel(options.model, options.effort);
|
|
24
|
+
}
|
|
25
|
+
function eventValue(value) {
|
|
26
|
+
const envelope = objectData(value);
|
|
27
|
+
if (envelope !== undefined && typeof envelope.data === "string")
|
|
28
|
+
return eventValue(envelope.data);
|
|
29
|
+
if (typeof value !== "string")
|
|
30
|
+
return value;
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(value);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { type: value };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function drive(execution, input, loader) {
|
|
39
|
+
admit(input.options);
|
|
40
|
+
const resumeSessionId = input.session.kind === "resume"
|
|
41
|
+
? opencodeSessionId(input.session.coordinate)
|
|
42
|
+
: undefined;
|
|
43
|
+
const events = new AgentEventChannel();
|
|
44
|
+
const state = createEventState();
|
|
45
|
+
const abortController = new AbortController();
|
|
46
|
+
const runtime = await loadOpencode(execution, input.cwd, abortController.signal, loader);
|
|
47
|
+
let closed = false;
|
|
48
|
+
const closeOnce = async () => {
|
|
49
|
+
if (closed)
|
|
50
|
+
return;
|
|
51
|
+
closed = true;
|
|
52
|
+
await runtime.close();
|
|
53
|
+
};
|
|
54
|
+
try {
|
|
55
|
+
const session = runtime.client.v2.session;
|
|
56
|
+
const model = input.options.model === undefined ? undefined : parseModel(input.options.model, input.options.effort);
|
|
57
|
+
const response = input.session.kind === "fresh"
|
|
58
|
+
? await session.create({ location: { directory: input.cwd }, ...(model === undefined ? {} : { model }) }, { throwOnError: true })
|
|
59
|
+
: await session.get({ sessionID: resumeSessionId }, { throwOnError: true });
|
|
60
|
+
const responseData = objectData(objectData(response)?.data);
|
|
61
|
+
const info = objectData(responseData?.data) ?? responseData;
|
|
62
|
+
const sessionId = textData(info?.id) ?? resumeSessionId;
|
|
63
|
+
if (sessionId === undefined)
|
|
64
|
+
throw new Error("OpenCode did not return a session id");
|
|
65
|
+
if (input.session.kind === "resume" && model !== undefined) {
|
|
66
|
+
await session.switchModel({ sessionID: sessionId, model }, { throwOnError: true });
|
|
67
|
+
}
|
|
68
|
+
events.emit({ type: "session", coordinate: coordinate(sessionId) });
|
|
69
|
+
const streamResult = await session.events({ sessionID: sessionId });
|
|
70
|
+
const iterator = streamResult.stream[Symbol.asyncIterator]();
|
|
71
|
+
const promptText = [input.body, ...input.launchTells.map((tell) => tell.text)].filter((part) => part.length > 0).join("\n\n");
|
|
72
|
+
const promptResponse = await session.prompt({ sessionID: sessionId, prompt: { text: promptText } }, { throwOnError: true });
|
|
73
|
+
const fence = admittedFence(sessionId, promptResponse);
|
|
74
|
+
let settled = false;
|
|
75
|
+
let settle;
|
|
76
|
+
const completion = new Promise((resolve) => { settle = resolve; });
|
|
77
|
+
const finish = async (result) => {
|
|
78
|
+
if (settled)
|
|
79
|
+
return;
|
|
80
|
+
settled = true;
|
|
81
|
+
events.end();
|
|
82
|
+
try {
|
|
83
|
+
await closeOnce();
|
|
84
|
+
settle(result);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
settle({ kind: "failed", diagnostic: `OpenCode cleanup failed: ${diagnostic(error)}` });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
void (async () => {
|
|
91
|
+
try {
|
|
92
|
+
await consumeSession(session, sessionId, iterator, state, events);
|
|
93
|
+
const messages = await session.messages({ sessionID: sessionId, order: "desc", limit: 32 }, { throwOnError: true });
|
|
94
|
+
const final = messages.data.data.find((message) => message.type === "assistant");
|
|
95
|
+
const answer = final?.type === "assistant" ? final.content.filter((part) => part.type === "text").map((part) => part.text).join("\n\n") : state.answer.join("\n\n");
|
|
96
|
+
const historyId = final?.type === "assistant" ? textData(final.id) : undefined;
|
|
97
|
+
if (state.failure !== undefined || final?.type === "assistant" && final.error !== undefined)
|
|
98
|
+
await finish({ kind: "failed", diagnostic: state.failure ?? diagnostic(final?.type === "assistant" ? final.error : undefined) });
|
|
99
|
+
else if (historyId === undefined) {
|
|
100
|
+
await finish({ kind: "failed", diagnostic: "OpenCode completed without a native assistant answer/history point" });
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
await finish({ kind: "answered", answer, historyId });
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
await finish({ kind: "failed", diagnostic: diagnostic(error) });
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
return {
|
|
110
|
+
admission: { fence },
|
|
111
|
+
events,
|
|
112
|
+
completion,
|
|
113
|
+
abort: async () => {
|
|
114
|
+
abortController.abort();
|
|
115
|
+
void session.interrupt({ sessionID: sessionId }, { throwOnError: true }).catch(() => undefined);
|
|
116
|
+
await finish({ kind: "failed", diagnostic: "OpenCode session interrupted" });
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
try {
|
|
122
|
+
await closeOnce();
|
|
123
|
+
}
|
|
124
|
+
catch (cleanupError) {
|
|
125
|
+
throw new Error(`OpenCode setup failed: ${diagnostic(error)}; cleanup failed: ${diagnostic(cleanupError)}`);
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function objectData(value) { return value !== null && typeof value === "object" ? value : undefined; }
|
|
131
|
+
function textData(value) { return typeof value === "string" && value.trim().length > 0 ? value : undefined; }
|
|
132
|
+
function admittedFence(sessionId, response) {
|
|
133
|
+
const outer = objectData(response);
|
|
134
|
+
const envelope = objectData(outer?.data);
|
|
135
|
+
const admission = objectData(envelope?.data) ?? envelope;
|
|
136
|
+
const id = textData(admission?.id);
|
|
137
|
+
const sequence = typeof admission?.admittedSeq === "number" && Number.isSafeInteger(admission.admittedSeq)
|
|
138
|
+
? admission.admittedSeq : undefined;
|
|
139
|
+
if (id === undefined && sequence === undefined)
|
|
140
|
+
throw new Error("OpenCode did not return prompt admission evidence");
|
|
141
|
+
return `${sessionId}:${sequence ?? id}`;
|
|
142
|
+
}
|
|
143
|
+
async function consumeSession(session, sessionId, iterator, state, events) {
|
|
144
|
+
const stream = (async () => {
|
|
145
|
+
for (;;) {
|
|
146
|
+
const next = await iterator.next();
|
|
147
|
+
if (next.done)
|
|
148
|
+
return;
|
|
149
|
+
mapEvent(eventValue(next.value), events, state);
|
|
150
|
+
}
|
|
151
|
+
})();
|
|
152
|
+
stream.catch(() => undefined);
|
|
153
|
+
await session.wait({ sessionID: sessionId }, { throwOnError: true });
|
|
154
|
+
await iterator.return?.();
|
|
155
|
+
await stream;
|
|
156
|
+
let after;
|
|
157
|
+
for (;;) {
|
|
158
|
+
const history = await session.history({ sessionID: sessionId, limit: 200, ...(after === undefined ? {} : { after }) }, { throwOnError: true });
|
|
159
|
+
for (const event of history.data.data)
|
|
160
|
+
mapEvent(event, events, state);
|
|
161
|
+
const latest = history.data.data.at(-1)?.durable?.seq;
|
|
162
|
+
if (!history.data.hasMore || latest === undefined || latest === after)
|
|
163
|
+
return;
|
|
164
|
+
after = latest;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, kind: "opencode-sdk" }) {
|
|
168
|
+
const execution = "kind" in input ? input : { name: OPENCODE_SDK_PROVIDER, kind: "opencode-sdk" };
|
|
169
|
+
const loader = "loader" in input ? input.loader : undefined;
|
|
170
|
+
return {
|
|
171
|
+
confinement: ({ cwd }) => ({ kind: "declared", writableRoots: [cwd] }),
|
|
172
|
+
admitOptions(options) {
|
|
173
|
+
try {
|
|
174
|
+
admit(options);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return { kind: "refused", diagnostic: diagnostic(error) };
|
|
178
|
+
}
|
|
179
|
+
return { kind: "admitted", options: Object.freeze({ ...options }) };
|
|
180
|
+
},
|
|
181
|
+
start: (input) => drive(execution, input, loader),
|
|
182
|
+
resume: (input) => drive(execution, input, loader),
|
|
183
|
+
fork: async (input) => {
|
|
184
|
+
const sessionId = opencodeSessionId(input.session);
|
|
185
|
+
const runtime = await loadOpencode(execution, input.cwd, new AbortController().signal, loader);
|
|
186
|
+
try {
|
|
187
|
+
const fork = runtime.client.session?.fork;
|
|
188
|
+
if (fork === undefined)
|
|
189
|
+
throw new Error("OpenCode fork is unavailable");
|
|
190
|
+
const result = objectData(await fork({ sessionID: sessionId, messageID: input.at }));
|
|
191
|
+
const data = objectData(result?.data);
|
|
192
|
+
const id = textData(data?.id);
|
|
193
|
+
if (id === undefined || id === sessionId)
|
|
194
|
+
throw new Error("OpenCode fork returned an invalid session id");
|
|
195
|
+
return { session: coordinate(id) };
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
await runtime.close();
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
export const opencodeProvider = createOpencodeProvider();
|