@astrosheep/keiyaku 4.5.7 → 4.5.8

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 (234) hide show
  1. package/README.md +10 -0
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +3 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +10 -9
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +35 -96
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +28 -49
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +21 -15
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +148 -89
  12. package/build/src/akuma/abort.d.ts +2 -1
  13. package/build/src/akuma/abort.js +6 -23
  14. package/build/src/akuma/akuma-handle.js +4 -2
  15. package/build/src/akuma/akuma-observe.d.ts +2 -0
  16. package/build/src/akuma/akuma-observe.js +1 -0
  17. package/build/src/akuma/akuma-product.js +9 -7
  18. package/build/src/akuma/akuma.d.ts +372 -9
  19. package/build/src/akuma/akuma.js +31 -1
  20. package/build/src/akuma/allowed.d.ts +1 -1
  21. package/build/src/akuma/allowed.js +1 -0
  22. package/build/src/akuma/body.d.ts +6 -4
  23. package/build/src/akuma/body.js +5 -3
  24. package/build/src/akuma/call-request.d.ts +80 -0
  25. package/build/src/akuma/call-request.js +211 -0
  26. package/build/src/akuma/heart/facts.d.ts +6 -78
  27. package/build/src/akuma/heart/index.d.ts +1 -1
  28. package/build/src/akuma/heart/request-authority.d.ts +5 -4
  29. package/build/src/akuma/heart/request-authority.js +20 -34
  30. package/build/src/akuma/heart/request-rows.js +12 -23
  31. package/build/src/akuma/heart/schema.d.ts +1 -1
  32. package/build/src/akuma/heart/schema.js +5 -13
  33. package/build/src/akuma/index.d.ts +1 -1
  34. package/build/src/akuma/index.js +1 -1
  35. package/build/src/akuma/projection-read.d.ts +1 -0
  36. package/build/src/akuma/projection-read.js +8 -1
  37. package/build/src/akuma/projection.d.ts +856 -137
  38. package/build/src/akuma/projection.js +190 -0
  39. package/build/src/akuma/provider.d.ts +30 -3
  40. package/build/src/akuma/provider.js +104 -0
  41. package/build/src/akuma/providers/acp/core.d.ts +2 -2
  42. package/build/src/akuma/providers/acp/core.js +13 -6
  43. package/build/src/akuma/providers/acp/index.js +5 -4
  44. package/build/src/akuma/providers/claude/index.d.ts +3 -3
  45. package/build/src/akuma/providers/claude/index.js +16 -5
  46. package/build/src/akuma/providers/codex-app-server/events.js +1 -1
  47. package/build/src/akuma/providers/codex-app-server/index.js +18 -6
  48. package/build/src/akuma/providers/grok-build/index.js +5 -4
  49. package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
  50. package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
  51. package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
  52. package/build/src/akuma/providers/pi/index.js +42 -14
  53. package/build/src/akuma/request-lifecycle.d.ts +3 -9
  54. package/build/src/akuma/request-lifecycle.js +4 -4
  55. package/build/src/akuma/request-rendezvous.d.ts +26 -0
  56. package/build/src/akuma/request-rendezvous.js +100 -0
  57. package/build/src/akuma/request-serve.d.ts +5 -65
  58. package/build/src/akuma/request-serve.js +81 -208
  59. package/build/src/akuma/request-wire.d.ts +109 -115
  60. package/build/src/akuma/request-wire.js +63 -318
  61. package/build/src/akuma/requests.d.ts +19 -47
  62. package/build/src/akuma/requests.js +51 -107
  63. package/build/src/akuma/turn-drive.d.ts +5 -3
  64. package/build/src/akuma/turn-drive.js +32 -10
  65. package/build/src/akuma-body.d.ts +7 -2
  66. package/build/src/akuma-body.js +68 -46
  67. package/build/src/cli/accepted.d.ts +1 -1
  68. package/build/src/cli/accepted.js +2 -2
  69. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  70. package/build/src/cli/commands/akuma-invoke.js +17 -9
  71. package/build/src/cli/commands/akuma.js +7 -7
  72. package/build/src/cli/commands/contract-invoke.d.ts +3 -2
  73. package/build/src/cli/commands/contract-invoke.js +26 -19
  74. package/build/src/cli/commands/contract.d.ts +13 -13
  75. package/build/src/cli/commands/contract.js +32 -17
  76. package/build/src/cli/commands/install.d.ts +1 -1
  77. package/build/src/cli/commands/install.js +1 -1
  78. package/build/src/cli/commands/task-invoke.d.ts +3 -0
  79. package/build/src/cli/commands/task-invoke.js +23 -140
  80. package/build/src/cli/commands/task.js +54 -20
  81. package/build/src/cli/invoke.js +39 -25
  82. package/build/src/cli/main.js +2 -1
  83. package/build/src/cli/parse.d.ts +2 -2
  84. package/build/src/cli/parse.js +37 -22
  85. package/build/src/cli/render/akuma-activity.d.ts +4 -1
  86. package/build/src/cli/render/akuma-activity.js +45 -30
  87. package/build/src/cli/render/akuma-tool.d.ts +5 -1
  88. package/build/src/cli/render/akuma.js +5 -9
  89. package/build/src/cli/render/audit.js +1 -1
  90. package/build/src/cli/render/catalog.js +80 -21
  91. package/build/src/cli/render/contract-observation.d.ts +2 -0
  92. package/build/src/cli/render/contract-observation.js +11 -0
  93. package/build/src/cli/render/contract.js +25 -65
  94. package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
  95. package/build/src/cli/render/kanshi-akuma.js +111 -0
  96. package/build/src/cli/render/kanshi.js +108 -213
  97. package/build/src/cli/render/receipt.d.ts +1 -2
  98. package/build/src/cli/render/receipt.js +76 -26
  99. package/build/src/cli/render/refusal.js +2 -10
  100. package/build/src/cli/render/region.js +5 -2
  101. package/build/src/cli/render/settings.d.ts +1 -1
  102. package/build/src/cli/render/settings.js +27 -8
  103. package/build/src/cli/render/task.js +22 -5
  104. package/build/src/cli/render/terminal.d.ts +28 -0
  105. package/build/src/cli/render/terminal.js +65 -0
  106. package/build/src/cli/result.d.ts +3 -3
  107. package/build/src/cli/runtime.js +3 -1
  108. package/build/src/cli/selectors.d.ts +2 -1
  109. package/build/src/cli/selectors.js +4 -3
  110. package/build/src/cli/square-edge.js +120 -36
  111. package/build/src/cli/usage.js +2 -1
  112. package/build/src/contract-guidance.d.ts +5 -0
  113. package/build/src/contract-guidance.js +121 -0
  114. package/build/src/contract-worktree.d.ts +0 -1
  115. package/build/src/contract-worktree.js +32 -83
  116. package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
  117. package/build/src/coordination/sqlite-transaction-lock.js +5 -0
  118. package/build/src/core/facts/codec.d.ts +2 -0
  119. package/build/src/core/facts/codec.js +4 -0
  120. package/build/src/core/verbs/placement.d.ts +6 -1
  121. package/build/src/core/verbs/placement.js +5 -3
  122. package/build/src/dispatch/index.d.ts +0 -2
  123. package/build/src/dispatch/index.js +54 -55
  124. package/build/src/git/admission.js +2 -2
  125. package/build/src/git/hooks.d.ts +9 -15
  126. package/build/src/git/hooks.js +23 -317
  127. package/build/src/git/nuke.d.ts +2 -1
  128. package/build/src/git/nuke.js +36 -18
  129. package/build/src/git/private-state-seat.d.ts +12 -0
  130. package/build/src/git/private-state-seat.js +39 -0
  131. package/build/src/git/process.d.ts +6 -0
  132. package/build/src/git/process.js +19 -7
  133. package/build/src/git/read-observation.js +29 -20
  134. package/build/src/git/reconcile.d.ts +11 -0
  135. package/build/src/git/reconcile.js +64 -11
  136. package/build/src/git/repository.d.ts +6 -2
  137. package/build/src/git/repository.js +8 -0
  138. package/build/src/git/target-placement.d.ts +1 -0
  139. package/build/src/git/target-placement.js +5 -0
  140. package/build/src/git/tender.d.ts +1 -0
  141. package/build/src/git/terminal-reconcile.d.ts +1 -1
  142. package/build/src/git/terminal-reconcile.js +22 -20
  143. package/build/src/git/workspace.d.ts +14 -0
  144. package/build/src/git/workspace.js +44 -0
  145. package/build/src/kanshi/index.d.ts +1 -1
  146. package/build/src/kanshi/read.js +16 -16
  147. package/build/src/kanshi/report.d.ts +2 -2
  148. package/build/src/library/address.d.ts +9 -3
  149. package/build/src/library/address.js +3 -2
  150. package/build/src/library/akuma-creation.d.ts +4 -3
  151. package/build/src/library/akuma-creation.js +19 -13
  152. package/build/src/library/audit.d.ts +1 -1
  153. package/build/src/library/audit.js +5 -3
  154. package/build/src/library/catalog.d.ts +3 -1
  155. package/build/src/library/catalog.js +20 -6
  156. package/build/src/library/composition.d.ts +371 -0
  157. package/build/src/library/composition.js +28 -0
  158. package/build/src/library/configuration.js +8 -2
  159. package/build/src/library/continuation.js +66 -30
  160. package/build/src/library/contract-bind.js +10 -2
  161. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +96 -0
  162. package/build/src/library/contract-forwarding-reconciliation-result.js +83 -0
  163. package/build/src/library/contract-forwarding-result.d.ts +6723 -0
  164. package/build/src/library/contract-forwarding-result.js +521 -0
  165. package/build/src/library/contract-forwarding.d.ts +72 -0
  166. package/build/src/library/contract-forwarding.js +125 -0
  167. package/build/src/library/contract-handle.d.ts +36 -0
  168. package/build/src/library/contract-handle.js +313 -0
  169. package/build/src/library/contract-operations.d.ts +1044 -67
  170. package/build/src/library/contract-operations.js +190 -118
  171. package/build/src/library/contract.d.ts +11 -32
  172. package/build/src/library/contract.js +16 -289
  173. package/build/src/library/fleet-result.d.ts +1454 -0
  174. package/build/src/library/fleet-result.js +96 -0
  175. package/build/src/library/fleet.d.ts +112 -54
  176. package/build/src/library/fleet.js +211 -71
  177. package/build/src/library/input.d.ts +2 -0
  178. package/build/src/library/input.js +4 -0
  179. package/build/src/library/keiyaku.d.ts +371 -20
  180. package/build/src/library/keiyaku.js +8 -21
  181. package/build/src/library/mutation.d.ts +3 -3
  182. package/build/src/library/mutation.js +5 -9
  183. package/build/src/library/nuke.d.ts +1 -1
  184. package/build/src/library/nuke.js +4 -3
  185. package/build/src/library/reconcile.d.ts +4 -0
  186. package/build/src/library/reconcile.js +2 -0
  187. package/build/src/library/refusal.d.ts +0 -9
  188. package/build/src/library/refusal.js +0 -7
  189. package/build/src/protocol/amend.js +56 -50
  190. package/build/src/protocol/attempt.d.ts +2 -0
  191. package/build/src/protocol/attempt.js +8 -1
  192. package/build/src/protocol/completion.js +7 -0
  193. package/build/src/protocol/deliver.js +6 -2
  194. package/build/src/protocol/operations.d.ts +4 -1
  195. package/build/src/protocol/operations.js +5 -2
  196. package/build/src/protocol/placement.d.ts +1 -0
  197. package/build/src/protocol/placement.js +52 -38
  198. package/build/src/protocol/read/observation.d.ts +1 -4
  199. package/build/src/protocol/read/observation.js +2 -15
  200. package/build/src/protocol/reintegrate.js +62 -58
  201. package/build/src/protocol/review.js +5 -0
  202. package/build/src/protocol/run.js +17 -13
  203. package/build/src/runtime/proc/run.d.ts +8 -0
  204. package/build/src/runtime/proc/run.js +27 -0
  205. package/build/src/runtime/proc/termination.js +26 -22
  206. package/build/src/runtime/proc/windows-launch.exe +0 -0
  207. package/build/src/settlement/holder.d.ts +2 -1
  208. package/build/src/settlement/holder.js +15 -4
  209. package/build/src/settlement/settle.js +99 -33
  210. package/build/src/task/board.d.ts +68 -27
  211. package/build/src/task/board.js +31 -1
  212. package/build/src/task/catalog.d.ts +4 -0
  213. package/build/src/task/catalog.js +23 -0
  214. package/build/src/task/compose-language.js +33 -7
  215. package/build/src/task/compose-parser.d.ts +1 -1
  216. package/build/src/task/compose-parser.js +1 -1
  217. package/build/src/task/compose.js +2 -0
  218. package/build/src/task/index.d.ts +12 -4
  219. package/build/src/task/index.js +97 -16
  220. package/build/src/task/mutation-result.d.ts +923 -0
  221. package/build/src/task/mutation-result.js +140 -0
  222. package/build/src/task/mutation.d.ts +552 -28
  223. package/build/src/task/mutation.js +304 -97
  224. package/build/src/task/operations.d.ts +1 -4
  225. package/build/src/task/operations.js +0 -6
  226. package/build/src/task/public.d.ts +2 -0
  227. package/build/src/task/public.js +1 -0
  228. package/build/src/workspace-place.d.ts +7 -2
  229. package/build/src/workspace-place.js +34 -24
  230. package/build/src/world.d.ts +1 -0
  231. package/build/src/world.js +11 -0
  232. package/package.json +8 -6
  233. package/build/src/akuma/request-execution.d.ts +0 -8
  234. package/build/src/akuma/request-execution.js +0 -98
