@astrosheep/keiyaku 1.0.0 → 1.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 +73 -56
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms_v2.js +103 -0
- package/build/agents/harness/activity-values.js +54 -0
- package/build/agents/harness/event-channel.js +55 -20
- package/build/agents/harness/events.js +47 -1
- package/build/agents/harness/execution-handle.js +292 -61
- package/build/agents/harness/index.js +103 -121
- package/build/agents/harness/pump.js +36 -47
- package/build/agents/harness/runtime.js +3 -0
- package/build/agents/launch-snapshot_v2.js +348 -0
- package/build/agents/opencode-sdk.js +42 -169
- package/build/agents/providers/claude-agent-sdk.js +240 -79
- package/build/agents/providers/codex-app-server.js +280 -72
- package/build/agents/providers/codex-sdk.js +159 -41
- package/build/agents/providers/event-type-tail.js +16 -0
- package/build/agents/providers/opencode-sdk.js +165 -59
- package/build/agents/providers/pi.js +254 -42
- package/build/agents/selector_v2.js +122 -0
- package/build/cli/commands/akuma.js +120 -42
- package/build/cli/completion.js +6 -7
- package/build/cli/flags.js +36 -10
- package/build/cli/help.js +42 -26
- package/build/cli/index.js +225 -93
- package/build/cli/parse.js +194 -75
- package/build/cli/projection-address.js +11 -0
- package/build/cli/render/address.js +88 -0
- package/build/cli/render/arc.js +10 -10
- package/build/cli/render/call.js +59 -0
- package/build/cli/render/compact-text.js +3 -0
- package/build/cli/render/errors.js +4 -2
- package/build/cli/render/format.js +6 -0
- package/build/cli/render/line-width.js +70 -0
- package/build/cli/render/misc.js +7 -4
- package/build/cli/render/petition.js +4 -3
- package/build/cli/render/projection-activity.js +201 -0
- package/build/cli/render/shared.js +46 -12
- package/build/cli/render/status.js +59 -10
- package/build/cli/render/tool-ledger-rollup.js +74 -0
- package/build/cli/render/tool-presentation.js +97 -0
- package/build/cli/render/wait.js +229 -0
- package/build/cli/skills-install.js +42 -24
- package/build/cli/subagent-guard.js +1 -1
- package/build/config/akuma-loader_v2.js +222 -0
- package/build/config/env-keys.js +0 -4
- package/build/config/env.js +0 -6
- package/build/config/settings.js +325 -12
- package/build/core/addressing.js +224 -0
- package/build/core/amend.js +30 -47
- package/build/core/arc.js +68 -60
- package/build/core/bind.js +105 -25
- package/build/core/call-persist.js +69 -0
- package/build/core/call.js +658 -0
- package/build/core/claim.js +72 -44
- package/build/core/command-io.js +19 -19
- package/build/core/context.js +11 -36
- package/build/core/draft.js +2 -3
- package/build/core/entry.js +101 -6
- package/build/core/execution-coordinate.js +62 -0
- package/build/core/execution-pact.js +141 -0
- package/build/core/forfeit.js +2 -3
- package/build/core/hints.js +10 -10
- package/build/core/ids.js +6 -0
- package/build/core/ledger.js +16 -1
- package/build/core/petition-claim-gates.js +14 -12
- package/build/core/petition-claim.js +1 -0
- package/build/core/petition-run.js +6 -2
- package/build/core/petition.js +63 -21
- package/build/core/places.js +106 -13
- package/build/core/projection/generation.js +412 -0
- package/build/core/projection/heart.js +316 -0
- package/build/core/projection/identity.js +81 -0
- package/build/core/projection/leash.js +87 -0
- package/build/core/projection/mint.js +154 -0
- package/build/core/projection-activity.js +219 -0
- package/build/core/projection-coordinate.js +35 -0
- package/build/core/projection-core.js +69 -463
- package/build/core/projection-generation-continuation.js +31 -0
- package/build/core/projection-generation-execution.js +148 -0
- package/build/core/projection-generation-identity.js +17 -0
- package/build/core/projection-generation-launcher.js +90 -0
- package/build/core/projection-generation-process.js +43 -0
- package/build/core/projection-generation-runner.js +144 -0
- package/build/core/projection-generation-runtime.js +15 -0
- package/build/core/projection-generation-store.js +707 -0
- package/build/core/projection-identity.js +11 -0
- package/build/core/projection-life-observer.js +87 -0
- package/build/core/projection-life-protocol.js +95 -0
- package/build/core/projection-mint.js +96 -24
- package/build/core/projection-runner-lock.js +230 -0
- package/build/core/projection-status.js +207 -107
- package/build/core/projection-tells.js +401 -0
- package/build/core/projection-wait.js +155 -0
- package/build/core/projection-wake.js +109 -231
- package/build/core/queue_v2.js +342 -0
- package/build/core/registry.js +6 -3
- package/build/core/render.js +63 -1
- package/build/core/renew.js +80 -53
- package/build/core/scope.js +186 -137
- package/build/core/seal.js +16 -19
- package/build/core/status.js +88 -25
- package/build/core/stored-agent-event.js +101 -0
- package/build/core/transcripts.js +214 -228
- package/build/core/verdict.js +27 -15
- package/build/core/worktree-bootstrap.js +187 -0
- package/build/core/worktree-path.js +72 -18
- package/build/flow-error.js +4 -6
- package/build/generated/version.js +1 -1
- package/build/git/branches.js +6 -1
- package/build/index.js +8 -5
- package/build/keiyaku.js +1 -3
- package/package.json +6 -5
- package/skills/keiyaku/SKILL.md +6 -47
- package/skills/keiyaku-akuma/SKILL.md +75 -0
- package/build/agents/effective-policy.js +0 -32
- package/build/agents/index.js +0 -114
- package/build/agents/selector.js +0 -28
- package/build/cli/render/summon.js +0 -17
- package/build/cli/render/tell.js +0 -11
- package/build/config/akuma-loader.js +0 -268
- package/build/core/queue.js +0 -73
- package/build/core/summon-persist.js +0 -73
- package/build/core/summon.js +0 -377
|
@@ -1,252 +1,130 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { ProjectionStateError } from "./atomic-publish.js";
|
|
6
1
|
import { FlowError } from "../flow-error.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
19
|
-
}
|
|
20
|
-
function isResumeLockDeadOrStale(lock, now, probePid) {
|
|
21
|
-
try {
|
|
22
|
-
probePid(lock.pid);
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
const created = Date.parse(lock.createdAt);
|
|
28
|
-
return !Number.isFinite(created) || now - created > RESUME_LOCK_STALE_MS;
|
|
29
|
-
}
|
|
30
|
-
function isAdmissionDeadOrHeartbeatStale(projectionDirectory, lock, now, probePid, heartbeatStaleMs) {
|
|
31
|
-
try {
|
|
32
|
-
probePid(lock.pid);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
2
|
+
import { parseResolvedAkumaLaunchSnapshot } from "../agents/launch-snapshot_v2.js";
|
|
3
|
+
import { ProjectionStateError } from "./atomic-publish.js";
|
|
4
|
+
import { applyContinuationEffort, snapshotPendingTells, } from "./projection-generation-continuation.js";
|
|
5
|
+
import { createProjectionExecutionId } from "./projection-generation-identity.js";
|
|
6
|
+
import { startProjectionGeneration } from "./projection-generation-launcher.js";
|
|
7
|
+
import { openProjectionGenerationStore, openProjectionGenerationStoreReadOnly, } from "./projection-generation-store.js";
|
|
8
|
+
import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./projection-generation-launcher.js";
|
|
9
|
+
import { observeProjectionLife } from "./projection-life-observer.js";
|
|
10
|
+
import { observeProjectionRunnerLock } from "./projection-runner-lock.js";
|
|
11
|
+
function readCurrentGeneration(projectionDirectory) {
|
|
12
|
+
const store = openProjectionGenerationStoreReadOnly(projectionDirectory);
|
|
37
13
|
try {
|
|
38
|
-
return
|
|
14
|
+
return store.readCurrentGeneration();
|
|
39
15
|
}
|
|
40
|
-
|
|
41
|
-
|
|
16
|
+
finally {
|
|
17
|
+
store.close();
|
|
42
18
|
}
|
|
43
19
|
}
|
|
44
|
-
function
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fs.appendFileSync(generationLogPath(projectionDirectory, executionId), `${line}\n`);
|
|
61
|
-
}
|
|
62
|
-
function ownLaunchInputs(projectionDirectory, launch = latestGenerationLaunch(projectionDirectory)) {
|
|
63
|
-
if (!launch)
|
|
64
|
-
return {};
|
|
65
|
-
const parent = latestGenerationResult(projectionDirectory);
|
|
20
|
+
function successorFacts(current, snapshot, nowMs) {
|
|
21
|
+
const verdictFacts = current.verdict?.facts;
|
|
22
|
+
const originalInputs = current.launch.facts.inputs;
|
|
23
|
+
const inputs = originalInputs
|
|
24
|
+
&& typeof originalInputs === "object"
|
|
25
|
+
&& !Array.isArray(originalInputs)
|
|
26
|
+
? originalInputs
|
|
27
|
+
: {};
|
|
28
|
+
const continuedInputs = {
|
|
29
|
+
...inputs,
|
|
30
|
+
...(verdictFacts?.session ? { session: verdictFacts.session } : {}),
|
|
31
|
+
...(typeof verdictFacts?.responsePath === "string"
|
|
32
|
+
? { resumePath: verdictFacts.responsePath }
|
|
33
|
+
: {}),
|
|
34
|
+
};
|
|
35
|
+
const { tellFence: _oldFence, ...inherited } = current.launch.facts;
|
|
66
36
|
return {
|
|
67
|
-
...
|
|
68
|
-
|
|
69
|
-
|
|
37
|
+
...inherited,
|
|
38
|
+
createdAt: new Date(nowMs).toISOString(),
|
|
39
|
+
launchedBy: "tell-wake",
|
|
40
|
+
parentExecutionId: current.launch.executionId,
|
|
41
|
+
tellFence: snapshot.tellIds,
|
|
42
|
+
inputs: applyContinuationEffort(continuedInputs, snapshot.effort),
|
|
70
43
|
};
|
|
71
44
|
}
|
|
72
|
-
function overlayEffort(inputs, effort) {
|
|
73
|
-
if (!effort)
|
|
74
|
-
return inputs;
|
|
75
|
-
const rawProfile = inputs.profileData;
|
|
76
|
-
if (!rawProfile || typeof rawProfile !== "object" || Array.isArray(rawProfile))
|
|
77
|
-
throw new ProjectionStateError("generation launch is missing immutable profileData");
|
|
78
|
-
const profile = rawProfile;
|
|
79
|
-
if (profile.provider === "opencode-sdk")
|
|
80
|
-
throw new FlowError("UNSUPPORTED_EFFORT_OVERRIDE", `Provider 'opencode-sdk' does not support effort override '${effort}'.`);
|
|
81
|
-
if (profile.provider === "claude-agent-sdk")
|
|
82
|
-
return { ...inputs, profileData: { ...profile, effort } };
|
|
83
|
-
if (profile.provider === "pi")
|
|
84
|
-
return { ...inputs, profileData: { ...profile, thinkingLevel: effort === "max" ? "xhigh" : effort } };
|
|
85
|
-
if (profile.provider === "codex-sdk") {
|
|
86
|
-
const { effort: _legacyEffort, ...canonicalProfile } = profile;
|
|
87
|
-
const priorThreadOptions = profile.threadOptions && typeof profile.threadOptions === "object" && !Array.isArray(profile.threadOptions) ? profile.threadOptions : {};
|
|
88
|
-
return { ...inputs, profileData: { ...canonicalProfile, threadOptions: { ...priorThreadOptions, modelReasoningEffort: effort === "max" ? "xhigh" : effort } } };
|
|
89
|
-
}
|
|
90
|
-
if (profile.provider === "codex-app-server") {
|
|
91
|
-
const priorInputConfig = inputs.config && typeof inputs.config === "object" && !Array.isArray(inputs.config) ? inputs.config : {};
|
|
92
|
-
const priorProfileConfig = profile.config && typeof profile.config === "object" && !Array.isArray(profile.config) ? profile.config : {};
|
|
93
|
-
const inputConfig = { ...priorInputConfig, [CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY]: effort };
|
|
94
|
-
const profileConfig = { ...priorProfileConfig, [CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY]: effort };
|
|
95
|
-
return { ...inputs, config: inputConfig, profileData: { ...profile, config: profileConfig } };
|
|
96
|
-
}
|
|
97
|
-
throw new ProjectionStateError("generation launch has an unknown provider");
|
|
98
|
-
}
|
|
99
|
-
function pendingReplaySnapshot(projectionDirectory, inputs) {
|
|
100
|
-
const profile = inputs.profileData;
|
|
101
|
-
const pending = ["inbox", "inflight", "submitted"]
|
|
102
|
-
.flatMap((layer) => listTellIds(projectionDirectory, layer).map((tellId) => ({ layer, tellId })))
|
|
103
|
-
.sort((left, right) => left.tellId.localeCompare(right.tellId));
|
|
104
|
-
const effort = pending.reduce((value, item) => {
|
|
105
|
-
const tell = item.layer === "submitted"
|
|
106
|
-
? readTellSubmitted(projectionDirectory, item.tellId)
|
|
107
|
-
: readTellOriginal(projectionDirectory, item.layer, item.tellId);
|
|
108
|
-
return tell.effort ?? value;
|
|
109
|
-
}, undefined);
|
|
110
|
-
return { tellFence: pending.map((item) => item.tellId), ...(effort ? { effort } : {}) };
|
|
111
|
-
}
|
|
112
45
|
export function assertTellEffortSupported(projectionDirectory, effort) {
|
|
113
46
|
if (!effort)
|
|
114
47
|
return;
|
|
115
|
-
const
|
|
116
|
-
if (!
|
|
48
|
+
const current = readCurrentGeneration(projectionDirectory);
|
|
49
|
+
if (!current)
|
|
117
50
|
throw new ProjectionStateError("projection has no immutable generation launch");
|
|
118
|
-
|
|
51
|
+
const inputs = current.launch.facts.inputs;
|
|
52
|
+
if (!inputs || typeof inputs !== "object" || Array.isArray(inputs)) {
|
|
53
|
+
throw new ProjectionStateError("generation launch inputs are missing");
|
|
54
|
+
}
|
|
55
|
+
let launchSnapshot;
|
|
56
|
+
try {
|
|
57
|
+
launchSnapshot = parseResolvedAkumaLaunchSnapshot(inputs.launchSnapshot);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
throw new ProjectionStateError("generation launch has an invalid immutable launchSnapshot", { cause: error });
|
|
61
|
+
}
|
|
62
|
+
const effortOptions = launchSnapshot.akuma.effortOptions;
|
|
63
|
+
if (effortOptions === undefined)
|
|
64
|
+
return;
|
|
65
|
+
if (!effortOptions.includes(effort)) {
|
|
66
|
+
throw new FlowError("INVALID_EFFORT_OVERRIDE", `Effort '${effort}' is not permitted; legal values: ${effortOptions.join(", ")}.`);
|
|
67
|
+
}
|
|
119
68
|
}
|
|
120
|
-
function
|
|
121
|
-
|
|
69
|
+
async function startCommittedSuccessor(projectionDirectory, executionId) {
|
|
70
|
+
const started = await startProjectionGeneration(projectionDirectory, executionId);
|
|
71
|
+
return started.status === "adopted" ? "woken" : "pending";
|
|
122
72
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
73
|
+
export async function tellProjection(projectionDirectory, effort) {
|
|
74
|
+
const nowMs = Date.now();
|
|
75
|
+
const life = observeProjectionLife(projectionDirectory, {
|
|
76
|
+
nowMs,
|
|
77
|
+
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
78
|
+
});
|
|
79
|
+
if (life.phase === "active")
|
|
80
|
+
return "active";
|
|
81
|
+
if (life.phase === "unknown" || life.phase === "minting" || life.phase === "quiescent") {
|
|
82
|
+
return "pending";
|
|
83
|
+
}
|
|
84
|
+
const current = readCurrentGeneration(projectionDirectory);
|
|
85
|
+
if (!current)
|
|
86
|
+
return "pending";
|
|
87
|
+
const snapshot = snapshotPendingTells(projectionDirectory);
|
|
88
|
+
const successorExecutionId = createProjectionExecutionId(nowMs);
|
|
89
|
+
const facts = successorFacts(current, { ...snapshot, ...(effort ? { effort } : {}) }, nowMs);
|
|
90
|
+
const store = openProjectionGenerationStore(projectionDirectory);
|
|
140
91
|
try {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
launchedBy,
|
|
154
|
-
inputs: inheritedInputs,
|
|
155
|
-
});
|
|
156
|
-
if (options.clearStaleAdmission) {
|
|
157
|
-
const staleAdmission = readAdmissionLock(projectionDirectory);
|
|
158
|
-
if (staleAdmission) {
|
|
159
|
-
if (!isAdmissionDeadOrHeartbeatStale(projectionDirectory, staleAdmission, deps.now(), deps.probePid, deps.admissionHeartbeatStaleMs)) {
|
|
160
|
-
throw new ProjectionStateError("refusing to clear a live admission while resuming projection");
|
|
161
|
-
}
|
|
162
|
-
const currentAdmission = readAdmissionLock(projectionDirectory);
|
|
163
|
-
if (!currentAdmission ||
|
|
164
|
-
!staleAdmission.executionId ||
|
|
165
|
-
currentAdmission.executionId !== staleAdmission.executionId ||
|
|
166
|
-
!isAdmissionDeadOrHeartbeatStale(projectionDirectory, currentAdmission, deps.now(), deps.probePid, deps.admissionHeartbeatStaleMs)) {
|
|
167
|
-
throw new ProjectionStateError("refusing to clear a changed or live admission while resuming projection");
|
|
168
|
-
}
|
|
169
|
-
releaseAdmissionLockOwned(projectionDirectory, staleAdmission.executionId);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
if (options.handoffExecutionId)
|
|
173
|
-
releaseAdmissionLockOwned(projectionDirectory, options.handoffExecutionId);
|
|
174
|
-
const absoluteProjectionDirectory = path.resolve(projectionDirectory);
|
|
175
|
-
const logFd = fs.openSync(generationLogPath(absoluteProjectionDirectory, executionId), "a");
|
|
176
|
-
try {
|
|
177
|
-
child = deps.spawn([process.execPath, COMPILED_ENTRY, "__keiyaku-wake", absoluteProjectionDirectory], logFd);
|
|
178
|
-
child.unref();
|
|
92
|
+
if (life.phase === "startup-timeout") {
|
|
93
|
+
const transition = store.supersedeStartupTimeout({
|
|
94
|
+
executionId: current.launch.executionId,
|
|
95
|
+
observedAtMs: nowMs,
|
|
96
|
+
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
97
|
+
diagnostic: { detail: "runner did not adopt before startup timeout" },
|
|
98
|
+
successorExecutionId,
|
|
99
|
+
successorFacts: facts,
|
|
100
|
+
});
|
|
101
|
+
return transition.status === "committed"
|
|
102
|
+
? await startCommittedSuccessor(projectionDirectory, successorExecutionId)
|
|
103
|
+
: "pending";
|
|
179
104
|
}
|
|
180
|
-
|
|
181
|
-
|
|
105
|
+
if (life.phase === "lost") {
|
|
106
|
+
const lock = observeProjectionRunnerLock(projectionDirectory);
|
|
107
|
+
if (lock.state !== "released")
|
|
108
|
+
return lock.state === "held" ? "active" : "pending";
|
|
109
|
+
const transition = store.replaceDeadRunner({
|
|
110
|
+
executionId: current.launch.executionId,
|
|
111
|
+
releasedLockEvidence: { observedAt: new Date(nowMs).toISOString() },
|
|
112
|
+
successorExecutionId,
|
|
113
|
+
successorFacts: facts,
|
|
114
|
+
});
|
|
115
|
+
return transition.status === "committed"
|
|
116
|
+
? await startCommittedSuccessor(projectionDirectory, successorExecutionId)
|
|
117
|
+
: "pending";
|
|
182
118
|
}
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (admission?.executionId === executionId) {
|
|
187
|
-
releaseResumeLockOwned(projectionDirectory, executionId);
|
|
188
|
-
return "started";
|
|
189
|
-
}
|
|
190
|
-
if (admission?.executionId && admission.executionId !== executionId) {
|
|
191
|
-
throw new ProjectionStateError(`admission ownership mismatch: expected ${executionId}, found ${admission.executionId}`);
|
|
192
|
-
}
|
|
193
|
-
deps.sleep(deps.pollMs);
|
|
194
|
-
}
|
|
195
|
-
// A child can acquire between the last poll and failure handling.
|
|
196
|
-
const finalAdmission = readAdmissionLock(projectionDirectory);
|
|
197
|
-
if (finalAdmission?.executionId === executionId) {
|
|
198
|
-
releaseResumeLockOwned(projectionDirectory, executionId);
|
|
199
|
-
return "started";
|
|
200
|
-
}
|
|
201
|
-
if (finalAdmission?.executionId && finalAdmission.executionId !== executionId) {
|
|
202
|
-
throw new ProjectionStateError(`admission ownership mismatch: expected ${executionId}, found ${finalAdmission.executionId}`);
|
|
203
|
-
}
|
|
204
|
-
child?.kill("SIGTERM");
|
|
205
|
-
const parentResult = latestGenerationResult(projectionDirectory);
|
|
206
|
-
publishGenerationResult(projectionDirectory, {
|
|
207
|
-
version: 1, executionId, status: "failed", createdAt: new Date(deps.now()).toISOString(),
|
|
208
|
-
session: parentResult?.session, detail: "wake child did not acquire admission before timeout",
|
|
119
|
+
const transition = store.launchIfSettled({
|
|
120
|
+
executionId: successorExecutionId,
|
|
121
|
+
facts,
|
|
209
122
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return
|
|
123
|
+
if (transition.status !== "committed")
|
|
124
|
+
return "pending";
|
|
125
|
+
return await startCommittedSuccessor(projectionDirectory, successorExecutionId);
|
|
213
126
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (admission?.executionId === executionId) {
|
|
217
|
-
releaseResumeLockOwned(projectionDirectory, executionId);
|
|
218
|
-
return "started";
|
|
219
|
-
}
|
|
220
|
-
// Never alter a competing admission. This launch has not become a generation.
|
|
221
|
-
child?.kill("SIGTERM");
|
|
222
|
-
if (!admission || admission.executionId === undefined) {
|
|
223
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
224
|
-
publishGenerationResult(projectionDirectory, { version: 1, executionId, status: "failed", createdAt: new Date(deps.now()).toISOString(), detail });
|
|
225
|
-
appendWakeLog(projectionDirectory, executionId, `wake spawn failure: ${detail}`);
|
|
226
|
-
}
|
|
227
|
-
releaseResumeLockOwned(projectionDirectory, executionId);
|
|
228
|
-
if (error instanceof ProjectionStateError)
|
|
229
|
-
throw error;
|
|
230
|
-
return "failed";
|
|
127
|
+
finally {
|
|
128
|
+
store.close();
|
|
231
129
|
}
|
|
232
130
|
}
|
|
233
|
-
export function resumeProjectionOverride(projectionDirectory, dependencies, effort) {
|
|
234
|
-
const phase = deriveProjectionPhase(projectionDirectory, (dependencies?.now ?? Date.now)());
|
|
235
|
-
if (phase === "active")
|
|
236
|
-
return "active";
|
|
237
|
-
if (phase === "done")
|
|
238
|
-
return "done";
|
|
239
|
-
// force allows recovery only after this function has observed a non-active,
|
|
240
|
-
// non-done phase. The orchestrator owns resume before it clears stale admission.
|
|
241
|
-
return wakeProjection(projectionDirectory, "resume-override", { force: true, clearStaleAdmission: true, effort, dependencies });
|
|
242
|
-
}
|
|
243
|
-
export function tellProjection(projectionDirectory, effort) {
|
|
244
|
-
const phase = deriveProjectionPhase(projectionDirectory, Date.now());
|
|
245
|
-
if (phase === "active")
|
|
246
|
-
return "active";
|
|
247
|
-
if (phase === "failed" || phase === "dismissed")
|
|
248
|
-
return "terminal";
|
|
249
|
-
if (phase === "lost")
|
|
250
|
-
return "lost";
|
|
251
|
-
return wakeProjection(projectionDirectory, "tell-wake", { effort }) === "started" ? "woken" : "pending";
|
|
252
|
-
}
|