@astrosheep/keiyaku 4.5.7 → 4.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) 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 +12 -11
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +33 -12
  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 +392 -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/archetype.js +1 -1
  23. package/build/src/akuma/body.d.ts +8 -5
  24. package/build/src/akuma/body.js +12 -5
  25. package/build/src/akuma/call-request.d.ts +80 -0
  26. package/build/src/akuma/call-request.js +211 -0
  27. package/build/src/akuma/heart/facts.d.ts +16 -78
  28. package/build/src/akuma/heart/facts.js +11 -0
  29. package/build/src/akuma/heart/index.d.ts +3 -2
  30. package/build/src/akuma/heart/index.js +4 -1
  31. package/build/src/akuma/heart/request-authority.d.ts +5 -4
  32. package/build/src/akuma/heart/request-authority.js +20 -34
  33. package/build/src/akuma/heart/request-rows.js +12 -23
  34. package/build/src/akuma/heart/schema.d.ts +1 -1
  35. package/build/src/akuma/heart/schema.js +5 -13
  36. package/build/src/akuma/index.d.ts +1 -1
  37. package/build/src/akuma/index.js +1 -1
  38. package/build/src/akuma/projection-read.d.ts +1 -0
  39. package/build/src/akuma/projection-read.js +8 -1
  40. package/build/src/akuma/projection.d.ts +907 -137
  41. package/build/src/akuma/projection.js +193 -9
  42. package/build/src/akuma/provider.d.ts +30 -3
  43. package/build/src/akuma/provider.js +111 -0
  44. package/build/src/akuma/providers/acp/core.d.ts +2 -2
  45. package/build/src/akuma/providers/acp/core.js +13 -6
  46. package/build/src/akuma/providers/acp/index.js +5 -4
  47. package/build/src/akuma/providers/claude/events.d.ts +2 -2
  48. package/build/src/akuma/providers/claude/events.js +16 -3
  49. package/build/src/akuma/providers/claude/index.d.ts +3 -3
  50. package/build/src/akuma/providers/claude/index.js +16 -5
  51. package/build/src/akuma/providers/codex-app-server/events.d.ts +2 -2
  52. package/build/src/akuma/providers/codex-app-server/events.js +25 -4
  53. package/build/src/akuma/providers/codex-app-server/index.js +18 -6
  54. package/build/src/akuma/providers/grok-build/index.js +5 -4
  55. package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
  56. package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
  57. package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
  58. package/build/src/akuma/providers/pi/index.js +72 -58
  59. package/build/src/akuma/request-lifecycle.d.ts +4 -10
  60. package/build/src/akuma/request-lifecycle.js +7 -5
  61. package/build/src/akuma/request-rendezvous.d.ts +26 -0
  62. package/build/src/akuma/request-rendezvous.js +100 -0
  63. package/build/src/akuma/request-serve.d.ts +6 -66
  64. package/build/src/akuma/request-serve.js +83 -210
  65. package/build/src/akuma/request-wire.d.ts +109 -115
  66. package/build/src/akuma/request-wire.js +63 -318
  67. package/build/src/akuma/requests.d.ts +19 -47
  68. package/build/src/akuma/requests.js +51 -107
  69. package/build/src/akuma/turn-drive.d.ts +5 -3
  70. package/build/src/akuma/turn-drive.js +39 -11
  71. package/build/src/akuma-body.d.ts +7 -2
  72. package/build/src/akuma-body.js +68 -46
  73. package/build/src/body/amend.js +18 -19
  74. package/build/src/cli/accepted.d.ts +1 -1
  75. package/build/src/cli/accepted.js +2 -2
  76. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  77. package/build/src/cli/commands/akuma-invoke.js +17 -9
  78. package/build/src/cli/commands/akuma.js +8 -8
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -2
  80. package/build/src/cli/commands/contract-invoke.js +42 -30
  81. package/build/src/cli/commands/contract.d.ts +16 -20
  82. package/build/src/cli/commands/contract.js +58 -25
  83. package/build/src/cli/commands/install.d.ts +2 -1
  84. package/build/src/cli/commands/install.js +3 -2
  85. package/build/src/cli/commands/task-invoke.d.ts +3 -0
  86. package/build/src/cli/commands/task-invoke.js +23 -140
  87. package/build/src/cli/commands/task.js +54 -20
  88. package/build/src/cli/invoke.js +39 -25
  89. package/build/src/cli/main.js +2 -1
  90. package/build/src/cli/parse.d.ts +2 -2
  91. package/build/src/cli/parse.js +56 -27
  92. package/build/src/cli/render/akuma-activity.d.ts +3 -0
  93. package/build/src/cli/render/akuma-activity.js +45 -29
  94. package/build/src/cli/render/akuma-tool.d.ts +5 -1
  95. package/build/src/cli/render/akuma.js +4 -8
  96. package/build/src/cli/render/audit.js +1 -1
  97. package/build/src/cli/render/catalog.js +80 -21
  98. package/build/src/cli/render/contract-observation.d.ts +2 -0
  99. package/build/src/cli/render/contract-observation.js +11 -0
  100. package/build/src/cli/render/contract.js +35 -62
  101. package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
  102. package/build/src/cli/render/kanshi-akuma.js +132 -0
  103. package/build/src/cli/render/kanshi.js +131 -213
  104. package/build/src/cli/render/receipt.d.ts +1 -2
  105. package/build/src/cli/render/receipt.js +76 -26
  106. package/build/src/cli/render/refusal.js +2 -10
  107. package/build/src/cli/render/region.js +5 -2
  108. package/build/src/cli/render/settings.d.ts +1 -1
  109. package/build/src/cli/render/settings.js +27 -8
  110. package/build/src/cli/render/task.js +22 -5
  111. package/build/src/cli/render/terminal.d.ts +32 -1
  112. package/build/src/cli/render/terminal.js +77 -1
  113. package/build/src/cli/result.d.ts +3 -3
  114. package/build/src/cli/runtime.js +4 -2
  115. package/build/src/cli/selectors.d.ts +2 -1
  116. package/build/src/cli/selectors.js +4 -3
  117. package/build/src/cli/square-edge.js +120 -36
  118. package/build/src/cli/usage.js +2 -1
  119. package/build/src/contract-guidance.d.ts +5 -0
  120. package/build/src/contract-guidance.js +121 -0
  121. package/build/src/contract-worktree.d.ts +0 -1
  122. package/build/src/contract-worktree.js +32 -83
  123. package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
  124. package/build/src/coordination/sqlite-transaction-lock.js +5 -0
  125. package/build/src/core/facts/codec.d.ts +2 -0
  126. package/build/src/core/facts/codec.js +4 -0
  127. package/build/src/core/verbs/placement.d.ts +6 -1
  128. package/build/src/core/verbs/placement.js +5 -3
  129. package/build/src/dispatch/index.d.ts +0 -2
  130. package/build/src/dispatch/index.js +54 -55
  131. package/build/src/git/admission.js +2 -2
  132. package/build/src/git/hooks.d.ts +9 -15
  133. package/build/src/git/hooks.js +23 -317
  134. package/build/src/git/nuke.d.ts +2 -1
  135. package/build/src/git/nuke.js +39 -21
  136. package/build/src/git/private-state-seat.d.ts +12 -0
  137. package/build/src/git/private-state-seat.js +39 -0
  138. package/build/src/git/process.d.ts +6 -0
  139. package/build/src/git/process.js +26 -11
  140. package/build/src/git/read-observation.js +29 -20
  141. package/build/src/git/reconcile.d.ts +13 -1
  142. package/build/src/git/reconcile.js +70 -11
  143. package/build/src/git/ref-migration.d.ts +22 -0
  144. package/build/src/git/ref-migration.js +55 -0
  145. package/build/src/git/repository.d.ts +10 -6
  146. package/build/src/git/repository.js +16 -8
  147. package/build/src/git/target-placement.d.ts +1 -0
  148. package/build/src/git/target-placement.js +5 -0
  149. package/build/src/git/tender.d.ts +1 -0
  150. package/build/src/git/terminal-reconcile.d.ts +1 -1
  151. package/build/src/git/terminal-reconcile.js +22 -20
  152. package/build/src/git/workspace.d.ts +14 -0
  153. package/build/src/git/workspace.js +44 -0
  154. package/build/src/kanshi/index.d.ts +1 -1
  155. package/build/src/kanshi/read.js +16 -16
  156. package/build/src/kanshi/report.d.ts +2 -2
  157. package/build/src/library/address.d.ts +9 -3
  158. package/build/src/library/address.js +3 -2
  159. package/build/src/library/akuma-creation.d.ts +4 -3
  160. package/build/src/library/akuma-creation.js +19 -13
  161. package/build/src/library/audit.d.ts +14 -6
  162. package/build/src/library/audit.js +13 -23
  163. package/build/src/library/catalog.d.ts +3 -1
  164. package/build/src/library/catalog.js +20 -6
  165. package/build/src/library/composition.d.ts +385 -0
  166. package/build/src/library/composition.js +28 -0
  167. package/build/src/library/configuration.js +8 -2
  168. package/build/src/library/continuation.js +66 -30
  169. package/build/src/library/contract-bind.js +10 -2
  170. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +102 -0
  171. package/build/src/library/contract-forwarding-reconciliation-result.js +92 -0
  172. package/build/src/library/contract-forwarding-result.d.ts +4464 -0
  173. package/build/src/library/contract-forwarding-result.js +521 -0
  174. package/build/src/library/contract-forwarding.d.ts +71 -0
  175. package/build/src/library/contract-forwarding.js +114 -0
  176. package/build/src/library/contract-handle.d.ts +38 -0
  177. package/build/src/library/contract-handle.js +314 -0
  178. package/build/src/library/contract-operations.d.ts +214 -69
  179. package/build/src/library/contract-operations.js +189 -118
  180. package/build/src/library/contract-types.d.ts +7 -3
  181. package/build/src/library/contract.d.ts +21 -34
  182. package/build/src/library/contract.js +29 -287
  183. package/build/src/library/fleet-result.d.ts +1580 -0
  184. package/build/src/library/fleet-result.js +98 -0
  185. package/build/src/library/fleet.d.ts +112 -54
  186. package/build/src/library/fleet.js +211 -71
  187. package/build/src/library/input.d.ts +3 -1
  188. package/build/src/library/input.js +11 -1
  189. package/build/src/library/keiyaku.d.ts +388 -20
  190. package/build/src/library/keiyaku.js +13 -21
  191. package/build/src/library/mutation.d.ts +3 -3
  192. package/build/src/library/mutation.js +5 -9
  193. package/build/src/library/nuke.d.ts +1 -1
  194. package/build/src/library/nuke.js +4 -3
  195. package/build/src/library/reconcile.d.ts +4 -0
  196. package/build/src/library/reconcile.js +2 -0
  197. package/build/src/library/refusal.d.ts +0 -9
  198. package/build/src/library/refusal.js +0 -7
  199. package/build/src/protocol/amend.js +56 -50
  200. package/build/src/protocol/attempt.d.ts +2 -0
  201. package/build/src/protocol/attempt.js +8 -1
  202. package/build/src/protocol/completion.js +7 -0
  203. package/build/src/protocol/deliver.js +6 -2
  204. package/build/src/protocol/operations.d.ts +4 -1
  205. package/build/src/protocol/operations.js +5 -2
  206. package/build/src/protocol/placement.d.ts +1 -0
  207. package/build/src/protocol/placement.js +52 -38
  208. package/build/src/protocol/read/observation.d.ts +1 -4
  209. package/build/src/protocol/read/observation.js +2 -15
  210. package/build/src/protocol/reintegrate.js +62 -58
  211. package/build/src/protocol/review.js +5 -0
  212. package/build/src/protocol/run.js +17 -13
  213. package/build/src/runtime/proc/run.d.ts +9 -0
  214. package/build/src/runtime/proc/run.js +51 -4
  215. package/build/src/runtime/proc/termination.js +26 -22
  216. package/build/src/runtime/proc/windows-launch.exe +0 -0
  217. package/build/src/settlement/holder.d.ts +2 -1
  218. package/build/src/settlement/holder.js +15 -4
  219. package/build/src/settlement/settle.js +99 -33
  220. package/build/src/task/board.d.ts +68 -27
  221. package/build/src/task/board.js +31 -1
  222. package/build/src/task/catalog.d.ts +4 -0
  223. package/build/src/task/catalog.js +23 -0
  224. package/build/src/task/compose-language.js +33 -7
  225. package/build/src/task/compose-parser.d.ts +1 -1
  226. package/build/src/task/compose-parser.js +1 -1
  227. package/build/src/task/compose.js +2 -0
  228. package/build/src/task/index.d.ts +12 -4
  229. package/build/src/task/index.js +97 -16
  230. package/build/src/task/mutation-result.d.ts +923 -0
  231. package/build/src/task/mutation-result.js +140 -0
  232. package/build/src/task/mutation.d.ts +552 -28
  233. package/build/src/task/mutation.js +304 -97
  234. package/build/src/task/operations.d.ts +1 -4
  235. package/build/src/task/operations.js +0 -6
  236. package/build/src/task/public.d.ts +2 -0
  237. package/build/src/task/public.js +1 -0
  238. package/build/src/workspace-place.d.ts +7 -2
  239. package/build/src/workspace-place.js +34 -24
  240. package/build/src/world.d.ts +1 -0
  241. package/build/src/world.js +11 -0
  242. package/package.json +8 -6
  243. package/build/src/akuma/request-execution.d.ts +0 -8
  244. package/build/src/akuma/request-execution.js +0 -98