@@ -1,4 +1,194 @@
1
+ /* eslint-disable max-lines -- Akuma owns one coherent activity projection and strict boundary schema. */
1
2
  import { decodeAgentEvent } from "./provider.js";
3
+ import { z } from "zod";
4
+ const nonblankTextSchema = z.string().refine((value) => value.trim() !== "");
5
+ const countSchema = z.number().int().nonnegative();
6
+ const timestampSchema = z.string().refine((value) => Number.isFinite(Date.parse(value)), "expected timestamp");
7
+ const diffstatSchema = z.object({ added: countSchema, removed: countSchema }).strict();
8
+ const fileChangeSchema = z
9
+ .object({
10
+ op: z.enum(["add", "update", "delete", "unspecified"]),
11
+ path: z.string(),
12
+ diffstat: diffstatSchema.optional(),
13
+ })
14
+ .strict();
15
+ const toolCallSchema = z.union([
16
+ z.object({ kind: z.literal("run"), command: z.string() }).strict(),
17
+ z
18
+ .object({
19
+ kind: z.literal("read"),
20
+ path: z.string(),
21
+ offset: countSchema.optional(),
22
+ limit: countSchema.optional(),
23
+ })
24
+ .strict(),
25
+ z
26
+ .object({
27
+ kind: z.literal("search"),
28
+ query: z.string(),
29
+ scope: z.enum(["content", "files", "web"]).optional(),
30
+ path: z.string().optional(),
31
+ glob: z.string().optional(),
32
+ })
33
+ .strict(),
34
+ z.object({ kind: z.literal("fileChange"), changes: z.array(fileChangeSchema).readonly() }).strict(),
35
+ z.object({ kind: z.literal("other"), display: z.string() }).strict(),
36
+ ]);
37
+ const toolResultSchema = z
38
+ .object({ status: z.enum(["ok", "error"]), message: z.string().optional(), exitCode: z.number().int().optional() })
39
+ .strict();
40
+ const tellDeliverySchema = z
41
+ .object({
42
+ deliveredAt: timestampSchema,
43
+ route: z.enum(["launch", "live"]),
44
+ turnSequence: countSchema,
45
+ receipt: z.enum(["unavailable", "required"]).optional(),
46
+ })
47
+ .strict();
48
+ const turnRowSchema = z
49
+ .object({
50
+ kind: z.literal("turn"),
51
+ sequence: countSchema,
52
+ turnSequence: countSchema,
53
+ bodySequence: countSchema,
54
+ at: timestampSchema,
55
+ })
56
+ .strict();
57
+ const callRowSchema = z
58
+ .object({
59
+ kind: z.literal("call"),
60
+ sequence: countSchema,
61
+ turnSequence: countSchema,
62
+ at: timestampSchema,
63
+ text: z.string(),
64
+ })
65
+ .strict();
66
+ const textRowSchema = (kind) => z
67
+ .object({
68
+ kind: z.literal(kind),
69
+ sequence: countSchema,
70
+ turnSequence: countSchema,
71
+ at: timestampSchema,
72
+ text: z.string(),
73
+ truncated: z.literal(true).optional(),
74
+ })
75
+ .strict();
76
+ const tellRowSchema = z
77
+ .object({
78
+ kind: z.literal("tell"),
79
+ sequence: countSchema,
80
+ at: timestampSchema,
81
+ tellId: nonblankTextSchema,
82
+ text: z.string(),
83
+ state: z.enum(["pending", "told"]),
84
+ deliveries: z.array(tellDeliverySchema).readonly(),
85
+ })
86
+ .strict();
87
+ const outcomeSchema = z.union([
88
+ z.object({ kind: z.literal("answered"), historyId: nonblankTextSchema, answer: z.string() }).strict(),
89
+ z.object({ kind: z.literal("failed"), historyId: nonblankTextSchema, diagnostic: z.string() }).strict(),
90
+ ]);
91
+ const outcomeRowSchema = z
92
+ .object({
93
+ kind: z.literal("outcome"),
94
+ sequence: countSchema,
95
+ turnSequence: countSchema,
96
+ at: timestampSchema,
97
+ outcome: outcomeSchema,
98
+ })
99
+ .strict();
100
+ const activeToolRowSchema = z
101
+ .object({
102
+ kind: z.literal("tool"),
103
+ sequence: countSchema,
104
+ turnSequence: countSchema,
105
+ at: timestampSchema,
106
+ name: nonblankTextSchema,
107
+ call: toolCallSchema,
108
+ state: z.literal("active"),
109
+ truncated: z.literal(true).optional(),
110
+ })
111
+ .strict();
112
+ const completedToolRowSchema = z
113
+ .object({
114
+ kind: z.literal("tool"),
115
+ sequence: countSchema,
116
+ turnSequence: countSchema,
117
+ at: timestampSchema,
118
+ completedAt: timestampSchema.optional(),
119
+ durationMs: z.number().finite().nonnegative().optional(),
120
+ name: nonblankTextSchema,
121
+ call: toolCallSchema,
122
+ state: toolResultSchema,
123
+ truncated: z.literal(true).optional(),
124
+ })
125
+ .strict();
126
+ const unsettledToolRowSchema = z
127
+ .object({
128
+ kind: z.literal("tool"),
129
+ sequence: countSchema,
130
+ turnSequence: countSchema,
131
+ at: timestampSchema,
132
+ name: nonblankTextSchema,
133
+ call: toolCallSchema,
134
+ state: z.literal("unsettled"),
135
+ truncated: z.literal(true).optional(),
136
+ })
137
+ .strict();
138
+ const snapshotFactRowSchema = z.union([
139
+ callRowSchema,
140
+ textRowSchema("said"),
141
+ textRowSchema("thought"),
142
+ textRowSchema("note"),
143
+ tellRowSchema,
144
+ ]);
145
+ const openSnapshotRowSchema = z.union([snapshotFactRowSchema, activeToolRowSchema, completedToolRowSchema]);
146
+ const idleSnapshotRowSchema = z.union([snapshotFactRowSchema, completedToolRowSchema]);
147
+ const snapshotEntrySchema = (row) => z.union([
148
+ z.object({ kind: z.literal("gap"), count: countSchema }).strict(),
149
+ z.object({ kind: z.literal("row"), row }).strict(),
150
+ ]);
151
+ const reportedFileChangeSchema = z
152
+ .object({
153
+ sequence: countSchema,
154
+ at: timestampSchema,
155
+ op: z.enum(["add", "update", "delete", "unspecified"]),
156
+ path: z.string(),
157
+ diffstat: diffstatSchema.optional(),
158
+ })
159
+ .strict();
160
+ const reportedChangeFields = {
161
+ reportedChanges: z.array(reportedFileChangeSchema).readonly(),
162
+ reportedChangesOmitted: countSchema,
163
+ };
164
+ export const activitySnapshotSchema = z.union([
165
+ z
166
+ .object({
167
+ kind: z.literal("unborn"),
168
+ entries: z.tuple([]).readonly(),
169
+ omitted: z.literal(0),
170
+ ...reportedChangeFields,
171
+ })
172
+ .strict(),
173
+ z
174
+ .object({
175
+ kind: z.literal("open"),
176
+ turn: turnRowSchema,
177
+ entries: z.array(snapshotEntrySchema(openSnapshotRowSchema)).readonly(),
178
+ omitted: countSchema,
179
+ ...reportedChangeFields,
180
+ })
181
+ .strict(),
182
+ z
183
+ .object({
184
+ kind: z.literal("idle"),
185
+ outcome: outcomeRowSchema.optional(),
186
+ entries: z.array(snapshotEntrySchema(idleSnapshotRowSchema)).readonly(),
187
+ omitted: countSchema,
188
+ ...reportedChangeFields,
189
+ })
190
+ .strict(),
191
+ ]);
2
192
  function publicOutcome(turnSequence, outcome) {
3
193
  const historyId = `turn/${turnSequence}`;
4
194
  return outcome.kind === "answered"
@@ -130,6 +130,33 @@ export type Session = Readonly<{
130
130
  text: string;
131
131
  }>): Promise<TellSubmission>;
