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