@@ -1,4 +1,196 @@
1
+ /* eslint-disable max-lines -- Akuma owns one coherent activity projection and strict boundary schema. */
1
2
  import { decodeAgentEvent } from "./provider.js";
3
+ import { projectTell } from "./heart/facts.js";
4
+ import { z } from "zod";
5
+ const nonblankTextSchema = z.string().refine((value) => value.trim() !== "");
6
+ const countSchema = z.number().int().nonnegative();
7
+ const timestampSchema = z.string().refine((value) => Number.isFinite(Date.parse(value)), "expected timestamp");
8
+ const diffstatSchema = z.object({ added: countSchema, removed: countSchema }).strict();
9
+ const fileChangeSchema = z
10
+ .object({
11
+ op: z.enum(["add", "update", "delete", "unspecified"]),
12
+ path: z.string(),
13
+ diffstat: diffstatSchema.optional(),
14
+ })
15
+ .strict();
16
+ const toolCallSchema = z.union([
17
+ z.object({ kind: z.literal("run"), command: z.string() }).strict(),
18
+ z
19
+ .object({
20
+ kind: z.literal("read"),
21
+ path: z.string(),
22
+ offset: countSchema.optional(),
23
+ limit: countSchema.optional(),
24
+ })
25
+ .strict(),
26
+ z
27
+ .object({
28
+ kind: z.literal("search"),
29
+ query: z.string(),
30
+ scope: z.enum(["content", "files", "web"]).optional(),
31
+ path: z.string().optional(),
32
+ glob: z.string().optional(),
33
+ })
34
+ .strict(),
35
+ z.object({ kind: z.literal("fileChange"), changes: z.array(fileChangeSchema).readonly() }).strict(),
36
+ z.object({ kind: z.literal("other"), display: z.string() }).strict(),
37
+ ]);
38
+ const toolResultSchema = z
39
+ .object({ status: z.enum(["ok", "error"]), message: z.string().optional(), exitCode: z.number().int().optional() })
40
+ .strict();
41
+ const tellDeliverySchema = z
42
+ .object({
43
+ deliveredAt: timestampSchema,
44
+ route: z.enum(["launch", "live"]),
45
+ turnSequence: countSchema,
46
+ receipt: z.enum(["unavailable", "required"]).optional(),
47
+ })
48
+ .strict()
49
+ .transform(({ receipt, ...delivery }) => (receipt === undefined ? delivery : { ...delivery, receipt }));
50
+ const turnRowSchema = z
51
+ .object({
52
+ kind: z.literal("turn"),
53
+ sequence: countSchema,
54
+ turnSequence: countSchema,
55
+ bodySequence: countSchema,
56
+ at: timestampSchema,
57
+ })
58
+ .strict();
59
+ const callRowSchema = z
60
+ .object({
61
+ kind: z.literal("call"),
62
+ sequence: countSchema,
63
+ turnSequence: countSchema,
64
+ at: timestampSchema,
65
+ text: z.string(),
66
+ })
67
+ .strict();
68
+ const textRowSchema = (kind) => z
69
+ .object({
70
+ kind: z.literal(kind),
71
+ sequence: countSchema,
72
+ turnSequence: countSchema,
73
+ at: timestampSchema,
74
+ text: z.string(),
75
+ truncated: z.literal(true).optional(),
76
+ })
77
+ .strict();
78
+ export const tellRowSchema = z
79
+ .object({
80
+ kind: z.literal("tell"),
81
+ sequence: countSchema,
82
+ at: timestampSchema,
83
+ tellId: nonblankTextSchema,
84
+ text: z.string(),
85
+ state: z.enum(["pending", "told"]),
86
+ deliveries: z.array(tellDeliverySchema).readonly(),
87
+ })
88
+ .strict();
89
+ const outcomeSchema = z.union([
90
+ z.object({ kind: z.literal("answered"), historyId: nonblankTextSchema, answer: z.string() }).strict(),
91
+ z.object({ kind: z.literal("failed"), historyId: nonblankTextSchema, diagnostic: z.string() }).strict(),
92
+ ]);
93
+ const outcomeRowSchema = z
94
+ .object({
95
+ kind: z.literal("outcome"),
96
+ sequence: countSchema,
97
+ turnSequence: countSchema,
98
+ at: timestampSchema,
99
+ outcome: outcomeSchema,
100
+ })
101
+ .strict();
102
+ const activeToolRowSchema = z
103
+ .object({
104
+ kind: z.literal("tool"),
105
+ sequence: countSchema,
106
+ turnSequence: countSchema,
107
+ at: timestampSchema,
108
+ name: nonblankTextSchema,
109
+ call: toolCallSchema,
110
+ state: z.literal("active"),
111
+ truncated: z.literal(true).optional(),
112
+ })
113
+ .strict();
114
+ const completedToolRowSchema = z
115
+ .object({
116
+ kind: z.literal("tool"),
117
+ sequence: countSchema,
118
+ turnSequence: countSchema,
119
+ at: timestampSchema,
120
+ completedAt: timestampSchema.optional(),
121
+ durationMs: z.number().finite().nonnegative().optional(),
122
+ name: nonblankTextSchema,
123
+ call: toolCallSchema,
124
+ state: toolResultSchema,
125
+ truncated: z.literal(true).optional(),
126
+ })
127
+ .strict();
128
+ const unsettledToolRowSchema = z
129
+ .object({
130
+ kind: z.literal("tool"),
131
+ sequence: countSchema,
132
+ turnSequence: countSchema,
133
+ at: timestampSchema,
134
+ name: nonblankTextSchema,
135
+ call: toolCallSchema,
136
+ state: z.literal("unsettled"),
137
+ truncated: z.literal(true).optional(),
138
+ })
139
+ .strict();
140
+ const snapshotFactRowSchema = z.union([
141
+ callRowSchema,
142
+ textRowSchema("said"),
143
+ textRowSchema("thought"),
144
+ textRowSchema("note"),
145
+ tellRowSchema,
146
+ ]);
147
+ const openSnapshotRowSchema = z.union([snapshotFactRowSchema, activeToolRowSchema, completedToolRowSchema]);
148
+ const idleSnapshotRowSchema = z.union([snapshotFactRowSchema, completedToolRowSchema]);
149
+ const snapshotEntrySchema = (row) => z.union([
150
+ z.object({ kind: z.literal("gap"), count: countSchema }).strict(),
151
+ z.object({ kind: z.literal("row"), row }).strict(),
152
+ ]);
153
+ const reportedFileChangeSchema = z
154
+ .object({
155
+ sequence: countSchema,
156
+ at: timestampSchema,
157
+ op: z.enum(["add", "update", "delete", "unspecified"]),
158
+ path: z.string(),
159
+ diffstat: diffstatSchema.optional(),
160
+ })
161
+ .strict();
162
+ const reportedChangeFields = {
163
+ reportedChanges: z.array(reportedFileChangeSchema).readonly(),
164
+ reportedChangesOmitted: countSchema,
165
+ };
166
+ export const activitySnapshotSchema = z.union([
167
+ z
168
+ .object({
169
+ kind: z.literal("unborn"),
170
+ entries: z.tuple([]).readonly(),
171
+ omitted: z.literal(0),
172
+ ...reportedChangeFields,
173
+ })
174
+ .strict(),
175
+ z
176
+ .object({
177
+ kind: z.literal("open"),
178
+ turn: turnRowSchema,
179
+ entries: z.array(snapshotEntrySchema(openSnapshotRowSchema)).readonly(),
180
+ omitted: countSchema,
181
+ ...reportedChangeFields,
182
+ })
183
+ .strict(),
184
+ z
185
+ .object({
186
+ kind: z.literal("idle"),
187
+ outcome: outcomeRowSchema.optional(),
188
+ entries: z.array(snapshotEntrySchema(idleSnapshotRowSchema)).readonly(),
189
+ omitted: countSchema,
190
+ ...reportedChangeFields,
191
+ })
192
+ .strict(),
193
+ ]);
2
194
  function publicOutcome(turnSequence, outcome) {
3
195
  const historyId = `turn/${turnSequence}`;
4
196
  return outcome.kind === "answered"
@@ -207,15 +399,7 @@ export function projectTurns(facts, retained = {
207
399
  turn.rows.push(row);
208
400
  }
209
401
  else if (fact.kind === "tell") {
210
- state.tells.push({
211
- kind: "tell",
212
- sequence: fact.sequence,
213
- at: fact.recordedAt,
214
- tellId: fact.id,
215
- text: fact.body,
216
- state: fact.state,
217
- deliveries: fact.deliveries,
218
- });
402
+ state.tells.push(projectTell(fact));
219
403
  }
220
404
  else {
221
405
  projectActivityEvent(state, fact);
@@ -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,114 @@ 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 establishmentSettled = false;
364
+ let retiring;
365
+ const remember = (operation, reportFailure) => {
366
+ const observed = operation.catch((error) => {
367
+ if (reportFailure)
368
+ cleanupFailures.push(error);
369
+ });
370
+ cleanupOperations.push(observed);
371
+ return operation;
372
+ };
373
+ const observeBackgroundRetirement = (operation) => {
374
+ void operation.catch(() => undefined);
375
+ };
376
+ const startRetirement = (owned, kind) => {
377
+ if (kind === "abort" && owned.resource.abort === undefined) {
378
+ const force = startRetirement(owned, "forceDispose");
379
+ owned.abort = force;
380
+ return force;
381
+ }
382
+ const existing = owned[kind];
383
+ if (existing !== undefined)
384
+ return existing;
385
+ const graceful = kind === "abort";
386
+ const dispose = graceful ? owned.resource.abort : owned.resource.forceDispose;
387
+ const operation = remember(Promise.resolve().then(() => dispose()), !graceful);
388
+ owned[kind] = operation;
389
+ return operation;
390
+ };
391
+ const retire = (kind) => {
392
+ if (kind === "forceDispose" || retiring === undefined)
393
+ retiring = kind;
394
+ const pending = resources.map((resource) => startRetirement(resource, kind));
395
+ return Promise.all(pending).then(() => undefined);
396
+ };
397
+ const own = (resource) => {
398
+ if (establishmentSettled)
399
+ throw new Error("cannot own a resource after provider establishment settles");
400
+ if (ownedResources.has(resource))
401
+ return;
402
+ const owned = { resource };
403
+ resources.push(owned);
404
+ ownedResources.set(resource, owned);
405
+ void resource.closed.catch((error) => {
406
+ cleanupFailures.push(error);
407
+ });
408
+ if (retiring !== undefined)
409
+ observeBackgroundRetirement(startRetirement(owned, retiring));
410
+ };
411
+ const cancelFromParent = () => {
412
+ if (!controller.signal.aborted)
413
+ controller.abort(parent.reason);
414
+ void retire("abort").catch(() => {
415
+ observeBackgroundRetirement(retire("forceDispose"));
416
+ });
417
+ };
418
+ parent.addEventListener("abort", cancelFromParent, { once: true });
419
+ if (parent.aborted)
420
+ cancelFromParent();
421
+ const result = Promise.resolve()
422
+ .then(() => establish({ signal: controller.signal, own }))
423
+ .catch((error) => {
424
+ if (!controller.signal.aborted)
425
+ observeBackgroundRetirement(retire("forceDispose"));
426
+ throw error;
427
+ })
428
+ .finally(() => {
429
+ establishmentSettled = true;
430
+ parent.removeEventListener("abort", cancelFromParent);
431
+ });
432
+ void result.then(() => setupComplete(), () => setupComplete());
433
+ const closed = (async () => {
434
+ await setupSettled;
435
+ for (;;) {
436
+ const operations = [...cleanupOperations];
437
+ const retired = resources.map((resource) => resource.resource.closed);
438
+ await Promise.allSettled([...operations, ...retired]);
439
+ if (operations.length === cleanupOperations.length)
440
+ break;
441
+ }
442
+ if (cleanupFailures.length > 0)
443
+ throw cleanupFailures[0];
444
+ })();
445
+ void closed.catch(() => undefined);
446
+ const control = async (operation) => {
447
+ if (!controller.signal.aborted)
448
+ controller.abort(new Error("provider attempt retired"));
449
+ await retire(operation);
450
+ };
451
+ return {
452
+ result,
453
+ closed,
454
+ abort: async () => await control("abort"),
455
+ forceDispose: async () => await control("forceDispose"),
456
+ };
457
+ }
@@ -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
  }
@@ -28,8 +28,8 @@ export declare const CLAUDE_SYSTEM_DISPOSITIONS: {
28
28
  readonly elicitation_complete: "drop";
29
29
  readonly files_persisted: "note";
30
30
  readonly hook_progress: "drop";
31
- readonly hook_response: "drop";
32
- readonly hook_started: "note";
31
+ readonly hook_response: "hook-response";
32
+ readonly hook_started: "drop";
33
33
  readonly informational: "note";
34
34
  readonly init: "drop";
35
35
  readonly local_command_output: "drop";
@@ -21,8 +21,8 @@ export const CLAUDE_SYSTEM_DISPOSITIONS = {
21
21
  elicitation_complete: "drop",
22
22
  files_persisted: "note",
23
23
  hook_progress: "drop",
24
- hook_response: "drop",
25
- hook_started: "note",
24
+ hook_response: "hook-response",
25
+ hook_started: "drop",
26
26
  informational: "note",
27
27
  init: "drop",
28
28
  local_command_output: "drop",
@@ -89,7 +89,6 @@ const CLAUDE_SYSTEM_NOTES = {
89
89
  const failed = Array.isArray(message.failed) ? message.failed.length : 0;
90
90
  return failed === 0 ? `Persisted ${files} files` : `Persisted ${files} files; ${failed} failed`;
91
91
  },
92
- hook_started: (message) => `Hook ${nonblank(message.hook_name) ?? "unknown"} started`,
93
92
  informational: (message) => nonblank(message.content) ?? "Informational notice",
94
93
  mirror_error: (message) => `Transcript mirror warning: ${nonblank(message.error) ?? nonblank(message.message) ?? "unknown error"}`,
95
94
  model_refusal_fallback: () => "Model refusal; using fallback",
@@ -253,6 +252,14 @@ function toolResultEvents(message, events, state) {
253
252
  });
254
253
  }
255
254
  }