132
132
  }>;
133
+ /**
134
+ * Synchronous custody for one provider establishment or fork attempt.
135
+ * `closed` is the sole proof that every resource created by the attempt retired.
136
+ */
137
+ export type ProviderAttempt<Result> = Readonly<{
138
+ result: Promise<Result>;
139
+ closed: Promise<void>;
140
+ abort(): Promise<void>;
141
+ forceDispose(): Promise<void>;
142
+ }>;
143
+ export type AttemptResource = Readonly<{
144
+ /** Resolves only when this physical resource has retired. */
145
+ closed: Promise<void>;
146
+ abort?(): Promise<void>;
147
+ forceDispose(): Promise<void>;
148
+ }>;
149
+ export type AttemptCustody = Readonly<{
150
+ signal: AbortSignal;
151
+ /** Register a resource in the attempt before awaiting further setup work. */
152
+ own(resource: AttemptResource): void;
153
+ }>;
154
+ /**
155
+ * Starts provider work after its caller has received custody. The input signal
156
+ * remains a cancellation notification; this attempt owns its own controller
157
+ * and any resulting native resource controls.
158
+ */
159
+ export declare function createProviderAttempt<Result>(parentSignal: AbortSignal | undefined, establish: (custody: AttemptCustody) => Promise<Result>): ProviderAttempt<Result>;
133
160
  export type DriveInput = Readonly<{
134
161
  body: string;
135
162
  launchTells: readonly Readonly<{
@@ -157,18 +184,18 @@ export type ProviderAdapter = Readonly<{
157
184
  session: ResumeCoordinate;
158
185
  at: string;
159
186
  cwd: string;
160
- }>): Promise<Readonly<{
187
+ }>): ProviderAttempt<Readonly<{
161
188
  session: ResumeCoordinate;
162
189
  }>>;
