@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
@@ -1,6 +1,7 @@
1
1
  import { StringTailBuffer } from "../string-tail-buffer.js";
2
2
  import { SUBAGENT_SESSION_VERSION, } from "../session.js";
3
- import { SubagentFailure, } from "../types.js";
3
+ import { OUTCOME_ERROR_VERSION, SubagentFailure, } from "../types.js";
4
+ import { isProviderTerminalError, } from "./provider-adapter.js";
4
5
  import { renderCapturedText } from "./runtime.js";
5
6
  export function createOutcomeState(maxCaptureChars) {
6
7
  return {
@@ -82,13 +83,55 @@ export function classifyFailure(shared, state, error, idleTimedOut) {
82
83
  exitCode: null,
83
84
  }, state.finalMessageCandidate);
84
85
  }
86
+ return classifyExecutionFailure(shared, state, error);
87
+ }
88
+ /**
89
+ * A rejected graceful settlement is terminal evidence, not an outcome intent.
90
+ * In particular, dismiss/external-cancel intent must not downgrade it to a
91
+ * clean dismissed result.
92
+ */
93
+ export function classifySettlementFailure(shared, state, error) {
94
+ return classifyExecutionFailure(shared, state, error);
95
+ }
96
+ function classifyFromFailureFact(shared, state, fact) {
97
+ return failWithError(shared, state, {
98
+ code: fact.code,
99
+ message: fact.message,
100
+ timeoutMs: fact.timeoutMs,
101
+ exitCode: fact.exitCode,
102
+ ...(fact.publicCause ? { publicCause: fact.publicCause } : {}),
103
+ }, state.finalMessageCandidate ?? fact.message, fact.diagnostics || renderCapturedText(state.diagnosticsBuffer), fact.output || renderCapturedText(state.outputBuffer));
104
+ }
105
+ function classifyFromAbortFact(shared, state, fact) {
106
+ // Abort without dismiss/external-cancel remains failed truth (historical bytes).
107
+ appendThrownMessage(state.diagnosticsBuffer, fact.message);
108
+ return failWithError(shared, state, {
109
+ code: "SUBAGENT_EXEC_ERROR",
110
+ message: fact.message,
111
+ timeoutMs: null,
112
+ exitCode: null,
113
+ }, state.finalMessageCandidate ?? fact.message);
114
+ }
115
+ function classifyExecutionFailure(shared, state, error) {
116
+ if (isProviderTerminalError(error)) {
117
+ if (error.fact.kind === "aborted") {
118
+ return classifyFromAbortFact(shared, state, error.fact);
119
+ }
120
+ return classifyFromFailureFact(shared, state, error.fact);
121
+ }
85
122
  if (error instanceof SubagentFailure) {
86
- return failWithError(shared, state, {
123
+ return classifyFromFailureFact(shared, state, {
124
+ kind: "failed",
87
125
  code: error.code,
88
126
  message: error.message,
89
127
  timeoutMs: error.timeoutMs,
90
128
  exitCode: error.exitCode,
91
- }, state.finalMessageCandidate ?? error.message, error.diagnostics || renderCapturedText(state.diagnosticsBuffer), error.output || renderCapturedText(state.outputBuffer));
129
+ diagnostics: error.diagnostics,
130
+ output: error.output,
131
+ });
132
+ }
133
+ if (error instanceof Error && error.name === "AbortError") {
134
+ return classifyFromAbortFact(shared, state, { kind: "aborted", message: error.message });
92
135
  }
93
136
  const message = error instanceof Error ? error.message : `Unknown ${shared.terms.provider} exec error`;
94
137
  appendThrownMessage(state.diagnosticsBuffer, message);
@@ -102,7 +145,7 @@ export function classifyFailure(shared, state, error, idleTimedOut) {
102
145
  export function failWithError(shared, state, error, finalMessage, diagnostics = renderCapturedText(state.diagnosticsBuffer), output = renderCapturedText(state.outputBuffer)) {
103
146
  return mintOutcome({
104
147
  status: "failed",
105
- error,
148
+ error: { version: OUTCOME_ERROR_VERSION, ...error },
106
149
  finalMessage: finalMessage ?? error.message,
107
150
  output,
108
151
  diagnostics,
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { FlowError } from "../../flow-error.js";
3
3
  import { createEventChannel } from "./event-channel.js";
4
4
  import { overflowMarkerFromDropped, prepareAgentEventForPersistence, } from "./execution-handle.js";
5
- import { classifyFailure, completeOutcome, createOutcomeState, failFromFailEvent, failWithError, processEvent, renderOutcomeCapturedText, } from "./outcome.js";
5
+ import { classifyFailure, classifySettlementFailure, completeOutcome, createOutcomeState, failFromFailEvent, failWithError, processEvent, renderOutcomeCapturedText, } from "./outcome.js";
6
6
  import { startLiveProjection } from "./router.js";
7
7
  import { createAbortError } from "./runtime.js";
8
8
  export function call(terms, options) {
@@ -12,17 +12,7 @@ export function revive(terms, options) {
12
12
  return createProjection(terms, terms, options);
13
13
  }
14
14
  function createProjection(terms, reviveTerms, options) {
15
- switch (terms.provider) {
16
- case "pi":
17
- case "claude-agent-sdk":
18
- case "codex-app-server":
19
- case "opencode-sdk":
20
- return createSteerableProjection(terms, reviveTerms, options);
21
- case "codex-sdk":
22
- return createReplayProjection(terms, reviveTerms, options);
23
- default:
24
- return assertNever(terms);
25
- }
15
+ return createSteerableProjection(terms, reviveTerms, options);
26
16
  }
27
17
  function createSteerableProjection(terms, reviveTerms, options) {
28
18
  const shared = createProjectionState(terms, reviveTerms, options);
@@ -84,31 +74,6 @@ function createSteerableProjection(terms, reviveTerms, options) {
84
74
  },
85
75
  };
86
76
  }
87
- function createReplayProjection(terms, reviveTerms, options) {
88
- const shared = createProjectionState(terms, reviveTerms, options);
89
- shared.onLive = (live) => {
90
- shared.live = live;
91
- };
92
- const outcome = settleProjection(shared);
93
- return {
94
- mode: "replay",
95
- get id() {
96
- return shared.id;
97
- },
98
- get events() {
99
- return shared.publicEvents.iterable;
100
- },
101
- get outcome() {
102
- return outcome;
103
- },
104
- abort(mode) {
105
- return abortProjection(shared, mode);
106
- },
107
- dismiss() {
108
- dismissProjection(shared);
109
- },
110
- };
111
- }
112
77
  function createProjectionState(terms, reviveTerms, options) {
113
78
  const shared = {
114
79
  id: randomUUID(),
@@ -129,9 +94,7 @@ function createProjectionState(terms, reviveTerms, options) {
129
94
  if (terms.signal) {
130
95
  const listener = () => {
131
96
  shared.externallyAborted = true;
132
- if (!shared.lifecycleAbort.signal.aborted) {
133
- shared.lifecycleAbort.abort(terms.signal?.reason);
134
- }
97
+ requestControlledGracefulAbort(shared, terms.signal?.reason);
135
98
  };
136
99
  terms.signal.addEventListener("abort", listener, { once: true });
137
100
  if (terms.signal.aborted) {
@@ -144,20 +107,17 @@ function dismissProjection(shared) {
144
107
  if (shared.dismissed || shared.terminal)
145
108
  return;
146
109
  shared.dismissed = true;
147
- if (!shared.lifecycleAbort.signal.aborted) {
148
- shared.lifecycleAbort.abort(createAbortError(`${shared.terms.provider} exec dismissed`));
149
- }
150
- requestDismissAbort(shared.live);
110
+ requestControlledGracefulAbort(shared, createAbortError(`${shared.terms.provider} exec dismissed`));
151
111
  }
152
112
  async function abortProjection(shared, mode = "graceful") {
153
113
  shared.explicitlyAborted = true;
154
- if (!shared.lifecycleAbort.signal.aborted) {
155
- shared.lifecycleAbort.abort(createAbortError(`${shared.terms.provider} exec cancelled by client`));
156
- }
157
114
  if (mode !== "force") {
158
- await shared.live?.abort("graceful");
115
+ await requestControlledGracefulAbort(shared, createAbortError(`${shared.terms.provider} exec cancelled by client`));
159
116
  return;
160
117
  }
118
+ if (!shared.lifecycleAbort.signal.aborted) {
119
+ shared.lifecycleAbort.abort(createAbortError(`${shared.terms.provider} exec cancelled by client`));
120
+ }
161
121
  shared.forceAbort ??= (async () => {
162
122
  const live = shared.live ?? (shared.terminal ? undefined : await new Promise((resolve) => {
163
123
  shared.forceWaiters.add(resolve);
@@ -171,12 +131,28 @@ function resolveForceWaiters(shared, live) {
171
131
  resolve(live);
172
132
  shared.forceWaiters.clear();
173
133
  }
174
- function requestDismissAbort(live) {
175
- if (!live)
176
- return;
177
- void Promise.resolve()
178
- .then(() => live.abort("graceful"))
179
- .catch(() => undefined);
134
+ function requestControlledGracefulAbort(shared, reason) {
135
+ if (shared.gracefulAbort)
136
+ return shared.gracefulAbort;
137
+ const live = shared.live;
138
+ if (!live) {
139
+ if (!shared.lifecycleAbort.signal.aborted)
140
+ shared.lifecycleAbort.abort(reason);
141
+ return Promise.resolve();
142
+ }
143
+ shared.gracefulAbort = (async () => {
144
+ try {
145
+ await live.abort("graceful");
146
+ }
147
+ finally {
148
+ if (!shared.lifecycleAbort.signal.aborted)
149
+ shared.lifecycleAbort.abort(reason);
150
+ }
151
+ })();
152
+ // Dismiss, timeout, and external cancellation are outcome-driven controls;
153
+ // keep their rejection observed while explicit abort still receives it.
154
+ shared.gracefulAbort.catch(() => undefined);
155
+ return shared.gracefulAbort;
180
156
  }
181
157
  function deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, observerEnabled) {
182
158
  const prepared = prepareAgentEventForPersistence(event);
@@ -211,7 +187,7 @@ async function settleProjection(shared) {
211
187
  clearTimeout(idleTimeout);
212
188
  idleTimeout = setTimeout(() => {
213
189
  idleTimedOut = true;
214
- shared.lifecycleAbort.abort(createAbortError(`${shared.terms.provider} exec idle timed out`));
190
+ requestControlledGracefulAbort(shared, createAbortError(`${shared.terms.provider} exec idle timed out`));
215
191
  }, shared.terms.idleTimeoutMs);
216
192
  idleTimeout.unref?.();
217
193
  };
@@ -222,7 +198,7 @@ async function settleProjection(shared) {
222
198
  if (abortRequested)
223
199
  return;
224
200
  abortRequested = true;
225
- requestDismissAbort(shared.live);
201
+ requestControlledGracefulAbort(shared, shared.lifecycleAbort.signal.reason);
226
202
  };
227
203
  shared.lifecycleAbort.signal.addEventListener("abort", requestLiveAbort, { once: true });
228
204
  try {
@@ -258,6 +234,16 @@ async function settleProjection(shared) {
258
234
  }
259
235
  const completionResult = await live.completion.then((value) => ({ ok: true, value }), (error) => ({ ok: false, error }));
260
236
  const completionOutcome = completionResult.ok ? completionResult.value : undefined;
237
+ // Outcome classification is the terminal publication boundary. A
238
+ // controlled abort may let the provider run settle before its graceful
239
+ // cleanup promise does, so prove that shared settlement before publishing
240
+ // dismissed, failed-for-another-reason, or completed truth.
241
+ const gracefulResult = shared.gracefulAbort
242
+ ? await shared.gracefulAbort.then(() => ({ ok: true }), (error) => ({ ok: false, error }))
243
+ : undefined;
244
+ if (gracefulResult && !gracefulResult.ok) {
245
+ return classifySettlementFailure(shared, state, gracefulResult.error);
246
+ }
261
247
  if (state.failEvent) {
262
248
  return failFromFailEvent(shared, state, state.failEvent);
263
249
  }
@@ -305,6 +291,3 @@ async function settleProjection(shared) {
305
291
  shared.publicEvents.close();
306
292
  }
307
293
  }
308
- function assertNever(_value) {
309
- throw new Error("Unhandled provider");
310
- }
@@ -0,0 +1,58 @@
1
+ import { SubagentFailure, } from "../types.js";
2
+ /**
3
+ * Typed rejection carrier for failed/aborted adapter settlement.
4
+ * Abort keeps name "AbortError" so existing name checks and CLI exit codes hold.
5
+ */
6
+ export class ProviderTerminalError extends Error {
7
+ fact;
8
+ constructor(fact, options) {
9
+ super(fact.message, options?.cause === undefined ? undefined : { cause: options.cause });
10
+ this.name = fact.kind === "aborted" ? "AbortError" : "ProviderTerminalError";
11
+ this.fact = fact;
12
+ }
13
+ }
14
+ export function isProviderTerminalError(error) {
15
+ return error instanceof ProviderTerminalError;
16
+ }
17
+ export function createProviderFailure(message, options = {}) {
18
+ return new ProviderTerminalError({
19
+ kind: "failed",
20
+ code: options.code ?? "SUBAGENT_EXEC_ERROR",
21
+ message,
22
+ timeoutMs: options.timeoutMs ?? null,
23
+ exitCode: options.exitCode ?? null,
24
+ ...(options.diagnostics !== undefined ? { diagnostics: options.diagnostics } : {}),
25
+ ...(options.output !== undefined ? { output: options.output } : {}),
26
+ ...(options.publicCause !== undefined ? { publicCause: options.publicCause } : {}),
27
+ }, options.cause === undefined ? undefined : { cause: options.cause });
28
+ }
29
+ /**
30
+ * Abort-fact constructor. Abort semantics for every live adapter:
31
+ * - abort("graceful") requests provider-native interrupt and keeps local signals
32
+ * open until native settlement proof (or bounded abort-settlement timeout);
33
+ * - abort("force") must establish zero later provider side effects or reject;
34
+ * - run settlement for a requested abort exits as kind "aborted" (name AbortError).
35
+ */
36
+ export function createAbortError(message, cause) {
37
+ return new ProviderTerminalError({ kind: "aborted", message }, cause === undefined ? undefined : { cause });
38
+ }
39
+ /** Normalize any thrown value into the typed failure/abort egress fact. */
40
+ export function toProviderTerminalError(error) {
41
+ if (error instanceof ProviderTerminalError)
42
+ return error;
43
+ if (error instanceof SubagentFailure) {
44
+ return new ProviderTerminalError({
45
+ kind: "failed",
46
+ code: error.code,
47
+ message: error.message,
48
+ timeoutMs: error.timeoutMs,
49
+ exitCode: error.exitCode,
50
+ diagnostics: error.diagnostics,
51
+ output: error.output,
52
+ }, { cause: error });
53
+ }
54
+ if (error instanceof Error && error.name === "AbortError") {
55
+ return new ProviderTerminalError({ kind: "aborted", message: error.message }, { cause: error });
56
+ }
57
+ return createProviderFailure(error instanceof Error ? error.message : String(error), { cause: error instanceof Error ? error : undefined });
58
+ }
@@ -1,30 +1,4 @@
1
- import { FlowError } from "../../flow-error.js";
2
- import { SUBAGENT_SESSION_VERSION, subagentSessionEnvelopeSchema, } from "../session.js";
3
- import { startClaudeAgentSdk, claudeSessionSchema, } from "../providers/claude-agent-sdk/adapter.js";
4
- import { startCodexAppServer, codexAppServerSessionSchema, } from "../providers/codex-app-server/adapter.js";
5
- import { startCodexSdk, codexThreadSchema, } from "../providers/codex-sdk.js";
6
- import { startOpencode, opencodeSessionSchema, } from "../providers/opencode-sdk/adapter.js";
7
- import { startPi, piSessionSchema, } from "../providers/pi/adapter.js";
8
- function admitResumeShape(provider, schema, session, resumePath) {
9
- const envelopeResult = subagentSessionEnvelopeSchema.safeParse(session);
10
- if (!envelopeResult.success) {
11
- throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session envelope.`);
12
- }
13
- if (session.provider !== provider) {
14
- throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' was created with provider '${session.provider}', but the selected subagent resolves to '${provider}'. Cross-provider resume is not supported.`);
15
- }
16
- if (session.version !== SUBAGENT_SESSION_VERSION) {
17
- throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' uses unsupported session version '${session.version}' for provider '${session.provider}'.`);
18
- }
19
- const shapeResult = schema.safeParse(session.handle);
20
- if (!shapeResult.success) {
21
- throw new FlowError("INVALID_RESPONSE_PATH", `Resume path file '${resumePath}' includes an invalid session handle for provider '${session.provider}'.`);
22
- }
23
- return shapeResult.data;
24
- }
25
- function assertNever(_value) {
26
- throw new Error("Unhandled provider");
27
- }
1
+ import { getProviderRegistration } from "../providers/registry.js";
28
2
  export async function startLiveProjection(terms, signal, reviveTerms, onIngress, observer) {
29
3
  const onSyncCheckpoint = observer
30
4
  ? (control, checkpoint) => {
@@ -38,164 +12,14 @@ export async function startLiveProjection(terms, signal, reviveTerms, onIngress,
38
12
  observer.onDeliveryOpportunity(control);
39
13
  }
40
14
  : undefined;
41
- switch (terms.provider) {
42
- case "pi": {
43
- if (reviveTerms) {
44
- const session = admitResumeShape("pi", piSessionSchema, reviveTerms.session, reviveTerms.resumePath);
45
- return startPi(terms.prompt, terms.workdir, {
46
- model: terms.model,
47
- thinkingLevel: terms.thinkingLevel,
48
- signal,
49
- sessionFile: session.sessionFile,
50
- appendSystemPromptOverride: terms.appendSystemPromptOverride,
51
- onSyncCheckpoint,
52
- onEvidence: terms.onEvidence,
53
- onIngress,
54
- initialTurn: terms.initialTurn,
55
- loader: terms.loader,
56
- });
57
- }
58
- return startPi(terms.prompt, terms.workdir, {
59
- model: terms.model,
60
- thinkingLevel: terms.thinkingLevel,
61
- signal,
62
- appendSystemPromptOverride: terms.appendSystemPromptOverride,
63
- onSyncCheckpoint,
64
- onEvidence: terms.onEvidence,
65
- onIngress,
66
- initialTurn: terms.initialTurn,
67
- loader: terms.loader,
68
- });
69
- }
70
- case "claude-agent-sdk": {
71
- const claudeOptions = {
72
- model: terms.model,
73
- executable: terms.executable,
74
- effort: terms.effort,
75
- env: terms.env,
76
- signal,
77
- permissionMode: terms.permissionMode,
78
- settingSources: terms.settingSources,
79
- allowDangerouslySkipPermissions: terms.allowDangerouslySkipPermissions,
80
- disallowedTools: terms.disallowedTools,
81
- systemPrompt: terms.systemPrompt,
82
- onSyncCheckpoint,
83
- onEvidence: terms.onEvidence,
84
- onIngress,
85
- initialTurn: terms.initialTurn,
86
- loader: terms.loader,
87
- };
88
- if (reviveTerms) {
89
- const session = admitResumeShape("claude-agent-sdk", claudeSessionSchema, reviveTerms.session, reviveTerms.resumePath);
90
- return startClaudeAgentSdk(terms.prompt, terms.workdir, {
91
- ...claudeOptions,
92
- sessionId: session.sessionId,
93
- });
94
- }
95
- return startClaudeAgentSdk(terms.prompt, terms.workdir, claudeOptions);
96
- }
97
- case "codex-app-server": {
98
- if (reviveTerms) {
99
- const session = admitResumeShape("codex-app-server", codexAppServerSessionSchema, reviveTerms.session, reviveTerms.resumePath);
100
- return startCodexAppServer(terms.prompt, terms.workdir, {
101
- model: terms.model,
102
- effort: terms.effort,
103
- profile: terms.profile,
104
- config: terms.config,
105
- sandboxPolicy: terms.sandboxPolicy,
106
- approvalPolicy: terms.approvalPolicy,
107
- executable: terms.executable,
108
- env: terms.env,
109
- signal,
110
- threadId: session.threadId,
111
- codexHome: session.codexHome,
112
- developerInstructions: terms.developerInstructions,
113
- onSyncCheckpoint,
114
- onDeliveryOpportunity,
115
- onEvidence: terms.onEvidence,
116
- onIngress,
117
- initialTurn: terms.initialTurn,
118
- });
119
- }
120
- return startCodexAppServer(terms.prompt, terms.workdir, {
121
- model: terms.model,
122
- effort: terms.effort,
123
- profile: terms.profile,
124
- config: terms.config,
125
- sandboxPolicy: terms.sandboxPolicy,
126
- approvalPolicy: terms.approvalPolicy,
127
- executable: terms.executable,
128
- env: terms.env,
129
- signal,
130
- developerInstructions: terms.developerInstructions,
131
- onSyncCheckpoint,
132
- onDeliveryOpportunity,
133
- onEvidence: terms.onEvidence,
134
- onIngress,
135
- initialTurn: terms.initialTurn,
136
- });
137
- }
138
- case "codex-sdk": {
139
- if (reviveTerms) {
140
- const session = admitResumeShape("codex-sdk", codexThreadSchema, reviveTerms.session, reviveTerms.resumePath);
141
- return startCodexSdk(terms.prompt, terms.workdir, {
142
- model: terms.model,
143
- profile: terms.profile,
144
- config: terms.config,
145
- threadOptions: terms.threadOptions,
146
- executable: terms.executable,
147
- signal,
148
- threadId: session.threadId,
149
- onEvidence: terms.onEvidence,
150
- onIngress,
151
- initialTurn: terms.initialTurn,
152
- codexConstructor: terms.codexConstructor,
153
- });
154
- }
155
- return startCodexSdk(terms.prompt, terms.workdir, {
156
- model: terms.model,
157
- profile: terms.profile,
158
- config: terms.config,
159
- threadOptions: terms.threadOptions,
160
- executable: terms.executable,
161
- signal,
162
- onEvidence: terms.onEvidence,
163
- onIngress,
164
- initialTurn: terms.initialTurn,
165
- codexConstructor: terms.codexConstructor,
166
- });
167
- }
168
- case "opencode-sdk": {
169
- if (reviveTerms) {
170
- const session = admitResumeShape("opencode-sdk", opencodeSessionSchema, reviveTerms.session, reviveTerms.resumePath);
171
- return startOpencode(terms.prompt, terms.workdir, {
172
- model: terms.model,
173
- effort: terms.effort,
174
- executable: terms.executable,
175
- env: terms.env,
176
- signal,
177
- sessionId: session.sessionId,
178
- onSyncCheckpoint,
179
- onDeliveryOpportunity,
180
- onEvidence: terms.onEvidence,
181
- onIngress,
182
- initialTurn: terms.initialTurn,
183
- });
184
- }
185
- return startOpencode(terms.prompt, terms.workdir, {
186
- model: terms.model,
187
- effort: terms.effort,
188
- executable: terms.executable,
189
- env: terms.env,
190
- signal,
191
- onSyncCheckpoint,
192
- onDeliveryOpportunity,
193
- onEvidence: terms.onEvidence,
194
- onIngress,
195
- initialTurn: terms.initialTurn,
196
- });
197
- }
198
- default:
199
- return assertNever(terms);
200
- }
15
+ const registration = getProviderRegistration(terms.provider);
16
+ return registration.startLive({
17
+ terms,
18
+ signal,
19
+ reviveTerms,
20
+ onSyncCheckpoint,
21
+ onDeliveryOpportunity,
22
+ onIngress,
23
+ observer,
24
+ });
201
25
  }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { getConfig } from "../../config/env.js";
3
3
  import { DISABLE_SUBAGENT_SPAWN_ENV_KEY } from "../../config/env-keys.js";
4
+ export const PROVIDER_ABORT_SETTLEMENT_TIMEOUT_MS = 5_000;
4
5
  export function sha256Utf8(value) {
5
6
  const bytes = Buffer.from(value, "utf8");
6
7
  return { sha256: createHash("sha256").update(bytes).digest("hex"), byteLength: bytes.byteLength };
@@ -33,10 +34,22 @@ export function emitProviderEvidence(callback, evidence, diagnostic) {
33
34
  reportFailure();
34
35
  }
35
36
  }
36
- export function createAbortError(message) {
37
- const error = new Error(message);
38
- error.name = "AbortError";
39
- return error;
37
+ export { createAbortError } from "./provider-adapter.js";
38
+ export async function awaitProviderAbortSettlement(provider, settlement, operation = "graceful abort could not prove provider terminal settlement") {
39
+ let timer;
40
+ const deadline = new Promise((_, reject) => {
41
+ timer = setTimeout(() => {
42
+ reject(new Error(`${provider} ${operation} within ${PROVIDER_ABORT_SETTLEMENT_TIMEOUT_MS}ms`));
43
+ }, PROVIDER_ABORT_SETTLEMENT_TIMEOUT_MS);
44
+ timer.unref?.();
45
+ });
46
+ try {
47
+ return await Promise.race([settlement, deadline]);
48
+ }
49
+ finally {
50
+ if (timer)
51
+ clearTimeout(timer);
52
+ }
40
53
  }
41
54
  export function coerceString(value) {
42
55
  return typeof value === "string" && value.trim().length > 0 ? value : undefined;
@@ -0,0 +1,15 @@
1
+ import { isSubagentProvider } from "../../keiyaku.js";
2
+ import { SUBAGENT_SESSION_VERSION, subagentSessionEnvelopeSchema, } from "../session.js";
3
+ import { getProviderRegistration } from "../providers/registry.js";
4
+ /** Validate a continuation envelope through its owning provider schema. */
5
+ export function isRecoverableSessionEnvelope(provider, value) {
6
+ if (!isSubagentProvider(provider))
7
+ return false;
8
+ const envelope = subagentSessionEnvelopeSchema.safeParse(value);
9
+ if (!envelope.success)
10
+ return false;
11
+ if (envelope.data.provider !== provider || envelope.data.version !== SUBAGENT_SESSION_VERSION) {
12
+ return false;
13
+ }
14
+ return getProviderRegistration(provider).sessionSchema.safeParse(envelope.data.handle).success;
15
+ }