@astrosheep/keiyaku 4.6.10 → 4.6.12

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 (135) hide show
  1. package/build/plugins/claude/skills/keiyaku/SKILL.md +3 -1
  2. package/build/plugins/claude/skills/keiyaku-babysit/SKILL.md +1 -1
  3. package/build/plugins/claude/skills/keiyaku-bind/SKILL.md +3 -2
  4. package/build/plugins/claude/skills/keiyaku-workflow/SKILL.md +9 -8
  5. package/build/plugins/codex/skills/keiyaku/SKILL.md +3 -1
  6. package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +1 -1
  7. package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +3 -2
  8. package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +9 -8
  9. package/build/plugins/opencode/skills/keiyaku/SKILL.md +3 -1
  10. package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +1 -1
  11. package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +3 -2
  12. package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +9 -8
  13. package/build/skills/keiyaku/SKILL.md +3 -1
  14. package/build/skills/keiyaku-babysit/SKILL.md +1 -1
  15. package/build/skills/keiyaku-bind/SKILL.md +3 -2
  16. package/build/skills/keiyaku-workflow/SKILL.md +9 -8
  17. package/build/src/akuma/akuma-errors.d.ts +5 -0
  18. package/build/src/akuma/akuma-errors.js +9 -0
  19. package/build/src/akuma/akuma-owner.d.ts +14 -19
  20. package/build/src/akuma/akuma-owner.js +56 -54
  21. package/build/src/akuma/akuma-selection-execution.d.ts +5 -4
  22. package/build/src/akuma/akuma-selection-execution.js +59 -17
  23. package/build/src/akuma/akuma.d.ts +1 -2
  24. package/build/src/akuma/allowed.d.ts +2 -3
  25. package/build/src/akuma/allowed.js +1 -3
  26. package/build/src/akuma/body.js +17 -7
  27. package/build/src/akuma/call-initial-tell.d.ts +18 -0
  28. package/build/src/akuma/call-initial-tell.js +18 -0
  29. package/build/src/akuma/call-request.d.ts +20 -4
  30. package/build/src/akuma/heart/facts.d.ts +13 -0
  31. package/build/src/akuma/heart/facts.js +6 -0
  32. package/build/src/akuma/heart/index.d.ts +29 -14
  33. package/build/src/akuma/heart/index.js +75 -29
  34. package/build/src/akuma/heart/rows.d.ts +9 -2
  35. package/build/src/akuma/heart/rows.js +72 -11
  36. package/build/src/akuma/heart/storage.d.ts +2 -8
  37. package/build/src/akuma/heart/storage.js +13 -13
  38. package/build/src/akuma/heart/tells.d.ts +3 -1
  39. package/build/src/akuma/heart/tells.js +18 -2
  40. package/build/src/akuma/provider.d.ts +2 -4
  41. package/build/src/akuma/provider.js +29 -6
  42. package/build/src/akuma/providers/acp/core.js +23 -21
  43. package/build/src/akuma/providers/claude/index.js +0 -8
  44. package/build/src/akuma/providers/codex-app-server/index.js +9 -8
  45. package/build/src/akuma/providers/execution-environment.d.ts +1 -1
  46. package/build/src/akuma/providers/execution-environment.js +1 -0
  47. package/build/src/akuma/providers/opencode-sdk/events.js +111 -81
  48. package/build/src/akuma/providers/opencode-sdk/index.js +179 -150
  49. package/build/src/akuma/providers/opencode-sdk/native-http.js +7 -3
  50. package/build/src/akuma/providers/opencode-sdk/session.d.ts +5 -3
  51. package/build/src/akuma/providers/opencode-sdk/session.js +32 -27
  52. package/build/src/akuma/providers/paseo/index.js +0 -2
  53. package/build/src/akuma/providers/pi/index.js +13 -17
  54. package/build/src/akuma/selection-observation.d.ts +78 -9
  55. package/build/src/akuma/selection-owner-port.d.ts +2 -1
  56. package/build/src/akuma/selection-owner-port.js +6 -1
  57. package/build/src/akuma/selection-request.d.ts +1 -1
  58. package/build/src/akuma/selection-request.js +8 -3
  59. package/build/src/akuma/turn-drive.js +79 -48
  60. package/build/src/akuma-body.js +5 -3
  61. package/build/src/cli/commands/akuma-grammar.js +2 -2
  62. package/build/src/cli/commands/akuma.js +24 -16
  63. package/build/src/cli/commands/contract-grammar.d.ts +3 -7
  64. package/build/src/cli/commands/contract-grammar.js +12 -10
  65. package/build/src/cli/commands/contract-help.d.ts +3 -13
  66. package/build/src/cli/commands/contract-help.js +7 -16
  67. package/build/src/cli/commands/contract.js +19 -35
  68. package/build/src/cli/commands/debt.js +30 -29
  69. package/build/src/cli/parse.js +0 -1
  70. package/build/src/cli/render/akuma-activity.d.ts +35 -8
  71. package/build/src/cli/render/akuma-activity.js +132 -52
  72. package/build/src/cli/render/akuma.d.ts +6 -0
  73. package/build/src/cli/render/akuma.js +67 -24
  74. package/build/src/cli/render/contract-observation.d.ts +16 -3
  75. package/build/src/cli/render/contract-observation.js +3 -0
  76. package/build/src/cli/render/contract.d.ts +2 -2
  77. package/build/src/cli/render/contract.js +4 -4
  78. package/build/src/cli/render/delivery-preview.d.ts +7 -0
  79. package/build/src/cli/render/{audit.js → delivery-preview.js} +29 -27
  80. package/build/src/cli/render/kanshi-akuma.d.ts +3 -1
  81. package/build/src/cli/render/kanshi-akuma.js +23 -8
  82. package/build/src/cli/render/kanshi.js +4 -0
  83. package/build/src/cli/render/receipt.js +3 -3
  84. package/build/src/cli/render/refusal.js +2 -2
  85. package/build/src/cli/square-identity.d.ts +15 -0
  86. package/build/src/cli/square-identity.js +180 -0
  87. package/build/src/contract-worktree.js +1 -1
  88. package/build/src/core/verbs/placement.d.ts +2 -1
  89. package/build/src/core/verbs/placement.js +1 -1
  90. package/build/src/debt/context.d.ts +0 -5
  91. package/build/src/debt/context.js +32 -44
  92. package/build/src/git/process.d.ts +1 -0
  93. package/build/src/git/process.js +18 -1
  94. package/build/src/git/read-observation.js +2 -1
  95. package/build/src/git/scratch.d.ts +5 -1
  96. package/build/src/git/scratch.js +41 -12
  97. package/build/src/git/target-placement.d.ts +22 -4
  98. package/build/src/git/target-placement.js +44 -20
  99. package/build/src/index.d.ts +2 -2
  100. package/build/src/kanshi/read.js +187 -105
  101. package/build/src/kanshi/report.d.ts +5 -0
  102. package/build/src/library/address.d.ts +18 -4
  103. package/build/src/library/address.js +27 -6
  104. package/build/src/library/contract-address.d.ts +6 -0
  105. package/build/src/library/contract-address.js +12 -1
  106. package/build/src/library/contract-execution.d.ts +2 -2
  107. package/build/src/library/contract-execution.js +13 -17
  108. package/build/src/library/contract-operations.d.ts +59 -94
  109. package/build/src/library/contract-operations.js +23 -19
  110. package/build/src/library/contract-outcomes.d.ts +3 -3
  111. package/build/src/library/contract-settings.d.ts +1 -0
  112. package/build/src/library/contract-settings.js +1 -1
  113. package/build/src/library/invocation.d.ts +2 -2
  114. package/build/src/library/invocation.js +1 -1
  115. package/build/src/library/keiyaku.d.ts +2 -2
  116. package/build/src/library/keiyaku.js +2 -2
  117. package/build/src/library/outcome.d.ts +148 -268
  118. package/build/src/library/outcome.js +4 -8
  119. package/build/src/library/refusal.d.ts +2 -2
  120. package/build/src/library/refusal.js +2 -2
  121. package/build/src/plugin/runtime.js +16 -21
  122. package/build/src/protocol/{audit.d.ts → delivery-preview.d.ts} +62 -97
  123. package/build/src/protocol/delivery-preview.js +167 -0
  124. package/build/src/protocol/progress.d.ts +1 -1
  125. package/build/src/runtime/proc/run.js +22 -4
  126. package/build/src/runtime/proc/windows-launch.exe +0 -0
  127. package/build/src/verification/execution.d.ts +1 -1
  128. package/build/src/verification/execution.js +5 -3
  129. package/package.json +4 -2
  130. package/skills/keiyaku/SKILL.md +3 -1
  131. package/skills/keiyaku-babysit/SKILL.md +1 -1
  132. package/skills/keiyaku-bind/SKILL.md +3 -2
  133. package/skills/keiyaku-workflow/SKILL.md +9 -8
  134. package/build/src/cli/render/audit.d.ts +0 -7
  135. package/build/src/protocol/audit.js +0 -203
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import type { DatabaseSync } from "node:sqlite";
3
- import type { BodyEnd, BodyFact, CallFact, KillFact, PauseFact, ResumeCoordinate, SessionFact, StopFact, TurnFact, TurnStartFact, TurnEndFact } from "./facts.js";
3
+ import type { BodyEnd, BodyFact, CallFact, InterruptCompletion, KillFact, PauseFact, ResumeCoordinate, SessionFact, StopFact, TurnFact, TurnStartFact, TurnEndFact } from "./facts.js";
4
4
  import { type AgentEvent } from "./activity-schema.js";