163
190
  start(input: DriveInput & Readonly<{
164
191
  session: Readonly<{
165
192
  kind: "fresh";
166
193
  }>;
167
- }>): Promise<Session>;
194
+ }>): ProviderAttempt<Session>;
168
195
  resume?(input: DriveInput & Readonly<{
169
196
  session: Readonly<{
170
197
  kind: "resume";
171
198
  coordinate: ResumeCoordinate;
172
199
  }>;
173
- }>): Promise<Session>;
200
+ }>): ProviderAttempt<Session>;
174
201
  }>;
@@ -344,3 +344,107 @@ export class AgentEventChannel {
344
344
  };
345
345
  }
346
346
  }
347
+ /**
348
+ * Starts provider work after its caller has received custody. The input signal
349
+ * remains a cancellation notification; this attempt owns its own controller
350
+ * and any resulting native resource controls.
351
+ */
352
+ export function createProviderAttempt(parentSignal, establish) {
353
+ const parent = parentSignal ?? new AbortController().signal;
354
+ const controller = new AbortController();
355
+ const resources = [];
356
+ const ownedResources = new Map();
357
+ const cleanupFailures = [];
358
+ const cleanupOperations = [];
359
+ let setupComplete;
360
+ const setupSettled = new Promise((resolve) => {
361
+ setupComplete = resolve;
362
+ });
363
+ let retiring;
364
+ const remember = (operation, reportFailure) => {
365
+ const observed = operation.catch((error) => {
366
+ if (reportFailure)
367
+ cleanupFailures.push(error);
368
+ });
369
+ cleanupOperations.push(observed);
370
+ return operation;
371
+ };
372
+ const observeBackgroundRetirement = (operation) => {
373
+ void operation.catch(() => undefined);
374
+ };
375
+ const startRetirement = (owned, kind) => {
376
+ if (kind === "abort" && owned.resource.abort === undefined) {
377
+ const force = startRetirement(owned, "forceDispose");
378
+ owned.abort = force;
379
+ return force;
380
+ }
381
+ const existing = owned[kind];
382
+ if (existing !== undefined)
383
+ return existing;
384
+ const graceful = kind === "abort";
385
+ const dispose = graceful ? owned.resource.abort : owned.resource.forceDispose;
386
+ const operation = remember(Promise.resolve().then(() => dispose()), !graceful);
387
+ owned[kind] = operation;
388
+ return operation;
389
+ };
390
+ const retire = (kind) => {
391
+ if (kind === "forceDispose" || retiring === undefined)
392
+ retiring = kind;
393
+ const pending = resources.map((resource) => startRetirement(resource, kind));
394
+ return Promise.all(pending).then(() => undefined);
395
+ };
396
+ const own = (resource) => {
397
+ if (ownedResources.has(resource))
398
+ return;
399
+ const owned = { resource };
400
+ resources.push(owned);
401
+ ownedResources.set(resource, owned);
402
+ void resource.closed.catch((error) => {
403
+ cleanupFailures.push(error);
404
+ });
405
+ if (retiring !== undefined)
406
+ observeBackgroundRetirement(startRetirement(owned, retiring));
407
+ };
408
+ const cancelFromParent = () => {
409
+ if (!controller.signal.aborted)
410
+ controller.abort(parent.reason);
411
+ void retire("abort").catch(() => {
412
+ observeBackgroundRetirement(retire("forceDispose"));
413
+ });
414
+ };
415
+ parent.addEventListener("abort", cancelFromParent, { once: true });
416
+ if (parent.aborted)
417
+ cancelFromParent();
418
+ const result = Promise.resolve()
419
+ .then(() => establish({ signal: controller.signal, own }))
420
+ .catch((error) => {
421
+ if (!controller.signal.aborted)
422
+ observeBackgroundRetirement(retire("forceDispose"));
423
+ throw error;
424
+ })
425
+ .finally(() => parent.removeEventListener("abort", cancelFromParent));
426
+ void result.then(() => setupComplete(), () => setupComplete());
427
+ const closed = (async () => {
428
+ await setupSettled;
429
+ for (;;) {
430
+ const operations = [...cleanupOperations];
431
+ const retired = resources.map((resource) => resource.resource.closed);
432
+ await Promise.allSettled([...operations, ...retired]);
433
+ if (operations.length === cleanupOperations.length)
434
+ break;
435
+ }
436
+ if (cleanupFailures.length > 0)
437
+ throw cleanupFailures[0];
438
+ })();
439
+ const control = async (operation) => {
440
+ if (!controller.signal.aborted)
441
+ controller.abort(new Error("provider attempt retired"));
442
+ await retire(operation);
443
+ };
444
+ return {
445
+ result,
446
+ closed,
447
+ abort: async () => await control("abort"),
448
+ forceDispose: async () => await control("forceDispose"),
449
+ };
450
+ }
@@ -1,6 +1,6 @@
1
1
  import type * as AcpSdk from "@agentclientprotocol/sdk";
