@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.
Files changed (238) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +13 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +25 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +47 -4
  8. package/build/agents/harness/projection.js +42 -59
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +50 -53
  21. package/build/agents/providers/codex-app-server/events.js +67 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/registration.js +145 -0
  36. package/build/agents/providers/pi/session-schema.js +6 -0
  37. package/build/agents/providers/pi/terms.js +1 -0
  38. package/build/agents/providers/registration.js +7 -0
  39. package/build/agents/providers/registry.js +76 -0
  40. package/build/agents/selector.js +2 -29
  41. package/build/agents/types.js +8 -0
  42. package/build/artifact-directories.js +22 -0
  43. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  44. package/build/cli/commands/akuma/view/handler.js +5 -4
  45. package/build/cli/commands/akuma.js +6 -0
  46. package/build/cli/commands/contract/amend/handler.js +8 -2
  47. package/build/cli/commands/contract/arc/handler.js +7 -1
  48. package/build/cli/commands/contract/bind/handler.js +4 -3
  49. package/build/cli/commands/contract/bind/meta.js +34 -7
  50. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  51. package/build/cli/commands/contract/log/handler.js +3 -3
  52. package/build/cli/commands/contract/petition/handler.js +4 -6
  53. package/build/cli/commands/contract/renew/handler.js +20 -6
  54. package/build/cli/commands/contract/renew/meta.js +5 -4
  55. package/build/cli/commands/metadata.js +58 -0
  56. package/build/cli/commands/projection/call/handler.js +6 -5
  57. package/build/cli/commands/projection/kill/handler.js +16 -32
  58. package/build/cli/commands/projection/shared.js +28 -0
  59. package/build/cli/commands/projection/status/handler.js +1 -1
  60. package/build/cli/commands/projection/tell/handler.js +14 -30
  61. package/build/cli/commands/projection/wait/handler.js +45 -22
  62. package/build/cli/commands/projection/wait/meta.js +7 -2
  63. package/build/cli/commands/registry.js +2 -54
  64. package/build/cli/commands/shared.js +10 -7
  65. package/build/cli/commands/skills/install/handler.js +2 -5
  66. package/build/cli/commands/skills/skills/handler.js +1 -1
  67. package/build/cli/commands/task/add/handler.js +5 -13
  68. package/build/cli/commands/task/catalog.js +2 -2
  69. package/build/cli/commands/task/done/handler.js +4 -8
  70. package/build/cli/commands/task/drop/handler.js +3 -6
  71. package/build/cli/commands/task/ls/handler.js +3 -5
  72. package/build/cli/commands/task/shared.js +14 -4
  73. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  74. package/build/cli/commands/task/show/meta.js +8 -0
  75. package/build/cli/commands/task/start/handler.js +3 -6
  76. package/build/cli/commands/task/stop/handler.js +4 -8
  77. package/build/cli/commands/task/task/handler.js +1 -1
  78. package/build/cli/commands/task/update/handler.js +4 -8
  79. package/build/cli/completion.js +1 -1
  80. package/build/cli/flags.js +19 -3
  81. package/build/cli/help.js +4 -11
  82. package/build/cli/index.js +57 -83
  83. package/build/cli/missing-subcommand.js +7 -0
  84. package/build/cli/parse-flags.js +14 -5
  85. package/build/cli/parse-metadata.js +3 -3
  86. package/build/cli/parse-selectors.js +1 -1
  87. package/build/cli/parse.js +21 -0
  88. package/build/cli/projection-address.js +7 -12
  89. package/build/cli/render/address.js +41 -37
  90. package/build/cli/render/arc.js +24 -12
  91. package/build/cli/render/call.js +11 -5
  92. package/build/cli/render/errors.js +36 -1
  93. package/build/cli/render/format.js +4 -0
  94. package/build/cli/render/kanshi.js +6 -12
  95. package/build/cli/render/kill.js +9 -0
  96. package/build/cli/render/misc.js +55 -12
  97. package/build/cli/render/petition.js +20 -15
  98. package/build/cli/render/projection-activity.js +3 -0
  99. package/build/cli/render/shared.js +60 -21
  100. package/build/cli/render/skills.js +10 -0
  101. package/build/cli/render/status.js +37 -14
  102. package/build/cli/render/success-response.js +77 -0
  103. package/build/cli/render/task.js +54 -14
  104. package/build/cli/render/tell.js +5 -0
  105. package/build/cli/render/terminal-failure.js +58 -0
  106. package/build/cli/render/types.js +1 -0
  107. package/build/cli/render/wait.js +38 -36
  108. package/build/cli/types.js +1 -1
  109. package/build/config/akuma-loader.js +0 -5
  110. package/build/config/env-keys.js +1 -1
  111. package/build/config/provider-policy-ownership.js +3 -32
  112. package/build/config/settings/disease.js +8 -1
  113. package/build/config/settings/loader.js +11 -3
  114. package/build/config/settings/provider-instance-fields.js +51 -0
  115. package/build/config/settings/schema.js +6 -176
  116. package/build/core/addressing.js +66 -16
  117. package/build/core/amend.js +135 -18
  118. package/build/core/arc.js +19 -67
  119. package/build/core/bind.js +82 -35
  120. package/build/core/call/call.js +15 -9
  121. package/build/core/call/context.js +6 -51
  122. package/build/core/call/execution.js +21 -22
  123. package/build/core/call/prompt.js +14 -1
  124. package/build/core/command-markdown.js +45 -0
  125. package/build/core/context.js +13 -7
  126. package/build/core/contract-view.js +20 -3
  127. package/build/core/draft.js +123 -16
  128. package/build/core/forfeit.js +5 -2
  129. package/build/core/hints.js +1 -75
  130. package/build/core/identifier-slug.js +66 -0
  131. package/build/core/ids.js +25 -11
  132. package/build/core/ledger.js +20 -68
  133. package/build/core/markdown/parser.js +0 -68
  134. package/build/core/markdown/render.js +68 -0
  135. package/build/core/markdown/sections.js +2 -1
  136. package/build/core/outcome-base.js +4 -0
  137. package/build/core/projection/generation/database.js +2 -38
  138. package/build/core/projection/generation/model.js +42 -1
  139. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  140. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
  141. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  142. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
  143. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  144. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
  145. package/build/core/projection/generation/store.js +2 -2
  146. package/build/core/projection/generation/transitions.js +1 -1
  147. package/build/core/projection/index.js +27 -0
  148. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  149. package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
  150. package/build/core/projection/projection-coordinate.js +61 -0
  151. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  152. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
  153. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  154. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  155. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
  156. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  157. package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
  158. package/build/core/projection/projection-terminal-failure.js +104 -0
  159. package/build/core/projection/projection-wait.js +278 -0
  160. package/build/core/projection/projection-wake.js +203 -0
  161. package/build/core/provider-loader-ports.js +4 -0
  162. package/build/core/ref-log.js +65 -40
  163. package/build/core/registry.js +63 -10
  164. package/build/core/renew-session.js +128 -0
  165. package/build/core/renew.js +426 -99
  166. package/build/core/scope.js +143 -66
  167. package/build/core/seal.js +139 -35
  168. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
  169. package/build/core/{claim.js → settlement/claim.js} +32 -8
  170. package/build/core/settlement/index.js +19 -0
  171. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
  172. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  173. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  174. package/build/core/settlement/petition-io.js +1 -0
  175. package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
  176. package/build/core/{petition.js → settlement/petition.js} +12 -10
  177. package/build/core/settlement/queue-read-model.js +143 -0
  178. package/build/core/settlement/queue-seat-allocation.js +156 -0
  179. package/build/core/settlement/queue.js +6 -0
  180. package/build/core/settlement/settlement.js +295 -0
  181. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  182. package/build/core/status/board.js +24 -23
  183. package/build/core/status/ledger-batch.js +158 -0
  184. package/build/core/status/reconciliation.js +10 -4
  185. package/build/core/stored-agent-event.js +3 -0
  186. package/build/core/task/board.js +40 -4
  187. package/build/core/task/commands.js +30 -21
  188. package/build/core/task/index.js +12 -0
  189. package/build/core/task/settlement-git.js +157 -65
  190. package/build/core/task/settlement-policy.js +54 -24
  191. package/build/core/task/task-contract.js +370 -0
  192. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  193. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  194. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  195. package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
  196. package/build/core/task/task-repository.js +1 -0
  197. package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
  198. package/build/core/task/task-store.js +1 -0
  199. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  200. package/build/core/task/task.js +5 -0
  201. package/build/core/task/tree.js +37 -0
  202. package/build/core/task/validation.js +5 -2
  203. package/build/core/transcripts.js +15 -3
  204. package/build/core/worktree-path.js +43 -28
  205. package/build/flow-error.js +85 -39
  206. package/build/fs/durability.js +27 -0
  207. package/build/fs/path-coordinate.js +16 -0
  208. package/build/generated/version.js +3 -2
  209. package/build/git/branches.js +2 -1
  210. package/build/git/core.js +106 -2
  211. package/build/git/path-coordinate.js +17 -0
  212. package/build/git/refs.js +57 -0
  213. package/build/git/streaming-batch.js +301 -0
  214. package/build/git/worktree.js +65 -0
  215. package/build/index.js +3 -3
  216. package/build/keiyaku.js +2 -36
  217. package/package.json +4 -10
  218. package/skills/keiyaku/SKILL.md +3 -2
  219. package/skills/keiyaku-akuma/SKILL.md +10 -6
  220. package/skills/keiyaku-task/SKILL.md +1 -1
  221. package/skills/keiyaku-workflow/SKILL.md +25 -4
  222. package/build/agents/providers/codex-sdk.js +0 -329
  223. package/build/cli/commands/task/view/meta.js +0 -8
  224. package/build/core/command-io.js +0 -174
  225. package/build/core/path-coordinate.js +0 -27
  226. package/build/core/petition-claim.js +0 -117
  227. package/build/core/petition-run.js +0 -83
  228. package/build/core/projection-coordinate.js +0 -63
  229. package/build/core/projection-wait.js +0 -165
  230. package/build/core/projection-wake.js +0 -136
  231. package/build/core/queue.js +0 -343
  232. package/build/core/task-contract.js +0 -237
  233. package/build/core/task.js +0 -4
  234. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  235. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  236. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  237. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  238. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -0,0 +1,135 @@