5
5
  export type SealRow = Readonly<{
6
6
  evidence: string;
@@ -136,9 +136,16 @@ export declare function insertActivityFact(database: DatabaseSync, input: Readon
136
136
  at: string;
137
137
  }>): number;
138
138
  export declare function insertStopControl(database: DatabaseSync, bodySequence: number, at: string): void;
139
- export declare function insertPauseControl(database: DatabaseSync, at: string): void;
139
+ export declare function insertInterruptBarrier(database: DatabaseSync, input: Readonly<{
140
+ bodySequence: number;
141
+ tellId: string;
142
+ at: string;
143
+ }>): PauseFact;
144
+ export declare function completeInterruptBarrier(database: DatabaseSync, expected?: PauseFact, consume?: boolean): InterruptCompletion;
140
145
  export declare function stopFact(database: DatabaseSync): StopFact | null;
141
146
  export declare function pauseFact(database: DatabaseSync): PauseFact | null;
147
+ export declare function bodyFact(database: DatabaseSync, sequence: number): BodyFact | null;
148
+ export declare function isTellProtectedByPause(database: DatabaseSync, tellId: string, bodySequence: number): boolean;
142
149
  export declare function insertKillFact(database: DatabaseSync, bodySequence: number, at: string): void;
143
150
  export declare function endBodyFact(database: DatabaseSync, input: Readonly<{
144
151
  sequence: number;
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { providerOptionsSchema, withoutModel } from "../provider-recipe.js";
3
3
  import { resumeCoordinateSchema } from "../coordinate.js";
4
+ import { HeartAuthorityCorruptionError } from "./facts.js";
4
5
  import { decodeResumeCoordinate, encodeResumeCoordinate as encodeCoordinate } from "../coordinate.js";
5
6
  import { agentEventSchema } from "./activity-schema.js";
6
7
  const sessionText = z.string().refine((value) => value.trim() !== "");
@@ -160,13 +161,40 @@ export function insertStopControl(database, bodySequence, at) {
160
161
  .prepare("INSERT OR IGNORE INTO control(kind, value_json, at) VALUES ('stop', ?, ?)")
161
162
  .run(json({ bodySequence }), at);
162
163
  }
163
- export function insertPauseControl(database, at) {
164
- const body = latestBodyFact(database);
165
- if (body === null)
166
- throw new Error("Akuma has no Body to interrupt");
167
- database
168
- .prepare("INSERT OR IGNORE INTO control(kind, value_json, at) VALUES ('pause', ?, ?)")
169
- .run(json({ bodySequence: body.sequence }), at);
164
+ export function insertInterruptBarrier(database, input) {
165
+ const value = json({ bodySequence: input.bodySequence, frontierTellId: input.tellId });
166
+ database.prepare("INSERT INTO control(kind, value_json, at) VALUES ('pause', ?, ?)").run(value, input.at);
167
+ return { bodySequence: input.bodySequence, requestedAt: input.at, frontierTellId: input.tellId };
168
+ }
169
+ export function completeInterruptBarrier(database, expected, consume = true) {
170
+ const barrier = pauseFact(database);
171
+ if (expected !== undefined && barrier !== null && !samePause(barrier, expected))
172
+ return settledPreviousBarrier(database, expected);
173
+ if (barrier === null) {
174
+ if (expected === undefined)
175
+ return { kind: "clear" };
176
+ return settledPreviousBarrier(database, expected);
177
+ }
178
+ const latest = latestBodyFact(database);
179
+ if (latest?.sequence !== barrier.bodySequence)
180
+ return { kind: "unavailable", evidence: "untidy" };
181
+ if (latest.hung !== undefined)
182
+ return { kind: "unavailable", evidence: "hung" };
183
+ if (latest.end === undefined || !consume)
184
+ return { kind: "unavailable", evidence: "untidy" };
185
+ deletePauseControl(database);
186
+ return { kind: "completed", body: latest };
187
+ }
188
+ function settledPreviousBarrier(database, expected) {
189
+ const previous = bodyFact(database, expected.bodySequence);
190
+ if (previous?.hung !== undefined)
191
+ return { kind: "unavailable", evidence: "hung" };
192
+ if (previous?.end !== undefined)
193
+ return { kind: "completed", body: previous };
194
+ return { kind: "unavailable", evidence: "untidy" };
195
+ }
196
+ function samePause(left, right) {
197
+ return left.bodySequence === right.bodySequence && left.frontierTellId === right.frontierTellId;
170
198
  }
171
199
  export function stopFact(database) {
172
200
  const row = database.prepare("SELECT value_json, at FROM control WHERE kind = 'stop'").get();
@@ -178,15 +206,48 @@ export function stopFact(database) {
178
206
  }
179
207
  return { bodySequence: value.bodySequence, requestedAt: row.at };
180
208
  }
209
+ const pauseControlSchema = z
210
+ .object({ bodySequence: z.number().int().positive(), frontierTellId: sessionText.optional() })
211
+ .strict();
181
212
  export function pauseFact(database) {
182
213
  const row = database.prepare("SELECT value_json, at FROM control WHERE kind = 'pause'").get();
183
214
  if (row === undefined)
184
215
  return null;
185
- const value = parsed(row.value_json);
186
- if (!Number.isSafeInteger(value.bodySequence) || value.bodySequence <= 0) {
187
- throw new Error("Akuma pause control has an invalid Body sequence");
216
+ let value;
217
+ try {
218
+ value = pauseControlSchema.parse(parsed(row.value_json));
188
219
  }
189
- return { bodySequence: value.bodySequence, requestedAt: row.at };
220
+ catch (error) {
221
+ throw new HeartAuthorityCorruptionError("Akuma pause control is malformed", { cause: error });
222
+ }
223
+ if (value.frontierTellId !== undefined) {
224
+ const tell = database.prepare("SELECT 1 FROM tells WHERE id = ?").get(value.frontierTellId);
225
+ if (tell === undefined)
226
+ throw new HeartAuthorityCorruptionError("Akuma pause control references a missing Tell frontier");
227
+ }
228
+ return {
229
+ bodySequence: value.bodySequence,
230
+ requestedAt: row.at,
231
+ ...(value.frontierTellId === undefined ? {} : { frontierTellId: value.frontierTellId }),
232
+ };
233
+ }
234
+ export function bodyFact(database, sequence) {
235
+ const row = database
236
+ .prepare(`SELECT sequence, leash_taken_at, hung_diagnostic, hung_at, end, ended_at FROM bodies WHERE sequence = ?`)
237
+ .get(sequence);
238
+ return row === undefined ? null : decodeBodyRow(row);
239
+ }
240
+ export function isTellProtectedByPause(database, tellId, bodySequence) {
241
+ const pause = pauseFact(database);
242
+ if (pause?.bodySequence !== bodySequence)
243
+ return false;
244
+ if (pause.frontierTellId === undefined)
245
+ return true;
246
+ const tell = database.prepare("SELECT sequence FROM tells WHERE id = ?").get(tellId);
247
+ const frontier = database.prepare("SELECT sequence FROM tells WHERE id = ?").get(pause.frontierTellId);
248
+ if (tell === undefined || frontier === undefined)
249
+ throw new HeartAuthorityCorruptionError("Akuma pause control references a missing Tell frontier");
250
+ return tell.sequence >= frontier.sequence;
190
251
  }
191
252
  export function insertKillFact(database, bodySequence, at) {
192
253
  database
@@ -1,6 +1,6 @@
1
1
  import { DatabaseSync } from "node:sqlite";
2
2
  import type { AkumaPaths } from "../identity.js";
3
- import type { BodyFact, LeashProbe, SessionFact, Soul, StopFact, TellFact } from "./facts.js";
3
+ import type { BodyFact, InterruptCompletion, LeashProbe, PauseFact, SessionFact, Soul, StopFact, TellFact } from "./facts.js";
4
4
  import { sealFact } from "./rows.js";
5
5
  export declare class HeartAbsentError extends Error {
6
6
  readonly path: string;
@@ -43,8 +43,8 @@ export declare class HeldAkumaLeash {
43
43
  at: string;
44
44
  }>): Promise<"born" | "sealed">;
45
45
  readSeal(): ReturnType<typeof sealFact>;
46
- clearPause(paths: AkumaPaths): Promise<void>;
47
46
  clearStop(paths: AkumaPaths): Promise<void>;
47
+ completeInterruptBarrier(paths: AkumaPaths, expected?: PauseFact): Promise<InterruptCompletion>;
48
48
  recordBody(paths: AkumaPaths, input: Readonly<{
49
49
  leashTakenAt: string;
50
50
  }>): Promise<BodyFact>;
@@ -57,12 +57,6 @@ export declare class HeldAkumaLeash {
57
57
  target: StopFact;
58
58
  result: "recorded" | "already-killed";
59
59
  }> | null>;
60
- recordInterruptTell(paths: AkumaPaths, tell: Omit<TellFact, "sequence" | "state" | "deliveries" | "binding">): Promise<Readonly<{
61
- kind: "not-born";
62
- } | {
63
- kind: "recorded";
64
- tell: TellFact;
65
- }>>;
66
60
  release(): void;
67
61
  }
68
62
  export declare function probeLeash(paths: AkumaPaths): Promise<LeashProbe>;
@@ -2,8 +2,9 @@ import { DatabaseSync } from "node:sqlite";
2
2
  import { lstat } from "node:fs/promises";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { setTimeout } from "node:timers/promises";
5
+ import { BodyControlledError } from "./facts.js";
5
6
  import { HEART_SCHEMA, LEASH_SCHEMA, assertHeartSchemaVersion, assertLeashSchemaVersion, heartSchemaIsCurrent, } from "./schema.js";
6
- import { deletePauseControl, deleteStopControl, insertBodyFact, insertKillFact, insertSealFact, insertSessionFact, killFactForBody, latestBodyFact, latestKillFact, markBodyHung, sealExists, sealFact, stopFact, } from "./rows.js";
7
+ import { deleteStopControl, completeInterruptBarrier as judgeInterruptBarrier, insertBodyFact, insertKillFact, insertSealFact, insertSessionFact, killFactForBody, latestBodyFact, latestKillFact, markBodyHung, sealExists, sealFact, stopFact, } from "./rows.js";
7
8
  import { insertTellFact } from "./tells.js";
8
9
  import { pruneActivityFacts } from "./timeline.js";
9
10
  import { insertSoulFact, soulFact } from "./soul.js";
@@ -269,16 +270,23 @@ export class HeldAkumaLeash {
269
270
  readSeal() {
270
271
  return sealFact(this.database);
271
272
  }
272
- async clearPause(paths) {
273
- await withHeartTransaction(paths, deletePauseControl);
274
- }
275
273
  async clearStop(paths) {
276
274
  await withHeartTransaction(paths, deleteStopControl);
277
275
  }
276
+ async completeInterruptBarrier(paths, expected) {
277
+ if (this.closed)
278
+ throw new Error("Akuma interrupt completion requires a held leash");
279
+ return await withHeartTransaction(paths, (heart) => judgeInterruptBarrier(heart, expected));
280
+ }
278
281
  async recordBody(paths, input) {
279
282
  if (this.closed || this.bodySequence !== undefined)
280
283
  throw new Error("Akuma leash cannot start another Body");
281
- const sequence = await withHeartTransaction(paths, (heart) => insertBodyFact(heart, input));
284
+ const sequence = await withHeartTransaction(paths, (heart) => {
285
+ const completion = judgeInterruptBarrier(heart);
286
+ if (completion.kind === "unavailable")
287
+ throw new BodyControlledError();
288
+ return insertBodyFact(heart, input);
289
+ });
282
290
  this.bodySequence = sequence;
283
291
  return { sequence, leashTakenAt: input.leashTakenAt };
284
292
  }
@@ -314,14 +322,6 @@ export class HeldAkumaLeash {
314
322
  return { target, result };
315
323
  });
316
324
  }
317
- async recordInterruptTell(paths, tell) {
318
- return await withHeartTransaction(paths, (heart) => {
319
- deletePauseControl(heart);
320
- if (soulFact(heart) === null)
321
- return { kind: "not-born" };
322
- return recordPendingTell(heart, tell);
323
- });
324
- }
325
325
  release() {
326
326
  if (this.closed)
327
327
  return;
@@ -1,3 +1,4 @@
1
+ import type { PauseFact } from "./facts.js";
1
2
  import type { DatabaseSync } from "node:sqlite";
2
3
  import { type TellDelivery, type TellDeliveryInput, type TellFact, type TellReceiptInput } from "./facts.js";
3
4
  type TellRow = Readonly<{
@@ -37,6 +38,7 @@ export declare function tellBatchAtSequences(database: DatabaseSync, sequences:
37
38
  export declare function hasPendingTell(database: DatabaseSync): boolean;
38
39
  export declare function insertTellFact(database: DatabaseSync, tell: Omit<TellFact, "sequence" | "state" | "deliveries">): number;
39
40
  export declare function tellFact(database: DatabaseSync, id: string): TellFact | null;
41
+ export declare function sameTellInput(existing: TellFact, tell: Omit<TellFact, "sequence" | "state" | "deliveries" | "binding">): boolean;
40
42
  export declare function insertTellDeliveryFact(database: DatabaseSync, input: TellDeliveryInput): boolean;
41
43
  export declare function insertTellReceiptFact(database: DatabaseSync, input: TellReceiptInput): boolean;
42
44
  export declare function tellIdsForFence(database: DatabaseSync, turnSequence: number, fence: string): readonly string[];
@@ -58,6 +60,6 @@ export declare function insertTellBindingFact(database: DatabaseSync, input: Rea
58
60
  boundAt: string;
59
61
  }>): void;
60
62
  export declare function turnParticipants(database: DatabaseSync, turnSequence: number): readonly string[];
61
- export declare function drainPendingTells(pending: readonly TellFact[], activeTurnSequences?: readonly number[]): readonly TellFact[];
63
+ export declare function drainPendingTells(pending: readonly TellFact[], activeTurnSequences?: readonly number[], pause?: PauseFact | null, bodySequence?: number): readonly TellFact[];
62
64
  export declare function openBoundTurns(database: DatabaseSync, bodySequence: number): readonly number[];
63
65
  export {};
@@ -92,6 +92,12 @@ export function tellFact(database, id) {
92
92
  .get(id);
93
93
  return row === undefined ? null : decodeTellRows(database, [row])[0];
94
94
  }
95
+ export function sameTellInput(existing, tell) {
96
+ return (existing.body === tell.body &&
97
+ existing.recordedAt === tell.recordedAt &&
98
+ existing.schemaJson === tell.schemaJson &&
99
+ existing.initiator === tell.initiator);
100
+ }
95
101
  export function insertTellDeliveryFact(database, input) {
96
102
  const result = database
97
103
  .prepare(`INSERT OR IGNORE INTO tell_deliveries(
@@ -228,8 +234,18 @@ export function turnParticipants(database, turnSequence) {
228
234
  }
229
235
  return [...participants];
230
236
  }
231
- export function drainPendingTells(pending, activeTurnSequences = []) {
232
- const eligible = pending.filter((tell) => tell.binding === undefined || !activeTurnSequences.includes(tell.binding.turnSequence));
237
+ export function drainPendingTells(pending, activeTurnSequences = [], pause, bodySequence) {
238
+ let available = pending;
239
+ if (pause !== undefined && pause !== null && pause.bodySequence === bodySequence) {
240
+ if (pause.frontierTellId === undefined) {
241
+ available = [];
242
+ }
243
+ else {
244
+ const frontier = pending.find((tell) => tell.id === pause.frontierTellId);
245
+ available = frontier === undefined ? [] : pending.filter((tell) => tell.sequence < frontier.sequence);
246
+ }
247
+ }
248
+ const eligible = available.filter((tell) => tell.binding === undefined || !activeTurnSequences.includes(tell.binding.turnSequence));
233
249
  if (eligible.length === 0)
234
250
  return [];
235
251
  const first = eligible[0];
@@ -37,6 +37,8 @@ export declare class AgentEventChannel implements AsyncIterable<AgentEvent> {
37
37
  private overflowCount;
38
38
  private ended;
39
39
  emit(event: AgentEvent): void;
40
+ /** Narration and protected slots; the reserved model slot is not one of them. */
41
+ private narrationCount;
40
42
  end(): void;
41
43
  [Symbol.asyncIterator](): AsyncIterator<AgentEvent>;
42
44
  }
@@ -88,10 +90,6 @@ export type Session = Readonly<{
88
90
  events: AsyncIterable<AgentEvent>;
89
91
  receipts?: AsyncIterable<TellReceipt>;
90
92
  completion: Promise<TurnResult>;
91
- /** Requests graceful adapter-owned cancellation. */
92
- abort(): Promise<void>;
93
- /** Fulfills only after forced adapter-owned disposal is proved. */
94
- forceDispose(): Promise<void>;
95
93
  tell?(tell: Readonly<{
96
94
  id: string;
97
95
  text: string;
@@ -213,8 +213,11 @@ export function unknownEvent(kind) {
213
213
  return { type: "unknown", kind };
214
214
  }
215
215
  // Slow consumers may lose reconstructible narration, but session coordinates,
216
- // notes, and error events stay observable. A full protected queue replaces one
217
- // protected item with a bounded aggregate note; `end` ignores later emission.
216
+ // notes, error events, and the latest runtime-model observation stay
217
+ // observable. The one queue reserves a single model slot, so that observation
218
+ // never competes for narration admission and no overflow can hide the model a
219
+ // Turn proved it ran. A full protected queue replaces one protected item with a
220
+ // bounded aggregate note; `end` ignores later emission.
218
221
  function isErrorEvent(event) {
219
222
  if (event.type === "note")
220
223
  return true;
@@ -222,8 +225,11 @@ function isErrorEvent(event) {
222
225
  return event.result.status === "error";
223
226
  return event.type === "unknown" && /(?:error|fail|abort|cancel|stop|terminal)/iu.test(event.kind);
224
227
  }
228
+ function isModelEvent(event) {
229
+ return event.type === "model";
230
+ }
225
231
  function isReconstructibleEvent(event) {
226
- return event.type !== "session" && !isErrorEvent(event);
232
+ return event.type !== "session" && !isModelEvent(event) && !isErrorEvent(event);
227
233
  }
228
234
  const AGENT_EVENT_OVERFLOW_PREFIX = "Agent event queue overflow";
229
235
  function overflowEvent(count) {
@@ -243,7 +249,20 @@ export class AgentEventChannel {
243
249
  waiter.resolve({ done: false, value: event });
244
250
  return;
245
251
  }
246
- if (this.queued.length >= AGENT_EVENT_QUEUE_LIMIT) {
252
+ if (isModelEvent(event)) {
253
+ // Model evidence is control state, never narration: it occupies the
254
+ // queue's one reserved model slot, so a narration flood and a full
255
+ // protected queue both leave it observable. An unconsumed predecessor is
256
+ // obsolete, and superseding it is what keeps the newest fact, an unnamed
257
+ // one clearing the confirmation it disconfirms.
258
+ const obsolete = this.queued.findIndex(isModelEvent);
259
+ if (obsolete === -1)
260
+ this.queued.push(event);
261
+ else
262
+ this.queued[obsolete] = event;
263
+ return;
264
+ }
265
+ if (this.narrationCount() >= AGENT_EVENT_QUEUE_LIMIT) {
247
266
  const reconstructible = this.queued.findIndex(isReconstructibleEvent);
248
267
  if (reconstructible === -1) {
249
268
  if (isReconstructibleEvent(event))
@@ -259,8 +278,8 @@ export class AgentEventChannel {
259
278
  }
260
279
  this.overflowMarker = undefined;
261
280
  }
262
- const protectedIndex = this.queued.findIndex((queued) => queued.type !== "session");
263
- const replacement = protectedIndex === -1 ? 0 : protectedIndex;
281
+ const protectedItem = this.queued.findIndex((queued) => queued.type !== "session" && !isModelEvent(queued));
282
+ const replacement = protectedItem === -1 ? this.queued.findIndex((queued) => !isModelEvent(queued)) : protectedItem;
264
283
  this.overflowCount += 1;
265
284
  const marker = overflowEvent(this.overflowCount);
266
285
  this.queued[replacement] = marker;
@@ -271,6 +290,10 @@ export class AgentEventChannel {
271
290
  }
272
291
  this.queued.push(event);
273
292
  }
293
+ /** Narration and protected slots; the reserved model slot is not one of them. */
294
+ narrationCount() {
295
+ return this.queued.length - Number(this.queued.some(isModelEvent));
296
+ }
274
297
  end() {
275
298
  if (this.ended)
276
299
  return;
@@ -160,25 +160,6 @@ function beginAcpPrompt(context, child, turn, sessionId, input) {
160
160
  admission: { fence: sessionId },
161
161
  events: turn.events,
162
162
  completion: turn.completion,
163
- abort: async () => {
164
- let result = { kind: "failed", diagnostic: "ACP turn cancelled" };
165
- try {
166
- await agent.notify(acp.methods.agent.session.cancel, { sessionId });
167
- }
168
- catch (error) {
169
- result = { kind: "failed", diagnostic: diagnostic(acp, error) };
170
- }
171
- if (!turn.open())
172
- await child.close(true);
173
- else
174
- await turn.finish(result, () => child.close(true));
175
- },
176
- forceDispose: async () => {
177
- if (!turn.open())
178
- await child.close(true);
179
- else
180
- await turn.finish({ kind: "failed", diagnostic: "ACP turn force-disposed" }, () => child.close(true));
181
- },
182
163
  };
183
164
  }
184
165
  export async function startAcpSession(launch, input, dependencies = {}, custody) {
@@ -190,10 +171,12 @@ export async function startAcpSession(launch, input, dependencies = {}, custody)
190
171
  cwd: input.cwd,
191
172
  env: akumaExecutionEnvironment(globalThis.process.env, launch.env, input.requests?.dir),
192
173
  });
174
+ let abortOwned = async () => await child.close(true);
175
+ let forceDisposeOwned = async () => await child.close(true);
193
176
  custody?.own({
194
177
  closed: child.exited.then(() => undefined),
195
- abort: async () => await child.close(true),
196
- forceDispose: async () => await child.close(true),
178
+ abort: () => abortOwned(),
179
+ forceDispose: () => forceDisposeOwned(),
197
180
  });
198
181
  let sessionId;
199
182
  let turn;
@@ -205,6 +188,25 @@ export async function startAcpSession(launch, input, dependencies = {}, custody)
205
188
  try {
206
189
  sessionId = await abortable(establishSession(acp, connection.agent, input, dependencies, launch.sessionOptions ?? []), signal);
207
190
  turn.events.emit({ type: "session", coordinate: { sessionId } });
191
+ abortOwned = async () => {
192
+ let result = { kind: "failed", diagnostic: "ACP turn cancelled" };
193
+ try {
194
+ await connection.agent.notify(acp.methods.agent.session.cancel, { sessionId: sessionId });
195
+ }
196
+ catch (error) {
197
+ result = { kind: "failed", diagnostic: diagnostic(acp, error) };
198
+ }
199
+ if (!turn.open())
200
+ await child.close(true);
201
+ else
202
+ await turn.finish(result, () => child.close(true));
203
+ };
204
+ forceDisposeOwned = async () => {
205
+ if (!turn.open())
206
+ await child.close(true);
207
+ else
208
+ await turn.finish({ kind: "failed", diagnostic: "ACP turn force-disposed" }, () => child.close(true));
209
+ };
208
210
  const session = beginAcpPrompt({ acp, agent: connection.agent }, child, turn, sessionId, input);
209
211
  return { session, agent: connection.agent, sessionId, open: turn.open };
210
212
  }
@@ -343,14 +343,6 @@ async function driveClaude(load, execution, drive, custody) {
343
343
  receipts,
344
344
  completion: observed.completion,
345
345
  tell: claudeTell(input, observed, accepted, driveState, run),
346
- async abort() {
347
- shutDown(new Error("Claude query aborted"));
348
- await observed.completion;
349
- },
350
- async forceDispose() {
351
- shutDown(new Error("Claude query force-disposed"));
352
- await observed.completion;
353
- },
354
346
  };
355
347
  }
356
348
  export function createClaudeProvider(loadOrExecution = async () => (await import("@anthropic-ai/claude-agent-sdk")), execution = {}) {
@@ -140,8 +140,6 @@ async function startCodex(execution, input, custody) {
140
140
  const events = new AgentEventChannel();
141
141
  const server = codexServer(execution, input);
142
142
  const closed = new Promise((resolve) => server.onExit(() => resolve()));
143
- const close = async () => await server.close(true);
144
- custody.own({ closed, abort: close, forceDispose: close });
145
143
  const state = { settled: false, tools: new Map() };
146
144
  let settle;
147
145
  const completion = new Promise((resolve) => {
@@ -159,6 +157,15 @@ async function startCodex(execution, input, custody) {
159
157
  };
160
158
  void server.endInputAndDrain().then(done, done);
161
159
  };
160
+ custody.own({
161
+ closed,
162
+ abort: async () => await abortTurn(server, state, finish),
163
+ forceDispose: async () => {
164
+ if (!state.settled)
165
+ settle({ kind: "failed", diagnostic: "codex app-server force-disposed" });
166
+ await server.close(true);
167
+ },
168
+ });
162
169
  server.onNotification((notification) => {
163
170
  const result = codexNotificationResult(notification, state, events);
164
171
  if (result !== undefined)
@@ -202,12 +209,6 @@ async function startCodex(execution, input, custody) {
202
209
  admission: { fence: state.turnId },
203
210
  events,
204
211
  completion,
205
- abort: () => abortTurn(server, state, finish),
206
- forceDispose: async () => {
207
- if (!state.settled)
208
- settle({ kind: "failed", diagnostic: "codex app-server force-disposed" });
209
- await server.close(true);
210
- },
211
212
  tell: (tell) => steerTurn(server, state, tell),
212
213
  };
213
214
  }
@@ -1,6 +1,6 @@
1
1
  export declare const AKUMA_REQUESTS_ENV = "AKUMA_REQUESTS";
2
2
  export declare const AKUMA_REQUESTS_ENTRY_ENV = "AKUMA_REQUESTS_ENTRY";
3
- declare const PARENT_HARNESS_IDENTITY_KEYS: readonly ["CLAUDE_CODE_SESSION_ID", "CLAUDE_CODE_CHILD_SESSION", "CLAUDECODE", "CODEX_THREAD_ID", "OPENCODE_SESSION_ID", "PI_SESSION_ID", "PI_SESSION_FILE", "PASEO_AGENT_ID", "SQUARE_PARTICIPANT_NAME", "KEIYAKU_EXTERNAL_CALLER"];
3
+ declare const PARENT_HARNESS_IDENTITY_KEYS: readonly ["CLAUDE_CODE_SESSION_ID", "CLAUDE_CODE_CHILD_SESSION", "CLAUDECODE", "CODEX_THREAD_ID", "OPENCODE_SESSION_ID", "PI_SESSION_ID", "PI_SESSION_FILE", "PASEO_AGENT_ID", "SQUARE_PARTICIPANT_NAME", "KEIYAKU_INITIATOR", "KEIYAKU_EXTERNAL_CALLER"];
4
4
  type Environment = Readonly<Record<string, string | undefined>>;
5
5
  /** Construct the environment for one isolated Akuma execution or native child. */
6
6
  export declare function akumaExecutionEnvironment(inherited: Environment, overrides?: Environment, requests?: string, nativeIdentity?: readonly (typeof PARENT_HARNESS_IDENTITY_KEYS)[number][]): Record<string, string>;
@@ -10,6 +10,7 @@ const PARENT_HARNESS_IDENTITY_KEYS = [
10
10
  "PI_SESSION_FILE",
11
11
  "PASEO_AGENT_ID",
12
12
  "SQUARE_PARTICIPANT_NAME",
13
+ "KEIYAKU_INITIATOR",
13
14
  "KEIYAKU_EXTERNAL_CALLER",
14
15
  ];
15
16
  /** Construct the environment for one isolated Akuma execution or native child. */