2
2
  import { spawnStdioProcess } from "../../../runtime/proc/stdio.js";
3
- import { type ProviderAdapter, type Session } from "../../provider.js";
3
+ import { type AttemptCustody, type ProviderAdapter, type Session } from "../../provider.js";
4
4
  import { type AcpToolInterpreter, type AcpToolUpdate } from "./events.js";
5
5
  export type { AcpToolInterpreter, AcpToolUpdate };
6
6
  export type AcpStartInput = Parameters<ProviderAdapter["start"]>[0] | Parameters<NonNullable<ProviderAdapter["resume"]>>[0];
@@ -21,4 +21,4 @@ type AcpLaunch = Readonly<{
21
21
  argv: readonly [string, ...string[]];
22
22
  env?: Readonly<Record<string, string>>;
23
23
  }>;
24
- export declare function startAcpSession(launch: AcpLaunch, input: AcpStartInput, dependencies?: AcpDependencies): Promise<AcpLiveSession>;
24
+ export declare function startAcpSession(launch: AcpLaunch, input: AcpStartInput, dependencies?: AcpDependencies, custody?: AttemptCustody): Promise<AcpLiveSession>;
@@ -149,7 +149,7 @@ function beginAcpPrompt(context, child, turn, sessionId, input) {
149
149
  },