1
+ import { z } from "zod";
2
+ import { executableSchema, providerCommonFields, providerEnvSchema, } from "../../../config/settings/provider-instance-fields.js";
3
+ import { FlowError } from "../../../flow-error.js";
4
+ import { exactKeys, optionalString, optionalStringRecord, } from "../../launch-snapshot/parse-helpers.js";
5
+ import { SUBAGENT_SESSION_VERSION, subagentSessionEnvelopeSchema, } from "../../session.js";
6
+ import { OPENCODE_SDK_PROVIDER } from "./session.js";
7
+ import { opencodeSessionSchema } from "./session-schema.js";
8
+ const startOpencode = async (prompt, cwd, options) => {
9
+ const { startOpencode: start } = await import("./adapter.js");
10
+ return start(prompt, cwd, options);
11
+ };
12
+ function admitResumeShape(schema, session, resumePath) {
13
+ const envelopeResult = subagentSessionEnvelopeSchema.safeParse(session);
14
+ if (!envelopeResult.success) {
15
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session envelope.`);
16
+ }
17
+ if (session.provider !== OPENCODE_SDK_PROVIDER) {
18
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' was created with provider '${session.provider}', but the selected subagent resolves to '${OPENCODE_SDK_PROVIDER}'. Cross-provider resume is not supported.`);
19
+ }
20
+ if (session.version !== SUBAGENT_SESSION_VERSION) {
21
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' uses unsupported session version '${session.version}' for provider '${session.provider}'.`);
22
+ }
23
+ const shapeResult = schema.safeParse(session.handle);
24
+ if (!shapeResult.success) {
25
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session handle for provider '${session.provider}'.`);
26
+ }
27
+ return shapeResult.data;
28
+ }
29
+ export const opencodeSdkRegistration = {
30
+ name: OPENCODE_SDK_PROVIDER,
31
+ start: startOpencode,
32
+ sessionSchema: opencodeSessionSchema,
33
+ effortField: "effort",
34
+ instanceSchema: z
35
+ .object({
36
+ kind: z.literal(OPENCODE_SDK_PROVIDER),
37
+ ...providerCommonFields,
38
+ executable: executableSchema.optional(),
39
+ env: providerEnvSchema,
40
+ })
41
+ .strict(),
42
+ builtinInstance: {
43
+ name: OPENCODE_SDK_PROVIDER,
44
+ source: "builtin",
45
+ instance: { kind: OPENCODE_SDK_PROVIDER, executable: "opencode" },
46
+ },
47
+ portablePolicy: {
48
+ access: new Set(),
49
+ network: new Set(),
50
+ webSearch: new Set(),
51
+ },
52
+ maxCaptureChars: (config) => config.opencode.execMaxCaptureChars,
53
+ sessionContinuationPromptPrefix: (body) => `[Akuma system prompt]\n${body}\n\n[Operator prompt]\n`,
54
+ buildCallTerms: ({ snapshot, baseTerms, replayPrompt }) => {
55
+ const provider = snapshot.provider;
56
+ return {
57
+ ...baseTerms,
58
+ prompt: replayPrompt,
59
+ provider: OPENCODE_SDK_PROVIDER,
60
+ ...(provider.model !== undefined ? { model: provider.model } : {}),
61
+ ...(provider.effort !== undefined ? { effort: provider.effort } : {}),
62
+ ...(provider.executable !== undefined ? { executable: provider.executable } : {}),
63
+ ...(provider.env !== undefined ? { env: provider.env } : {}),
64
+ };
65
+ },
66
+ resolveLaunchProvider: ({ common, instance, model, effort }) => {
67
+ if (instance.kind !== OPENCODE_SDK_PROVIDER) {
68
+ throw new Error(`resolveLaunchProvider expected ${OPENCODE_SDK_PROVIDER}`);
69
+ }
70
+ const opencodeInstance = instance;
71
+ return Object.freeze({
72
+ ...common,
73
+ kind: OPENCODE_SDK_PROVIDER,
74
+ ...(opencodeInstance.executable !== undefined ? { executable: opencodeInstance.executable } : {}),
75
+ ...(model !== undefined ? { model } : {}),
76
+ ...(effort !== undefined ? { effort } : {}),
77
+ ...(opencodeInstance.env !== undefined ? { env: opencodeInstance.env } : {}),
78
+ });
79
+ },
80
+ parseLaunchProvider: (provider, common) => {
81
+ exactKeys(provider, ["name", "source", "kind", "executable", "model", "effort", "env"], "provider");
82
+ const executable = optionalString(provider, "executable", "provider");
83
+ const model = optionalString(provider, "model", "provider");
84
+ const effort = optionalString(provider, "effort", "provider");
85
+ const env = optionalStringRecord(provider, "env");
86
+ return Object.freeze({
87
+ ...common,
88
+ kind: OPENCODE_SDK_PROVIDER,
89
+ ...(executable !== undefined ? { executable } : {}),
90
+ ...(model !== undefined ? { model } : {}),
91
+ ...(effort !== undefined ? { effort } : {}),
92
+ ...(env !== undefined ? { env } : {}),
93
+ });
94
+ },
95
+ applyEffort: (provider, effort) => Object.freeze({ ...provider, effort }),
96
+ providerEffortAndModel: (provider) => {
97
+ const resolved = provider;
98
+ return { model: resolved.model, effort: resolved.effort };
99
+ },
100
+ attachLoader: (terms, ports) => {
101
+ if (terms.provider !== OPENCODE_SDK_PROVIDER)
102
+ return terms;
103
+ const loader = ports.opencodeSdk;
104
+ return loader ? { ...terms, loader } : terms;
105
+ },
106
+ nativePolicyOwnership: () => null,
107
+ startLive: async ({ terms, signal, reviveTerms, onSyncCheckpoint, onDeliveryOpportunity, onIngress, }) => {
108
+ if (terms.provider !== OPENCODE_SDK_PROVIDER) {
109
+ throw new Error(`startLive expected ${OPENCODE_SDK_PROVIDER}`);
110
+ }
111
+ const opencodeTerms = terms;
112
+ const options = {
113
+ model: opencodeTerms.model,
114
+ effort: opencodeTerms.effort,
115
+ executable: opencodeTerms.executable,
116
+ env: opencodeTerms.env,
117
+ signal,
118
+ onSyncCheckpoint,
119
+ onDeliveryOpportunity,
120
+ onEvidence: opencodeTerms.onEvidence,
121
+ onIngress,
122
+ initialTurn: opencodeTerms.initialTurn,
123
+ loader: opencodeTerms.loader,
124
+ };
125
+ const { startOpencode: start } = await import("./adapter.js");
126
+ if (reviveTerms) {
127
+ const session = admitResumeShape(opencodeSessionSchema, reviveTerms.session, reviveTerms.resumePath);
128
+ return start(opencodeTerms.prompt, opencodeTerms.workdir, {
129
+ ...options,
130
+ sessionId: session.sessionId,
131
+ });
132
+ }
133
+ return start(opencodeTerms.prompt, opencodeTerms.workdir, options);
134
+ },
135
+ };
@@ -0,0 +1,2 @@
1
+ import { z } from "zod";
2
+ export const opencodeSessionSchema = z.object({ sessionId: z.string().trim().min(1) });
@@ -18,6 +18,8 @@ export class OpencodeIdleMonitor {
18
18
  settledIngressEpoch = 0;
19
19
  observedIngressEpoch = 0;
20
20
  fallbackDelayMs = OPENCODE_SILENT_FALLBACK_INITIAL_MS;
21
+ observedActive = false;
22
+ terminalObserved = false;
21
23
  disposed = false;
22
24
  constructor(session, sessionId, signal) {
23
25
  this.session = session;
@@ -47,6 +49,9 @@ export class OpencodeIdleMonitor {
47
49
  if (!this.queryRunning)
48
50
  this.scheduleTrailingQuery();
49
51
  }
52
+ terminal() {
53
+ this.terminalObserved = true;
54
+ }
50
55
  dispose() {
51
56
  if (this.disposed)
52
57
  return;
@@ -85,6 +90,8 @@ export class OpencodeIdleMonitor {
85
90
  this.rejectCycle = undefined;
86
91
  this.settledIngressEpoch = this.ingressEpoch;
87
92
  this.fallbackDelayMs = OPENCODE_SILENT_FALLBACK_INITIAL_MS;
93
+ this.observedActive = false;
94
+ this.terminalObserved = false;
88
95
  resolve?.();
89
96
  }
90
97
  fail(error) {
@@ -111,13 +118,20 @@ export class OpencodeIdleMonitor {
111
118
  this.scheduleTrailingQuery();
112
119
  return;
113
120
  }
114
- if (!Object.prototype.hasOwnProperty.call(active, this.sessionId)) {
115
- this.finish();
116
- return;
117
- }
118
121
  if (hadUnobservedIngress) {
119
122
  this.scheduleTrailingQuery();
120
123
  }
124
+ else if (Object.prototype.hasOwnProperty.call(active, this.sessionId)) {
125
+ this.observedActive = true;
126
+ const delayMs = this.fallbackDelayMs;
127
+ this.fallbackDelayMs = Math.min(delayMs * 2, OPENCODE_SILENT_FALLBACK_MAX_MS);
128
+ this.schedule(delayMs);
129
+ }
130
+ else if (this.observedActive ||
131
+ this.terminalObserved ||
132
+ this.ingressEpoch === this.settledIngressEpoch) {
133
+ this.finish();
134
+ }
121
135
  else {
122
136
  const delayMs = this.fallbackDelayMs;
123
137
  this.fallbackDelayMs = Math.min(delayMs * 2, OPENCODE_SILENT_FALLBACK_MAX_MS);
@@ -135,7 +149,6 @@ export class OpencodeIdleMonitor {
135
149
  }
136
150
  }
137
151
  }
138
- let opencodeSdkLoader;
139
152
  export function sessionIdFrom(info, fallback) {
140
153
  return coerceString(info.id) ?? fallback;
141
154
  }
@@ -158,9 +171,9 @@ export async function withAbort(promise, signal, message) {
158
171
  });
159
172
  });
160
173
  }
161
- export async function loadOpencodeSdk(executable, cwd, env, signal) {
162
- if (opencodeSdkLoader)
163
- return opencodeSdkLoader(cwd);
174
+ export async function loadOpencodeSdk(executable, cwd, env, signal, loader) {
175
+ if (loader)
176
+ return loader(cwd);
164
177
  const sdk = await createOpencode({
165
178
  executable,
166
179
  directory: cwd,
@@ -172,9 +185,3 @@ export async function loadOpencodeSdk(executable, cwd, env, signal) {
172
185
  close: sdk.server?.close,
173
186
  };
174
187
  }
175
- export function setOpencodeSdkLoaderForTests(loader) {
176
- opencodeSdkLoader = loader;
177
- }
178
- export function resetOpencodeSdkLoaderForTests() {
179
- opencodeSdkLoader = undefined;
180
- }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,16 +1,11 @@
1
- import { z } from "zod";
2
1
  import { FlowError } from "../../../flow-error.js";
3
2
  import { appendDebugLog } from "../../../telemetry/debug-log.js";
4
3
  import { createProjectionDriver } from "../../harness/execution-handle.js";
4
+ import { createProviderFailure, toProviderTerminalError, } from "../../harness/provider-adapter.js";
5
5
  import { createAbortError, emitProviderEvidence, sha256Utf8 } from "../../harness/runtime.js";
6
- import { SubagentFailure } from "../../types.js";
7
6
  import { createPiCheckpointController, PI_PROVIDER, toPiSdkThinkingLevel, } from "./checkpoint.js";
8
7
  import { bindPiSessionEvents } from "./events.js";
9
- /** Opaque Pi resume handle: exact session file path (UUID alone is ambiguous). */
10
- export const piSessionSchema = z.object({
11
- sessionFile: z.string().trim().min(1),
12
- sessionId: z.string().trim().min(1).optional(),
13
- });
8
+ export { piSessionSchema } from "./session-schema.js";
14
9
  /** Exact model shape: `<provider>/<id>` (single slash separating non-empty sides). */
15
10
  const PI_MODEL_PATTERN = /^[^/]+\/[^/]+$/;
16
11
  const importPiSdk = new Function("specifier", "return import(specifier)");
@@ -18,21 +13,8 @@ async function loadPiSdk(loader) {
18
13
  if (loader) {
19
14
  return loader();
20
15
  }
21
- if (_piSdkLoader) {
22
- return _piSdkLoader();
23
- }
24
16
  return (await importPiSdk("@earendil-works/pi-coding-agent"));
25
17
  }
26
- /** @deprecated Use PiOptions.loader instead. Kept for pipeline-based tests pending loader channel. */
27
- let _piSdkLoader;
28
- /** @deprecated Use PiOptions.loader instead. */
29
- export function setPiSdkLoaderForTests(loader) {
30
- _piSdkLoader = loader;
31
- }
32
- /** @deprecated Use PiOptions.loader instead. */
33
- export function resetPiSdkLoaderForTests() {
34
- _piSdkLoader = undefined;
35
- }
36
18
  export function parsePiModelRef(model) {
37
19
  const trimmed = model.trim();
38
20
  if (!PI_MODEL_PATTERN.test(trimmed)) {
@@ -45,6 +27,23 @@ export function parsePiModelRef(model) {
45
27
  };
46
28
  }
47
29
  export async function startPi(prompt, cwd, options = {}) {
30
+ // Pre-driver awaits (sdk/session/model/resource/createAgentSession) sit outside
31
+ // createProjectionDriver's run gate; map every startup throw to the typed
32
+ // terminal boundary here. FlowError (settings shape) remains unnormalized.
33
+ try {
34
+ return await openPiProjectionDriver(prompt, cwd, options);
35
+ }
36
+ catch (error) {
37
+ if (error instanceof FlowError)
38
+ throw error;
39
+ throw toProviderTerminalError(error);
40
+ }
41
+ }
42
+ async function openPiProjectionDriver(prompt, cwd, options) {
43
+ // Pre-driver cancellation is immediately signal-cancellable — check before any SDK work.
44
+ if (options.signal?.aborted) {
45
+ throw createAbortError(`${PI_PROVIDER} exec cancelled by client`, options.signal.reason);
46
+ }
48
47
  const resumeSessionFile = options.sessionFile?.trim();
49
48
  appendDebugLog(`startPi: mode=${resumeSessionFile ? "resume" : "fresh"} model=${options.model ?? "default"}`, { cwd, section: PI_PROVIDER });
50
49
  const sdk = await loadPiSdk(options.loader);
@@ -53,22 +52,20 @@ export async function startPi(prompt, cwd, options = {}) {
53
52
  ? sessionManagerApi.open(resumeSessionFile)
54
53
  : sessionManagerApi.create(cwd);
55
54
  let model;
56
- let authStorage;
57
- let modelRegistry;
55
+ let modelRuntime;
58
56
  if (options.model !== undefined && options.model.trim().length > 0) {
59
57
  const original = options.model.trim();
60
58
  const ref = parsePiModelRef(original);
61
- authStorage = sdk.AuthStorage.create();
62
- modelRegistry = sdk.ModelRegistry.create(authStorage);
63
- model = modelRegistry.find(ref.provider, ref.id);
59
+ modelRuntime = await sdk.ModelRuntime.create();
60
+ model = modelRuntime.getModel(ref.provider, ref.id);
64
61
  if (!model) {
65
- throw new SubagentFailure("SUBAGENT_PROVIDER_UNAVAILABLE", `${PI_PROVIDER} model '${original}' is not available`);
62
+ throw createProviderFailure(`${PI_PROVIDER} model '${original}' is not available`, { code: "SUBAGENT_PROVIDER_UNAVAILABLE" });
66
63
  }
67
64
  }
68
65
  // With an Akuma body, retain the default loader's discovery and override only its append list.
69
66
  // The override deliberately suppresses discovered APPEND_SYSTEM.md content.
70
67
  if (options.appendSystemPromptOverride && (!sdk.DefaultResourceLoader || !sdk.getAgentDir)) {
71
- throw new Error(`${PI_PROVIDER} SDK does not expose DefaultResourceLoader and getAgentDir`);
68
+ throw createProviderFailure(`${PI_PROVIDER} SDK does not expose DefaultResourceLoader and getAgentDir`);
72
69
  }
73
70
  let emittedAppendEvidence = false;
74
71
  let pendingEvidenceDiagnostics = 0;
@@ -94,12 +91,15 @@ export async function startPi(prompt, cwd, options = {}) {
94
91
  })
95
92
  : undefined;
96
93
  await resourceLoader?.reload();
94
+ if (options.signal?.aborted) {
95
+ throw createAbortError(`${PI_PROVIDER} exec cancelled by client`, options.signal.reason);
96
+ }
97
97
  // Empty bodies retain the SDK's default ResourceLoader path unchanged.
98
98
  const created = await sdk.createAgentSession({
99
99
  cwd,
100
100
  sessionManager,
101
101
  ...(options.thinkingLevel ? { thinkingLevel: toPiSdkThinkingLevel(options.thinkingLevel) } : {}),
102
- ...(model ? { model, authStorage, modelRegistry } : {}),
102
+ ...(model ? { model, modelRuntime } : {}),
103
103
  ...(resourceLoader ? { resourceLoader } : {}),
104
104
  });
105
105
  const session = created.session;
@@ -131,6 +131,7 @@ export async function startPi(prompt, cwd, options = {}) {
131
131
  resolveRunSettled = resolve;
132
132
  });
133
133
  let activePrompt;
134
+ let activeState;
134
135
  let abortRequested;
135
136
  const requestAbort = async (state) => {
136
137
  state.abortIntent = true;
@@ -159,13 +160,14 @@ export async function startPi(prompt, cwd, options = {}) {
159
160
  enqueue: checkpointController.enqueue,
160
161
  onIngress: options.onIngress,
161
162
  async abort(mode) {
163
+ const state = activeState ?? { abortIntent: true, settled: false };
162
164
  if (mode !== "force") {
163
- await requestAbort({ abortIntent: true, settled: false });
165
+ await requestAbort(state);
164
166
  return;
165
167
  }
166
168
  let failure;
167
169
  try {
168
- await requestAbort({ abortIntent: true, settled: false });
170
+ await requestAbort(state);
169
171
  }
170
172
  catch (error) {
171
173
  failure = error;
@@ -196,6 +198,7 @@ export async function startPi(prompt, cwd, options = {}) {
196
198
  assistantMessageOrdinal: -1,
197
199
  activityCalls: new Map(),
198
200
  };
201
+ activeState = state;
199
202
  let settleOutcome;
200
203
  const outcome = new Promise((resolve, reject) => {
201
204
  settleOutcome = {
@@ -250,6 +253,7 @@ export async function startPi(prompt, cwd, options = {}) {
250
253
  throw error;
251
254
  }
252
255
  finally {
256
+ activeState = undefined;
253
257
  builder.signal.removeEventListener("abort", onAbort);
254
258
  options.signal?.removeEventListener("abort", onAbort);
255
259
  await abortRequested?.catch(() => undefined);
@@ -263,6 +267,8 @@ export async function startPi(prompt, cwd, options = {}) {
263
267
  },
264
268
  });
265
269
  }
270
+ // Compiler-level binding to the named ProviderAdapter function contract.
271
+ startPi;
266
272
  function readSessionManager(sdk) {
267
273
  const store = sdk[`Session${"Man"}${"ager"}`];
268
274
  return store;
@@ -0,0 +1,145 @@
1
+ import { z } from "zod";
2
+ import { providerCommonFields, providerEnvSchema, } from "../../../config/settings/provider-instance-fields.js";
3
+ import { FlowError } from "../../../flow-error.js";
4
+ import { exactKeys, optionalString, optionalStringRecord, } from "../../launch-snapshot/parse-helpers.js";
5
+ import { SUBAGENT_SESSION_VERSION, subagentSessionEnvelopeSchema, } from "../../session.js";
6
+ import { PI_PROVIDER } from "./checkpoint.js";
7
+ import { piSessionSchema } from "./session-schema.js";
8
+ const startPi = async (prompt, cwd, options) => {
9
+ const { startPi: start } = await import("./adapter.js");
10
+ return start(prompt, cwd, options);
11
+ };
12
+ function admitResumeShape(schema, session, resumePath) {
13
+ const envelopeResult = subagentSessionEnvelopeSchema.safeParse(session);
14
+ if (!envelopeResult.success) {
15
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session envelope.`);
16
+ }
17
+ if (session.provider !== PI_PROVIDER) {
18
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' was created with provider '${session.provider}', but the selected subagent resolves to '${PI_PROVIDER}'. Cross-provider resume is not supported.`);
19
+ }
20
+ if (session.version !== SUBAGENT_SESSION_VERSION) {
21
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' uses unsupported session version '${session.version}' for provider '${session.provider}'.`);
22
+ }
23
+ const shapeResult = schema.safeParse(session.handle);
24
+ if (!shapeResult.success) {
25
+ throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session handle for provider '${session.provider}'.`);
26
+ }
27
+ return shapeResult.data;
28
+ }
29
+ export const piRegistration = {
30
+ name: PI_PROVIDER,
31
+ start: startPi,
32
+ sessionSchema: piSessionSchema,
33
+ effortField: "thinkingLevel",
34
+ instanceSchema: z
35
+ .object({
36
+ kind: z.literal(PI_PROVIDER),
37
+ ...providerCommonFields,
38
+ env: providerEnvSchema,
39
+ })
40
+ .strict()
41
+ .superRefine((data, ctx) => {
42
+ if (data.env === undefined)
43
+ return;
44
+ if (Object.keys(data.env).length === 0)
45
+ return;
46
+ ctx.addIssue({
47
+ code: z.ZodIssueCode.custom,
48
+ path: ["env"],
49
+ message: `env injection not supported for provider ${PI_PROVIDER}`,
50
+ });
51
+ }),
52
+ builtinInstance: {
53
+ name: PI_PROVIDER,
54
+ source: "builtin",
55
+ instance: { kind: PI_PROVIDER },
56
+ },
57
+ portablePolicy: {
58
+ access: new Set(),
59
+ network: new Set(),
60
+ webSearch: new Set(),
61
+ },
62
+ maxCaptureChars: (config) => config.codex.execMaxCaptureChars,
63
+ buildCallTerms: ({ snapshot, baseTerms, body }) => {
64
+ const provider = snapshot.provider;
65
+ return {
66
+ ...baseTerms,
67
+ provider: PI_PROVIDER,
68
+ ...(provider.model !== undefined ? { model: provider.model } : {}),
69
+ ...(provider.thinkingLevel !== undefined ? { thinkingLevel: provider.thinkingLevel } : {}),
70
+ appendSystemPromptOverride: body ? () => [body] : undefined,
71
+ onCheckpoint() { },
72
+ };
73
+ },
74
+ resolveLaunchProvider: ({ common, instance, model, effort }) => {
75
+ if (instance.kind !== PI_PROVIDER) {
76
+ throw new Error(`resolveLaunchProvider expected ${PI_PROVIDER}`);
77
+ }
78
+ const piInstance = instance;
79
+ return Object.freeze({
80
+ ...common,
81
+ kind: PI_PROVIDER,
82
+ ...(model !== undefined ? { model } : {}),
83
+ ...(effort !== undefined ? { thinkingLevel: effort } : {}),
84
+ ...(piInstance.env !== undefined ? { env: piInstance.env } : {}),
85
+ });
86
+ },
87
+ parseLaunchProvider: (provider, common) => {
88
+ exactKeys(provider, ["name", "source", "kind", "model", "thinkingLevel", "env"], "provider");
89
+ const model = optionalString(provider, "model", "provider");
90
+ const thinkingLevel = optionalString(provider, "thinkingLevel", "provider");
91
+ const env = optionalStringRecord(provider, "env");
92
+ return Object.freeze({
93
+ ...common,
94
+ kind: PI_PROVIDER,
95
+ ...(model !== undefined ? { model } : {}),
96
+ ...(thinkingLevel !== undefined ? { thinkingLevel } : {}),
97
+ ...(env !== undefined ? { env } : {}),
98
+ });
99
+ },
100
+ applyEffort: (provider, effort) => Object.freeze({ ...provider, thinkingLevel: effort }),
101
+ providerEffortAndModel: (provider) => {
102
+ const resolved = provider;
103
+ return { model: resolved.model, effort: resolved.thinkingLevel };
104
+ },
105
+ attachLoader: (terms, ports) => {
106
+ if (terms.provider !== PI_PROVIDER)
107
+ return terms;
108
+ const loader = ports.pi;
109
+ return loader ? { ...terms, loader } : terms;
110
+ },
111
+ nativePolicyOwnership: () => null,
112
+ startLive: async ({ terms, signal, reviveTerms, onSyncCheckpoint, onIngress }) => {
113
+ if (terms.provider !== PI_PROVIDER) {
114
+ throw new Error(`startLive expected ${PI_PROVIDER}`);
115
+ }
116
+ const piTerms = terms;
117
+ const { startPi: start } = await import("./adapter.js");
118
+ if (reviveTerms) {
119
+ const session = admitResumeShape(piSessionSchema, reviveTerms.session, reviveTerms.resumePath);
120
+ return start(piTerms.prompt, piTerms.workdir, {
121
+ model: piTerms.model,
122
+ thinkingLevel: piTerms.thinkingLevel,
123
+ signal,
124
+ sessionFile: session.sessionFile,
125
+ appendSystemPromptOverride: piTerms.appendSystemPromptOverride,
126
+ onSyncCheckpoint,
127
+ onEvidence: piTerms.onEvidence,
128
+ onIngress,
129
+ initialTurn: piTerms.initialTurn,
130
+ loader: piTerms.loader,
131
+ });
132
+ }
133
+ return start(piTerms.prompt, piTerms.workdir, {
134
+ model: piTerms.model,
135
+ thinkingLevel: piTerms.thinkingLevel,
136
+ signal,
137
+ appendSystemPromptOverride: piTerms.appendSystemPromptOverride,
138
+ onSyncCheckpoint,
139
+ onEvidence: piTerms.onEvidence,
140
+ onIngress,
141
+ initialTurn: piTerms.initialTurn,
142
+ loader: piTerms.loader,
143
+ });
144
+ },
145
+ };
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ /** Opaque Pi resume handle: exact session file path (UUID alone is ambiguous). */
3
+ export const piSessionSchema = z.object({
4
+ sessionFile: z.string().trim().min(1),
5
+ sessionId: z.string().trim().min(1).optional(),
6
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Identity helper that locks `Name` across name / instanceSchema / builtinInstance.
3
+ * `const Name` keeps the literal; a schema whose kind is not exactly that literal fails here.
4
+ */
5
+ export function defineProviderRegistration(registration) {
6
+ return registration;
7
+ }
@@ -0,0 +1,76 @@
1
+ import { z } from "zod";
2
+ import { claudeAgentSdkRegistration } from "./claude-agent-sdk/registration.js";
3
+ import { codexAppServerRegistration } from "./codex-app-server/registration.js";
4
+ import { opencodeSdkRegistration } from "./opencode-sdk/registration.js";
5
+ import { piRegistration } from "./pi/registration.js";
6
+ /**
7
+ * Sole provider-vocabulary registration surface.
8
+ * Add/remove a live provider by editing that provider's directory and this list.
9
+ * Runtime tables, schemas, and builtin Akuma profiles are derived from the list.
10
+ */
11
+ export const PROVIDER_REGISTRATIONS = [
12
+ codexAppServerRegistration,
13
+ claudeAgentSdkRegistration,
14
+ opencodeSdkRegistration,
15
+ piRegistration,
16
+ ];
17
+ function nonEmptyProviderNames(registrations) {
18
+ if (registrations.length === 0) {
19
+ throw new Error("PROVIDER_REGISTRATIONS must not be empty");
20
+ }
21
+ return registrations.map((registration) => registration.name);
22
+ }
23
+ export const SUBAGENT_PROVIDERS = nonEmptyProviderNames(PROVIDER_REGISTRATIONS);
24
+ const providerNameSet = new Set(SUBAGENT_PROVIDERS);
25
+ export function isSubagentProvider(value) {
26
+ return providerNameSet.has(value);
27
+ }
28
+ function indexByProviderName(pick) {
29
+ const out = {};
30
+ for (const registration of PROVIDER_REGISTRATIONS) {
31
+ out[registration.name] = pick(registration);
32
+ }
33
+ return Object.freeze(out);
34
+ }
35
+ const registrationByName = new Map(PROVIDER_REGISTRATIONS.map((registration) => [registration.name, registration]));
36
+ export function getProviderRegistration(name) {
37
+ const registration = registrationByName.get(name);
38
+ if (!registration) {
39
+ throw new Error(`Unknown provider '${name}'`);
40
+ }
41
+ return registration;
42
+ }
43
+ export const PROVIDER_REGISTRATION_BY_NAME = indexByProviderName((registration) => registration);
44
+ export const BUILTIN_PROVIDER_INSTANCE_CANDIDATES = indexByProviderName((registration) => ({
45
+ name: registration.name,
46
+ source: "builtin",
47
+ instance: registration.builtinInstance.instance,
48
+ }));
49
+ export const PROVIDER_PORTABLE_POLICY = indexByProviderName((registration) => registration.portablePolicy);
50
+ /**
51
+ * Closed provider-instance schema derived solely from PROVIDER_REGISTRATIONS.
52
+ * Each contribution's instanceSchema is a kind-literal object schema (possibly
53
+ * refined); Zod's discriminable marker is erased by ProviderInstanceSchema, so
54
+ * the non-empty tuple assertion restores only that structural requirement.
55
+ */
56
+ export const providerInstanceSchema = z.discriminatedUnion("kind", (() => {
57
+ const schemas = PROVIDER_REGISTRATIONS.map((registration) => registration.instanceSchema);
58
+ if (schemas.length === 0) {
59
+ throw new Error("PROVIDER_REGISTRATIONS must not be empty");
60
+ }
61
+ return schemas;
62
+ })());
63
+ /** Product builtin Akuma profiles contributed by registrations. */
64
+ export const BUILTIN_AGENT_PROFILES = Object.freeze(Object.fromEntries(PROVIDER_REGISTRATIONS.flatMap((registration) => {
65
+ const open = registration;
66
+ const profiles = open.builtinAkumaProfiles ?? [];
67
+ return profiles.map((profile) => [
68
+ profile.name,
69
+ Object.freeze({
70
+ provider: registration.name,
71
+ description: profile.description,
72
+ }),
73
+ ]);
74
+ })));
75
+ export const BUILTIN_AGENT_NAMES = Object.freeze(Object.keys(BUILTIN_AGENT_PROFILES));
76
+ export { defineProviderRegistration } from "./registration.js";
@@ -5,35 +5,8 @@ import { resolveDefaultAgentName } from "../config/settings/knobs.js";
5
5
  import { loadKeiyakuSettings } from "../config/settings/loader.js";
6
6
  import { nativePolicyOwnership } from "../config/provider-policy-ownership.js";
7
7
  import { FlowError } from "../flow-error.js";
8
- const ACCESS_VALUES = ["read", "write", "auto"];
9
- const NETWORK_VALUES = ["disabled", "enabled"];
10
- const SUPPORTED_EXPLICIT = {
11
- "codex-sdk": {
12
- access: new Set(ACCESS_VALUES),
13
- network: new Set(NETWORK_VALUES),
14
- webSearch: new Set(["disabled", "cached", "live"]),
15
- },
16
- "codex-app-server": {
17
- access: new Set(["write"]),
18
- network: new Set(NETWORK_VALUES),
19
- webSearch: new Set(),
20
- },
21
- "claude-agent-sdk": {
22
- access: new Set(ACCESS_VALUES),
23
- network: new Set(),
24
- webSearch: new Set(["disabled"]),
25
- },
26
- "opencode-sdk": {
27
- access: new Set(),
28
- network: new Set(),
29
- webSearch: new Set(),
30
- },
31
- pi: {
32
- access: new Set(),
33
- network: new Set(),
34
- webSearch: new Set(),
35
- },
36
- };
8
+ import { PROVIDER_PORTABLE_POLICY } from "./providers/registry.js";
9
+ const SUPPORTED_EXPLICIT = PROVIDER_PORTABLE_POLICY;
37
10
  export function formatAvailableAgentNames(names) {
38
11
  if (names.length === 0)
39
12
  return "(none configured)";
@@ -1,3 +1,11 @@
1
+ export const SUBAGENT_FAILURE_CODES = [
2
+ "SUBAGENT_TIMEOUT",
3
+ "SUBAGENT_NON_ZERO_EXIT",
4
+ "SUBAGENT_SPAWN_ERROR",
5
+ "SUBAGENT_EXEC_ERROR",
6
+ "SUBAGENT_PROVIDER_UNAVAILABLE",
7
+ ];
8
+ export const OUTCOME_ERROR_VERSION = 1;
1
9
  export class SubagentFailure extends Error {
2
10
  code;
3
11
  timeoutMs;