@astrosheep/keiyaku 4.6.11 → 4.6.13
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.
- package/build/plugins/claude/skills/keiyaku/SKILL.md +3 -1
- package/build/plugins/claude/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-bind/SKILL.md +3 -2
- package/build/plugins/claude/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/plugins/codex/skills/keiyaku/SKILL.md +3 -1
- package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +3 -2
- package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/plugins/opencode/skills/keiyaku/SKILL.md +3 -1
- package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +3 -2
- package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/skills/keiyaku/SKILL.md +3 -1
- package/build/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/skills/keiyaku-bind/SKILL.md +3 -2
- package/build/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/src/akuma/akuma-errors.d.ts +5 -0
- package/build/src/akuma/akuma-errors.js +9 -0
- package/build/src/akuma/akuma-owner.d.ts +14 -19
- package/build/src/akuma/akuma-owner.js +56 -54
- package/build/src/akuma/akuma-selection-execution.d.ts +5 -4
- package/build/src/akuma/akuma-selection-execution.js +59 -17
- package/build/src/akuma/akuma.d.ts +1 -2
- package/build/src/akuma/allowed.d.ts +2 -3
- package/build/src/akuma/allowed.js +1 -3
- package/build/src/akuma/body.js +17 -7
- package/build/src/akuma/call-initial-tell.d.ts +18 -0
- package/build/src/akuma/call-initial-tell.js +18 -0
- package/build/src/akuma/call-request.d.ts +20 -4
- package/build/src/akuma/heart/facts.d.ts +13 -0
- package/build/src/akuma/heart/facts.js +6 -0
- package/build/src/akuma/heart/index.d.ts +29 -14
- package/build/src/akuma/heart/index.js +75 -29
- package/build/src/akuma/heart/rows.d.ts +9 -2
- package/build/src/akuma/heart/rows.js +72 -11
- package/build/src/akuma/heart/storage.d.ts +2 -8
- package/build/src/akuma/heart/storage.js +13 -13
- package/build/src/akuma/heart/tells.d.ts +3 -1
- package/build/src/akuma/heart/tells.js +18 -2
- package/build/src/akuma/provider.d.ts +2 -4
- package/build/src/akuma/provider.js +29 -6
- package/build/src/akuma/providers/acp/core.js +23 -21
- package/build/src/akuma/providers/claude/index.js +0 -8
- package/build/src/akuma/providers/codex-app-server/index.js +9 -8
- package/build/src/akuma/providers/execution-environment.d.ts +1 -1
- package/build/src/akuma/providers/execution-environment.js +1 -0
- package/build/src/akuma/providers/opencode-sdk/events.js +111 -81
- package/build/src/akuma/providers/opencode-sdk/index.js +179 -150
- package/build/src/akuma/providers/opencode-sdk/native-http.js +7 -3
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +5 -3
- package/build/src/akuma/providers/opencode-sdk/session.js +32 -27
- package/build/src/akuma/providers/paseo/index.js +0 -2
- package/build/src/akuma/providers/pi/index.js +23 -20
- package/build/src/akuma/selection-observation.d.ts +78 -9
- package/build/src/akuma/selection-owner-port.d.ts +2 -1
- package/build/src/akuma/selection-owner-port.js +6 -1
- package/build/src/akuma/selection-request.d.ts +1 -1
- package/build/src/akuma/selection-request.js +8 -3
- package/build/src/akuma/turn-drive.js +79 -48
- package/build/src/akuma-body.js +5 -3
- package/build/src/cli/commands/akuma-grammar.js +2 -2
- package/build/src/cli/commands/akuma.js +24 -16
- package/build/src/cli/commands/contract-grammar.d.ts +3 -7
- package/build/src/cli/commands/contract-grammar.js +12 -10
- package/build/src/cli/commands/contract-help.d.ts +3 -13
- package/build/src/cli/commands/contract-help.js +7 -16
- package/build/src/cli/commands/contract.js +19 -35
- package/build/src/cli/commands/debt.js +30 -29
- package/build/src/cli/parse.js +0 -1
- package/build/src/cli/render/akuma-activity.d.ts +36 -9
- package/build/src/cli/render/akuma-activity.js +142 -65
- package/build/src/cli/render/akuma.d.ts +6 -0
- package/build/src/cli/render/akuma.js +67 -24
- package/build/src/cli/render/catalog.js +0 -1
- package/build/src/cli/render/contract-observation.d.ts +16 -3
- package/build/src/cli/render/contract-observation.js +3 -0
- package/build/src/cli/render/contract.d.ts +2 -2
- package/build/src/cli/render/contract.js +4 -4
- package/build/src/cli/render/delivery-preview.d.ts +7 -0
- package/build/src/cli/render/{audit.js → delivery-preview.js} +29 -27
- package/build/src/cli/render/kanshi-akuma.d.ts +3 -1
- package/build/src/cli/render/kanshi-akuma.js +23 -8
- package/build/src/cli/render/kanshi.js +4 -1
- package/build/src/cli/render/receipt.js +3 -3
- package/build/src/cli/render/refusal.js +2 -2
- package/build/src/cli/render/terminal.d.ts +0 -1
- package/build/src/cli/render/terminal.js +1 -1
- package/build/src/cli/square-identity.d.ts +15 -0
- package/build/src/cli/square-identity.js +180 -0
- package/build/src/contract-worktree.js +1 -1
- package/build/src/core/verbs/placement.d.ts +2 -1
- package/build/src/core/verbs/placement.js +1 -1
- package/build/src/debt/context.d.ts +0 -5
- package/build/src/debt/context.js +32 -44
- package/build/src/git/process.d.ts +1 -0
- package/build/src/git/process.js +18 -1
- package/build/src/git/read-observation.js +2 -1
- package/build/src/git/scratch.d.ts +5 -1
- package/build/src/git/scratch.js +41 -12
- package/build/src/git/target-placement.d.ts +22 -4
- package/build/src/git/target-placement.js +44 -20
- package/build/src/index.d.ts +2 -2
- package/build/src/kanshi/read.js +187 -105
- package/build/src/kanshi/report.d.ts +5 -0
- package/build/src/library/address.d.ts +18 -4
- package/build/src/library/address.js +27 -6
- package/build/src/library/contract-address.d.ts +6 -0
- package/build/src/library/contract-address.js +12 -1
- package/build/src/library/contract-execution.d.ts +2 -2
- package/build/src/library/contract-execution.js +13 -17
- package/build/src/library/contract-operations.d.ts +59 -94
- package/build/src/library/contract-operations.js +23 -19
- package/build/src/library/contract-outcomes.d.ts +3 -3
- package/build/src/library/contract-settings.d.ts +1 -0
- package/build/src/library/contract-settings.js +1 -1
- package/build/src/library/invocation.d.ts +2 -2
- package/build/src/library/invocation.js +1 -1
- package/build/src/library/keiyaku.d.ts +2 -2
- package/build/src/library/keiyaku.js +2 -2
- package/build/src/library/outcome.d.ts +148 -268
- package/build/src/library/outcome.js +4 -8
- package/build/src/library/refusal.d.ts +2 -2
- package/build/src/library/refusal.js +2 -2
- package/build/src/plugin/runtime.js +15 -19
- package/build/src/protocol/{audit.d.ts → delivery-preview.d.ts} +62 -97
- package/build/src/protocol/delivery-preview.js +167 -0
- package/build/src/protocol/progress.d.ts +1 -1
- package/build/src/runtime/proc/run.js +22 -4
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/verification/execution.js +5 -3
- package/package.json +5 -2
- package/skills/keiyaku/SKILL.md +3 -1
- package/skills/keiyaku-babysit/SKILL.md +1 -1
- package/skills/keiyaku-bind/SKILL.md +3 -2
- package/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/src/cli/render/audit.d.ts +0 -7
- 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 (
|
|
14
|
+
if ("kind" in interrupted)
|
|
14
15
|
throw new AkumaProviderError(`Tell interrupt unavailable: ${interrupted.evidence}`);
|
|
15
|
-
tell = interrupted
|
|
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) => {
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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));
|
package/build/src/akuma/body.js
CHANGED
|
@@ -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
|
|
287
|
-
if (
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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 {
|
|
4
|
-
import {
|
|
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
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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);
|