150
150
  };
151
151
  }
152
- export async function startAcpSession(launch, input, dependencies = {}) {
152
+ export async function startAcpSession(launch, input, dependencies = {}, custody) {
153
153
  const acp = await import("@agentclientprotocol/sdk");
154
154
  const signal = input.signal ?? new AbortController().signal;
155
155
  signal.throwIfAborted();
@@ -162,6 +162,11 @@ export async function startAcpSession(launch, input, dependencies = {}) {
162
162
  ...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
163
163
  },
164
164
  });
165
+ custody?.own({
166
+ closed: child.exited.then(() => undefined),
167
+ abort: async () => await child.close(true),
168
+ forceDispose: async () => await child.close(true),
169
+ });
165
170
  let sessionId;
166
171
  let turn;
167
172
  const connection = createAcpClient(acp, (notification) => {
@@ -177,11 +182,13 @@ export async function startAcpSession(launch, input, dependencies = {}) {
177
182
  }
178
183
  catch (error) {
179
184
  connection.close(error);
180
- try {
181
- await child.close(true);
182
- }
183
- catch (cleanup) {
184
- throw new Error(`${diagnostic(acp, error)}; ACP cleanup failed: ${diagnostic(acp, cleanup)}`, { cause: error });
185
+ if (custody === undefined) {
186
+ try {
187
+ await child.close(true);
188
+ }
189
+ catch (cleanup) {
190
+ throw new Error(`${diagnostic(acp, error)}; ACP cleanup failed: ${diagnostic(acp, cleanup)}`, { cause: error });
191
+ }
185
192
  }
186
193
  turn.events.end();
187
194
  throw error;
@@ -1,3 +1,4 @@
1
+ import { createProviderAttempt } from "../../provider.js";
1
2
  import { decodeAcpConfig } from "./config.js";
2
3
  import { startAcpSession } from "./core.js";
3
4
  export { decodeAcpConfig } from "./config.js";
@@ -53,16 +54,16 @@ export function createAcpProvider(execution, dependencies = {}) {
53
54
  if (execution.executable === undefined)
54
55
  throw new TypeError("ACP provider execution requires executable");
55
56
  const config = decodeAcpConfig(execution.config);
56
- const drive = async (input) => {
57
+ const drive = async (input, custody) => {
57
58
  const launch = {
58
59
  argv: argv(execution, config, input.options),
59
60
  ...(execution.env === undefined ? {} : { env: execution.env }),
60
61
  };
61
- return (await startAcpSession(launch, input, dependencies)).session;
62
+ return (await startAcpSession(launch, input, dependencies, custody)).session;
62
63
  };
63
64
  return {
64
65
  admitOptions: (options) => optionAdmission(options, config),
65
- start: async (input) => await drive(input),
66
- resume: async (input) => await drive(input),
66
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await drive({ ...input, signal: custody.signal }, custody)),
67
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await drive({ ...input, signal: custody.signal }, custody)),
67
68
  };
68
69
  }
@@ -27,18 +27,18 @@ export declare const claudeProvider: Readonly<{
27
27
  session: ResumeCoordinate;
28
28
  at: string;
29
29
  cwd: string;
30
- }>): Promise<Readonly<{
30
+ }>): import("../../provider.js").ProviderAttempt<Readonly<{
31
31
  session: ResumeCoordinate;
32
32
  }>>;
33
33
  start(input: import("../../provider.js").DriveInput & Readonly<{
34
34
  session: Readonly<{
35
35
  kind: "fresh";
36
36
  }>;
37
- }>): Promise<Session>;
37
+ }>): import("../../provider.js").ProviderAttempt<Session>;
38
38
  resume?(input: import("../../provider.js").DriveInput & Readonly<{
39
39
  session: Readonly<{
40
40
  kind: "resume";
41
41
  coordinate: ResumeCoordinate;
42
42
  }>;
43
- }>): Promise<Session>;
43
+ }>): import("../../provider.js").ProviderAttempt<Session>;
44
44
  }>;
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { abortable } from "../../abort.js";
3
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, } from "../../provider.js";
3
+ import { AKUMA_REQUESTS_ENV, AgentEventChannel, createProviderAttempt, } from "../../provider.js";
4
4
  import { emitClaudeMessage } from "./events.js";
