@astrosheep/keiyaku 2.9.2 → 2.9.4
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/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +13 -94
- package/build/agents/harness/activity-values.js +16 -0
- package/build/agents/harness/event-persistence.js +218 -0
- package/build/agents/harness/execution-handle.js +25 -222
- package/build/agents/harness/index.js +3 -1
- package/build/agents/harness/outcome.js +47 -4
- package/build/agents/harness/projection.js +42 -59
- package/build/agents/harness/provider-adapter.js +58 -0
- package/build/agents/harness/router.js +11 -187
- package/build/agents/harness/runtime.js +17 -4
- package/build/agents/harness/session.js +15 -0
- package/build/agents/launch-snapshot/model.js +15 -236
- package/build/agents/launch-snapshot/parse-helpers.js +114 -0
- package/build/agents/launch-snapshot/resolve.js +17 -43
- package/build/agents/opencode-sdk.js +45 -12
- package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
- package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
- package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
- package/build/agents/providers/codex-app-server/adapter.js +50 -53
- package/build/agents/providers/codex-app-server/events.js +67 -11
- package/build/agents/providers/codex-app-server/identity.js +1 -0
- package/build/agents/providers/codex-app-server/policy.js +29 -0
- package/build/agents/providers/codex-app-server/registration.js +248 -0
- package/build/agents/providers/codex-app-server/session-schema.js +6 -0
- package/build/agents/providers/codex-app-server/session.js +23 -17
- package/build/agents/providers/codex-app-server/terms.js +1 -0
- package/build/agents/providers/opencode-sdk/adapter.js +94 -50
- package/build/agents/providers/opencode-sdk/events.js +8 -4
- package/build/agents/providers/opencode-sdk/registration.js +135 -0
- package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
- package/build/agents/providers/opencode-sdk/session.js +21 -14
- package/build/agents/providers/opencode-sdk/terms.js +1 -0
- package/build/agents/providers/pi/adapter.js +36 -30
- package/build/agents/providers/pi/registration.js +145 -0
- package/build/agents/providers/pi/session-schema.js +6 -0
- package/build/agents/providers/pi/terms.js +1 -0
- package/build/agents/providers/registration.js +7 -0
- package/build/agents/providers/registry.js +76 -0
- package/build/agents/selector.js +2 -29
- package/build/agents/types.js +8 -0
- package/build/artifact-directories.js +22 -0
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/view/handler.js +5 -4
- package/build/cli/commands/akuma.js +6 -0
- package/build/cli/commands/contract/amend/handler.js +8 -2
- package/build/cli/commands/contract/arc/handler.js +7 -1
- package/build/cli/commands/contract/bind/handler.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +34 -7
- package/build/cli/commands/contract/forfeit/handler.js +4 -4
- package/build/cli/commands/contract/log/handler.js +3 -3
- package/build/cli/commands/contract/petition/handler.js +4 -6
- package/build/cli/commands/contract/renew/handler.js +20 -6
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +58 -0
- package/build/cli/commands/projection/call/handler.js +6 -5
- package/build/cli/commands/projection/kill/handler.js +16 -32
- package/build/cli/commands/projection/shared.js +28 -0
- package/build/cli/commands/projection/status/handler.js +1 -1
- package/build/cli/commands/projection/tell/handler.js +14 -30
- package/build/cli/commands/projection/wait/handler.js +45 -22
- package/build/cli/commands/projection/wait/meta.js +7 -2
- package/build/cli/commands/registry.js +2 -54
- package/build/cli/commands/shared.js +10 -7
- package/build/cli/commands/skills/install/handler.js +2 -5
- package/build/cli/commands/skills/skills/handler.js +1 -1
- package/build/cli/commands/task/add/handler.js +5 -13
- package/build/cli/commands/task/catalog.js +2 -2
- package/build/cli/commands/task/done/handler.js +4 -8
- package/build/cli/commands/task/drop/handler.js +3 -6
- package/build/cli/commands/task/ls/handler.js +3 -5
- package/build/cli/commands/task/shared.js +14 -4
- package/build/cli/commands/task/{view → show}/handler.js +7 -8
- package/build/cli/commands/task/show/meta.js +8 -0
- package/build/cli/commands/task/start/handler.js +3 -6
- package/build/cli/commands/task/stop/handler.js +4 -8
- package/build/cli/commands/task/task/handler.js +1 -1
- package/build/cli/commands/task/update/handler.js +4 -8
- package/build/cli/completion.js +1 -1
- package/build/cli/flags.js +19 -3
- package/build/cli/help.js +4 -11
- package/build/cli/index.js +57 -83
- package/build/cli/missing-subcommand.js +7 -0
- package/build/cli/parse-flags.js +14 -5
- package/build/cli/parse-metadata.js +3 -3
- package/build/cli/parse-selectors.js +1 -1
- package/build/cli/parse.js +21 -0
- package/build/cli/projection-address.js +7 -12
- package/build/cli/render/address.js +41 -37
- package/build/cli/render/arc.js +24 -12
- package/build/cli/render/call.js +11 -5
- package/build/cli/render/errors.js +36 -1
- package/build/cli/render/format.js +4 -0
- package/build/cli/render/kanshi.js +6 -12
- package/build/cli/render/kill.js +9 -0
- package/build/cli/render/misc.js +55 -12
- package/build/cli/render/petition.js +20 -15
- package/build/cli/render/projection-activity.js +3 -0
- package/build/cli/render/shared.js +60 -21
- package/build/cli/render/skills.js +10 -0
- package/build/cli/render/status.js +37 -14
- package/build/cli/render/success-response.js +77 -0
- package/build/cli/render/task.js +54 -14
- package/build/cli/render/tell.js +5 -0
- package/build/cli/render/terminal-failure.js +58 -0
- package/build/cli/render/types.js +1 -0
- package/build/cli/render/wait.js +38 -36
- package/build/cli/types.js +1 -1
- package/build/config/akuma-loader.js +0 -5
- package/build/config/env-keys.js +1 -1
- package/build/config/provider-policy-ownership.js +3 -32
- package/build/config/settings/disease.js +8 -1
- package/build/config/settings/loader.js +11 -3
- package/build/config/settings/provider-instance-fields.js +51 -0
- package/build/config/settings/schema.js +6 -176
- package/build/core/addressing.js +66 -16
- package/build/core/amend.js +135 -18
- package/build/core/arc.js +19 -67
- package/build/core/bind.js +82 -35
- package/build/core/call/call.js +15 -9
- package/build/core/call/context.js +6 -51
- package/build/core/call/execution.js +21 -22
- package/build/core/call/prompt.js +14 -1
- package/build/core/command-markdown.js +45 -0
- package/build/core/context.js +13 -7
- package/build/core/contract-view.js +20 -3
- package/build/core/draft.js +123 -16
- package/build/core/forfeit.js +5 -2
- package/build/core/hints.js +1 -75
- package/build/core/identifier-slug.js +66 -0
- package/build/core/ids.js +25 -11
- package/build/core/ledger.js +20 -68
- package/build/core/markdown/parser.js +0 -68
- package/build/core/markdown/render.js +68 -0
- package/build/core/markdown/sections.js +2 -1
- package/build/core/outcome-base.js +4 -0
- package/build/core/projection/generation/database.js +2 -38
- package/build/core/projection/generation/model.js +42 -1
- package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
- package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
- package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
- package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
- package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
- package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
- package/build/core/projection/generation/store.js +2 -2
- package/build/core/projection/generation/transitions.js +1 -1
- package/build/core/projection/index.js +27 -0
- package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
- package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
- package/build/core/projection/projection-coordinate.js +61 -0
- package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
- package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
- package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
- package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
- package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
- package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
- package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
- package/build/core/projection/projection-terminal-failure.js +104 -0
- package/build/core/projection/projection-wait.js +278 -0
- package/build/core/projection/projection-wake.js +203 -0
- package/build/core/provider-loader-ports.js +4 -0
- package/build/core/ref-log.js +65 -40
- package/build/core/registry.js +63 -10
- package/build/core/renew-session.js +128 -0
- package/build/core/renew.js +426 -99
- package/build/core/scope.js +143 -66
- package/build/core/seal.js +139 -35
- package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
- package/build/core/{claim.js → settlement/claim.js} +32 -8
- package/build/core/settlement/index.js +19 -0
- package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
- package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
- package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
- package/build/core/settlement/petition-io.js +1 -0
- package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
- package/build/core/{petition.js → settlement/petition.js} +12 -10
- package/build/core/settlement/queue-read-model.js +143 -0
- package/build/core/settlement/queue-seat-allocation.js +156 -0
- package/build/core/settlement/queue.js +6 -0
- package/build/core/settlement/settlement.js +295 -0
- package/build/core/{verdict.js → settlement/verdict.js} +9 -37
- package/build/core/status/board.js +24 -23
- package/build/core/status/ledger-batch.js +158 -0
- package/build/core/status/reconciliation.js +10 -4
- package/build/core/stored-agent-event.js +3 -0
- package/build/core/task/board.js +40 -4
- package/build/core/task/commands.js +30 -21
- package/build/core/task/index.js +12 -0
- package/build/core/task/settlement-git.js +157 -65
- package/build/core/task/settlement-policy.js +54 -24
- package/build/core/task/task-contract.js +370 -0
- package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
- package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
- package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
- package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
- package/build/core/task/task-repository.js +1 -0
- package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
- package/build/core/task/task-store.js +1 -0
- package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
- package/build/core/task/task.js +5 -0
- package/build/core/task/tree.js +37 -0
- package/build/core/task/validation.js +5 -2
- package/build/core/transcripts.js +15 -3
- package/build/core/worktree-path.js +43 -28
- package/build/flow-error.js +85 -39
- package/build/fs/durability.js +27 -0
- package/build/fs/path-coordinate.js +16 -0
- package/build/generated/version.js +3 -2
- package/build/git/branches.js +2 -1
- package/build/git/core.js +106 -2
- package/build/git/path-coordinate.js +17 -0
- package/build/git/refs.js +57 -0
- package/build/git/streaming-batch.js +301 -0
- package/build/git/worktree.js +65 -0
- package/build/index.js +3 -3
- package/build/keiyaku.js +2 -36
- package/package.json +4 -10
- package/skills/keiyaku/SKILL.md +3 -2
- package/skills/keiyaku-akuma/SKILL.md +10 -6
- package/skills/keiyaku-task/SKILL.md +1 -1
- package/skills/keiyaku-workflow/SKILL.md +25 -4
- package/build/agents/providers/codex-sdk.js +0 -329
- package/build/cli/commands/task/view/meta.js +0 -8
- package/build/core/command-io.js +0 -174
- package/build/core/path-coordinate.js +0 -27
- package/build/core/petition-claim.js +0 -117
- package/build/core/petition-run.js +0 -83
- package/build/core/projection-coordinate.js +0 -63
- package/build/core/projection-wait.js +0 -165
- package/build/core/projection-wake.js +0 -136
- package/build/core/queue.js +0 -343
- package/build/core/task-contract.js +0 -237
- package/build/core/task.js +0 -4
- /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
- /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
- /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
- /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
- /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { executableSchema, isConfigKeyForbidden, providerCommonFields, providerEnvSchema, structuredTomlValueSchema, writableRootItemSchema, } from "../../../config/settings/provider-instance-fields.js";
|
|
4
|
+
import { exactKeys, optionalJsonObject, optionalString, optionalStringRecord, parseAbsoluteRootList, snapshotError, } from "../../launch-snapshot/parse-helpers.js";
|
|
5
|
+
import { SUBAGENT_SESSION_VERSION, subagentSessionEnvelopeSchema, } from "../../session.js";
|
|
6
|
+
import { FlowError } from "../../../flow-error.js";
|
|
7
|
+
import { CODEX_APP_SERVER_PROVIDER } from "./identity.js";
|
|
8
|
+
import { buildCodexAppServerWorkspaceWriteSandboxPolicy, configOwnsApprovalPolicy, configOwnsSandboxPolicy, } from "./policy.js";
|
|
9
|
+
import { codexAppServerSessionSchema } from "./session-schema.js";
|
|
10
|
+
const startCodexAppServer = async (prompt, cwd, options) => {
|
|
11
|
+
const { startCodexAppServer: start } = await import("./adapter.js");
|
|
12
|
+
return start(prompt, cwd, options);
|
|
13
|
+
};
|
|
14
|
+
const ACCESS_VALUES = new Set(["write"]);
|
|
15
|
+
const NETWORK_VALUES = new Set(["disabled", "enabled"]);
|
|
16
|
+
function admitResumeShape(schema, session, resumePath) {
|
|
17
|
+
const envelopeResult = subagentSessionEnvelopeSchema.safeParse(session);
|
|
18
|
+
if (!envelopeResult.success) {
|
|
19
|
+
throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session envelope.`);
|
|
20
|
+
}
|
|
21
|
+
if (session.provider !== CODEX_APP_SERVER_PROVIDER) {
|
|
22
|
+
throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' was created with provider '${session.provider}', but the selected subagent resolves to '${CODEX_APP_SERVER_PROVIDER}'. Cross-provider resume is not supported.`);
|
|
23
|
+
}
|
|
24
|
+
if (session.version !== SUBAGENT_SESSION_VERSION) {
|
|
25
|
+
throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' uses unsupported session version '${session.version}' for provider '${session.provider}'.`);
|
|
26
|
+
}
|
|
27
|
+
const shapeResult = schema.safeParse(session.handle);
|
|
28
|
+
if (!shapeResult.success) {
|
|
29
|
+
throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session handle for provider '${session.provider}'.`);
|
|
30
|
+
}
|
|
31
|
+
return shapeResult.data;
|
|
32
|
+
}
|
|
33
|
+
function parseSandboxPolicy(value) {
|
|
34
|
+
const sandbox = value;
|
|
35
|
+
if (sandbox === null || typeof sandbox !== "object" || Array.isArray(sandbox)) {
|
|
36
|
+
throw snapshotError("provider.sandboxPolicy", "must be a plain JSON object");
|
|
37
|
+
}
|
|
38
|
+
const record = sandbox;
|
|
39
|
+
exactKeys(record, ["type", "writableRoots", "networkAccess", "excludeTmpdirEnvVar", "excludeSlashTmp"], "provider.sandboxPolicy");
|
|
40
|
+
if (record.type !== "workspaceWrite")
|
|
41
|
+
throw snapshotError("provider.sandboxPolicy.type", "must equal 'workspaceWrite'");
|
|
42
|
+
const roots = parseAbsoluteRootList(record.writableRoots, "provider.sandboxPolicy.writableRoots");
|
|
43
|
+
for (const key of ["networkAccess", "excludeTmpdirEnvVar", "excludeSlashTmp"]) {
|
|
44
|
+
if (typeof record[key] !== "boolean")
|
|
45
|
+
throw snapshotError(`provider.sandboxPolicy.${key}`, "must be a boolean");
|
|
46
|
+
}
|
|
47
|
+
return Object.freeze({
|
|
48
|
+
type: "workspaceWrite",
|
|
49
|
+
writableRoots: roots,
|
|
50
|
+
networkAccess: record.networkAccess,
|
|
51
|
+
excludeTmpdirEnvVar: record.excludeTmpdirEnvVar,
|
|
52
|
+
excludeSlashTmp: record.excludeSlashTmp,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export const codexAppServerRegistration = {
|
|
56
|
+
name: CODEX_APP_SERVER_PROVIDER,
|
|
57
|
+
start: startCodexAppServer,
|
|
58
|
+
sessionSchema: codexAppServerSessionSchema,
|
|
59
|
+
rejectsGlobalWritableRoots: true,
|
|
60
|
+
effortField: "effort",
|
|
61
|
+
instanceSchema: z
|
|
62
|
+
.object({
|
|
63
|
+
kind: z.literal(CODEX_APP_SERVER_PROVIDER),
|
|
64
|
+
...providerCommonFields,
|
|
65
|
+
executable: executableSchema.optional(),
|
|
66
|
+
profile: z.string().optional(),
|
|
67
|
+
config: structuredTomlValueSchema.optional(),
|
|
68
|
+
writableRoots: z.array(writableRootItemSchema).optional(),
|
|
69
|
+
env: providerEnvSchema,
|
|
70
|
+
})
|
|
71
|
+
.strict()
|
|
72
|
+
.superRefine((data, ctx) => {
|
|
73
|
+
if (data.config) {
|
|
74
|
+
for (const key of Object.keys(data.config)) {
|
|
75
|
+
if (isConfigKeyForbidden(key)) {
|
|
76
|
+
ctx.addIssue({
|
|
77
|
+
code: z.ZodIssueCode.custom,
|
|
78
|
+
path: ["config", key],
|
|
79
|
+
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}),
|
|
85
|
+
builtinInstance: {
|
|
86
|
+
name: CODEX_APP_SERVER_PROVIDER,
|
|
87
|
+
source: "builtin",
|
|
88
|
+
instance: { kind: CODEX_APP_SERVER_PROVIDER, executable: "codex" },
|
|
89
|
+
},
|
|
90
|
+
builtinAkumaProfiles: [
|
|
91
|
+
{ name: "akuma-codex", description: "Default Codex app-server Akuma." },
|
|
92
|
+
],
|
|
93
|
+
portablePolicy: {
|
|
94
|
+
access: new Set(ACCESS_VALUES),
|
|
95
|
+
network: new Set(NETWORK_VALUES),
|
|
96
|
+
webSearch: new Set(),
|
|
97
|
+
},
|
|
98
|
+
maxCaptureChars: (config) => config.codex.execMaxCaptureChars,
|
|
99
|
+
buildCallTerms: ({ snapshot, baseTerms }) => {
|
|
100
|
+
const provider = snapshot.provider;
|
|
101
|
+
return {
|
|
102
|
+
...baseTerms,
|
|
103
|
+
provider: CODEX_APP_SERVER_PROVIDER,
|
|
104
|
+
...(provider.model !== undefined ? { model: provider.model } : {}),
|
|
105
|
+
...(provider.effort !== undefined ? { effort: provider.effort } : {}),
|
|
106
|
+
...(provider.profile !== undefined ? { profile: provider.profile } : {}),
|
|
107
|
+
...(provider.config !== undefined ? { config: provider.config } : {}),
|
|
108
|
+
...(provider.executable !== undefined ? { executable: provider.executable } : {}),
|
|
109
|
+
...(provider.sandboxPolicy !== undefined
|
|
110
|
+
? { sandboxPolicy: provider.sandboxPolicy }
|
|
111
|
+
: {}),
|
|
112
|
+
...(provider.approvalPolicy !== undefined ? { approvalPolicy: provider.approvalPolicy } : {}),
|
|
113
|
+
...(provider.env !== undefined ? { env: provider.env } : {}),
|
|
114
|
+
developerInstructions: snapshot.akuma.body || undefined,
|
|
115
|
+
onCheckpoint() { },
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
resolveLaunchProvider: ({ common, instance, model, effort, executionCwd, projectRoot, network, }) => {
|
|
119
|
+
if (instance.kind !== CODEX_APP_SERVER_PROVIDER) {
|
|
120
|
+
throw new Error(`resolveLaunchProvider expected ${CODEX_APP_SERVER_PROVIDER}`);
|
|
121
|
+
}
|
|
122
|
+
const codexInstance = instance;
|
|
123
|
+
const roots = [
|
|
124
|
+
path.resolve(executionCwd),
|
|
125
|
+
...(codexInstance.writableRoots ?? []).map((root) => path.resolve(projectRoot, root)),
|
|
126
|
+
];
|
|
127
|
+
const writableRoots = [...new Set(roots)];
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
...common,
|
|
130
|
+
kind: CODEX_APP_SERVER_PROVIDER,
|
|
131
|
+
...(codexInstance.executable !== undefined ? { executable: codexInstance.executable } : {}),
|
|
132
|
+
...(codexInstance.profile !== undefined ? { profile: codexInstance.profile } : {}),
|
|
133
|
+
...(codexInstance.config !== undefined ? { config: codexInstance.config } : {}),
|
|
134
|
+
...(model !== undefined ? { model } : {}),
|
|
135
|
+
...(effort !== undefined ? { effort } : {}),
|
|
136
|
+
...(!configOwnsSandboxPolicy(codexInstance.config)
|
|
137
|
+
? {
|
|
138
|
+
sandboxPolicy: buildCodexAppServerWorkspaceWriteSandboxPolicy({
|
|
139
|
+
writableRoots,
|
|
140
|
+
networkAccess: network === "enabled",
|
|
141
|
+
}),
|
|
142
|
+
}
|
|
143
|
+
: {}),
|
|
144
|
+
...(!configOwnsApprovalPolicy(codexInstance.config) ? { approvalPolicy: "never" } : {}),
|
|
145
|
+
...(codexInstance.env !== undefined ? { env: codexInstance.env } : {}),
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
parseLaunchProvider: (provider, common) => {
|
|
149
|
+
exactKeys(provider, ["name", "source", "kind", "executable", "profile", "config", "model", "effort", "sandboxPolicy", "approvalPolicy", "env"], "provider");
|
|
150
|
+
if ("approvalPolicy" in provider && provider.approvalPolicy !== "never") {
|
|
151
|
+
throw snapshotError("provider.approvalPolicy", "must equal 'never'");
|
|
152
|
+
}
|
|
153
|
+
const executable = optionalString(provider, "executable", "provider");
|
|
154
|
+
const profile = optionalString(provider, "profile", "provider");
|
|
155
|
+
const config = optionalJsonObject(provider, "config");
|
|
156
|
+
const model = optionalString(provider, "model", "provider");
|
|
157
|
+
const effort = optionalString(provider, "effort", "provider");
|
|
158
|
+
const sandboxPolicy = "sandboxPolicy" in provider ? parseSandboxPolicy(provider.sandboxPolicy) : undefined;
|
|
159
|
+
const env = optionalStringRecord(provider, "env");
|
|
160
|
+
return Object.freeze({
|
|
161
|
+
...common,
|
|
162
|
+
kind: CODEX_APP_SERVER_PROVIDER,
|
|
163
|
+
...(executable !== undefined ? { executable } : {}),
|
|
164
|
+
...(profile !== undefined ? { profile } : {}),
|
|
165
|
+
...(config !== undefined ? { config } : {}),
|
|
166
|
+
...(model !== undefined ? { model } : {}),
|
|
167
|
+
...(effort !== undefined ? { effort } : {}),
|
|
168
|
+
...(sandboxPolicy !== undefined ? { sandboxPolicy } : {}),
|
|
169
|
+
...(provider.approvalPolicy === "never" ? { approvalPolicy: "never" } : {}),
|
|
170
|
+
...(env !== undefined ? { env } : {}),
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
applyEffort: (provider, effort) => Object.freeze({ ...provider, effort }),
|
|
174
|
+
providerEffortAndModel: (provider) => {
|
|
175
|
+
const resolved = provider;
|
|
176
|
+
return { model: resolved.model, effort: resolved.effort };
|
|
177
|
+
},
|
|
178
|
+
attachLoader: (terms) => terms,
|
|
179
|
+
nativePolicyOwnership: (instanceName, instance) => {
|
|
180
|
+
if (instance.kind !== CODEX_APP_SERVER_PROVIDER)
|
|
181
|
+
return null;
|
|
182
|
+
const config = instance.config;
|
|
183
|
+
if (!config) {
|
|
184
|
+
return Object.freeze({
|
|
185
|
+
access: Object.freeze([]),
|
|
186
|
+
network: Object.freeze([]),
|
|
187
|
+
webSearch: Object.freeze([]),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const access = [];
|
|
191
|
+
const network = [];
|
|
192
|
+
const webSearch = [];
|
|
193
|
+
const owns = (key) => Object.prototype.hasOwnProperty.call(config, key);
|
|
194
|
+
const add = (target, key) => {
|
|
195
|
+
target.push({ path: `providers.${instanceName}.config.${key}`, value: config[key] });
|
|
196
|
+
};
|
|
197
|
+
if (owns("sandbox_mode"))
|
|
198
|
+
add(access, "sandbox_mode");
|
|
199
|
+
if (owns("approval_policy"))
|
|
200
|
+
add(access, "approval_policy");
|
|
201
|
+
for (const key of Object.keys(config)) {
|
|
202
|
+
if (key !== "sandbox_workspace_write" && !key.startsWith("sandbox_workspace_write."))
|
|
203
|
+
continue;
|
|
204
|
+
add(access, key);
|
|
205
|
+
add(network, key);
|
|
206
|
+
}
|
|
207
|
+
if (owns("web_search"))
|
|
208
|
+
add(webSearch, "web_search");
|
|
209
|
+
return Object.freeze({
|
|
210
|
+
access: Object.freeze(access),
|
|
211
|
+
network: Object.freeze(network),
|
|
212
|
+
webSearch: Object.freeze(webSearch),
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
startLive: async ({ terms, signal, reviveTerms, onSyncCheckpoint, onDeliveryOpportunity, onIngress, }) => {
|
|
216
|
+
if (terms.provider !== CODEX_APP_SERVER_PROVIDER) {
|
|
217
|
+
throw new Error(`startLive expected ${CODEX_APP_SERVER_PROVIDER}`);
|
|
218
|
+
}
|
|
219
|
+
const codexTerms = terms;
|
|
220
|
+
const options = {
|
|
221
|
+
model: codexTerms.model,
|
|
222
|
+
effort: codexTerms.effort,
|
|
223
|
+
profile: codexTerms.profile,
|
|
224
|
+
config: codexTerms.config,
|
|
225
|
+
sandboxPolicy: codexTerms.sandboxPolicy,
|
|
226
|
+
approvalPolicy: codexTerms.approvalPolicy,
|
|
227
|
+
executable: codexTerms.executable,
|
|
228
|
+
env: codexTerms.env,
|
|
229
|
+
signal,
|
|
230
|
+
developerInstructions: codexTerms.developerInstructions,
|
|
231
|
+
onSyncCheckpoint,
|
|
232
|
+
onDeliveryOpportunity,
|
|
233
|
+
onEvidence: codexTerms.onEvidence,
|
|
234
|
+
onIngress,
|
|
235
|
+
initialTurn: codexTerms.initialTurn,
|
|
236
|
+
};
|
|
237
|
+
const { startCodexAppServer: start } = await import("./adapter.js");
|
|
238
|
+
if (reviveTerms) {
|
|
239
|
+
const session = admitResumeShape(codexAppServerSessionSchema, reviveTerms.session, reviveTerms.resumePath);
|
|
240
|
+
return start(codexTerms.prompt, codexTerms.workdir, {
|
|
241
|
+
...options,
|
|
242
|
+
threadId: session.threadId,
|
|
243
|
+
codexHome: session.codexHome,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return start(codexTerms.prompt, codexTerms.workdir, options);
|
|
247
|
+
},
|
|
248
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Session handle for codex-app-server: thread id plus optional CODEX_HOME for cross-process resume. */
|
|
3
|
+
export const codexAppServerSessionSchema = z.object({
|
|
4
|
+
threadId: z.string().trim().min(1),
|
|
5
|
+
codexHome: z.string().trim().min(1).optional(),
|
|
6
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { emitProviderEvidence, sha256Utf8 } from "../../harness/runtime.js";
|
|
3
3
|
import { coerceString } from "../../harness/runtime.js";
|
|
4
|
-
|
|
4
|
+
import { CODEX_APP_SERVER_PROVIDER } from "./identity.js";
|
|
5
5
|
export class CodexAppServerProcess {
|
|
6
6
|
onEvidence;
|
|
7
7
|
onEvidenceDiagnostic;
|
|
@@ -243,24 +243,30 @@ export class CodexAppServerProcess {
|
|
|
243
243
|
// Graceful cleanup is best-effort.
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
-
const forceKill =
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this.child.kill("SIGKILL");
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
// Graceful escalation is best-effort.
|
|
254
|
-
}
|
|
246
|
+
const forceKill = setTimeout(() => {
|
|
247
|
+
if (!this.exitSettled) {
|
|
248
|
+
try {
|
|
249
|
+
this.child.kill("SIGKILL");
|
|
255
250
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
251
|
+
catch {
|
|
252
|
+
// The hard bound below reports the missing exit proof.
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}, 1000);
|
|
256
|
+
forceKill.unref?.();
|
|
257
|
+
let hardBoundTimer;
|
|
258
|
+
const hardBound = new Promise((_, reject) => {
|
|
259
|
+
hardBoundTimer = setTimeout(() => reject(new Error(`${CODEX_APP_SERVER_PROVIDER} process exit was not observed`)), 2000);
|
|
260
|
+
const timer = hardBoundTimer;
|
|
262
261
|
timer.unref?.();
|
|
263
262
|
});
|
|
264
|
-
|
|
263
|
+
try {
|
|
264
|
+
await Promise.race([this.exitPromise, hardBound]);
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
clearTimeout(forceKill);
|
|
268
|
+
if (hardBoundTimer)
|
|
269
|
+
clearTimeout(hardBoundTimer);
|
|
270
|
+
}
|
|
265
271
|
}
|
|
266
272
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { FlowError } from "../../../flow-error.js";
|
|
3
2
|
import { appendDebugBlock, appendDebugLog } from "../../../telemetry/debug-log.js";
|
|
4
3
|
import { SUBAGENT_SESSION_VERSION } from "../../session.js";
|
|
5
4
|
import { createProjectionDriver } from "../../harness/execution-handle.js";
|
|
6
|
-
import {
|
|
5
|
+
import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
6
|
+
import { awaitProviderAbortSettlement, buildSubagentEnv, coerceString, createAbortError, emitProviderEvidence, sha256Utf8, } from "../../harness/runtime.js";
|
|
7
7
|
import { renderEventTypeSummary } from "../event-type-tail.js";
|
|
8
8
|
import { createOpencodeEventController } from "./events.js";
|
|
9
|
-
import { OPENCODE_DEFAULT_EXECUTABLE, OPENCODE_SDK_PROVIDER, OpencodeIdleMonitor, loadOpencodeSdk, sessionIdFrom, withAbort,
|
|
10
|
-
export
|
|
9
|
+
import { OPENCODE_DEFAULT_EXECUTABLE, OPENCODE_SDK_PROVIDER, OpencodeIdleMonitor, loadOpencodeSdk, sessionIdFrom, withAbort, } from "./session.js";
|
|
10
|
+
export { opencodeSessionSchema } from "./session-schema.js";
|
|
11
11
|
export function parseOpencodeModelRef(model, effort) {
|
|
12
12
|
const trimmed = model.trim();
|
|
13
13
|
const slash = trimmed.indexOf("/");
|
|
@@ -20,11 +20,12 @@ export function parseOpencodeModelRef(model, effort) {
|
|
|
20
20
|
...(effort !== undefined ? { variant: effort } : {}),
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
export function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
export async function settleOpencodeGracefulInterrupt(input) {
|
|
24
|
+
await input.interrupt();
|
|
25
|
+
await input.waitForIdle();
|
|
26
|
+
input.stopStream();
|
|
27
|
+
await input.waitForStream();
|
|
28
|
+
await input.drainHistory();
|
|
28
29
|
}
|
|
29
30
|
export async function startOpencode(prompt, cwd, options = {}) {
|
|
30
31
|
const sessionId = options.sessionId?.trim();
|
|
@@ -42,6 +43,9 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
42
43
|
let enqueueFailure;
|
|
43
44
|
let acceptingEnqueue = true;
|
|
44
45
|
let builderRef;
|
|
46
|
+
let streamTask;
|
|
47
|
+
let gracefulInterruptRequested = false;
|
|
48
|
+
let gracefulProviderSettlement;
|
|
45
49
|
let resolveRunSettled;
|
|
46
50
|
let rejectRunSettled;
|
|
47
51
|
const runSettled = new Promise((resolve, reject) => {
|
|
@@ -50,12 +54,17 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
50
54
|
});
|
|
51
55
|
runSettled.catch(() => undefined);
|
|
52
56
|
let eventController;
|
|
53
|
-
const asProviderError = (error) => error instanceof Error ? error :
|
|
57
|
+
const asProviderError = (error) => error instanceof Error ? error : createProviderFailure(String(error));
|
|
54
58
|
const rememberEnqueueFailure = (error) => {
|
|
55
59
|
const failure = asProviderError(error);
|
|
56
60
|
enqueueFailure ??= failure;
|
|
57
61
|
return failure;
|
|
58
62
|
};
|
|
63
|
+
const activeExecutionSignal = () => {
|
|
64
|
+
if (!executionSignal)
|
|
65
|
+
throw new Error(`${OPENCODE_SDK_PROVIDER} cannot continue: no active execution signal`);
|
|
66
|
+
return executionSignal;
|
|
67
|
+
};
|
|
59
68
|
const promptSteer = async (client, text, tellId) => {
|
|
60
69
|
if (options.onEvidence) {
|
|
61
70
|
emitProviderEvidence(options.onEvidence, {
|
|
@@ -66,13 +75,16 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
66
75
|
...sha256Utf8(text),
|
|
67
76
|
}, (textDiagnostic) => builderRef?.emit({ type: "diagnostic", text: textDiagnostic }));
|
|
68
77
|
}
|
|
69
|
-
const
|
|
78
|
+
const signal = activeExecutionSignal();
|
|
79
|
+
const receipt = await withAbort(client.v2.session.prompt({
|
|
70
80
|
sessionID: activeSessionId,
|
|
71
81
|
// OpenCode documents no client-id idempotence; omit it and retain the
|
|
72
82
|
// at-least-once retry window until such a contract exists.
|
|
73
83
|
prompt: { text },
|
|
74
84
|
delivery: "steer",
|
|
75
|
-
|
|
85
|
+
// V2 only schedules an admitted input for execution when resumed.
|
|
86
|
+
resume: true,
|
|
87
|
+
}, { throwOnError: true, signal }), signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
|
|
76
88
|
const admitted = receipt.data.data;
|
|
77
89
|
const ordinal = admitted.admittedSeq;
|
|
78
90
|
if (options.onEvidence) {
|
|
@@ -93,7 +105,8 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
93
105
|
const applyBaseModel = async (client) => {
|
|
94
106
|
if (!baseModelRef)
|
|
95
107
|
return;
|
|
96
|
-
|
|
108
|
+
const signal = activeExecutionSignal();
|
|
109
|
+
await withAbort(client.v2.session.switchModel({ sessionID: activeSessionId, model: baseModelRef }, { throwOnError: true, signal }), signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
|
|
97
110
|
};
|
|
98
111
|
const submitTell = async (tell) => {
|
|
99
112
|
if (!sdk || !activeSessionId) {
|
|
@@ -105,12 +118,12 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
105
118
|
if (!baseModelRef) {
|
|
106
119
|
throw new FlowError("INVALID_EFFORT_OVERRIDE", `Effort '${tellEffort}' requires an explicit model.`);
|
|
107
120
|
}
|
|
108
|
-
const signal =
|
|
121
|
+
const signal = activeExecutionSignal();
|
|
109
122
|
await waitIdle();
|
|
110
|
-
await client.v2.session.switchModel({
|
|
123
|
+
await withAbort(client.v2.session.switchModel({
|
|
111
124
|
sessionID: activeSessionId,
|
|
112
125
|
model: { providerID: baseModelRef.providerID, id: baseModelRef.id, variant: tellEffort },
|
|
113
|
-
}, { throwOnError: true, signal });
|
|
126
|
+
}, { throwOnError: true, signal }), signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
|
|
114
127
|
const ack = await promptSteer(client, tell.text, tell.id);
|
|
115
128
|
return {
|
|
116
129
|
ack,
|
|
@@ -176,29 +189,17 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
176
189
|
onIngress: options.onIngress,
|
|
177
190
|
async abort(mode) {
|
|
178
191
|
acceptingEnqueue = false;
|
|
179
|
-
streamAbort?.abort();
|
|
180
192
|
if (mode === "force") {
|
|
181
|
-
|
|
193
|
+
streamAbort?.abort();
|
|
194
|
+
const attempts = [
|
|
195
|
+
awaitProviderAbortSettlement(OPENCODE_SDK_PROVIDER, Promise.resolve().then(() => sdk?.close?.("force")), "forced server close did not settle"),
|
|
196
|
+
awaitProviderAbortSettlement(OPENCODE_SDK_PROVIDER, runSettled, "forced execution teardown did not settle"),
|
|
197
|
+
];
|
|
182
198
|
if (sdk && activeSessionId) {
|
|
183
|
-
|
|
184
|
-
await sdk.client.v2.session.interrupt({ sessionID: activeSessionId }, { throwOnError: true });
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
failures.push(error);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
try {
|
|
191
|
-
await sdk?.close?.("force");
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
failures.push(error);
|
|
195
|
-
}
|
|
196
|
-
try {
|
|
197
|
-
await runSettled;
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
failures.push(error);
|
|
199
|
+
attempts.push(awaitProviderAbortSettlement(OPENCODE_SDK_PROVIDER, Promise.resolve().then(() => sdk.client.v2.session.interrupt({ sessionID: activeSessionId }, { throwOnError: true })), "forced native interrupt did not settle"));
|
|
201
200
|
}
|
|
201
|
+
const failures = (await Promise.allSettled(attempts))
|
|
202
|
+
.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
202
203
|
if (failures.length === 1)
|
|
203
204
|
throw failures[0];
|
|
204
205
|
if (failures.length > 1) {
|
|
@@ -207,18 +208,40 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
207
208
|
return;
|
|
208
209
|
}
|
|
209
210
|
if (sdk && activeSessionId) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
gracefulInterruptRequested = true;
|
|
212
|
+
gracefulProviderSettlement ??= awaitProviderAbortSettlement(OPENCODE_SDK_PROVIDER, settleOpencodeGracefulInterrupt({
|
|
213
|
+
interrupt: () => sdk.client.v2.session.interrupt({ sessionID: activeSessionId }, { throwOnError: true }),
|
|
214
|
+
waitForIdle: () => {
|
|
215
|
+
if (!idleMonitor)
|
|
216
|
+
throw new Error(`${OPENCODE_SDK_PROVIDER} graceful abort lacked idle monitor`);
|
|
217
|
+
return idleMonitor.waitForIdle();
|
|
218
|
+
},
|
|
219
|
+
// Native inactivity is terminal authority. Only then stop the
|
|
220
|
+
// local stream and drain all durable history admitted so far.
|
|
221
|
+
stopStream: () => {
|
|
222
|
+
if (!streamAbort)
|
|
223
|
+
throw new Error(`${OPENCODE_SDK_PROVIDER} graceful abort lacked event stream`);
|
|
224
|
+
streamAbort.abort();
|
|
225
|
+
},
|
|
226
|
+
waitForStream: () => {
|
|
227
|
+
if (!streamTask)
|
|
228
|
+
throw new Error(`${OPENCODE_SDK_PROVIDER} graceful abort lacked stream task`);
|
|
229
|
+
return streamTask;
|
|
230
|
+
},
|
|
231
|
+
drainHistory: () => {
|
|
232
|
+
if (!eventController)
|
|
233
|
+
throw new Error(`${OPENCODE_SDK_PROVIDER} graceful abort lacked event history controller`);
|
|
234
|
+
return eventController.drainHistory(eventController.lastSeenSeq());
|
|
235
|
+
},
|
|
236
|
+
}));
|
|
237
|
+
await gracefulProviderSettlement;
|
|
216
238
|
}
|
|
217
239
|
},
|
|
218
240
|
async run(builder) {
|
|
219
241
|
builderRef = builder;
|
|
220
242
|
streamAbort = new AbortController();
|
|
221
243
|
const combined = new AbortController();
|
|
244
|
+
const streamSignal = AbortSignal.any([combined.signal, streamAbort.signal]);
|
|
222
245
|
executionSignal = combined.signal;
|
|
223
246
|
const forward = () => combined.abort();
|
|
224
247
|
builder.signal.addEventListener("abort", forward, { once: true });
|
|
@@ -228,7 +251,7 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
228
251
|
const control = { enqueue };
|
|
229
252
|
try {
|
|
230
253
|
const env = buildSubagentEnv(options.env);
|
|
231
|
-
sdk = await withAbort(loadOpencodeSdk(executable, cwd, env, combined.signal), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
|
|
254
|
+
sdk = await withAbort(loadOpencodeSdk(executable, cwd, env, combined.signal, options.loader), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
|
|
232
255
|
const sessionResponse = sessionId
|
|
233
256
|
? await withAbort(sdk.client.v2.session.get({ sessionID: sessionId }, { throwOnError: true, signal: combined.signal }), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`)
|
|
234
257
|
: await withAbort(sdk.client.v2.session.create({
|
|
@@ -239,13 +262,14 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
239
262
|
if (!activeSessionId) {
|
|
240
263
|
const message = `${OPENCODE_SDK_PROVIDER} exec could not determine session id`;
|
|
241
264
|
builder.emit({ type: "fail", message });
|
|
242
|
-
throw
|
|
265
|
+
throw createProviderFailure(message);
|
|
243
266
|
}
|
|
244
267
|
idleMonitor = new OpencodeIdleMonitor(sdk.client.v2.session, activeSessionId, combined.signal);
|
|
245
268
|
eventController = createOpencodeEventController({
|
|
246
269
|
builder,
|
|
247
270
|
control,
|
|
248
271
|
idleMonitorIngress: () => idleMonitor?.ingress(),
|
|
272
|
+
idleMonitorTerminal: () => idleMonitor?.terminal(),
|
|
249
273
|
onEvidence: options.onEvidence,
|
|
250
274
|
onSyncCheckpoint: options.onSyncCheckpoint,
|
|
251
275
|
onDeliveryOpportunity: options.onDeliveryOpportunity,
|
|
@@ -259,23 +283,40 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
259
283
|
if (sessionId && baseModelRef) {
|
|
260
284
|
await applyBaseModel(sdk.client);
|
|
261
285
|
}
|
|
262
|
-
const
|
|
263
|
-
|
|
286
|
+
const eventsResultTask = withAbort(sdk.client.v2.session.events({ sessionID: activeSessionId }, { signal: streamSignal }), streamSignal, `${OPENCODE_SDK_PROVIDER} event stream cancelled by client`);
|
|
287
|
+
streamTask = (async () => {
|
|
264
288
|
try {
|
|
265
|
-
|
|
266
|
-
|
|
289
|
+
const eventsResult = await eventsResultTask;
|
|
290
|
+
const iterator = eventsResult.stream[Symbol.asyncIterator]();
|
|
291
|
+
for (;;) {
|
|
292
|
+
const next = await withAbort(iterator.next(), streamSignal, `${OPENCODE_SDK_PROVIDER} event stream cancelled by client`);
|
|
293
|
+
if (next.done || streamSignal.aborted)
|
|
267
294
|
break;
|
|
295
|
+
const item = next.value;
|
|
268
296
|
eventController.processUnknownStreamItem(item);
|
|
269
297
|
}
|
|
270
298
|
}
|
|
271
299
|
catch (error) {
|
|
272
|
-
if (!
|
|
300
|
+
if (!streamSignal.aborted)
|
|
273
301
|
throw error;
|
|
274
302
|
}
|
|
275
303
|
})();
|
|
304
|
+
streamTask.catch(() => undefined);
|
|
305
|
+
// Preserve provider sequencing: establish the stream before prompting.
|
|
306
|
+
// The acquisition itself lives inside streamTask, so local shutdown can
|
|
307
|
+
// always settle it even when the SDK never resolves events().
|
|
308
|
+
await eventsResultTask;
|
|
276
309
|
await promptSteer(sdk.client, prompt);
|
|
277
310
|
await waitIdle();
|
|
311
|
+
if (gracefulInterruptRequested) {
|
|
312
|
+
await gracefulProviderSettlement;
|
|
313
|
+
throw createAbortError(`${OPENCODE_SDK_PROVIDER} session interrupted`);
|
|
314
|
+
}
|
|
278
315
|
await drainEnqueueTail();
|
|
316
|
+
if (gracefulInterruptRequested) {
|
|
317
|
+
await gracefulProviderSettlement;
|
|
318
|
+
throw createAbortError(`${OPENCODE_SDK_PROVIDER} session interrupted`);
|
|
319
|
+
}
|
|
279
320
|
streamAbort.abort();
|
|
280
321
|
await streamTask;
|
|
281
322
|
await eventController.drainHistory(eventController.lastSeenSeq());
|
|
@@ -290,7 +331,7 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
290
331
|
const message = coerceString(finalAssistant.error.message) ?? `${OPENCODE_SDK_PROVIDER} assistant error`;
|
|
291
332
|
builder.emit({ type: "diagnostic", text: message });
|
|
292
333
|
builder.emit({ type: "fail", message });
|
|
293
|
-
throw
|
|
334
|
+
throw createProviderFailure(message);
|
|
294
335
|
}
|
|
295
336
|
if (finalMessage) {
|
|
296
337
|
builder.emit({
|
|
@@ -326,6 +367,7 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
326
367
|
idleMonitor?.dispose();
|
|
327
368
|
idleMonitor = undefined;
|
|
328
369
|
executionSignal = undefined;
|
|
370
|
+
streamTask = undefined;
|
|
329
371
|
try {
|
|
330
372
|
await sdk?.close?.();
|
|
331
373
|
}
|
|
@@ -342,3 +384,5 @@ export async function startOpencode(prompt, cwd, options = {}) {
|
|
|
342
384
|
},
|
|
343
385
|
});
|
|
344
386
|
}
|
|
387
|
+
// Compiler-level binding to the named ProviderAdapter function contract.
|
|
388
|
+
startOpencode;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { activityCallFromNamedTool } from "../../harness/activity-values.js";
|
|
2
2
|
import { AGENT_THOUGHT_TEXT_MAX_CHARS, } from "../../harness/events.js";
|
|
3
|
+
import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
3
4
|
import { coerceString, emitProviderEvidence } from "../../harness/runtime.js";
|
|
4
5
|
import { appendEventType } from "../event-type-tail.js";
|
|
5
|
-
import { OPENCODE_SDK_PROVIDER } from "./session.js";
|
|
6
|
+
import { OPENCODE_SDK_PROVIDER, withAbort } from "./session.js";
|
|
6
7
|
const OPENCODE_EVENT_PROMPTED = "session.next.prompted";
|
|
7
8
|
function isSessionDurableEvent(value) {
|
|
8
9
|
return Boolean(value
|
|
@@ -116,7 +117,7 @@ function extractFinalAssistantMessage(messages) {
|
|
|
116
117
|
return messages.find((message) => message.type === "assistant");
|
|
117
118
|
}
|
|
118
119
|
export function createOpencodeEventController(options) {
|
|
119
|
-
const { builder, control, idleMonitorIngress, onEvidence, onSyncCheckpoint, onDeliveryOpportunity, getSessionId, client, } = options;
|
|
120
|
+
const { builder, control, idleMonitorIngress, idleMonitorTerminal, onEvidence, onSyncCheckpoint, onDeliveryOpportunity, getSessionId, client, } = options;
|
|
120
121
|
const recordedEventTypes = [];
|
|
121
122
|
let totalEventCount = 0;
|
|
122
123
|
let currentTurnAdmitted = false;
|
|
@@ -186,6 +187,7 @@ export function createOpencodeEventController(options) {
|
|
|
186
187
|
});
|
|
187
188
|
}
|
|
188
189
|
clearLastTurnFailureOnTerminalEvidence();
|
|
190
|
+
idleMonitorTerminal();
|
|
189
191
|
break;
|
|
190
192
|
}
|
|
191
193
|
case "session.next.reasoning.ended": {
|
|
@@ -263,15 +265,17 @@ export function createOpencodeEventController(options) {
|
|
|
263
265
|
outputTokens: tokens.output,
|
|
264
266
|
});
|
|
265
267
|
clearLastTurnFailureOnTerminalEvidence();
|
|
268
|
+
idleMonitorTerminal();
|
|
266
269
|
break;
|
|
267
270
|
}
|
|
268
271
|
case "session.next.step.failed": {
|
|
269
272
|
const message = coerceString(event.data.error?.message) ?? `${OPENCODE_SDK_PROVIDER} step failed`;
|
|
270
|
-
const failure =
|
|
273
|
+
const failure = createProviderFailure(message);
|
|
271
274
|
currentTurnFailure = failure;
|
|
272
275
|
lastTurnFailure = failure;
|
|
273
276
|
builder.emit({ type: "diagnostic", text: message });
|
|
274
277
|
builder.emit({ type: "fail", message });
|
|
278
|
+
idleMonitorTerminal();
|
|
275
279
|
break;
|
|
276
280
|
}
|
|
277
281
|
default:
|
|
@@ -317,7 +321,7 @@ export function createOpencodeEventController(options) {
|
|
|
317
321
|
let cursor = after;
|
|
318
322
|
for (;;) {
|
|
319
323
|
const priorCursor = cursor;
|
|
320
|
-
const page = await client.v2.session.history({ sessionID: getSessionId(), after: cursor }, { throwOnError: true, signal: options.executionSignal });
|
|
324
|
+
const page = await withAbort(client.v2.session.history({ sessionID: getSessionId(), after: cursor }, { throwOnError: true, signal: options.executionSignal }), options.executionSignal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
|
|
321
325
|
const body = page.data;
|
|
322
326
|
const events = body.data ?? [];
|
|
323
327
|
for (const event of events) {
|