@astrosheep/keiyaku 4.6.8 → 4.6.9
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 +40 -14
- package/build/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/build/plugins/claude/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/claude/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/claude/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/claude/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/build/plugins/codex/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/codex/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/opencode/index.js +1 -1
- package/build/plugins/opencode/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/opencode/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/skills/keiyaku/SKILL.md +5 -6
- package/build/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/src/akuma/akuma-instance.d.ts +2 -0
- package/build/src/akuma/akuma-instance.js +10 -0
- package/build/src/akuma/akuma-observe.d.ts +33 -8
- package/build/src/akuma/akuma-observe.js +88 -64
- package/build/src/akuma/akuma-owner.d.ts +11 -2
- package/build/src/akuma/akuma-owner.js +45 -35
- package/build/src/akuma/akuma-selection-execution.js +9 -1
- package/build/src/akuma/akuma-stream.d.ts +22 -0
- package/build/src/akuma/akuma-stream.js +116 -0
- package/build/src/akuma/akuma-wait.d.ts +1 -40
- package/build/src/akuma/akuma-wait.js +23 -84
- package/build/src/akuma/akuma.d.ts +17 -11
- package/build/src/akuma/akuma.js +2 -0
- package/build/src/akuma/allowed.d.ts +7 -12
- package/build/src/akuma/allowed.js +13 -4
- package/build/src/akuma/body-supervisor.d.ts +5 -5
- package/build/src/akuma/body-supervisor.js +22 -20
- package/build/src/akuma/body.js +28 -1
- package/build/src/akuma/call-request.d.ts +12 -22
- package/build/src/akuma/heart/activity-schema.d.ts +12 -0
- package/build/src/akuma/heart/activity-schema.js +6 -0
- package/build/src/akuma/heart/index.d.ts +59 -2
- package/build/src/akuma/heart/index.js +87 -8
- package/build/src/akuma/heart/rows.js +5 -3
- package/build/src/akuma/heart/soul.d.ts +9 -0
- package/build/src/akuma/heart/soul.js +29 -0
- package/build/src/akuma/heart/storage.d.ts +15 -0
- package/build/src/akuma/heart/storage.js +55 -0
- package/build/src/akuma/heart/timeline.d.ts +7 -0
- package/build/src/akuma/heart/timeline.js +10 -0
- package/build/src/akuma/index.d.ts +1 -0
- package/build/src/akuma/projection.d.ts +25 -0
- package/build/src/akuma/projection.js +24 -7
- package/build/src/akuma/provider-recipe.d.ts +11 -0
- package/build/src/akuma/provider-recipe.js +23 -0
- package/build/src/akuma/provider.d.ts +25 -0
- package/build/src/akuma/provider.js +99 -7
- package/build/src/akuma/providers/acp/events.js +25 -2
- package/build/src/akuma/providers/claude/events.js +76 -10
- package/build/src/akuma/providers/claude/index.js +2 -5
- package/build/src/akuma/providers/codex-app-server/events.js +17 -2
- package/build/src/akuma/providers/codex-app-server/index.js +15 -9
- package/build/src/akuma/providers/decode.d.ts +8 -0
- package/build/src/akuma/providers/decode.js +21 -0
- package/build/src/akuma/providers/grok-build/index.js +10 -26
- package/build/src/akuma/providers/opencode-sdk/event-stream.d.ts +3 -3
- package/build/src/akuma/providers/opencode-sdk/event-stream.js +2 -25
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +7 -43
- package/build/src/akuma/providers/opencode-sdk/events.js +147 -284
- package/build/src/akuma/providers/opencode-sdk/index.js +267 -421
- package/build/src/akuma/providers/opencode-sdk/native-http.d.ts +9 -0
- package/build/src/akuma/providers/opencode-sdk/native-http.js +69 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +42 -36
- package/build/src/akuma/providers/opencode-sdk/session.js +75 -82
- package/build/src/akuma/providers/paseo/events.d.ts +8 -0
- package/build/src/akuma/providers/paseo/events.js +58 -11
- package/build/src/akuma/providers/paseo/index.js +11 -7
- package/build/src/akuma/providers/pi/events.d.ts +2 -0
- package/build/src/akuma/providers/pi/events.js +32 -24
- package/build/src/akuma/providers/pi/index.js +21 -35
- package/build/src/akuma/request-lifecycle.d.ts +1 -2
- package/build/src/akuma/request-lifecycle.js +2 -8
- package/build/src/akuma/request-rendezvous.js +38 -55
- package/build/src/akuma/request-serve.d.ts +1 -1
- package/build/src/akuma/request-serve.js +77 -113
- package/build/src/akuma/selection-observation.d.ts +57 -51
- package/build/src/akuma/selection-observation.js +3 -3
- package/build/src/akuma/turn-drive.d.ts +3 -0
- package/build/src/akuma/turn-drive.js +26 -5
- package/build/src/akuma-body.js +12 -22
- package/build/src/cli/akuma-caller.js +10 -0
- package/build/src/cli/commands/akuma-grammar.js +3 -4
- package/build/src/cli/commands/akuma.js +13 -51
- package/build/src/cli/commands/contract-grammar.d.ts +7 -6
- package/build/src/cli/commands/contract-grammar.js +27 -32
- package/build/src/cli/commands/contract-help.d.ts +16 -7
- package/build/src/cli/commands/contract-help.js +20 -11
- package/build/src/cli/commands/contract.js +11 -11
- package/build/src/cli/commands/debt-grammar.d.ts +19 -0
- package/build/src/cli/commands/debt-grammar.js +323 -0
- package/build/src/cli/commands/debt-query.d.ts +5 -0
- package/build/src/cli/commands/{task-query.js → debt-query.js} +9 -9
- package/build/src/cli/commands/{task.d.ts → debt.d.ts} +2 -2
- package/build/src/cli/commands/{task.js → debt.js} +83 -143
- package/build/src/cli/coordinates.d.ts +1 -1
- package/build/src/cli/coordinates.js +1 -1
- package/build/src/cli/parse.d.ts +4 -4
- package/build/src/cli/parse.js +17 -16
- package/build/src/cli/render/akuma-activity.d.ts +11 -83
- package/build/src/cli/render/akuma-activity.js +20 -382
- package/build/src/cli/render/catalog.d.ts +2 -2
- package/build/src/cli/render/catalog.js +5 -5
- package/build/src/cli/render/contract.js +2 -2
- package/build/src/cli/render/debt.d.ts +28 -0
- package/build/src/cli/render/{task.js → debt.js} +51 -112
- package/build/src/cli/render/kanshi.js +28 -28
- package/build/src/cli/render/marks.d.ts +5 -5
- package/build/src/cli/render/marks.js +4 -10
- package/build/src/cli/render/nuke.js +1 -1
- package/build/src/cli/runtime.js +3 -3
- package/build/src/cli/scan.d.ts +1 -1
- package/build/src/cli/usage.d.ts +1 -1
- package/build/src/cli/usage.js +4 -4
- package/build/src/contract-worktree.js +3 -3
- package/build/src/debt/board.d.ts +160 -0
- package/build/src/{task → debt}/board.js +89 -89
- package/build/src/debt/catalog.d.ts +11 -0
- package/build/src/debt/catalog.js +59 -0
- package/build/src/{task → debt}/context.d.ts +1 -1
- package/build/src/{task → debt}/context.js +1 -1
- package/build/src/{task → debt}/created-observation.d.ts +6 -10
- package/build/src/{task → debt}/created-observation.js +7 -7
- package/build/src/debt/document.d.ts +37 -0
- package/build/src/{task → debt}/document.js +37 -37
- package/build/src/debt/identity.d.ts +21 -0
- package/build/src/{task → debt}/identity.js +35 -35
- package/build/src/debt/index.d.ts +111 -0
- package/build/src/debt/index.js +203 -0
- package/build/src/{task → debt}/input.d.ts +8 -8
- package/build/src/{task → debt}/input.js +21 -21
- package/build/src/debt/lifecycle-operations.d.ts +6 -0
- package/build/src/{task → debt}/lifecycle-operations.js +27 -31
- package/build/src/debt/mutation-result.d.ts +981 -0
- package/build/src/debt/mutation-result.js +109 -0
- package/build/src/debt/mutation.d.ts +424 -0
- package/build/src/debt/mutation.js +223 -0
- package/build/src/debt/operation-types.d.ts +71 -0
- package/build/src/debt/operations.d.ts +28 -0
- package/build/src/{task → debt}/operations.js +59 -60
- package/build/src/debt/query.d.ts +76 -0
- package/build/src/{task → debt}/query.js +55 -55
- package/build/src/{task → debt}/store.d.ts +13 -13
- package/build/src/{task → debt}/store.js +58 -49
- package/build/src/debt/view.d.ts +4 -0
- package/build/src/debt/view.js +7 -0
- package/build/src/identity/coordinates.d.ts +1 -1
- package/build/src/index.d.ts +13 -13
- package/build/src/index.js +9 -9
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +33 -33
- package/build/src/kanshi/report.d.ts +7 -7
- package/build/src/kanshi/select.js +4 -4
- package/build/src/library/akumas.d.ts +2 -3
- package/build/src/library/bind.d.ts +3 -3
- package/build/src/library/bind.js +5 -5
- package/build/src/library/contract-creation.js +5 -5
- package/build/src/library/contract-execution.js +4 -4
- package/build/src/library/contract-types.d.ts +2 -2
- package/build/src/library/input.d.ts +2 -2
- package/build/src/library/input.js +5 -5
- package/build/src/library/invocation.js +2 -2
- package/build/src/library/nuke.js +3 -3
- package/build/src/library/outcome.d.ts +126 -126
- package/build/src/library/outcome.js +3 -3
- package/build/src/library/selection.d.ts +1 -4
- package/build/src/library/selection.js +11 -62
- package/build/src/plugin/public.d.ts +5 -0
- package/build/src/plugin/runtime.js +3 -1
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/fence.d.ts +3 -3
- package/build/src/settlement/fence.js +4 -4
- package/build/src/settlement/holder.d.ts +16 -16
- package/build/src/settlement/holder.js +69 -63
- package/build/src/settlement/settle.d.ts +5 -5
- package/build/src/settlement/settle.js +53 -53
- package/package.json +12 -6
- package/plugins/paseo/plugin/index.server.ts +17 -0
- package/plugins/paseo/plugin/paseo-plugin.json +4 -0
- package/plugins/paseo/plugin/server/attach.ts +52 -0
- package/plugins/paseo/plugin/server/keiyaku.ts +44 -0
- package/plugins/paseo/plugin/server/message.ts +60 -0
- package/plugins/paseo/plugin/server/mirror.ts +193 -0
- package/plugins/paseo/plugin/server/model.ts +14 -0
- package/plugins/paseo/plugin/server/provider.ts +276 -0
- package/plugins/paseo/plugin/server/sdk-worker.ts +237 -0
- package/plugins/paseo/plugin/server/sdk.ts +289 -0
- package/plugins/paseo/plugin/server/stop.ts +26 -0
- package/plugins/paseo/plugin/server/transport.ts +35 -0
- package/plugins/paseo/plugin/shared/attach-rpc.ts +10 -0
- package/plugins/paseo/plugin/shared/refresh-rpc.ts +6 -0
- package/plugins/paseo/plugin/tsconfig.json +6 -0
- package/skills/keiyaku/SKILL.md +5 -6
- package/skills/keiyaku-akuma/SKILL.md +1 -1
- package/skills/keiyaku-akuma/references/automation.md +8 -8
- package/skills/keiyaku-babysit/SKILL.md +1 -1
- package/skills/keiyaku-bind/SKILL.md +9 -9
- package/skills/keiyaku-debt/SKILL.md +91 -0
- package/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/claude/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/codex/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +0 -104
- package/build/skills/keiyaku-task/SKILL.md +0 -104
- package/build/src/cli/commands/task-grammar.d.ts +0 -19
- package/build/src/cli/commands/task-grammar.js +0 -384
- package/build/src/cli/commands/task-query.d.ts +0 -5
- package/build/src/cli/render/task.d.ts +0 -39
- package/build/src/task/board.d.ts +0 -168
- package/build/src/task/catalog.d.ts +0 -11
- package/build/src/task/catalog.js +0 -59
- package/build/src/task/compose-language.d.ts +0 -57
- package/build/src/task/compose-language.js +0 -364
- package/build/src/task/compose-parser.d.ts +0 -32
- package/build/src/task/compose-parser.js +0 -180
- package/build/src/task/compose.d.ts +0 -16
- package/build/src/task/compose.js +0 -189
- package/build/src/task/document.d.ts +0 -39
- package/build/src/task/identity.d.ts +0 -21
- package/build/src/task/index.d.ts +0 -138
- package/build/src/task/index.js +0 -269
- package/build/src/task/lifecycle-operations.d.ts +0 -6
- package/build/src/task/mutation-result.d.ts +0 -1726
- package/build/src/task/mutation-result.js +0 -198
- package/build/src/task/mutation.d.ts +0 -590
- package/build/src/task/mutation.js +0 -262
- package/build/src/task/operation-types.d.ts +0 -71
- package/build/src/task/operations.d.ts +0 -28
- package/build/src/task/query.d.ts +0 -76
- package/build/src/task/view.d.ts +0 -4
- package/build/src/task/view.js +0 -7
- package/skills/keiyaku-task/SKILL.md +0 -104
- /package/build/src/{task → debt}/operation-types.js +0 -0
|
@@ -1,445 +1,301 @@
|
|
|
1
|
-
import { AgentEventChannel, createProviderAttempt, } from "../../provider.js";
|
|
1
|
+
import { AgentEventChannel, createProviderAttempt, launchPrompt, tellRegistry, } from "../../provider.js";
|
|
2
2
|
import { nonblank, object } from "../decode.js";
|
|
3
3
|
import { providerLifecycle } from "../lifecycle.js";
|
|
4
4
|
import { ownEventStream } from "./event-stream.js";
|
|
5
|
-
import { createEventState, mapEvent } from "./events.js";
|
|
5
|
+
import { createEventState, diagnostic, mapEvent } from "./events.js";
|
|
6
6
|
import { coordinate, loadOpencode, messageId, OPENCODE_SDK_PROVIDER, parseModel, } from "./session.js";
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
const nested = object(value?.data);
|
|
10
|
-
return nonblank(nested?.message) ?? nonblank(value?.message) ?? String(error);
|
|
11
|
-
}
|
|
12
|
-
function opencodeSessionId(coordinateValue) {
|
|
13
|
-
if (!("sessionId" in coordinateValue) || coordinateValue.sessionId === undefined) {
|
|
7
|
+
function sessionId(value) {
|
|
8
|
+
if (!("sessionId" in value) || value.sessionId === undefined)
|
|
14
9
|
throw new Error("OpenCode resume requires sessionId");
|
|
15
|
-
|
|
16
|
-
return coordinateValue.sessionId;
|
|
10
|
+
return value.sessionId;
|
|
17
11
|
}
|
|
18
12
|
function admit(options) {
|
|
19
13
|
if (options.network !== undefined)
|
|
20
14
|
throw new Error("OpenCode does not support explicit network");
|
|
21
|
-
if (options.systemPromptMode === "replace")
|
|
22
|
-
throw new Error("OpenCode
|
|
23
|
-
}
|
|
15
|
+
if (options.systemPromptMode === "replace")
|
|
16
|
+
throw new Error("OpenCode v2 does not support replacing the native system prompt per Turn");
|
|
24
17
|
if (options.model !== undefined)
|
|
25
18
|
parseModel(options.model);
|
|
19
|
+
if (options.effort !== undefined && options.model === undefined)
|
|
20
|
+
throw new Error("OpenCode effort requires an explicit model");
|
|
26
21
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return { type: value };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function promptBody(input, messageID) {
|
|
38
|
-
const model = input.options.model === undefined ? undefined : parseModel(input.options.model);
|
|
39
|
-
const promptText = [input.body, ...input.launchTells.map((tell) => tell.text)]
|
|
40
|
-
.filter((part) => part.length > 0)
|
|
41
|
-
.join("\n\n");
|
|
42
|
-
return {
|
|
43
|
-
messageID,
|
|
44
|
-
...(model === undefined ? {} : { model }),
|
|
45
|
-
...(input.options.effort === undefined ? {} : { variant: input.options.effort }),
|
|
46
|
-
...(input.options.systemPrompt === undefined ? {} : { system: input.options.systemPrompt }),
|
|
47
|
-
parts: [{ type: "text", text: promptText }],
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function messageRows(value, sessionId) {
|
|
51
|
-
const response = object(value);
|
|
52
|
-
const rows = Array.isArray(response?.data) ? response.data : Array.isArray(value) ? value : [];
|
|
53
|
-
return rows.flatMap((row) => {
|
|
54
|
-
const entry = object(row);
|
|
55
|
-
const info = object(entry?.info);
|
|
56
|
-
if (info?.sessionID !== sessionId)
|
|
57
|
-
return [];
|
|
58
|
-
return [{ info, parts: Array.isArray(entry?.parts) ? entry.parts : [] }];
|
|
22
|
+
function ownRuntime(custody, runtime) {
|
|
23
|
+
let closing;
|
|
24
|
+
let resolve;
|
|
25
|
+
let reject;
|
|
26
|
+
const closed = new Promise((yes, no) => {
|
|
27
|
+
resolve = yes;
|
|
28
|
+
reject = no;
|
|
59
29
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return row;
|
|
65
|
-
const at = Number(object(row.info.time)?.created ?? 0);
|
|
66
|
-
const latestAt = Number(object(latest.info.time)?.created ?? 0);
|
|
67
|
-
return at > latestAt || (at === latestAt && String(row.info.id) > String(latest.info.id)) ? row : latest;
|
|
68
|
-
}, undefined);
|
|
69
|
-
}
|
|
70
|
-
async function readTurnResult(input) {
|
|
71
|
-
try {
|
|
72
|
-
const response = await input.session.messages({
|
|
73
|
-
path: { id: input.sessionId },
|
|
74
|
-
query: { directory: input.cwd },
|
|
75
|
-
throwOnError: true,
|
|
76
|
-
});
|
|
77
|
-
const rows = messageRows(response, input.sessionId);
|
|
78
|
-
const user = rows.find((row) => row.info.role === "user" && row.info.id === input.messageID);
|
|
79
|
-
if (user === undefined)
|
|
80
|
-
return { kind: "failed", diagnostic: "OpenCode completed without the admitted user message" };
|
|
81
|
-
const userId = nonblank(user.info.id);
|
|
82
|
-
const assistant = newest(rows.filter((row) => row.info.role === "assistant" && row.info.parentID === userId && row.info.summary !== true));
|
|
83
|
-
const historyId = nonblank(assistant?.info.id);
|
|
84
|
-
if (input.state.failure !== undefined)
|
|
85
|
-
return { kind: "failed", diagnostic: input.state.failure };
|
|
86
|
-
if (assistant === undefined)
|
|
87
|
-
return { kind: "failed", diagnostic: "OpenCode completed without a native assistant answer" };
|
|
88
|
-
if (assistant.info.error !== undefined)
|
|
89
|
-
return { kind: "failed", diagnostic: diagnostic(assistant.info.error) };
|
|
90
|
-
const answer = assistant.parts
|
|
91
|
-
.map((part) => object(part))
|
|
92
|
-
.filter((part) => part?.type === "text")
|
|
93
|
-
.map((part) => part?.text)
|
|
94
|
-
.filter((part) => typeof part === "string")
|
|
95
|
-
.join("\n\n");
|
|
96
|
-
return historyId === undefined ? { kind: "answered", answer } : { kind: "answered", answer, historyId };
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
return { kind: "failed", diagnostic: diagnostic(error) };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function nativeSessionId(value) {
|
|
103
|
-
const event = object(value);
|
|
104
|
-
const properties = object(event?.properties);
|
|
105
|
-
const direct = nonblank(properties?.sessionID);
|
|
106
|
-
if (direct !== undefined)
|
|
107
|
-
return direct;
|
|
108
|
-
const info = object(properties?.info);
|
|
109
|
-
const part = object(properties?.part);
|
|
110
|
-
return nonblank(info?.sessionID) ?? nonblank(part?.sessionID);
|
|
111
|
-
}
|
|
112
|
-
function scopedProperties(value, sessionId) {
|
|
113
|
-
const properties = object(object(value)?.properties);
|
|
114
|
-
return nativeSessionId(value) === sessionId ? properties : undefined;
|
|
115
|
-
}
|
|
116
|
-
function nativeProgress(type, properties, busy) {
|
|
117
|
-
if (type === "session.idle")
|
|
118
|
-
return { busy, terminal: busy };
|
|
119
|
-
if (type !== "session.status")
|
|
120
|
-
return { busy, terminal: false };
|
|
121
|
-
const status = object(properties.status)?.type;
|
|
122
|
-
const nextBusy = busy || status === "busy";
|
|
123
|
-
return { busy: nextBusy, terminal: status === "idle" && nextBusy };
|
|
124
|
-
}
|
|
125
|
-
function startsTurn(value, sessionId, messageID) {
|
|
126
|
-
const event = object(value);
|
|
127
|
-
const info = object(object(event?.properties)?.info);
|
|
128
|
-
return (event?.type === "message.updated" && info?.sessionID === sessionId && info.role === "user" && info.id === messageID);
|
|
129
|
-
}
|
|
130
|
-
function recognizeAdmission(value, sessionId, admission) {
|
|
131
|
-
const event = object(value);
|
|
132
|
-
const properties = object(event?.properties);
|
|
133
|
-
const info = object(properties?.info);
|
|
134
|
-
if (event?.type === "message.updated" &&
|
|
135
|
-
info?.sessionID === sessionId &&
|
|
136
|
-
info.role === "user" &&
|
|
137
|
-
typeof info.id === "string") {
|
|
138
|
-
admission.admit(info.id);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function observeEvent(input) {
|
|
142
|
-
const observedSessionId = nativeSessionId(input.value);
|
|
143
|
-
if (observedSessionId !== undefined && observedSessionId !== input.sessionId) {
|
|
144
|
-
return { active: input.active, busy: input.busy, terminal: false };
|
|
145
|
-
}
|
|
146
|
-
const event = object(input.value);
|
|
147
|
-
const properties = scopedProperties(input.value, input.sessionId);
|
|
148
|
-
if (properties === undefined) {
|
|
149
|
-
if (input.active)
|
|
150
|
-
mapEvent(input.value, input.events, input.state);
|
|
151
|
-
return { active: input.active, busy: input.busy, terminal: false };
|
|
152
|
-
}
|
|
153
|
-
const starts = !input.active && startsTurn(input.value, input.sessionId, input.messageID);
|
|
154
|
-
const active = input.active || starts;
|
|
155
|
-
const busy = starts ? false : input.busy;
|
|
156
|
-
recognizeAdmission(input.value, input.sessionId, input.admission);
|
|
157
|
-
const submittedError = event?.type === "session.error" && input.submissionState.started;
|
|
158
|
-
if (!active && !submittedError)
|
|
159
|
-
return { active, busy, terminal: false };
|
|
160
|
-
mapEvent(input.value, input.events, input.state);
|
|
161
|
-
if (event?.type === "session.error") {
|
|
162
|
-
input.admission.end();
|
|
163
|
-
return {
|
|
164
|
-
active,
|
|
165
|
-
busy,
|
|
166
|
-
terminal: true,
|
|
167
|
-
result: { kind: "failed", diagnostic: input.state.failure ?? diagnostic(properties.error) },
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
const progress = nativeProgress(event?.type, properties, busy);
|
|
171
|
-
return { active, busy: progress.busy, terminal: progress.terminal };
|
|
172
|
-
}
|
|
173
|
-
async function observeTurn(input) {
|
|
174
|
-
let active = false;
|
|
175
|
-
let busy = false;
|
|
176
|
-
try {
|
|
177
|
-
for (;;) {
|
|
178
|
-
const next = await input.stream.next();
|
|
179
|
-
if (next.done)
|
|
180
|
-
return { kind: "failed", diagnostic: "OpenCode event stream ended before Turn completion" };
|
|
181
|
-
const observed = observeEvent({
|
|
182
|
-
value: eventValue(next.value),
|
|
183
|
-
sessionId: input.sessionId,
|
|
184
|
-
messageID: input.messageID,
|
|
185
|
-
active,
|
|
186
|
-
busy,
|
|
187
|
-
admission: input.admission,
|
|
188
|
-
events: input.events,
|
|
189
|
-
state: input.state,
|
|
190
|
-
submissionState: input.submissionState,
|
|
191
|
-
});
|
|
192
|
-
active = observed.active;
|
|
193
|
-
busy = observed.busy;
|
|
194
|
-
if (observed.result !== undefined)
|
|
195
|
-
return observed.result;
|
|
196
|
-
if (!observed.terminal)
|
|
197
|
-
continue;
|
|
198
|
-
input.admission.end();
|
|
199
|
-
return await readTurnResult({ ...input, messageID: input.admission.latest() });
|
|
30
|
+
const close = () => {
|
|
31
|
+
if (closing === undefined) {
|
|
32
|
+
closing = runtime.close();
|
|
33
|
+
void closing.then(resolve, reject);
|
|
200
34
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
finally {
|
|
206
|
-
input.admission.end();
|
|
207
|
-
}
|
|
35
|
+
return closing;
|
|
36
|
+
};
|
|
37
|
+
custody.own({ closed, abort: close, forceDispose: close });
|
|
38
|
+
return close;
|
|
208
39
|
}
|
|
209
40
|
function liveAdmission(initial) {
|
|
210
41
|
let latest = initial;
|
|
211
42
|
let ended = false;
|
|
212
|
-
const
|
|
43
|
+
const submitted = new Set([initial]);
|
|
44
|
+
const tells = tellRegistry();
|
|
213
45
|
const wait = (id) => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return new Promise((resolve, reject) => {
|
|
217
|
-
pending.set(id, { resolve, reject });
|
|
218
|
-
});
|
|
46
|
+
submitted.add(id);
|
|
47
|
+
return tells.register(id, id);
|
|
219
48
|
};
|
|
220
49
|
return {
|
|
221
50
|
initial: wait(initial),
|
|
222
51
|
latest: () => latest,
|
|
223
52
|
open: () => !ended,
|
|
53
|
+
owns: (id) => submitted.has(id),
|
|
224
54
|
wait,
|
|
225
55
|
admit(id) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return;
|
|
229
|
-
pending.delete(id);
|
|
230
|
-
latest = id;
|
|
231
|
-
waiter.resolve({ kind: "accepted", fence: id });
|
|
56
|
+
if (tells.accept(id))
|
|
57
|
+
latest = id;
|
|
232
58
|
},
|
|
233
|
-
fail
|
|
234
|
-
|
|
235
|
-
pending.delete(id);
|
|
236
|
-
},
|
|
237
|
-
end() {
|
|
238
|
-
if (ended)
|
|
239
|
-
return;
|
|
59
|
+
fail: tells.fail,
|
|
60
|
+
end: () => {
|
|
240
61
|
ended = true;
|
|
241
|
-
|
|
242
|
-
waiter.resolve({ kind: "turn-ended" });
|
|
243
|
-
pending.clear();
|
|
62
|
+
tells.end();
|
|
244
63
|
},
|
|
245
64
|
};
|
|
246
65
|
}
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
settlement ??= (async () => {
|
|
253
|
-
// Whole closure is proved before narration ends. The driver therefore
|
|
254
|
-
// stays inside its Heart/control observation race while retirement is
|
|
255
|
-
// outstanding, so a rejected or unprovable closure still reaches attempt
|
|
256
|
-
// custody's bounded hung handling instead of parking on a pending
|
|
257
|
-
// completion. Ending narration first would move that await outside the
|
|
258
|
-
// driver's race and make the Body unresponsive to stop or Heart loss.
|
|
259
|
-
await close();
|
|
260
|
-
events.end();
|
|
261
|
-
resolve(result);
|
|
262
|
-
})();
|
|
263
|
-
return settlement;
|
|
264
|
-
};
|
|
265
|
-
});
|
|
266
|
-
return { completion, finish };
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* One memoized whole-close: stream retirement and existing server retirement
|
|
270
|
-
* start together on every terminal path, so a live socket can never outwait the
|
|
271
|
-
* server's death.
|
|
272
|
-
*/
|
|
273
|
-
function wholeClosure(retireStream, closeServer) {
|
|
274
|
-
let closing;
|
|
275
|
-
return () => {
|
|
276
|
-
closing ??= Promise.all([retireStream(), closeServer()]).then(() => undefined);
|
|
277
|
-
return closing;
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
async function forceDisposeOpencode(abortController, close, finish) {
|
|
281
|
-
abortController.abort();
|
|
282
|
-
await close();
|
|
283
|
-
await finish({ kind: "failed", diagnostic: "OpenCode session force-disposed" });
|
|
284
|
-
}
|
|
285
|
-
async function loadDriveRuntime(execution, input, signal, loader, onRuntime) {
|
|
286
|
-
const runtime = await loadOpencode({
|
|
287
|
-
execution,
|
|
288
|
-
cwd: input.cwd,
|
|
289
|
-
signal,
|
|
290
|
-
...(loader === undefined ? {} : { loader }),
|
|
291
|
-
...(onRuntime === undefined ? {} : { onRuntime }),
|
|
292
|
-
...(input.requests === undefined ? {} : { requests: input.requests.dir }),
|
|
293
|
-
});
|
|
294
|
-
if (signal.aborted) {
|
|
295
|
-
await runtime.close();
|
|
296
|
-
signal.throwIfAborted();
|
|
66
|
+
async function answer(runtime, id, assistantID, terminalID) {
|
|
67
|
+
const response = object(await runtime.client.session.message({ id, messageID: assistantID }));
|
|
68
|
+
const message = object(response?.data);
|
|
69
|
+
if (message?.id !== assistantID || message.type !== "assistant" || object(message.time)?.completed === undefined) {
|
|
70
|
+
return { kind: "failed", diagnostic: "OpenCode completed without its exact completed assistant message" };
|
|
297
71
|
}
|
|
298
|
-
|
|
72
|
+
if (message.error !== undefined)
|
|
73
|
+
return { kind: "failed", diagnostic: diagnostic(message.error) };
|
|
74
|
+
if (!Array.isArray(message.content))
|
|
75
|
+
return { kind: "failed", diagnostic: "OpenCode assistant content missing" };
|
|
76
|
+
const text = message.content
|
|
77
|
+
.flatMap((item) => {
|
|
78
|
+
const part = object(item);
|
|
79
|
+
return part?.type === "text" && typeof part.text === "string" ? [part.text] : [];
|
|
80
|
+
})
|
|
81
|
+
.join("\n\n");
|
|
82
|
+
// v2's terminal event projects a real idle message with this exact ID. Forking
|
|
83
|
+
// BEFORE that message includes the answer, without copying later native work.
|
|
84
|
+
const boundary = terminalID.replace(/^evt_/u, "msg_");
|
|
85
|
+
const projected = object(object(await runtime.client.session.message({ id, messageID: boundary }))?.data);
|
|
86
|
+
if (projected?.id !== boundary || projected.type !== "idle" || projected.outcome !== "succeeded") {
|
|
87
|
+
return { kind: "failed", diagnostic: "OpenCode terminal fork boundary was not projected" };
|
|
88
|
+
}
|
|
89
|
+
return { kind: "answered", answer: text, historyId: boundary };
|
|
299
90
|
}
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
let
|
|
303
|
-
let
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
91
|
+
async function observe(input) {
|
|
92
|
+
const state = createEventState(input.id);
|
|
93
|
+
let delivered;
|
|
94
|
+
let step;
|
|
95
|
+
try {
|
|
96
|
+
for (;;) {
|
|
97
|
+
const next = await input.stream.next();
|
|
98
|
+
if (next.done)
|
|
99
|
+
return { kind: "failed", diagnostic: "OpenCode event stream ended before Turn completion" };
|
|
100
|
+
const event = object(next.value);
|
|
101
|
+
const data = object(event?.data);
|
|
102
|
+
if (data?.sessionID !== input.id)
|
|
103
|
+
continue;
|
|
104
|
+
const type = event?.type;
|
|
105
|
+
if (type === "session.inbox.enqueued" && typeof data.inboxID === "string" && object(data.item)?.type === "user") {
|
|
106
|
+
input.admission.admit(data.inboxID);
|
|
107
|
+
}
|
|
108
|
+
if (type === "session.inbox.delivered" && typeof data.inboxID === "string") {
|
|
109
|
+
if (!input.admission.owns(data.inboxID)) {
|
|
110
|
+
return {
|
|
111
|
+
kind: "failed",
|
|
112
|
+
diagnostic: "OpenCode consumed another writer's input; exact answer attribution unavailable",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
input.admission.admit(data.inboxID);
|
|
116
|
+
delivered = data.inboxID;
|
|
117
|
+
}
|
|
118
|
+
if (type === "session.step.started" && delivered !== undefined && typeof data.assistantMessageID === "string") {
|
|
119
|
+
step = { id: data.assistantMessageID, input: delivered, ended: false };
|
|
120
|
+
}
|
|
121
|
+
if (data.assistantMessageID !== undefined && data.assistantMessageID !== step?.id)
|
|
122
|
+
continue;
|
|
123
|
+
if (delivered !== undefined)
|
|
124
|
+
mapEvent(event, input.events, state);
|
|
125
|
+
if (type === "session.step.ended" && step !== undefined)
|
|
126
|
+
step.ended = true;
|
|
127
|
+
if (type === "session.step.failed" || type === "session.execution.failed") {
|
|
128
|
+
return { kind: "failed", diagnostic: diagnostic(data.error) };
|
|
129
|
+
}
|
|
130
|
+
if (type === "session.execution.interrupted")
|
|
131
|
+
return { kind: "failed", diagnostic: "OpenCode native execution interrupted" };
|
|
132
|
+
if (type !== "session.execution.succeeded" || delivered === undefined)
|
|
133
|
+
continue;
|
|
134
|
+
if (step === undefined || !step.ended || step.input !== input.admission.latest()) {
|
|
135
|
+
return { kind: "failed", diagnostic: "OpenCode completed without an assistant for the admitted input" };
|
|
136
|
+
}
|
|
137
|
+
const terminalID = nonblank(event?.id);
|
|
138
|
+
if (terminalID === undefined || !terminalID.startsWith("evt_"))
|
|
139
|
+
return { kind: "failed", diagnostic: "OpenCode terminal identity missing" };
|
|
140
|
+
return await answer(input.runtime, input.id, step.id, terminalID);
|
|
312
141
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
return { kind: "failed", diagnostic: `OpenCode observation failed: ${diagnostic(error)}` };
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
input.admission.end();
|
|
148
|
+
}
|
|
317
149
|
}
|
|
318
|
-
async function
|
|
150
|
+
async function openSession(runtime, input) {
|
|
151
|
+
const model = input.options.model === undefined
|
|
152
|
+
? undefined
|
|
153
|
+
: {
|
|
154
|
+
...parseModel(input.options.model),
|
|
155
|
+
...(input.options.effort === undefined ? {} : { variant: input.options.effort }),
|
|
156
|
+
};
|
|
319
157
|
const session = runtime.client.session;
|
|
320
|
-
const response =
|
|
321
|
-
? session.create({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
158
|
+
const response = input.session.kind === "fresh"
|
|
159
|
+
? await session.create({
|
|
160
|
+
body: { location: { directory: input.cwd }, ...(model === undefined ? {} : { model }) },
|
|
161
|
+
})
|
|
162
|
+
: await session.get({ id: sessionId(input.session.coordinate) });
|
|
163
|
+
const info = object(object(response)?.data);
|
|
164
|
+
const id = nonblank(info?.id);
|
|
165
|
+
if (id === undefined)
|
|
327
166
|
throw new Error("OpenCode did not return a session id");
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
};
|
|
351
|
-
return {
|
|
352
|
-
admission: { fence: input.sessionId },
|
|
353
|
-
events: input.events,
|
|
354
|
-
completion: input.completion,
|
|
355
|
-
tell: (tell) => submit({ ...tell, id: input.nextMessageId() }),
|
|
356
|
-
abort: async () => {
|
|
357
|
-
input.admission.end();
|
|
358
|
-
input.abortController.abort();
|
|
359
|
-
void input.session
|
|
360
|
-
.abort({ path: { id: input.sessionId }, query: { directory: input.cwd }, throwOnError: true })
|
|
361
|
-
.catch(() => undefined);
|
|
362
|
-
await input.finish({ kind: "failed", diagnostic: "OpenCode session interrupted" });
|
|
363
|
-
},
|
|
364
|
-
forceDispose: () => {
|
|
365
|
-
input.admission.end();
|
|
366
|
-
return forceDisposeOpencode(input.abortController, input.close, input.finish);
|
|
367
|
-
},
|
|
368
|
-
};
|
|
167
|
+
const active = object(object(await session.active())?.data);
|
|
168
|
+
if (active === undefined)
|
|
169
|
+
throw new Error("OpenCode active execution response missing");
|
|
170
|
+
if (active[id] !== undefined)
|
|
171
|
+
throw new Error("OpenCode native session is already busy");
|
|
172
|
+
let selected = object(info?.model);
|
|
173
|
+
if (input.session.kind === "resume" && model !== undefined) {
|
|
174
|
+
await session.switchModel({ id, model });
|
|
175
|
+
selected = object(object(object(await session.get({ id }))?.data)?.model);
|
|
176
|
+
}
|
|
177
|
+
if (model !== undefined &&
|
|
178
|
+
(selected?.providerID !== model.providerID ||
|
|
179
|
+
selected.id !== model.id ||
|
|
180
|
+
(selected.variant ?? "default") !== (input.options.effort ?? "default"))) {
|
|
181
|
+
throw new Error("OpenCode did not select the requested model");
|
|
182
|
+
}
|
|
183
|
+
if (input.options.systemPrompt !== undefined) {
|
|
184
|
+
// Native instruction entries join the system transcript and survive resume
|
|
185
|
+
// and fork. Updating the same entry announces an upstream instruction change.
|
|
186
|
+
await session.instructions({ id, text: input.options.systemPrompt });
|
|
187
|
+
}
|
|
188
|
+
return id;
|
|
369
189
|
}
|
|
370
190
|
async function drive(execution, input, loader, custody) {
|
|
371
191
|
admit(input.options);
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
192
|
+
if (input.schemaJson !== undefined)
|
|
193
|
+
throw new Error("OpenCode v2 does not expose native structured answer schemas");
|
|
194
|
+
if (input.session.kind === "resume")
|
|
195
|
+
sessionId(input.session.coordinate);
|
|
196
|
+
let closeRuntime;
|
|
197
|
+
const runtime = await loadOpencode({
|
|
198
|
+
execution,
|
|
199
|
+
cwd: input.cwd,
|
|
200
|
+
signal: custody.signal,
|
|
201
|
+
...(loader === undefined ? {} : { loader }),
|
|
202
|
+
onRuntime: (ready) => {
|
|
203
|
+
closeRuntime = ownRuntime(custody, ready);
|
|
204
|
+
},
|
|
205
|
+
...(input.requests === undefined ? {} : { requests: input.requests.dir }),
|
|
381
206
|
});
|
|
382
207
|
const stream = ownEventStream({
|
|
383
208
|
directory: input.cwd,
|
|
384
|
-
signal:
|
|
385
|
-
|
|
386
|
-
subscribe:
|
|
209
|
+
signal: custody.signal,
|
|
210
|
+
custody,
|
|
211
|
+
subscribe: runtime.client.event.subscribe,
|
|
387
212
|
});
|
|
388
|
-
|
|
213
|
+
let closing;
|
|
214
|
+
const close = () => (closing ??= Promise.all([stream.retire(), closeRuntime()]).then(() => undefined));
|
|
389
215
|
try {
|
|
390
|
-
const
|
|
391
|
-
const events = new AgentEventChannel();
|
|
392
|
-
const state = createEventState(sessionId);
|
|
393
|
-
const messageSequence = { value: 0 };
|
|
394
|
-
const nextMessageId = () => messageId(messageSequence.value++);
|
|
395
|
-
const messageID = nextMessageId();
|
|
396
|
-
const admission = liveAdmission(messageID);
|
|
397
|
-
const submissionState = { started: false };
|
|
398
|
-
events.emit({ type: "session", coordinate: coordinate(sessionId) });
|
|
216
|
+
const id = await openSession(runtime, input);
|
|
399
217
|
await stream.ready;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
throwOnError: true,
|
|
218
|
+
custody.signal.throwIfAborted();
|
|
219
|
+
const events = new AgentEventChannel();
|
|
220
|
+
events.emit({ type: "session", coordinate: coordinate(id) });
|
|
221
|
+
let sequence = 0;
|
|
222
|
+
const initial = messageId(sequence++);
|
|
223
|
+
const admission = liveAdmission(initial);
|
|
224
|
+
let control;
|
|
225
|
+
let finishing = false;
|
|
226
|
+
let finish;
|
|
227
|
+
const completion = new Promise((resolve) => {
|
|
228
|
+
let settlement;
|
|
229
|
+
finish = (result) => {
|
|
230
|
+
finishing = true;
|
|
231
|
+
return (settlement ??= (async () => {
|
|
232
|
+
await close();
|
|
233
|
+
events.end();
|
|
234
|
+
resolve(result);
|
|
235
|
+
})());
|
|
236
|
+
};
|
|
420
237
|
});
|
|
421
|
-
|
|
422
|
-
//
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
238
|
+
const observed = observe({ runtime, stream, id, admission, events });
|
|
239
|
+
// Start observation before submission: admission broadcasts can precede HTTP completion.
|
|
240
|
+
void observed
|
|
241
|
+
.then(async (result) => {
|
|
242
|
+
// Explicit interrupt owns its response too: a terminal broadcast can be
|
|
243
|
+
// published before that response, and must not close its connection early.
|
|
244
|
+
if (control !== undefined)
|
|
245
|
+
await control;
|
|
246
|
+
await finish(result);
|
|
247
|
+
})
|
|
248
|
+
.catch(() => undefined);
|
|
249
|
+
const prompt = async (nativeID, text) => {
|
|
250
|
+
const response = object(object(await runtime.client.session.prompt({ id, body: { id: nativeID, text } }))?.data);
|
|
251
|
+
if (response?.id !== nativeID || response.sessionID !== id || response.type !== "user") {
|
|
252
|
+
throw new Error("OpenCode returned no exact native inbox admission");
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
await prompt(initial, launchPrompt(input.body, input.launchTells));
|
|
256
|
+
custody.signal.throwIfAborted();
|
|
257
|
+
let submissions = admission.initial.then(() => undefined);
|
|
258
|
+
return {
|
|
259
|
+
admission: { fence: initial },
|
|
433
260
|
events,
|
|
434
261
|
completion,
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
262
|
+
tell(tell) {
|
|
263
|
+
const submit = async () => {
|
|
264
|
+
if (!admission.open())
|
|
265
|
+
return { kind: "turn-ended" };
|
|
266
|
+
const nativeID = messageId(sequence++);
|
|
267
|
+
const receipt = admission.wait(nativeID);
|
|
268
|
+
void prompt(nativeID, tell.text).catch((error) => admission.fail(nativeID, error));
|
|
269
|
+
return await receipt;
|
|
270
|
+
};
|
|
271
|
+
const next = submissions.then(submit);
|
|
272
|
+
submissions = next.then(() => undefined, () => undefined);
|
|
273
|
+
return next;
|
|
274
|
+
},
|
|
275
|
+
abort: async () => {
|
|
276
|
+
admission.end();
|
|
277
|
+
if (finishing) {
|
|
278
|
+
await close();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// The v2 interrupt response follows execution settlement. Do not hide its
|
|
282
|
+
// refusal; server and connection retirement must still run on failure.
|
|
283
|
+
control ??= runtime.client.session.interrupt({ id });
|
|
284
|
+
try {
|
|
285
|
+
await control;
|
|
286
|
+
}
|
|
287
|
+
finally {
|
|
288
|
+
await finish({ kind: "failed", diagnostic: "OpenCode session interrupted" });
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
forceDispose: async () => {
|
|
292
|
+
admission.end();
|
|
293
|
+
await finish({ kind: "failed", diagnostic: "OpenCode session force-disposed" });
|
|
294
|
+
},
|
|
295
|
+
};
|
|
439
296
|
}
|
|
440
297
|
catch (error) {
|
|
441
|
-
|
|
442
|
-
await wholeClose();
|
|
298
|
+
await close();
|
|
443
299
|
throw error;
|
|
444
300
|
}
|
|
445
301
|
}
|
|
@@ -450,18 +306,15 @@ export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, ki
|
|
|
450
306
|
admitOptions(options) {
|
|
451
307
|
try {
|
|
452
308
|
admit(options);
|
|
309
|
+
return { kind: "admitted", options: Object.freeze({ ...options }) };
|
|
453
310
|
}
|
|
454
311
|
catch (error) {
|
|
455
312
|
return { kind: "refused", diagnostic: diagnostic(error) };
|
|
456
313
|
}
|
|
457
|
-
return {
|
|
458
|
-
kind: "admitted",
|
|
459
|
-
options: Object.freeze({ ...options }),
|
|
460
|
-
};
|
|
461
314
|
},
|
|
462
315
|
...providerLifecycle((input, custody) => drive(execution, input, loader, custody)),
|
|
463
316
|
fork: (input) => createProviderAttempt(new AbortController().signal, async (custody) => {
|
|
464
|
-
const
|
|
317
|
+
const id = sessionId(input.session);
|
|
465
318
|
let close;
|
|
466
319
|
const runtime = await loadOpencode({
|
|
467
320
|
execution,
|
|
@@ -469,34 +322,27 @@ export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, ki
|
|
|
469
322
|
signal: custody.signal,
|
|
470
323
|
...(loader === undefined ? {} : { loader }),
|
|
471
324
|
onRuntime: (ready) => {
|
|
472
|
-
|
|
473
|
-
let settleRuntimeClosed;
|
|
474
|
-
let rejectRuntimeClosed;
|
|
475
|
-
const runtimeClosed = new Promise((resolve, reject) => {
|
|
476
|
-
settleRuntimeClosed = resolve;
|
|
477
|
-
rejectRuntimeClosed = reject;
|
|
478
|
-
});
|
|
479
|
-
close = () => {
|
|
480
|
-
if (closing === undefined) {
|
|
481
|
-
closing = ready.close();
|
|
482
|
-
void closing.then(settleRuntimeClosed, rejectRuntimeClosed);
|
|
483
|
-
}
|
|
484
|
-
return closing;
|
|
485
|
-
};
|
|
486
|
-
custody.own({ closed: runtimeClosed, abort: close, forceDispose: close });
|
|
325
|
+
close = ownRuntime(custody, ready);
|
|
487
326
|
},
|
|
488
327
|
});
|
|
489
328
|
try {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
const
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
329
|
+
custody.signal.throwIfAborted();
|
|
330
|
+
const point = object(object(await runtime.client.session.message({ id, messageID: input.at }))?.data);
|
|
331
|
+
if (point?.id !== input.at || point.type !== "idle" || point.outcome !== "succeeded") {
|
|
332
|
+
throw new Error("OpenCode fork requires the retained successful native terminal boundary");
|
|
333
|
+
}
|
|
334
|
+
const response = object(object(await runtime.client.session.fork({ id, before: input.at }))?.data);
|
|
335
|
+
const child = nonblank(response?.id);
|
|
336
|
+
const fork = object(response?.fork);
|
|
337
|
+
const boundary = object(fork?.boundary);
|
|
338
|
+
if (child === undefined ||
|
|
339
|
+
child === id ||
|
|
340
|
+
fork?.sessionID !== id ||
|
|
341
|
+
boundary?.type !== "before" ||
|
|
342
|
+
boundary.messageID !== input.at) {
|
|
343
|
+
throw new Error("OpenCode fork did not preserve the exact native boundary");
|
|
344
|
+
}
|
|
345
|
+
return { session: coordinate(child) };
|
|
500
346
|
}
|
|
501
347
|
finally {
|
|
502
348
|
await close();
|