5
5
  import { claudeUserMessage, createClaudeInput, isClaudeTurnEnded } from "./input.js";
6
6
  export { CLAUDE_MESSAGE_DISPOSITIONS, CLAUDE_SYSTEM_DISPOSITIONS } from "./events.js";
@@ -282,7 +282,7 @@ function observeClaudeQuery(context) {
282
282
  settleReceipts,
283
283
  };
284
284
  }
285
- async function driveClaude(load, execution, drive) {
285
+ async function driveClaude(load, execution, drive, custody) {
286
286
  if (drive.session.kind === "resume")
287
287
  claudeSessionId(drive.session.coordinate);
288
288
  const signal = drive.signal ?? new AbortController().signal;
@@ -324,6 +324,17 @@ async function driveClaude(load, execution, drive) {
324
324
  },
325
325
  },
326
326
  });
327
+ custody.own({
328
+ closed: observed.completion.then(() => undefined),
329
+ abort: async () => {
330
+ shutDown(new Error("Claude query aborted"));
331
+ await observed.completion;
332
+ },
333
+ forceDispose: async () => {
334
+ shutDown(new Error("Claude query force-disposed"));
335
+ await observed.completion;
336
+ },
337
+ });
327
338
  if (signal.aborted)
328
339
  abortSetup();
329
340
  try {
@@ -356,9 +367,9 @@ export function createClaudeProvider(loadOrExecution = async () => (await import
356
367
  const selectedExecution = typeof loadOrExecution === "function" ? execution : loadOrExecution;
357
368
  return {
358
369
  admitOptions: admitClaudeOptions,
359
- fork: (input) => forkClaude(load, selectedExecution, input),
360
- start: (input) => driveClaude(load, selectedExecution, input),
361
- resume: (input) => driveClaude(load, selectedExecution, input),
370
+ fork: (input) => createProviderAttempt(new AbortController().signal, async () => await forkClaude(load, selectedExecution, input)),
371
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await driveClaude(load, selectedExecution, { ...input, signal: custody.signal }, custody)),
372
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await driveClaude(load, selectedExecution, { ...input, signal: custody.signal }, custody)),
362
373
  };
363
374
  }
364
375
  export const claudeProvider = createClaudeProvider(async () => (await import("@anthropic-ai/claude-agent-sdk")));
