@astrosheep/keiyaku 4.6.11 → 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 +15 -19
  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 -1
  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
@@ -7,12 +7,13 @@ export async function executeTellAkuma(input) {
7
7
  if (input.interrupt === true) {
8
8
  const interrupted = await owner.interrupt(input.body, {
9
9
  ...(input.tellId === undefined ? {} : { tellId: input.tellId }),
10
+ ...(input.recordedAt === undefined ? {} : { recordedAt: input.recordedAt }),
10
11
  ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
11
12
  ...(input.signal === undefined ? {} : { signal: input.signal }),
12
13
  });
13
- if (interrupted.kind === "unavailable")
14
+ if ("kind" in interrupted)
14
15
  throw new AkumaProviderError(`Tell interrupt unavailable: ${interrupted.evidence}`);
15
- tell = interrupted.tell;
16
+ tell = interrupted;
16
17
  }
17
18
  else {
18
19
  tell = await owner.tell(input.body, input.tellId, input.recordedAt, undefined, {
@@ -73,7 +74,10 @@ export async function observeAdmittedAskAkuma(input) {
73
74
  void input.wake?.then((receipt) => {
74
75
  settled = receipt;
75
76
  wakeState = receipt.wake.kind === "failed" ? "failed" : "settled";
76
- }, (error) => { wakeError = { error }; wakeState = "failed"; });
77
+ }, (error) => {
78
+ wakeError = { error };
79
+ wakeState = "failed";
80
+ });
77
81
  const admittedAt = Date.parse(tell.row.at);
78
82
  const observedAt = performance.timeOrigin + performance.now();
79
83
  const elapsed = input.startedAt === undefined
@@ -98,23 +102,61 @@ export async function observeAdmittedAskAkuma(input) {
98
102
  completedAt: observed.completedAt,
99
103
  };
100
104
  }
105
+ /**
106
+ * The interrupt half of Ask: admit the Tell together with its yield request,
107
+ * then observe the answer bound to that same admission. Kept out of the public
108
+ * operation so ordinary and interrupted admission each read as one flow; both
109
+ * share `observeAdmittedAskAkuma`.
110
+ */
111
+ async function executeInterruptAskAkuma(input, owner) {
112
+ const admission = await owner.admitInterrupt(input.body, {
113
+ ...(input.tellId === undefined ? {} : { tellId: input.tellId }),
114
+ ...(input.recordedAt === undefined ? {} : { recordedAt: input.recordedAt }),
115
+ ...(input.schemaJson === undefined ? {} : { schemaJson: input.schemaJson }),
116
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
117
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
118
+ });
119
+ if (admission.kind === "unavailable")
120
+ throw new AkumaProviderError(`Tell interrupt unavailable: ${admission.evidence}`);
121
+ const observing = new AbortController();
122
+ const signal = input.signal === undefined ? observing.signal : AbortSignal.any([input.signal, observing.signal]);
123
+ const wake = owner.completeInterruptAdmission(admission, undefined, signal);
124
+ try {
125
+ const receipt = admission.barrier === undefined
126
+ ? undefined
127
+ : {
128
+ ...(await owner.admittedReceipt(admission.tell.id)),
129
+ interruption: { kind: "pending", bodySequence: admission.barrier.bodySequence },
130
+ };
131
+ return await observeAdmittedAskAkuma({
132
+ path: input.path,
133
+ id: input.id,
134
+ tellId: admission.tell.id,
135
+ ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
136
+ startedAt: performance.now(),
137
+ wake,
138
+ ...(receipt === undefined ? {} : { receipt }),
139
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
140
+ ...(input.onObserve === undefined ? {} : { onObserve: input.onObserve }),
141
+ ...(input.outcomeObserved === undefined ? {} : { outcomeObserved: input.outcomeObserved }),
142
+ });
143
+ }
144
+ finally {
145
+ if (!observing.signal.aborted)
146
+ observing.abort(new Error("interrupt observation ended"));
147
+ await wake.catch(() => undefined);
148
+ }
149
+ }
101
150
  export async function executeAskAkuma(input) {
102
151
  input.signal?.throwIfAborted();
103
152
  const owner = new AkumaOwner(input.id, input.path);
104
- const admission = input.interrupt === true
105
- ? await owner.admitInterrupt(input.body, {
106
- ...(input.tellId === undefined ? {} : { tellId: input.tellId }),
107
- ...(input.schemaJson === undefined ? {} : { schemaJson: input.schemaJson }),
108
- ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
109
- ...(input.signal === undefined ? {} : { signal: input.signal }),
110
- })
111
- : await owner.admitTell(input.body, input.tellId, input.recordedAt, undefined, {
112
- ...(input.schemaJson === undefined ? {} : { schemaJson: input.schemaJson }),
113
- ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
114
- ...(input.signal === undefined ? {} : { signal: input.signal }),
115
- });
116
- if (admission.kind === "unavailable")
117
- throw new AkumaProviderError(`Tell interrupt unavailable: ${admission.evidence}`);
153
+ if (input.interrupt === true)
154
+ return await executeInterruptAskAkuma(input, owner);
155
+ const admission = await owner.admitTell(input.body, input.tellId, input.recordedAt, undefined, {
156
+ ...(input.schemaJson === undefined ? {} : { schemaJson: input.schemaJson }),
157
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
158
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
159
+ });
118
160
  if (admission.kind === "not-born")
119
161
  throw new AkumaNotBornError(input.id);
120
162
  return await observeAdmittedAskAkuma({
@@ -78,10 +78,9 @@ export declare const akumaStatusSchema: z.ZodObject<{
78
78
  "akuma.call": "akuma.call";
79
79
  "akuma.kill": "akuma.kill";
80
80
  "akuma.tell": "akuma.tell";
81
- "contract.audit": "contract.audit";
82
81
  "contract.deliver": "contract.deliver";
83
82
  "contract.review": "contract.review";
84
- }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
83
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.deliver" | "contract.review")[]>>;
85
84
  timeline: z.ZodUnion<readonly [z.ZodObject<{
86
85
  reportedChanges: z.ZodReadonly<z.ZodArray<z.ZodObject<{
87
86
  sequence: z.ZodNumber;
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.audit", "contract.deliver", "contract.review", "debt.add", "debt.addDocument", "debt.done", "debt.drop", "debt.update"];
2
+ export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.deliver", "contract.review", "debt.add", "debt.addDocument", "debt.done", "debt.drop", "debt.update"];
3
3
  export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
4
4
  export type AllowedActions = readonly AllowedAction[];
5
5
  export declare const DEFAULT_ALLOWED_ACTIONS: AllowedActions;
@@ -15,10 +15,9 @@ export declare const allowedActionsSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
15
15
  "akuma.call": "akuma.call";
16
16
  "akuma.kill": "akuma.kill";
17
17
  "akuma.tell": "akuma.tell";
18
- "contract.audit": "contract.audit";
19
18
  "contract.deliver": "contract.deliver";
20
19
  "contract.review": "contract.review";
21
- }>>, z.ZodTransform<AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
20
+ }>>, z.ZodTransform<AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.deliver" | "contract.review")[]>>;
22
21
  export declare function historicalAllowedActions(value: unknown): AllowedActions;
23
22
  export declare function unionAllowedActions(base: AllowedActions, additions: AllowedActions): AllowedActions;
24
23
  export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
@@ -3,14 +3,12 @@ import { z } from "zod";
3
3
  const AKUMA_MUTATION_ACTIONS = Object.freeze(["akuma.call", "akuma.kill", "akuma.tell"]);
4
4
  export const ALLOWED_ACTIONS = Object.freeze([
5
5
  ...AKUMA_MUTATION_ACTIONS,
6
- "contract.audit",
7
6
  "contract.deliver",
8
7
  "contract.review",
9
8
  ...DEBT_MUTATION_ACTIONS,
10
9
  ]);
11
10
  export const DEFAULT_ALLOWED_ACTIONS = Object.freeze([
12
11
  ...AKUMA_MUTATION_ACTIONS,
13
- "contract.audit",
14
12
  "contract.deliver",
15
13
  ...DEBT_MUTATION_ACTIONS,
16
14
  ]);
@@ -48,7 +46,7 @@ export function historicalAllowedActions(value) {
48
46
  return decodeAllowedActions(value);
49
47
  // Persisted permissions retain the same surviving capabilities after rename;
50
48
  // removed planning/progress operations confer no replacement permission.
51
- const retired = new Set(["task.compose", "task.start", "task.stop", "task.hold", "task.resume"]);
49
+ const retired = new Set(["contract.audit", "task.compose", "task.start", "task.stop", "task.hold", "task.resume"]);
52
50
  return decodeAllowedActions(value
53
51
  .filter((action) => !retired.has(action))
54
52
  .map((action) => typeof action === "string" && action.startsWith("task.") ? `debt.${action.slice("task.".length)}` : action));
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
  import { abortable, abortableDelay } from "./abort.js";
8
8
  import { BodySupervisor } from "./body-supervisor.js";
9
9
  import { driveTurn, turnRecipe } from "./turn-drive.js";
10
- import { HeldAkumaLeash, breakBody, decidePendingTellDisposition, drainPendingTells, endTurn, failOpenBoundTurns, finishBodyIfIdle, heartExists, isHeartAbsent, probeLeash, projectTell, readHeart, readLatestTurnForBody, readOpenBoundTurns, readOpenPendingTellDisposition, readTell, readTurn, readTurnParticipants, readNonterminalRequests, resolvePendingTellDisposition, } from "./heart/index.js";
10
+ import { HeldAkumaLeash, BodyControlledError, breakBody, decidePendingTellDisposition, drainPendingTells, endTurn, failOpenBoundTurns, finishBodyIfIdle, heartExists, isHeartAbsent, probeLeash, projectTell, readHeart, readLatestTurnForBody, readOpenBoundTurns, readOpenPendingTellDisposition, readTell, readTurn, readTurnParticipants, readNonterminalRequests, resolvePendingTellDisposition, } from "./heart/index.js";
11
11
  import { akumaPathsSchema, pathsForAkuId, worldRootForAkumaPaths } from "./identity.js";
12
12
  import { admitCallInitialTell, } from "./call-initial-tell.js";
13
13
  import { akumaExecutionEnvironment } from "./providers/execution-environment.js";
@@ -248,14 +248,14 @@ async function prepareBodyExecution(launch, leash, runtime, adapter) {
248
248
  if (birth === null)
249
249
  return null;
250
250
  const { soul } = birth;
251
+ if (!(await prepareBodyStart(launch.paths, leash)))
252
+ return null;
251
253
  if (launch.seed === undefined && launch.initialBody === undefined) {
252
254
  const [heart, requests] = await Promise.all([readHeart(launch.paths), readNonterminalRequests(launch.paths)]);
253
255
  if (heart.pending.length === 0 && requests.length === 0)
254
256
  return null;
255
257
  }
256
258
  const selected = adapter ?? (await resolveProviderExecution(soul.provider)).adapter;
257
- if (!(await prepareBodyStart(launch.paths, leash)))
258
- return null;
259
259
  return { ...birth, adapter: selected };
260
260
  }
261
261
  async function runPreparedBody(input) {
@@ -283,9 +283,10 @@ async function preserveBodyFailure(paths, leash, error, at) {
283
283
  }
284
284
  }
285
285
  async function prepareBodyStart(paths, leash) {
286
- const before = await readHeart(paths);
287
- if (before.pause !== null)
286
+ const completion = await leash.completeInterruptBarrier(paths);
287
+ if (completion.kind === "unavailable")
288
288
  return false;
289
+ const before = await readHeart(paths);
289
290
  if (before.stop === null)
290
291
  return true;
291
292
  if (before.latestBody?.sequence === before.stop.bodySequence && before.latestBody.end === "put-down") {
@@ -409,7 +410,8 @@ async function runBodyTurns(input) {
409
410
  const { launch, soul, adapter, bodySequence, supervisor, runtime, turnStarted, turnOutcome } = input;
410
411
  let initial = launch.initialBody;
411
412
  for (;;) {
412
- const launchTells = drainPendingTells(supervisor.current().pending, await readOpenBoundTurns(launch.paths, bodySequence));
413
+ const control = supervisor.current();
414
+ const launchTells = drainPendingTells(control.pending, await readOpenBoundTurns(launch.paths, bodySequence), control.pause, bodySequence);
413
415
  if (supervisor.signal.aborted) {
414
416
  await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
415
417
  return;
@@ -594,8 +596,11 @@ export async function driveAkumaBody(launch, adapter, runtimeInput = {}) {
594
596
  let emitTurnOutcome;
595
597
  try {
596
598
  const prepared = await prepareBodyExecution(launch, leash, runtime, adapter);
597
- if (prepared === null)
599
+ if (prepared === null) {
600
+ if ((await readHeart(launch.paths)).pause !== null)
601
+ return "held";
598
602
  return;
603
+ }
599
604
  emitBodyEnd = bodyEndEmitter(launch, prepared.soul, world);
600
605
  emitTurnStarted = turnStartedEmitter(launch, prepared.soul, world);
601
606
  emitTurnOutcome = turnOutcomeEmitter(launch, prepared.soul, world);
@@ -629,6 +634,11 @@ export async function driveAkumaBody(launch, adapter, runtimeInput = {}) {
629
634
  });
630
635
  }
631
636
  catch (error) {
637
+ if (error instanceof BodyControlledError) {
638
+ if (bodySequence !== undefined)
639
+ await breakBody(launch.paths, { sequence: bodySequence, end: "put-down", at: runtime.now() });
640
+ return "held";
641
+ }
632
642
  await preserveBodyFailure(launch.paths, leash, error, runtime.now());
633
643
  throw error;
634
644
  }
@@ -140,6 +140,24 @@ export declare const tellResultSchema: z.ZodObject<{
140
140
  };
141
141
  } | undefined;
142
142
  }>>]>;
143
+ interruption: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
144
+ kind: z.ZodLiteral<"completed">;
145
+ bodySequence: z.ZodNumber;
146
+ putDown: z.ZodEnum<{
147
+ "was-idle": "was-idle";
148
+ "self-aborted": "self-aborted";
149
+ }>;
150
+ }, z.core.$strict>, z.ZodObject<{
151
+ kind: z.ZodLiteral<"pending">;
152
+ bodySequence: z.ZodNumber;
153
+ }, z.core.$strict>, z.ZodObject<{
154
+ kind: z.ZodLiteral<"unavailable">;
155
+ bodySequence: z.ZodNumber;
156
+ evidence: z.ZodEnum<{
157
+ hung: "hung";
158
+ untidy: "untidy";
159
+ }>;
160
+ }, z.core.$strict>], "kind">>;
143
161
  }, z.core.$strict>;
144
162
  export type TellWake = z.infer<typeof tellWakeSchema>;
145
163
  export type TellResult = z.infer<typeof tellResultSchema>;
@@ -19,6 +19,23 @@ const failedTellWakeSchema = z
19
19
  })
20
20
  .strict()
21
21
  .transform(({ child, ...wake }) => (child === undefined ? wake : { ...wake, child }));
22
+ const interruptReceiptSchema = z.discriminatedUnion("kind", [
23
+ z
24
+ .object({
25
+ kind: z.literal("completed"),
26
+ bodySequence: z.number().int().positive(),
27
+ putDown: z.enum(["was-idle", "self-aborted"]),
28
+ })
29
+ .strict(),
30
+ z.object({ kind: z.literal("pending"), bodySequence: z.number().int().positive() }).strict(),
31
+ z
32
+ .object({
33
+ kind: z.literal("unavailable"),
34
+ bodySequence: z.number().int().positive(),
35
+ evidence: z.enum(["hung", "untidy"]),
36
+ })
37
+ .strict(),
38
+ ]);
22
39
  const tellWakeSchema = z.union([
23
40
  z.object({ kind: z.literal("told") }).strict(),
24
41
  z.object({ kind: z.literal("held") }).strict(),
@@ -30,6 +47,7 @@ export const tellResultSchema = z
30
47
  admission: z.object({ fact: z.literal("recorded"), tellId: nonblankTextSchema }).strict(),
31
48
  row: tellRowSchema,
32
49
  wake: tellWakeSchema,
50
+ interruption: interruptReceiptSchema.optional(),
33
51
  })
34
52
  .strict();
35
53
  export async function admitCallInitialTell(input) {
@@ -16,10 +16,9 @@ declare const akumaCallRecipeSchema: z.ZodObject<{
16
16
  "akuma.call": "akuma.call";
17
17
  "akuma.kill": "akuma.kill";
18
18
  "akuma.tell": "akuma.tell";
19
- "contract.audit": "contract.audit";
20
19
  "contract.deliver": "contract.deliver";
21
20
  "contract.review": "contract.review";
22
- }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
21
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.deliver" | "contract.review")[]>>;
23
22
  provider: z.ZodPipe<z.ZodObject<{
24
23
  name: z.ZodString;
25
24
  kind: z.ZodEnum<{
@@ -96,10 +95,9 @@ declare const akumaCallPayloadSchema: z.ZodObject<{
96
95
  "akuma.call": "akuma.call";
97
96
  "akuma.kill": "akuma.kill";
98
97
  "akuma.tell": "akuma.tell";
99
- "contract.audit": "contract.audit";
100
98
  "contract.deliver": "contract.deliver";
101
99
  "contract.review": "contract.review";
102
- }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
100
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("debt.add" | "debt.addDocument" | "debt.done" | "debt.drop" | "debt.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.deliver" | "contract.review")[]>>;
103
101
  provider: z.ZodPipe<z.ZodObject<{
104
102
  name: z.ZodString;
105
103
  kind: z.ZodEnum<{
@@ -266,6 +264,24 @@ declare const liveResultSchema: z.ZodObject<{
266
264
  };
267
265
  } | undefined;
268
266
  }>>]>;
267
+ interruption: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
268
+ kind: z.ZodLiteral<"completed">;
269
+ bodySequence: z.ZodNumber;
270
+ putDown: z.ZodEnum<{
271
+ "was-idle": "was-idle";
272
+ "self-aborted": "self-aborted";
273
+ }>;
274
+ }, z.core.$strict>, z.ZodObject<{
275
+ kind: z.ZodLiteral<"pending">;
276
+ bodySequence: z.ZodNumber;
277
+ }, z.core.$strict>, z.ZodObject<{
278
+ kind: z.ZodLiteral<"unavailable">;
279
+ bodySequence: z.ZodNumber;
280
+ evidence: z.ZodEnum<{
281
+ hung: "hung";
282
+ untidy: "untidy";
283
+ }>;
284
+ }, z.core.$strict>], "kind">>;
269
285
  }, z.core.$strict>>;
270
286
  tellFailure: z.ZodOptional<z.ZodString>;
271
287
  }, z.core.$strict>;
@@ -199,7 +199,20 @@ export type StopFact = Readonly<{
199
199
  export type PauseFact = Readonly<{
200
200
  bodySequence: number;
201
201
  requestedAt: string;
202
+ frontierTellId?: string;
202
203
  }>;
204
+ export type InterruptCompletion = Readonly<{
205
+ kind: "clear";
206
+ }> | Readonly<{
207
+ kind: "completed";
208
+ body: BodyFact;
209
+ }> | Readonly<{
210
+ kind: "unavailable";
211
+ evidence: "hung" | "untidy";
212
+ }>;
213
+ export declare class BodyControlledError extends Error {
214
+ constructor();
215
+ }
203
216
  export type SealFact = Readonly<{
204
217
  evidence: string;
205
218
  at: string;
@@ -30,6 +30,12 @@ export class AkumaBusyError extends Error {
30
30
  this.name = "AkumaBusyError";
31
31
  }
32
32
  }
33
+ export class BodyControlledError extends Error {
34
+ constructor() {
35
+ super("Akuma Body admission is fenced by durable control");
36
+ this.name = "BodyControlledError";
37
+ }
38
+ }
33
39
  export function life(input) {
34
40
  if (input.body?.hung !== undefined)
35
41
  return "hung";
@@ -1,14 +1,17 @@
1
1
  import type { AkumaPaths } from "../identity.js";
2
- import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, KillFact, SealFact, SessionFact, Soul, TellDeliveryInput, TellFact, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact } from "./facts.js";
2
+ import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, InterruptCompletion, KillFact, PauseFact, SealFact, SessionFact, Soul, TellDeliveryInput, TellFact, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact } from "./facts.js";
3
3
  import type { AgentEvent } from "./activity-schema.js";
4
4
  import { type StatusFactInput, type ActivityFactSlice } from "./timeline.js";
5
5
  import { type HeartRead } from "./storage.js";
6
6
  export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
7
7
  export { admitRequest, beginRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, unproveRequest, voidRequest, } from "./request-authority.js";
8
- export { AkumaBusyError, life, lifeAt, projectTell } from "./facts.js";
8
+ export { AkumaBusyError, BodyControlledError, life, lifeAt, projectTell } from "./facts.js";
9
9
  export { drainPendingTells } from "./tells.js";
10
- export type { AkumaLife, AkumaOrigin, BodyEnd, HeartSnapshot, KillEvidence, RequestFact, SessionFact, Soul, TellFact, TurnEndFact, TurnOutcome, } from "./facts.js";
10
+ export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, InterruptCompletion, PauseFact, HeartSnapshot, KillEvidence, RequestFact, SessionFact, Soul, TellFact, TurnEndFact, TurnOutcome, } from "./facts.js";
11
11
  export type { ResumeCoordinate } from "../coordinate.js";
12
+ export declare function readInterruptCompletion(paths: AkumaPaths, expected: PauseFact): Promise<Extract<InterruptCompletion, {
13
+ kind: "completed" | "unavailable";
14
+ }>>;
12
15
  export declare function readSoul(paths: AkumaPaths): Promise<Soul | null>;
13
16
  export declare function heartExists(paths: AkumaPaths): Promise<boolean>;
14
17
  export declare function readSeal(paths: AkumaPaths): Promise<SealFact | null>;
@@ -83,14 +86,32 @@ type LifeSnapshot = Pick<HeartSnapshot, "soul" | "latestBody" | "latestSession"
83
86
  }>;