255
+ function hookResponseNote(value) {
256
+ const outcome = nonblank(value.outcome);
257
+ if (outcome !== "error" && outcome !== "cancelled")
258
+ return undefined;
259
+ const hook = nonblank(value.hook_name) ?? "unknown";
260
+ const exitCode = number(value.exit_code);
261
+ return `Hook ${hook} ${outcome}${exitCode === undefined ? "" : ` (exit ${exitCode})`}`;
262
+ }
256
263
  function emitSystemMessage(value, events) {
257
264
  const subtype = nonblank(value?.subtype) ?? "unknown";
258
265
  if (!Object.hasOwn(CLAUDE_SYSTEM_DISPOSITIONS, subtype)) {
@@ -262,6 +269,12 @@ function emitSystemMessage(value, events) {
262
269
  const systemDisposition = CLAUDE_SYSTEM_DISPOSITIONS[subtype];
263
270
  if (systemDisposition === "drop")
264
271
  return;
272
+ if (systemDisposition === "hook-response") {
273
+ const note = hookResponseNote(value);
274
+ if (note !== undefined)
275
+ events.emit(noteEvent(note));
276
+ return;
277
+ }
265
278
  if (systemDisposition === "control-progress" && value?.status !== "api_retry")
266
279
  return;
267
280
  const note = CLAUDE_SYSTEM_NOTES[subtype];
@@ -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")));
@@ -26,8 +26,8 @@ export declare const CODEX_NOTIFICATION_DISPOSITIONS: {
26
26
  readonly "fuzzyFileSearch/sessionCompleted": "drop";
27
27
  readonly "fuzzyFileSearch/sessionUpdated": "drop";
28
28
  readonly guardianWarning: "note";
29
- readonly "hook/completed": "drop";
30
- readonly "hook/started": "note";
29
+ readonly "hook/completed": "hook-completed";
30
+ readonly "hook/started": "drop";
31
31
  readonly "item/agentMessage/delta": "drop";
32
32
  readonly "item/autoApprovalReview/completed": "note";
33
33
  readonly "item/autoApprovalReview/started": "note";
@@ -15,8 +15,8 @@ export const CODEX_NOTIFICATION_DISPOSITIONS = {
15
15
  "fuzzyFileSearch/sessionCompleted": "drop",
16
16
  "fuzzyFileSearch/sessionUpdated": "drop",
17
17
  guardianWarning: "note",
18
- "hook/completed": "drop",
19
- "hook/started": "note",
18
+ "hook/completed": "hook-completed",
19
+ "hook/started": "drop",
20
20
  "item/agentMessage/delta": "drop",
21
21
  "item/autoApprovalReview/completed": "note",
22
22
  "item/autoApprovalReview/started": "note",
@@ -273,8 +273,6 @@ function notificationAction(method, params) {
273
273
  return "Filesystem changed";
274
274
  case "guardianWarning":
275
275
  return `Guardian warning: ${message ?? "action warned"}`;
276
- case "hook/started":
277
- return `Hook ${codexText(params.name) ?? codexText(params.hookName) ?? "unknown"} started`;
278
276
  case "item/autoApprovalReview/completed":
279
277
  return "Action approval review completed";
280
278
  case "item/autoApprovalReview/started":
@@ -299,6 +297,23 @@ function notificationAction(method, params) {
299
297
  return method;
300
298
  }
301
299
  }
300
+ function hookCompletionNote(params) {
301
+ const run = codexObject(params.run);
302
+ const status = codexText(run?.status);
303
+ const issue = Array.isArray(run?.entries)
304
+ ? run.entries.map(codexObject).find((entry) => {
305
+ const kind = codexText(entry?.kind);
306
+ return kind === "warning" || kind === "error" || kind === "stop";
307
+ })
308
+ : undefined;
309
+ const issueKind = codexText(issue?.kind);
310
+ if (status !== "failed" && status !== "blocked" && status !== "stopped" && issueKind === undefined)
311
+ return undefined;
312
+ const eventName = codexText(run?.eventName) ?? "unknown";
313
+ const label = status === "failed" || status === "blocked" || status === "stopped" ? status : issueKind;
314
+ const diagnostic = codexText(run?.statusMessage) ?? codexText(issue?.text);
315
+ return `Hook ${eventName} ${label}${diagnostic === undefined ? "" : `: ${diagnostic}`}`;
316
+ }
302
317
  function observeError(params, state, events) {
303
318
  const detail = turnError(params.error) ?? codexText(params.message) ?? "codex app-server error";
304
319
  state.error = detail;
@@ -328,6 +343,12 @@ export function codexNotificationResult(notification, state, events) {
328
343
  switch (disposition) {
329
344
  case "drop":
330
345
  return undefined;
346
+ case "hook-completed": {
347
+ const note = hookCompletionNote(params);
348
+ if (note !== undefined)
349
+ events.emit(noteEvent(note));
350
+ return undefined;
351
+ }
331
352
  case "note":
332
353
  events.emit(noteEvent(notificationAction(method, params)));
333
354
  return undefined;