@@ -274,7 +274,7 @@ function notificationAction(method, params) {
274
274
  case "guardianWarning":
275
275
  return `Guardian warning: ${message ?? "action warned"}`;
276
276
  case "hook/started":
277
- return `Hook ${codexText(params.name) ?? codexText(params.hookName) ?? "unknown"} started`;
277
+ return `Hook ${codexText(params.name) ?? codexText(params.hookName) ?? codexText(params.hook_name) ?? "unknown"} started`;
278
278
  case "item/autoApprovalReview/completed":
279
279
  return "Action approval review completed";
280
280
  case "item/autoApprovalReview/started":
@@ -1,5 +1,5 @@
1
1
  import { LineRpcProcess } from "../../../runtime/proc/line-rpc.js";
2
- import { AgentEventChannel, AKUMA_REQUESTS_ENV, } from "../../provider.js";
2
+ import { AgentEventChannel, AKUMA_REQUESTS_ENV, createProviderAttempt, } from "../../provider.js";
3
3
  import { codexNotificationResult, codexObject, codexText } from "./events.js";
4
4
  export { CODEX_ITEM_DISPOSITIONS, CODEX_NOTIFICATION_DISPOSITIONS } from "./events.js";
5
5
  function diagnostic(error) {
@@ -97,7 +97,7 @@ async function admitTurn(server, input, state, events, config) {
97
97
  sandboxPolicy: sandbox(input.cwd, input.options, input.requests),
98
98
  }));
99
99
  }
100
- async function forkCodex(execution, input) {
100
+ async function forkCodex(execution, input, custody) {
101
101
  if (!("sessionId" in input.session))
102
102
  throw new Error("Codex app-server fork requires sessionId");
103
103
  const server = new LineRpcProcess({
@@ -105,6 +105,12 @@ async function forkCodex(execution, input) {
105
105
  cwd: input.cwd,
106
106
  ...(execution.env === undefined ? {} : { env: { ...process.env, ...execution.env } }),
107
107
  });
108
+ const closed = new Promise((resolve) => server.onExit(() => resolve()));
109
+ custody.own({
110
+ closed,
111
+ abort: async () => await server.close(true),
112
+ forceDispose: async () => await server.close(true),
113
+ });
108
114
  try {
109
115
  await initialize(server);
110
116
  const child = threadId(await server.request("thread/fork", {
@@ -139,12 +145,18 @@ function codexServer(execution, input) {
139
145
  }),
140
146
  });
141
147
  }
142
- async function startCodex(execution, input) {
148
+ async function startCodex(execution, input, custody) {
143
149
  const signal = input.signal ?? new AbortController().signal;
144
150
  if (input.session.kind === "resume" && !("sessionId" in input.session.coordinate))
145
151
  throw new Error("Codex app-server resume requires sessionId");
146
152
  const events = new AgentEventChannel();
147
153
  const server = codexServer(execution, input);
154
+ const closed = new Promise((resolve) => server.onExit(() => resolve()));
155
+ custody.own({
156
+ closed,
157
+ abort: async () => await server.close(true),
158
+ forceDispose: async () => await server.close(true),
159
+ });
148
160
  const state = { settled: false, tools: new Map() };
149
161
  let settle;
150
162
  const completion = new Promise((resolve) => {
@@ -220,8 +232,8 @@ export function createCodexAppServerProvider(input = "codex") {
220
232
  ...(options.readonly === undefined ? {} : { readonly: { enforcement: "native" } }),
221
233
  };
222
234
  },
223
- fork: (input) => forkCodex(execution, input),
224
- start: (input) => startCodex(execution, input),
225
- resume: (input) => startCodex(execution, input),
235
+ fork: (input) => createProviderAttempt(new AbortController().signal, async (custody) => await forkCodex(execution, input, custody)),
236
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await startCodex(execution, { ...input, signal: custody.signal }, custody)),
237
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await startCodex(execution, { ...input, signal: custody.signal }, custody)),
226
238
  };
227
239
  }
@@ -1,3 +1,4 @@
1
+ import { createProviderAttempt } from "../../provider.js";
1
2
  import { startAcpSession, } from "../acp/core.js";
2
3
  const INTERJECT_METHOD = "x.ai/interject";
3
4
  function nonblank(value) {
@@ -131,7 +132,7 @@ function withInterject(live) {
131
132
  export function createGrokBuildProvider(execution, dependencies = {}) {
132
133
  if (execution.executable === undefined)
133
134
  throw new TypeError("Grok Build provider execution requires executable");
134
- const drive = async (input) => {
135
+ const drive = async (input, custody) => {
135
136
  const launch = {
136
137
  argv: argv(execution, input.options),
137
138
  ...(execution.env === undefined ? {} : { env: execution.env }),
@@ -140,11 +141,11 @@ export function createGrokBuildProvider(execution, dependencies = {}) {
140
141
  ...dependencies,
141
142
  interpretTool: interpretGrokTool,
142
143
  ...grokSessionMeta(execution.config, input.options),
143
- }));
144
+ }, custody));
144
145
  };
145
146
  return {
146
147
  admitOptions: optionAdmission,
147
- start: async (input) => await drive(input),
148
- resume: async (input) => await drive(input),
148
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await drive({ ...input, signal: custody.signal }, custody)),
149
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await drive({ ...input, signal: custody.signal }, custody)),
149
150
  };
150
151
  }