84
87
  /** Lifecycle probes do not load narration, Tell bodies, or delivery histories. */
85
88
  export declare function readLifeSnapshot(paths: AkumaPaths, signal?: AbortSignal, read?: HeartRead): Promise<LifeSnapshot>;
86
- export declare function recordTell(paths: AkumaPaths, tell: Omit<TellFact, "sequence" | "state" | "deliveries" | "binding">, options?: Readonly<{
87
- interrupt?: boolean;
88
- }>): Promise<Readonly<{
89
+ export type InterruptTellAdmission = Readonly<{
89
90
  kind: "not-born";
90
- } | {
91
+ }> | Readonly<{
92
+ kind: "unavailable";
93
+ evidence: "hung" | "untidy";
94
+ }> | Readonly<{
91
95
  kind: "recorded";
92
96
  tell: TellFact;
93
- }>>;
97
+ predecessorWasRunning: boolean;
98
+ barrier?: PauseFact;
99
+ }>;
100
+ type TellAdmission = Readonly<{
101
+ kind: "not-born";
102
+ }> | Readonly<{
103
+ kind: "recorded";
104
+ tell: TellFact;
105
+ }>;
106
+ type TellInput = Omit<TellFact, "sequence" | "state" | "deliveries" | "binding">;
107
+ export declare function recordTell(paths: AkumaPaths, tell: TellInput, options: Readonly<{
108
+ interrupt: true;
109
+ signal?: AbortSignal;
110
+ }>): Promise<InterruptTellAdmission>;
111
+ export declare function recordTell(paths: AkumaPaths, tell: TellInput, options?: Readonly<{
112
+ interrupt?: false;
113
+ signal?: AbortSignal;
114
+ }>): Promise<TellAdmission>;
94
115
  export declare function readTell(paths: AkumaPaths, id: string): Promise<TellFact | null>;
