@astrosheep/keiyaku 4.0.1 → 4.0.2
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/README.md +137 -5
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +98 -58
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +152 -53
- package/build/src/akuma/abort.d.ts +2 -0
- package/build/src/akuma/abort.js +50 -0
- package/build/src/akuma/akuma.d.ts +23 -14
- package/build/src/akuma/akuma.js +120 -104
- package/build/src/akuma/archetype.d.ts +7 -6
- package/build/src/akuma/archetype.js +68 -41
- package/build/src/akuma/body.d.ts +3 -5
- package/build/src/akuma/body.js +408 -213
- package/build/src/akuma/heart/facts.d.ts +25 -34
- package/build/src/akuma/heart/facts.js +18 -7
- package/build/src/akuma/heart/index.d.ts +39 -42
- package/build/src/akuma/heart/index.js +134 -93
- package/build/src/akuma/heart/rows.d.ts +9 -31
- package/build/src/akuma/heart/rows.js +26 -44
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +7 -15
- package/build/src/akuma/heart/soul.d.ts +8 -0
- package/build/src/akuma/heart/soul.js +136 -0
- package/build/src/akuma/heart/storage.d.ts +28 -14
- package/build/src/akuma/heart/storage.js +86 -26
- package/build/src/akuma/identity.d.ts +2 -2
- package/build/src/akuma/identity.js +9 -9
- package/build/src/akuma/index.d.ts +6 -1
- package/build/src/akuma/index.js +6 -0
- package/build/src/akuma/projection.d.ts +117 -38
- package/build/src/akuma/projection.js +208 -97
- package/build/src/akuma/provider-recipe.d.ts +24 -0
- package/build/src/akuma/provider-recipe.js +100 -0
- package/build/src/akuma/provider.d.ts +12 -4
- package/build/src/akuma/provider.js +117 -70
- package/build/src/akuma/providers/acp/core.d.ts +19 -0
- package/build/src/akuma/providers/acp/core.js +159 -0
- package/build/src/akuma/providers/acp/events.d.ts +20 -0
- package/build/src/akuma/providers/acp/events.js +73 -0
- package/build/src/akuma/providers/acp/index.d.ts +12 -0
- package/build/src/akuma/providers/acp/index.js +94 -0
- package/build/src/akuma/providers/claude/events.js +66 -13
- package/build/src/akuma/providers/claude/index.d.ts +2 -1
- package/build/src/akuma/providers/claude/index.js +35 -21
- package/build/src/akuma/providers/codex-app-server/events.js +4 -1
- package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
- package/build/src/akuma/providers/codex-app-server/index.js +24 -31
- package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
- package/build/src/akuma/providers/grok-build/index.js +70 -0
- package/build/src/akuma/providers/index.d.ts +5 -3
- package/build/src/akuma/providers/index.js +24 -59
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
- package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
- package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
- package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
- package/build/src/akuma/providers/pi/events.js +49 -11
- package/build/src/akuma/providers/pi/index.d.ts +1 -1
- package/build/src/akuma/providers/pi/index.js +50 -21
- package/build/src/akuma/publication.d.ts +3 -3
- package/build/src/akuma/publication.js +28 -40
- package/build/src/akuma/requests.d.ts +9 -7
- package/build/src/akuma/requests.js +92 -65
- package/build/src/alias/index.d.ts +2 -2
- package/build/src/alias/index.js +10 -10
- package/build/src/body/decode.js +7 -1
- package/build/src/body/region.d.ts +2 -0
- package/build/src/body/region.js +77 -7
- package/build/src/cli/accepted.d.ts +17 -13
- package/build/src/cli/accepted.js +72 -34
- package/build/src/cli/actor.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
- package/build/src/cli/commands/akuma-invoke.js +14 -9
- package/build/src/cli/commands/akuma.d.ts +13 -3
- package/build/src/cli/commands/akuma.js +67 -34
- package/build/src/cli/commands/contract.d.ts +18 -5
- package/build/src/cli/commands/contract.js +9 -3
- package/build/src/cli/commands/task-invoke.d.ts +17 -4
- package/build/src/cli/commands/task-invoke.js +43 -20
- package/build/src/cli/commands/task-query.d.ts +5 -0
- package/build/src/cli/commands/task-query.js +247 -0
- package/build/src/cli/commands/task.d.ts +3 -1
- package/build/src/cli/commands/task.js +51 -13
- package/build/src/cli/coordinates.d.ts +18 -0
- package/build/src/cli/coordinates.js +120 -0
- package/build/src/cli/draft.d.ts +8 -0
- package/build/src/cli/draft.js +96 -0
- package/build/src/cli/invoke.d.ts +4 -3
- package/build/src/cli/invoke.js +160 -179
- package/build/src/cli/main.js +34 -6
- package/build/src/cli/parse.d.ts +9 -2
- package/build/src/cli/parse.js +29 -7
- package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
- package/build/src/cli/render/akuma-tool-command.js +144 -0
- package/build/src/cli/render/akuma-tool.d.ts +2 -2
- package/build/src/cli/render/akuma-tool.js +30 -4
- package/build/src/cli/render/akuma.d.ts +1 -0
- package/build/src/cli/render/akuma.js +211 -116
- package/build/src/cli/render/audit.d.ts +3 -0
- package/build/src/cli/render/audit.js +104 -0
- package/build/src/cli/render/contract.d.ts +3 -3
- package/build/src/cli/render/contract.js +192 -79
- package/build/src/cli/render/kanshi.js +263 -168
- package/build/src/cli/render/receipt.d.ts +19 -0
- package/build/src/cli/render/receipt.js +106 -0
- package/build/src/cli/render/refusal.d.ts +16 -2
- package/build/src/cli/render/refusal.js +88 -20
- package/build/src/cli/render/region.d.ts +2 -0
- package/build/src/cli/render/region.js +17 -0
- package/build/src/cli/render/task.d.ts +2 -1
- package/build/src/cli/render/task.js +223 -60
- package/build/src/cli/render/terminal.d.ts +7 -0
- package/build/src/cli/render/terminal.js +46 -0
- package/build/src/cli/render/text.js +6 -3
- package/build/src/cli/result.d.ts +115 -20
- package/build/src/cli/usage.d.ts +1 -0
- package/build/src/cli/usage.js +3 -0
- package/build/src/contract-worktree.d.ts +6 -5
- package/build/src/contract-worktree.js +95 -57
- package/build/src/coordination/durable-file.d.ts +4 -3
- package/build/src/coordination/durable-file.js +29 -25
- package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
- package/build/src/coordination/sqlite-transaction-lock.js +22 -5
- package/build/src/core/facts/fold.js +1 -6
- package/build/src/core/facts/gate.d.ts +1 -0
- package/build/src/core/facts/gate.js +1 -0
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +0 -11
- package/build/src/core/verbs/deliver.d.ts +1 -1
- package/build/src/core/verbs/deliver.js +1 -7
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +4 -1
- package/build/src/dispatch/index.d.ts +2 -2
- package/build/src/dispatch/index.js +17 -17
- package/build/src/git/admission.d.ts +1 -1
- package/build/src/git/admission.js +11 -11
- package/build/src/git/hooks.js +13 -13
- package/build/src/git/integration.d.ts +14 -15
- package/build/src/git/integration.js +80 -44
- package/build/src/git/observe.d.ts +5 -3
- package/build/src/git/observe.js +18 -12
- package/build/src/git/read-observation.js +9 -9
- package/build/src/git/reconcile.d.ts +2 -0
- package/build/src/git/reconcile.js +138 -95
- package/build/src/git/repository.d.ts +27 -19
- package/build/src/git/repository.js +155 -83
- package/build/src/git/scratch.d.ts +7 -3
- package/build/src/git/scratch.js +44 -37
- package/build/src/git/target-placement.d.ts +40 -4
- package/build/src/git/target-placement.js +221 -101
- package/build/src/git/tender.d.ts +6 -5
- package/build/src/git/tender.js +24 -43
- package/build/src/git/terminal-seal.d.ts +1 -1
- package/build/src/git/terminal-seal.js +7 -5
- package/build/src/git/workspace.d.ts +36 -4
- package/build/src/git/workspace.js +53 -18
- package/build/src/identity/selector.js +1 -1
- package/build/src/index.d.ts +3 -3
- package/build/src/index.js +1 -1
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +2 -1
- package/build/src/kanshi/read.js +118 -27
- package/build/src/kanshi/report.d.ts +50 -0
- package/build/src/kanshi/select.d.ts +5 -0
- package/build/src/kanshi/select.js +32 -0
- package/build/src/library/address.d.ts +12 -3
- package/build/src/library/address.js +77 -46
- package/build/src/library/akuma-creation.d.ts +6 -1
- package/build/src/library/akuma-creation.js +86 -13
- package/build/src/library/audit.d.ts +18 -0
- package/build/src/library/audit.js +44 -0
- package/build/src/library/bind.js +4 -4
- package/build/src/library/catalog.d.ts +1 -1
- package/build/src/library/catalog.js +5 -8
- package/build/src/library/contract.d.ts +5 -6
- package/build/src/library/contract.js +11 -35
- package/build/src/library/delivery.d.ts +2 -1
- package/build/src/library/fleet.d.ts +2 -2
- package/build/src/library/fleet.js +35 -71
- package/build/src/library/keiyaku.d.ts +3 -2
- package/build/src/library/keiyaku.js +1 -0
- package/build/src/library/mutation.d.ts +7 -6
- package/build/src/library/mutation.js +23 -25
- package/build/src/library/reconcile.d.ts +29 -0
- package/build/src/library/reconcile.js +179 -0
- package/build/src/library/region.d.ts +12 -0
- package/build/src/library/region.js +15 -1
- package/build/src/library/repo.d.ts +4 -16
- package/build/src/library/repo.js +13 -38
- package/build/src/protocol/attempt.js +6 -6
- package/build/src/protocol/bind.js +4 -4
- package/build/src/protocol/intent.d.ts +15 -2
- package/build/src/protocol/intent.js +21 -6
- package/build/src/protocol/operations.d.ts +73 -22
- package/build/src/protocol/operations.js +292 -88
- package/build/src/protocol/outcome.d.ts +9 -3
- package/build/src/protocol/outcome.js +3 -1
- package/build/src/protocol/placement.d.ts +15 -4
- package/build/src/protocol/placement.js +58 -25
- package/build/src/protocol/read/status.d.ts +8 -1
- package/build/src/protocol/read/status.js +61 -14
- package/build/src/runtime/proc/line-rpc.d.ts +3 -8
- package/build/src/runtime/proc/line-rpc.js +16 -43
- package/build/src/runtime/proc/run.d.ts +15 -29
- package/build/src/runtime/proc/run.js +113 -105
- package/build/src/runtime/proc/stdio.d.ts +18 -0
- package/build/src/runtime/proc/stdio.js +58 -0
- package/build/src/settings.d.ts +2 -2
- package/build/src/settings.js +8 -8
- package/build/src/settlement/fence.d.ts +0 -4
- package/build/src/settlement/fence.js +0 -9
- package/build/src/settlement/holder.d.ts +11 -1
- package/build/src/settlement/holder.js +25 -0
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/settlement/settle.js +52 -89
- package/build/src/task/board.d.ts +3 -3
- package/build/src/task/board.js +6 -9
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +18 -10
- package/build/src/task/context.d.ts +3 -3
- package/build/src/task/context.js +14 -14
- package/build/src/task/document.d.ts +2 -1
- package/build/src/task/document.js +15 -5
- package/build/src/task/index.d.ts +19 -7
- package/build/src/task/index.js +42 -23
- package/build/src/task/operations.d.ts +15 -9
- package/build/src/task/operations.js +78 -48
- package/build/src/task/query.d.ts +67 -0
- package/build/src/task/query.js +279 -0
- package/build/src/task/store.d.ts +2 -2
- package/build/src/task/store.js +40 -25
- package/build/src/verification/execution.d.ts +4 -2
- package/build/src/verification/execution.js +9 -5
- package/build/src/workspace-place.d.ts +41 -0
- package/build/src/workspace-place.js +386 -0
- package/build/src/world.d.ts +13 -2
- package/build/src/world.js +81 -48
- package/package.json +3 -2
- package/build/src/protocol/read/audit.d.ts +0 -31
- package/build/src/protocol/read/audit.js +0 -55
package/build/src/akuma/body.js
CHANGED
|
@@ -1,33 +1,151 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { abortableDelay } from "./abort.js";
|
|
3
|
+
import { HeldAkumaLeash, appendActivity, beginTurn, breakBody, endTurn, finishBodyIfIdle, heartExists, isHeartAbsent, readHeart, recordSession, recordTellDeliveries, recordTellReceipt, } from "./heart/index.js";
|
|
4
|
+
import { encodeAgentEvent, } from "./provider.js";
|
|
5
|
+
import { resolveProviderExecution } from "./providers/index.js";
|
|
5
6
|
import { BodyRequestPump, clearBodyRequestTransport, settleBodyRequests, } from "./requests.js";
|
|
6
|
-
import {
|
|
7
|
+
import { spawnDetachedProcess, } from "../runtime/proc/run.js";
|
|
7
8
|
const LEASH_RETRY_MS = 25;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const BODY_CONTROL_OBSERVATION_MS = 100;
|
|
10
|
+
export const CONTROL_RESPONSE_MS = 1_000;
|
|
11
|
+
class BodySupervisor {
|
|
12
|
+
paths;
|
|
13
|
+
bodySequence;
|
|
14
|
+
leash;
|
|
15
|
+
signal;
|
|
16
|
+
controller = new AbortController();
|
|
17
|
+
finished = new AbortController();
|
|
18
|
+
observer;
|
|
19
|
+
waiter;
|
|
20
|
+
observation;
|
|
21
|
+
stopping;
|
|
22
|
+
constructor(paths, bodySequence, leash, observation) {
|
|
23
|
+
this.paths = paths;
|
|
24
|
+
this.bodySequence = bodySequence;
|
|
25
|
+
this.leash = leash;
|
|
26
|
+
this.signal = this.controller.signal;
|
|
27
|
+
this.observation = observation;
|
|
28
|
+
this.observer = this.observe();
|
|
29
|
+
}
|
|
30
|
+
static async open(paths, bodySequence, leash) {
|
|
31
|
+
return new BodySupervisor(paths, bodySequence, leash, await readHeart(paths));
|
|
32
|
+
}
|
|
33
|
+
get reason() { return this.stopping; }
|
|
34
|
+
current() { return this.observation; }
|
|
35
|
+
async recordHung(diagnostic, at) {
|
|
36
|
+
await this.leash.recordBodyHung(this.paths, { sequence: this.bodySequence, diagnostic, at });
|
|
37
|
+
}
|
|
38
|
+
async refresh() {
|
|
39
|
+
this.publish(await readHeart(this.paths));
|
|
40
|
+
return this.observation;
|
|
41
|
+
}
|
|
42
|
+
next(after) {
|
|
43
|
+
if (this.observation !== after)
|
|
44
|
+
return Promise.resolve(this.observation);
|
|
45
|
+
return new Promise((resolve) => { this.waiter = resolve; });
|
|
46
|
+
}
|
|
47
|
+
cancel(reason) {
|
|
48
|
+
if (this.stopping !== undefined)
|
|
49
|
+
return;
|
|
50
|
+
this.stopping = reason;
|
|
51
|
+
this.controller.abort(new Error(reason === "control"
|
|
52
|
+
? "Akuma Body interrupted by durable control"
|
|
53
|
+
: "Akuma Heart disappeared"));
|
|
54
|
+
this.waiter?.(this.observation);
|
|
55
|
+
this.waiter = undefined;
|
|
56
|
+
}
|
|
57
|
+
async close() {
|
|
58
|
+
this.finished.abort();
|
|
59
|
+
await this.observer;
|
|
60
|
+
this.waiter = undefined;
|
|
61
|
+
}
|
|
62
|
+
publish(snapshot) {
|
|
63
|
+
this.observation = snapshot;
|
|
64
|
+
this.waiter?.(snapshot);
|
|
65
|
+
this.waiter = undefined;
|
|
66
|
+
}
|
|
67
|
+
async observe() {
|
|
68
|
+
for (;;) {
|
|
69
|
+
const snapshot = this.observation;
|
|
70
|
+
if (!await heartExists(this.paths)) {
|
|
71
|
+
this.cancel("heart-gone");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (bodyControlRequested(snapshot, this.bodySequence)) {
|
|
75
|
+
this.cancel("control");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await abortableDelay(BODY_CONTROL_OBSERVATION_MS, this.finished.signal);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await this.refresh();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (await heartExists(this.paths))
|
|
89
|
+
throw error;
|
|
90
|
+
this.cancel("heart-gone");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
10
95
|
}
|
|
11
96
|
function serializeEffects() {
|
|
12
97
|
let tail = Promise.resolve();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
98
|
+
let failed = false;
|
|
99
|
+
let failure;
|
|
100
|
+
const serialize = async (effect) => {
|
|
101
|
+
const result = tail.then(effect);
|
|
102
|
+
tail = result.then(() => undefined, (error) => {
|
|
103
|
+
if (!failed)
|
|
104
|
+
failure = error;
|
|
105
|
+
failed = true;
|
|
106
|
+
});
|
|
107
|
+
return await result;
|
|
108
|
+
};
|
|
109
|
+
serialize.drain = async () => {
|
|
110
|
+
await tail;
|
|
111
|
+
if (failed)
|
|
112
|
+
throw failure;
|
|
24
113
|
};
|
|
114
|
+
return serialize;
|
|
25
115
|
}
|
|
26
116
|
function missing(error) {
|
|
27
|
-
return error.code === "ENOENT";
|
|
117
|
+
return isHeartAbsent(error) || error.code === "ENOENT";
|
|
118
|
+
}
|
|
119
|
+
async function writeProviderEvent(input, active, event) {
|
|
120
|
+
if (input.supervisor.signal.aborted)
|
|
121
|
+
return;
|
|
122
|
+
const at = input.now();
|
|
123
|
+
if (event.type === "session") {
|
|
124
|
+
await recordSession(input.paths, {
|
|
125
|
+
provider: input.soul.provider.name,
|
|
126
|
+
coordinate: event.coordinate,
|
|
127
|
+
cwd: active.cwd,
|
|
128
|
+
options: active.options,
|
|
129
|
+
admittedAt: at,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
await appendActivity(input.paths, {
|
|
133
|
+
turnSequence: active.turnSequence,
|
|
134
|
+
event: encodeAgentEvent(event),
|
|
135
|
+
at,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function bodyControlRequested(snapshot, bodySequence) {
|
|
139
|
+
return snapshot.stop?.bodySequence === bodySequence
|
|
140
|
+
|| snapshot.pause?.bodySequence === bodySequence;
|
|
28
141
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
142
|
+
async function putDownIfControlled(paths, supervisor, bodySequence, now) {
|
|
143
|
+
if (supervisor.reason === "control" && await heartExists(paths)) {
|
|
144
|
+
await breakBody(paths, { sequence: bodySequence, end: "put-down", at: now() });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function turnRecipe(paths, soul) {
|
|
148
|
+
const latest = (await readHeart(paths)).latestSession;
|
|
31
149
|
const admitted = latest?.provider === soul.provider.name ? latest : undefined;
|
|
32
150
|
const session = admitted?.coordinate;
|
|
33
151
|
return {
|
|
@@ -39,7 +157,7 @@ function turnRecipe(paths, soul) {
|
|
|
39
157
|
async function takeLeash(paths) {
|
|
40
158
|
for (;;) {
|
|
41
159
|
try {
|
|
42
|
-
const leash = HeldAkumaLeash.try(paths);
|
|
160
|
+
const leash = await HeldAkumaLeash.try(paths);
|
|
43
161
|
if (leash !== null)
|
|
44
162
|
return leash;
|
|
45
163
|
}
|
|
@@ -48,52 +166,48 @@ async function takeLeash(paths) {
|
|
|
48
166
|
return null;
|
|
49
167
|
throw error;
|
|
50
168
|
}
|
|
51
|
-
await
|
|
169
|
+
await abortableDelay(LEASH_RETRY_MS);
|
|
52
170
|
}
|
|
53
171
|
}
|
|
54
|
-
async function settlePredecessor(paths) {
|
|
55
|
-
const predecessor = readHeart(paths).latestBody;
|
|
56
|
-
if (predecessor === null)
|
|
57
|
-
return true;
|
|
58
|
-
const probe = probeProcessTree(predecessor.collar);
|
|
59
|
-
if (probe.kind === "gone")
|
|
60
|
-
return true;
|
|
61
|
-
if (probe.kind === "unverifiable")
|
|
62
|
-
return false;
|
|
63
|
-
const evidence = await putDownProcessTree(predecessor.collar);
|
|
64
|
-
return evidence !== "unavailable" && evidence !== "alive-after-sigkill";
|
|
65
|
-
}
|
|
66
172
|
async function startTurnDrive(input) {
|
|
67
|
-
const { cwd, options, session } = turnRecipe(input.paths, input.soul);
|
|
173
|
+
const { cwd, options, session } = await turnRecipe(input.paths, input.soul);
|
|
68
174
|
if (session !== undefined && input.adapter.resume === undefined)
|
|
69
175
|
return { kind: "resume-unsupported" };
|
|
70
|
-
const turn = beginTurn(input.paths, {
|
|
176
|
+
const turn = await beginTurn(input.paths, {
|
|
71
177
|
bodySequence: input.bodySequence,
|
|
72
178
|
startedAt: input.now(),
|
|
73
179
|
...(input.call === undefined ? {} : { call: input.call }),
|
|
74
180
|
});
|
|
75
181
|
const requests = input.soul.confinement.kind === "declared"
|
|
76
|
-
?
|
|
182
|
+
? await BodyRequestPump.open({
|
|
77
183
|
paths: input.paths,
|
|
78
184
|
parent: input.soul,
|
|
79
185
|
bodySequence: input.bodySequence,
|
|
80
186
|
now: input.now,
|
|
81
187
|
spawn: input.runtimeSpawn,
|
|
188
|
+
signal: input.supervisor.signal,
|
|
82
189
|
})
|
|
83
190
|
: null;
|
|
191
|
+
const driveInput = {
|
|
192
|
+
body: input.body,
|
|
193
|
+
launchTells: input.launchTells.map((tell) => ({ id: tell.id, text: tell.body })),
|
|
194
|
+
cwd,
|
|
195
|
+
options,
|
|
196
|
+
signal: input.supervisor.signal,
|
|
197
|
+
...(requests === null ? {} : { requests: { dir: requests.directory } }),
|
|
198
|
+
};
|
|
199
|
+
const setup = session === undefined
|
|
200
|
+
? input.adapter.start({ ...driveInput, session: { kind: "fresh" } })
|
|
201
|
+
: input.adapter.resume({ ...driveInput, session: { kind: "resume", coordinate: session } });
|
|
84
202
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
};
|
|
92
|
-
const selected = session === undefined
|
|
93
|
-
? await input.adapter.start({ ...driveInput, session: { kind: "fresh" } })
|
|
94
|
-
: await input.adapter.resume({ ...driveInput, session: { kind: "resume", coordinate: session } });
|
|
203
|
+
const selected = await setup;
|
|
204
|
+
if (input.supervisor.signal.aborted) {
|
|
205
|
+
await retireProviderCustody(input, turn.sequence, selected);
|
|
206
|
+
await requests?.close();
|
|
207
|
+
return { kind: "stopped" };
|
|
208
|
+
}
|
|
95
209
|
if (input.launchTells.length > 0) {
|
|
96
|
-
recordTellDeliveries(input.paths, input.launchTells.map((tell) => ({
|
|
210
|
+
await recordTellDeliveries(input.paths, input.launchTells.map((tell) => ({
|
|
97
211
|
tellId: tell.id,
|
|
98
212
|
route: "launch",
|
|
99
213
|
turnSequence: turn.sequence,
|
|
@@ -107,40 +221,79 @@ async function startTurnDrive(input) {
|
|
|
107
221
|
...(session === undefined ? {} : { resume: session }) };
|
|
108
222
|
}
|
|
109
223
|
catch (error) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
224
|
+
await requests?.close();
|
|
225
|
+
if (input.supervisor.signal.aborted) {
|
|
226
|
+
if (input.supervisor.reason === "heart-gone")
|
|
227
|
+
throw error;
|
|
228
|
+
return { kind: "stopped" };
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
kind: "failed",
|
|
114
232
|
turnSequence: turn.sequence,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
118
|
-
return { kind: "start-failed", turnSequence: turn.sequence, diagnostic };
|
|
233
|
+
diagnostic: error instanceof Error ? error.message : String(error),
|
|
234
|
+
};
|
|
119
235
|
}
|
|
120
236
|
}
|
|
121
|
-
function
|
|
237
|
+
async function retireProviderCustody(input, turnSequence, drive) {
|
|
238
|
+
const outcome = await Promise.race([
|
|
239
|
+
drive.abort().then(() => ({ kind: "retired" }), (error) => ({ kind: "held", error })),
|
|
240
|
+
abortableDelay(CONTROL_RESPONSE_MS).then(() => ({
|
|
241
|
+
kind: "held",
|
|
242
|
+
error: new Error(`provider custody remained live after ${CONTROL_RESPONSE_MS}ms`),
|
|
243
|
+
})),
|
|
244
|
+
]);
|
|
245
|
+
if (outcome.kind === "retired")
|
|
246
|
+
return;
|
|
247
|
+
if (await heartExists(input.paths)) {
|
|
248
|
+
const diagnostic = outcome.error instanceof Error ? outcome.error.message : String(outcome.error);
|
|
249
|
+
const at = input.now();
|
|
250
|
+
try {
|
|
251
|
+
await input.supervisor.recordHung(diagnostic, at);
|
|
252
|
+
await appendActivity(input.paths, {
|
|
253
|
+
turnSequence,
|
|
254
|
+
event: encodeAgentEvent({ type: "note", text: `Provider custody was not retired: ${diagnostic}` }),
|
|
255
|
+
at,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch { /* undisposed provider custody still owns the leash */ }
|
|
259
|
+
}
|
|
260
|
+
for (;;)
|
|
261
|
+
await abortableDelay(60_000);
|
|
262
|
+
}
|
|
263
|
+
function pumpReceipts(writers) {
|
|
264
|
+
const { input, turnSequence, drive, writeWitness, mayWrite } = writers;
|
|
122
265
|
return (async () => {
|
|
123
266
|
if (drive.receipts === undefined)
|
|
124
267
|
return;
|
|
125
268
|
for await (const receipt of drive.receipts) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
269
|
+
if (!mayWrite())
|
|
270
|
+
return;
|
|
271
|
+
await writeWitness(async () => {
|
|
272
|
+
if (!mayWrite())
|
|
273
|
+
return;
|
|
274
|
+
await recordTellReceipt(input.paths, receipt.evidence === "exact"
|
|
275
|
+
? { ...receipt, receivedAt: input.now() }
|
|
276
|
+
: { ...receipt, turnSequence, receivedAt: input.now() });
|
|
277
|
+
});
|
|
129
278
|
}
|
|
130
279
|
})();
|
|
131
280
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
281
|
+
function observeReceiptFailure(receiptPump) {
|
|
282
|
+
return receiptPump.then(() => new Promise(() => { }), (error) => Promise.reject(error));
|
|
283
|
+
}
|
|
284
|
+
async function submitPendingLiveTells(writers, pending, attempted, tellLive) {
|
|
285
|
+
const { input, turnSequence, drive, writeWitness, mayWrite } = writers;
|
|
286
|
+
for (const tell of pending) {
|
|
136
287
|
if (attempted.has(tell.id))
|
|
137
288
|
continue;
|
|
138
289
|
attempted.add(tell.id);
|
|
139
290
|
const outcome = await writeWitness(async () => {
|
|
140
|
-
const submission = await
|
|
291
|
+
const submission = await tellLive({ id: tell.id, text: tell.body });
|
|
292
|
+
if (!mayWrite())
|
|
293
|
+
return "turn-ended";
|
|
141
294
|
if (submission.kind === "turn-ended")
|
|
142
295
|
return "turn-ended";
|
|
143
|
-
recordTellDeliveries(input.paths, [{
|
|
296
|
+
await recordTellDeliveries(input.paths, [{
|
|
144
297
|
tellId: tell.id,
|
|
145
298
|
route: "live",
|
|
146
299
|
turnSequence,
|
|
@@ -155,81 +308,93 @@ async function submitPendingLiveTells(input, turnSequence, drive, attempted, wri
|
|
|
155
308
|
}
|
|
156
309
|
return "live";
|
|
157
310
|
}
|
|
311
|
+
async function stopActiveDrive(input, active) {
|
|
312
|
+
await retireProviderCustody(input, active.turnSequence, active.drive);
|
|
313
|
+
await active.requests?.close();
|
|
314
|
+
}
|
|
315
|
+
function hasUnattemptedTell(liveTells, tellPump, pending, attempted) {
|
|
316
|
+
return liveTells && tellPump === null && pending.some((tell) => !attempted.has(tell.id));
|
|
317
|
+
}
|
|
318
|
+
function persistProviderEvent(_input, _active, event) {
|
|
319
|
+
return event.type === "session" ? event.coordinate : undefined;
|
|
320
|
+
}
|
|
321
|
+
async function settleCompletion(result, session, requests) {
|
|
322
|
+
await requests?.close();
|
|
323
|
+
return result.kind === "answered" ? { ...result, ...(session === undefined ? {} : { session }) } : result;
|
|
324
|
+
}
|
|
158
325
|
async function consumeTurnDrive(input, active) {
|
|
159
|
-
const { turnSequence, drive, requests,
|
|
326
|
+
const { turnSequence, drive, requests, resume } = active;
|
|
327
|
+
let heart = input.supervisor.current();
|
|
160
328
|
let turnSession = resume;
|
|
161
329
|
const attempted = new Set(input.launchTells.map((tell) => tell.id));
|
|
162
330
|
const writeWitness = serializeEffects();
|
|
163
|
-
|
|
164
|
-
const
|
|
331
|
+
let writesOpen = true;
|
|
332
|
+
const mayWrite = () => writesOpen && !input.supervisor.signal.aborted;
|
|
333
|
+
const writers = { input, turnSequence, drive, writeWitness, mayWrite };
|
|
334
|
+
const receiptPump = pumpReceipts(writers);
|
|
335
|
+
const receiptFailure = observeReceiptFailure(receiptPump);
|
|
165
336
|
const iterator = drive.events[Symbol.asyncIterator]();
|
|
166
337
|
let pending = iterator.next();
|
|
167
338
|
let liveTells = true;
|
|
339
|
+
let tellPump = null;
|
|
340
|
+
let tellObservation = null;
|
|
168
341
|
try {
|
|
169
342
|
for (;;) {
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
await drive.abort();
|
|
174
|
-
}
|
|
175
|
-
finally {
|
|
176
|
-
await draining;
|
|
177
|
-
await receiptPump;
|
|
178
|
-
}
|
|
343
|
+
if (input.supervisor.signal.aborted) {
|
|
344
|
+
writesOpen = false;
|
|
345
|
+
await stopActiveDrive(input, active);
|
|
179
346
|
return { kind: "stopped" };
|
|
180
347
|
}
|
|
181
|
-
if (liveTells) {
|
|
182
|
-
|
|
348
|
+
if (hasUnattemptedTell(liveTells, tellPump, heart.pending, attempted)) {
|
|
349
|
+
if (drive.tell === undefined) {
|
|
350
|
+
writesOpen = false;
|
|
351
|
+
await stopActiveDrive(input, active);
|
|
352
|
+
return { kind: "handoff" };
|
|
353
|
+
}
|
|
354
|
+
tellPump = submitPendingLiveTells(writers, heart.pending, attempted, drive.tell);
|
|
355
|
+
tellObservation = tellPump.then((result) => ({ kind: "tell", result }));
|
|
183
356
|
}
|
|
184
357
|
const next = await Promise.race([
|
|
185
|
-
pending,
|
|
186
|
-
|
|
358
|
+
pending.then((event) => ({ kind: "event", event })),
|
|
359
|
+
input.supervisor.next(heart).then((observation) => ({ kind: "heart", observation })),
|
|
360
|
+
...(tellObservation === null ? [] : [tellObservation]),
|
|
187
361
|
receiptFailure,
|
|
188
362
|
...(requests === null ? [] : [requests.failure]),
|
|
189
363
|
]);
|
|
190
|
-
if (next ===
|
|
364
|
+
if (next.kind === "event" && !next.event.done) {
|
|
365
|
+
await writeProviderEvent(input, active, next.event.value);
|
|
366
|
+
}
|
|
367
|
+
if (next.kind === "heart") {
|
|
368
|
+
heart = next.observation;
|
|
191
369
|
continue;
|
|
192
|
-
if (next.done)
|
|
193
|
-
break;
|
|
194
|
-
const event = next.value;
|
|
195
|
-
const at = input.now();
|
|
196
|
-
if (event.type === "session") {
|
|
197
|
-
turnSession = event.coordinate;
|
|
198
|
-
recordSession(input.paths, {
|
|
199
|
-
provider: input.soul.provider.name,
|
|
200
|
-
coordinate: event.coordinate,
|
|
201
|
-
cwd,
|
|
202
|
-
options,
|
|
203
|
-
admittedAt: at,
|
|
204
|
-
});
|
|
205
370
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
371
|
+
if (next.kind === "tell") {
|
|
372
|
+
tellPump = null;
|
|
373
|
+
tellObservation = null;
|
|
374
|
+
liveTells = next.result === "live";
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (next.event.done)
|
|
378
|
+
break;
|
|
379
|
+
turnSession = persistProviderEvent(input, active, next.event.value) ?? turnSession;
|
|
211
380
|
pending = iterator.next();
|
|
212
381
|
}
|
|
213
382
|
const result = await drive.completion;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
: result;
|
|
383
|
+
if (tellPump !== null)
|
|
384
|
+
await tellPump;
|
|
385
|
+
await writeWitness.drain();
|
|
386
|
+
writesOpen = false;
|
|
387
|
+
return await settleCompletion(result, turnSession, requests);
|
|
220
388
|
}
|
|
221
389
|
catch (error) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
await
|
|
225
|
-
|
|
226
|
-
catch { /* the pump failure owns the turn result */ }
|
|
227
|
-
try {
|
|
228
|
-
await receiptPump;
|
|
390
|
+
writesOpen = false;
|
|
391
|
+
if (input.supervisor.signal.aborted) {
|
|
392
|
+
await stopActiveDrive(input, active);
|
|
393
|
+
return { kind: "stopped" };
|
|
229
394
|
}
|
|
230
|
-
|
|
395
|
+
await retireProviderCustody(input, turnSequence, drive);
|
|
231
396
|
try {
|
|
232
|
-
await
|
|
397
|
+
await requests?.close();
|
|
233
398
|
}
|
|
234
399
|
catch { /* preserve the first pump failure */ }
|
|
235
400
|
return { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error) };
|
|
@@ -244,64 +409,62 @@ async function driveTurn(input) {
|
|
|
244
409
|
active = started;
|
|
245
410
|
}
|
|
246
411
|
catch (error) {
|
|
247
|
-
if (!heartExists(input.paths))
|
|
248
|
-
|
|
412
|
+
if (!await heartExists(input.paths)) {
|
|
413
|
+
input.supervisor.cancel("heart-gone");
|
|
414
|
+
return { kind: "stopped" };
|
|
415
|
+
}
|
|
249
416
|
throw error;
|
|
250
417
|
}
|
|
251
418
|
try {
|
|
252
|
-
|
|
419
|
+
const result = await consumeTurnDrive(input, active);
|
|
420
|
+
return result.kind === "stopped" || result.kind === "handoff"
|
|
421
|
+
? result
|
|
422
|
+
: { ...result, turnSequence: active.turnSequence };
|
|
253
423
|
}
|
|
254
424
|
catch (error) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
await draining;
|
|
425
|
+
if (await heartExists(input.paths)) {
|
|
426
|
+
await active.requests?.close();
|
|
258
427
|
return { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error),
|
|
259
428
|
turnSequence: active.turnSequence };
|
|
260
429
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
catch { /* the heart loss still owns shutdown */ }
|
|
265
|
-
try {
|
|
266
|
-
await draining;
|
|
267
|
-
}
|
|
268
|
-
catch { /* the vanished heart owns the request loss */ }
|
|
269
|
-
return { kind: "heart-gone" };
|
|
430
|
+
input.supervisor.cancel("heart-gone");
|
|
431
|
+
await stopActiveDrive(input, active);
|
|
432
|
+
return { kind: "stopped" };
|
|
270
433
|
}
|
|
271
434
|
}
|
|
272
|
-
function bornSoul(launch, leash, now) {
|
|
273
|
-
const before = readHeart(launch.paths);
|
|
274
|
-
if (before.soul !== null)
|
|
275
|
-
leash.clearPause(launch.paths);
|
|
435
|
+
async function bornSoul(launch, leash, now) {
|
|
436
|
+
const before = await readHeart(launch.paths);
|
|
437
|
+
if (before.soul !== null)
|
|
276
438
|
return before.soul;
|
|
277
|
-
}
|
|
278
439
|
if (launch.seed === undefined)
|
|
279
440
|
throw new Error("Akuma wake has no born soul");
|
|
280
441
|
const candidate = { ...launch.seed, createdAt: now };
|
|
281
|
-
if (leash.birth(launch.paths, candidate, launch.birthSession) === "sealed")
|
|
442
|
+
if (await leash.birth(launch.paths, candidate, launch.birthSession) === "sealed")
|
|
282
443
|
return null;
|
|
283
|
-
const born = readHeart(launch.paths).soul;
|
|
444
|
+
const born = (await readHeart(launch.paths)).soul;
|
|
284
445
|
if (born === null || born.id !== candidate.id) {
|
|
285
446
|
throw new Error("Akuma birth identity does not match its directory");
|
|
286
447
|
}
|
|
287
448
|
return born;
|
|
288
449
|
}
|
|
289
|
-
function launchCwd(launch) {
|
|
450
|
+
async function launchCwd(launch) {
|
|
290
451
|
if (launch.seed !== undefined)
|
|
291
452
|
return launch.seed.cwd;
|
|
292
|
-
const
|
|
293
|
-
const soul = snapshot.soul;
|
|
453
|
+
const soul = (await readHeart(launch.paths)).soul;
|
|
294
454
|
if (soul === null)
|
|
295
455
|
throw new Error("Akuma wake has no born soul");
|
|
296
|
-
return
|
|
297
|
-
? snapshot.latestSession.cwd
|
|
298
|
-
: soul.cwd;
|
|
456
|
+
return (await turnRecipe(launch.paths, soul)).cwd;
|
|
299
457
|
}
|
|
300
|
-
function persistTurn(paths, turnSequence, result, completedAt) {
|
|
301
|
-
endTurn(paths, {
|
|
458
|
+
async function persistTurn(paths, turnSequence, result, completedAt) {
|
|
459
|
+
await endTurn(paths, {
|
|
302
460
|
turnSequence,
|
|
303
461
|
outcome: result.kind === "answered" && result.session !== undefined
|
|
304
|
-
? {
|
|
462
|
+
? {
|
|
463
|
+
kind: "answered",
|
|
464
|
+
session: result.session,
|
|
465
|
+
answer: result.answer,
|
|
466
|
+
...(result.historyId === undefined ? {} : { historyId: result.historyId }),
|
|
467
|
+
}
|
|
305
468
|
: result.kind === "answered"
|
|
306
469
|
? { kind: "failed", diagnostic: "Provider answered without a resumable session" }
|
|
307
470
|
: { kind: "failed", diagnostic: result.diagnostic },
|
|
@@ -311,87 +474,114 @@ function persistTurn(paths, turnSequence, result, completedAt) {
|
|
|
311
474
|
}
|
|
312
475
|
function defaultRuntime() {
|
|
313
476
|
return {
|
|
314
|
-
collar: currentProcessCollar(),
|
|
315
477
|
now: () => new Date().toISOString(),
|
|
316
|
-
|
|
317
|
-
spawnChild: async (launch) => await spawnAkumaBody(launch),
|
|
478
|
+
spawnChild: spawnAkumaBody,
|
|
318
479
|
};
|
|
319
480
|
}
|
|
320
|
-
function
|
|
321
|
-
|
|
481
|
+
async function prepareBodyStart(paths, leash) {
|
|
482
|
+
const before = await readHeart(paths);
|
|
483
|
+
if (before.pause !== null)
|
|
484
|
+
return false;
|
|
485
|
+
if (before.stop === null)
|
|
486
|
+
return true;
|
|
487
|
+
if (before.latestBody?.sequence === before.stop.bodySequence && before.latestBody.end === "put-down") {
|
|
488
|
+
await leash.settleStop(paths);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
await leash.clearStop(paths);
|
|
492
|
+
}
|
|
493
|
+
return true;
|
|
322
494
|
}
|
|
323
|
-
async function
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
495
|
+
async function recoverBodyRequests(input) {
|
|
496
|
+
const { launch, soul, bodySequence, supervisor, runtime } = input;
|
|
497
|
+
try {
|
|
498
|
+
const result = await settleBodyRequests(launch.paths, soul, runtime.now, supervisor.signal);
|
|
499
|
+
await clearBodyRequestTransport(launch.paths);
|
|
500
|
+
if (result === "settled")
|
|
501
|
+
return true;
|
|
502
|
+
await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
if (!supervisor.signal.aborted)
|
|
506
|
+
throw error;
|
|
507
|
+
await clearBodyRequestTransport(launch.paths);
|
|
508
|
+
await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
|
|
509
|
+
}
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
async function runBodyTurns(input) {
|
|
513
|
+
const { launch, soul, adapter, bodySequence, supervisor, runtime } = input;
|
|
514
|
+
let initial = launch.initialBody;
|
|
515
|
+
for (;;) {
|
|
516
|
+
const launchTells = supervisor.current().pending;
|
|
517
|
+
if (supervisor.signal.aborted) {
|
|
518
|
+
await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (initial === undefined && launchTells.length === 0) {
|
|
522
|
+
const finished = await finishBodyIfIdle(launch.paths, { sequence: bodySequence, at: runtime.now() });
|
|
523
|
+
if (finished.kind === "finished")
|
|
524
|
+
return;
|
|
525
|
+
if (finished.kind === "controlled") {
|
|
526
|
+
supervisor.cancel("control");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const result = await driveTurn({
|
|
532
|
+
paths: launch.paths,
|
|
533
|
+
soul,
|
|
534
|
+
adapter,
|
|
535
|
+
bodySequence,
|
|
536
|
+
supervisor,
|
|
537
|
+
runtimeSpawn: runtime.spawnChild ?? spawnAkumaBody,
|
|
538
|
+
body: initial ?? "",
|
|
539
|
+
...(initial === undefined ? {} : { call: initial }),
|
|
540
|
+
launchTells,
|
|
541
|
+
now: runtime.now,
|
|
542
|
+
});
|
|
543
|
+
if (result.kind === "stopped") {
|
|
544
|
+
await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (result.kind === "handoff") {
|
|
548
|
+
await breakBody(launch.paths, { sequence: bodySequence, end: "put-down", at: runtime.now() });
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (result.kind === "resume-unsupported"
|
|
552
|
+
|| await persistTurn(launch.paths, result.turnSequence, result, runtime.now()) === "failed") {
|
|
553
|
+
await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
await supervisor.refresh();
|
|
557
|
+
initial = undefined;
|
|
558
|
+
}
|
|
334
559
|
}
|
|
335
560
|
export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()) {
|
|
336
561
|
const leash = await takeLeash(launch.paths);
|
|
337
562
|
if (leash === null)
|
|
338
563
|
return;
|
|
339
564
|
try {
|
|
340
|
-
const soul = bornSoul(launch, leash, runtime.now());
|
|
565
|
+
const soul = await bornSoul(launch, leash, runtime.now());
|
|
341
566
|
if (soul === null)
|
|
342
567
|
return;
|
|
343
|
-
const selected = adapter ??
|
|
344
|
-
|
|
345
|
-
if (body === null)
|
|
568
|
+
const selected = adapter ?? resolveProviderExecution(soul.provider).adapter;
|
|
569
|
+
if (!await prepareBodyStart(launch.paths, leash))
|
|
346
570
|
return;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
const result = await driveTurn({
|
|
358
|
-
paths: launch.paths,
|
|
359
|
-
soul,
|
|
360
|
-
adapter: selected,
|
|
361
|
-
bodySequence: body.sequence,
|
|
362
|
-
runtimeSpawn: childSpawner(runtime),
|
|
363
|
-
body: initial ?? "",
|
|
364
|
-
...(initial === undefined ? {} : { call: initial }),
|
|
365
|
-
launchTells,
|
|
366
|
-
now: runtime.now,
|
|
367
|
-
});
|
|
368
|
-
if (result.kind === "heart-gone") {
|
|
369
|
-
await runtime.putDownOwnTree(runtime.collar);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
if (result.kind === "stopped") {
|
|
373
|
-
breakBody(launch.paths, { sequence: body.sequence, end: "put-down", at: runtime.now() });
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
if (result.kind === "resume-unsupported") {
|
|
377
|
-
breakBody(launch.paths, { sequence: body.sequence, end: "broke-off", at: runtime.now() });
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
if (result.kind === "start-failed") {
|
|
381
|
-
breakBody(launch.paths, { sequence: body.sequence, end: "broke-off", at: runtime.now() });
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
if (persistTurn(launch.paths, result.turnSequence, result, runtime.now()) === "failed") {
|
|
385
|
-
breakBody(launch.paths, { sequence: body.sequence, end: "broke-off", at: runtime.now() });
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
initial = undefined;
|
|
571
|
+
const body = await leash.recordBody(launch.paths, { leashTakenAt: runtime.now() });
|
|
572
|
+
const supervisor = await BodySupervisor.open(launch.paths, body.sequence, leash);
|
|
573
|
+
const execution = { launch, soul, adapter: selected, bodySequence: body.sequence, supervisor, runtime };
|
|
574
|
+
try {
|
|
575
|
+
if (await recoverBodyRequests(execution))
|
|
576
|
+
await runBodyTurns(execution);
|
|
577
|
+
}
|
|
578
|
+
finally {
|
|
579
|
+
await supervisor.close();
|
|
389
580
|
}
|
|
390
581
|
}
|
|
391
582
|
catch (error) {
|
|
392
|
-
if (heartExists(launch.paths))
|
|
583
|
+
if (await heartExists(launch.paths))
|
|
393
584
|
throw error;
|
|
394
|
-
await runtime.putDownOwnTree(runtime.collar);
|
|
395
585
|
}
|
|
396
586
|
finally {
|
|
397
587
|
leash.release();
|
|
@@ -399,11 +589,16 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
|
|
|
399
589
|
}
|
|
400
590
|
export async function spawnAkumaBody(launch) {
|
|
401
591
|
const encoded = Buffer.from(JSON.stringify(launch), "utf8").toString("base64url");
|
|
402
|
-
|
|
592
|
+
const actorId = launch.seed?.id ?? (await readHeart(launch.paths)).soul?.id;
|
|
593
|
+
if (actorId === undefined)
|
|
594
|
+
throw new Error("Akuma wake has no born soul");
|
|
595
|
+
const owned = await spawnDetachedProcess({
|
|
403
596
|
argv: [process.execPath, ...process.execArgv, fileURLToPath(import.meta.url), encoded],
|
|
404
|
-
cwd: launchCwd(launch),
|
|
597
|
+
cwd: await launchCwd(launch),
|
|
598
|
+
env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
|
|
405
599
|
log: launch.paths.log,
|
|
406
600
|
});
|
|
601
|
+
owned.release();
|
|
407
602
|
}
|
|
408
603
|
async function main() {
|
|
409
604
|
const encoded = process.argv[2];
|