95
116
  export declare function recordTellDeliveries(paths: AkumaPaths, inputs: readonly TellDeliveryInput[]): Promise<void>;
96
117
  export declare function recordTellReceipt(paths: AkumaPaths, input: TellReceiptInput): Promise<void>;
@@ -117,12 +138,6 @@ export declare function requestStop(paths: AkumaPaths, at: string, signal?: Abor
117
138
  kind: "already-killed" | "already-stopped";
118
139
  body: BodyFact;
119
140
  }>>;
120
- export declare function requestPause(paths: AkumaPaths, at: string, signal?: AbortSignal): Promise<Readonly<{
121
- kind: "not-born";
122
- } | {
123
- kind: "requested";
124
- body: BodyFact;
125
- }>>;
126
141
  export declare function breakBody(paths: AkumaPaths, input: Readonly<{
127
142
  sequence: number;
128
143
  end: Exclude<BodyEnd, "exited">;
@@ -1,16 +1,21 @@
1
1
  import { parsePublicHistoryId } from "../identity.js";
2
2
  import { decodeRuntimeModel, withoutModel } from "../provider-recipe.js";
3
- import { answeredTurnFact, endBodyFact, finishBodyFact, deleteStopControl, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertKillFact, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, latestTurnFactForBody, lastAnsweredTurnFact, pauseFact, sessionFactForCoordinate, stopFact, turnFact, } from "./rows.js";
4
- import { insertTellDeliveryFact, dispositionSnapshotProven, insertTellBindingFact, insertTellDispositionSnapshot, insertTellReceiptFact, insertUndeliveredTellReceipts, latestOpenTellDisposition, openBoundTurns, openTellDispositionIds, pendingTellFacts, hasPendingTell, resolveTellDispositionSnapshot, tellDispositionResolved, tellFact, tellIdsForFence, turnParticipants, } from "./tells.js";
3
+ import { BodyControlledError } from "./facts.js";
4
+ import { answeredTurnFact, completeInterruptBarrier as judgeInterruptBarrier, endBodyFact, finishBodyFact, deleteStopControl, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertKillFact, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, insertInterruptBarrier, isTellProtectedByPause, latestKillFact, latestSessionFact, latestTurnFactForBody, lastAnsweredTurnFact, pauseFact, sessionFactForCoordinate, stopFact, turnFact, } from "./rows.js";
5
+ import { insertTellDeliveryFact, dispositionSnapshotProven, insertTellBindingFact, insertTellDispositionSnapshot, insertTellReceiptFact, insertUndeliveredTellReceipts, latestOpenTellDisposition, openBoundTurns, openTellDispositionIds, pendingTellFacts, hasPendingTell, resolveTellDispositionSnapshot, sameTellInput, tellDispositionResolved, tellFact, tellIdsForFence, turnParticipants, } from "./tells.js";
5
6
  import { activityFactSlice, statusFacts, readStatusBatch, decodeTimelineBatch, timelineDataVersion, lastActivityAt as readLastActivityAt, pruneActivityFacts, } from "./timeline.js";
6
7
  import { createHeartReader, isHeartAbsent, readSealFromLeash, recordPendingTell, withHeartTransaction, withReadOnlyHeart, } from "./storage.js";
7
8
  import { setSoulModel, soulFact } from "./soul.js";
8
9
  import { AkumaBusyError, HeartAuthorityCorruptionError } from "./facts.js";
9
10
  export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
10
11
  export { admitRequest, beginRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, unproveRequest, voidRequest, } from "./request-authority.js";
11
- export { AkumaBusyError, life, lifeAt, projectTell } from "./facts.js";
12
+ export { AkumaBusyError, BodyControlledError, life, lifeAt, projectTell } from "./facts.js";
12
13
  export { drainPendingTells } from "./tells.js";
13
14
  const ACTIVITY_LIMIT = 5_000;
15
+ export async function readInterruptCompletion(paths, expected) {
16
+ const result = await withReadOnlyHeart(paths, (heart) => judgeInterruptBarrier(heart, expected, false));
17
+ return result.kind === "clear" ? { kind: "unavailable", evidence: "untidy" } : result;
18
+ }
14
19
  export async function readSoul(paths) {
15
20
  try {
16
21
  return await withReadOnlyHeart(paths, soulFact);
@@ -173,28 +178,54 @@ export async function readLifeSnapshot(paths, signal, read = (body, signal) => w
173
178
  throw error;
174
179
  }
175
180
  }
176
- function sameTellInput(existing, tell) {
177
- return (existing.body === tell.body &&
178
- existing.recordedAt === tell.recordedAt &&
179
- existing.schemaJson === tell.schemaJson &&
180
- existing.initiator === tell.initiator);
181
+ function judgeExistingTell(inputs, existing) {
182
+ const { heart, tell, body, pause, interrupt } = inputs;
183
+ if (!sameTellInput(existing, interrupt ? { ...tell, recordedAt: existing.recordedAt } : tell))
184
+ throw new Error(`tell ${tell.id} reused different input`);
185
+ if (!interrupt)
186
+ return { kind: "recorded", tell: existing };
187
+ const barrier = body !== null && pause !== null && isTellProtectedByPause(heart, tell.id, body.sequence) ? pause : undefined;
188
+ return {
189
+ kind: "recorded",
190
+ tell: existing,
191
+ predecessorWasRunning: body !== null && body.end === undefined,
192
+ ...(barrier === undefined ? {} : { barrier }),
193
+ };
194
+ }
195
+ function judgeNewTell(inputs) {
196
+ const { heart, tell, body, pause, interrupt } = inputs;
197
+ if (interrupt) {
198
+ if (body?.hung !== undefined)
199
+ return { kind: "unavailable", evidence: "hung" };
200
+ if (body === null)
201
+ return { kind: "unavailable", evidence: "untidy" };
202
+ if (pause !== null && pause.bodySequence !== body.sequence)
203
+ return { kind: "unavailable", evidence: "untidy" };
204
+ const admitted = recordPendingTell(heart, tell);
205
+ const barrier = pause ??
206
+ insertInterruptBarrier(heart, {
207
+ bodySequence: body.sequence,
208
+ tellId: tell.id,
209
+ at: tell.recordedAt,
210
+ });
211
+ return { ...admitted, predecessorWasRunning: body.end === undefined, barrier };
212
+ }
213
+ const running = body !== null && body.end === undefined && body.hung === undefined;
214
+ if (tell.schemaJson !== undefined && running)
215
+ throw new AkumaBusyError();
216
+ return recordPendingTell(heart, tell);
181
217
  }
182
218
  export async function recordTell(paths, tell, options = {}) {
219
+ const interrupt = options.interrupt === true;
183
220
  return await withHeartTransaction(paths, (heart) => {
184
221
  if (soulFact(heart) === null)
185
222
  return { kind: "not-born" };
186
223
  const existing = tellFact(heart, tell.id);
187
- if (existing !== null) {
188
- if (!sameTellInput(existing, tell))
189
- throw new Error(`tell ${tell.id} reused different input`);
190
- return { kind: "recorded", tell: existing };
191
- }
192
224
  const body = latestBodyFact(heart);
193
- const running = body !== null && body.end === undefined && body.hung === undefined;
194
- if (tell.schemaJson !== undefined && running && options.interrupt !== true)
195
- throw new AkumaBusyError();
196
- return recordPendingTell(heart, tell);
197
- });
225
+ const pause = interrupt ? pauseFact(heart) : null;
226
+ const inputs = { heart, tell, body, pause, interrupt };
227
+ return existing === null ? judgeNewTell(inputs) : judgeExistingTell(inputs, existing);
228
+ }, options.signal);
198
229
  }
199
230
  export async function readTell(paths, id) {
200
231
  return await withReadOnlyHeart(paths, (heart) => tellFact(heart, id));
@@ -205,6 +236,11 @@ export async function recordTellDeliveries(paths, inputs) {
205
236
  const current = tellFact(heart, input.tellId);
206
237
  if (current === null)
207
238
  throw new Error(`unknown tell ${input.tellId}`);
239
+ const turn = turnFact(heart, input.turnSequence);
240
+ if (turn === null)
241
+ throw new Error(`unknown Akuma Turn ${input.turnSequence}`);
242
+ if (isTellProtectedByPause(heart, input.tellId, turn.bodySequence))
243
+ throw new BodyControlledError();
208
244
  }
209
245
  let changed = false;
210
246
  for (const input of inputs)
@@ -217,6 +253,13 @@ export async function recordTellReceipt(paths, input) {
217
253
  const tellIds = input.evidence === "exact" ? [input.tellId] : tellIdsForFence(heart, input.turnSequence, input.fence);
218
254
  if (tellIds.length === 0)
219
255
  throw new Error("tell receipt has no delivery mapping");
256
+ const pause = pauseFact(heart);
257
+ for (const tellId of tellIds) {
258
+ if (tellFact(heart, tellId) === null)
259
+ throw new Error(`unknown tell ${tellId}`);
260
+ if (pause !== null && isTellProtectedByPause(heart, tellId, pause.bodySequence))
261
+ throw new BodyControlledError();
262
+ }
220
263
  const changed = insertTellReceiptFact(heart, input);
221
264
  pruneActivityFacts(heart, ACTIVITY_LIMIT, changed);
222
265
  });
@@ -231,6 +274,11 @@ export async function decidePendingTellDisposition(paths, input) {
231
274
  const existing = openTellDispositionIds(heart, input.bodySequence);
232
275
  if (existing !== null)
233
276
  return { bodySequence: input.bodySequence, tellIds: existing };
277
+ if (pauseFact(heart)?.bodySequence === input.bodySequence) {
278
+ if (input.handoff && body.end === undefined)
279
+ endBodyFact(heart, { sequence: input.bodySequence, end: "put-down", at: input.at });
280
+ return null;
281
+ }
234
282
  const pending = pendingTellFacts(heart);
235
283
  const tellIds = pending.map((tell) => tell.id);
236
284
  if (tellIds.length === 0)
@@ -298,22 +346,15 @@ export async function requestStop(paths, at, signal) {
298
346
  return { kind: "requested", body };
299
347
  }, signal);
300
348
  }
301
- export async function requestPause(paths, at, signal) {
302
- return await withHeartTransaction(paths, (heart) => {
303
- if (soulFact(heart) === null)
304
- return { kind: "not-born" };
305
- const body = latestBodyFact(heart);
306
- if (body === null)
307
- throw new Error("Akuma has no Body to interrupt");
308
- insertPauseControl(heart, at);
309
- return { kind: "requested", body };
310
- }, signal);
311
- }
312
349
  export async function breakBody(paths, input) {
313
350
  await withHeartTransaction(paths, (heart) => endBodyFact(heart, input));
314
351
  }
315
352
  export async function beginTurn(paths, input) {
316
353
  return await withHeartTransaction(paths, (heart) => {
354
+ if (pauseFact(heart)?.bodySequence === input.bodySequence)
355
+ throw new BodyControlledError();
356
+ if (stopFact(heart)?.bodySequence === input.bodySequence)
357
+ throw new BodyControlledError();
317
358
  const fact = insertTurnStartFact(heart, input);
318
359
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
319
360
  return fact;
@@ -321,9 +362,14 @@ export async function beginTurn(paths, input) {
321
362
  }
322
363
  export async function bindTellsToTurn(paths, input) {
323
364
  await withHeartTransaction(paths, (heart) => {
365
+ const turn = turnFact(heart, input.turnSequence);
366
+ if (turn === null)
367
+ throw new Error(`Akuma Turn ${input.turnSequence} is missing`);
324
368
  for (const tellId of input.tellIds) {
325
369
  if (tellFact(heart, tellId) === null)
326
370
  throw new Error(`unknown tell ${tellId}`);
371
+ if (isTellProtectedByPause(heart, tellId, turn.bodySequence))
372
+ throw new BodyControlledError();
327
373
  insertTellBindingFact(heart, { tellId, turnSequence: input.turnSequence, boundAt: input.boundAt });
328
374
  }
329
375
  pruneActivityFacts(heart, ACTIVITY_LIMIT);