@astrosheep/keiyaku 2.9.11 → 2.9.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/README.md +79 -85
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +17 -4
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +4 -4
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +14 -10
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/handler.js +4 -5
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +2 -2
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/compose/handler.js +18 -0
- package/build/cli/commands/task/compose/meta.js +10 -0
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +48 -12
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +14 -8
- package/build/cli/parse-flags.js +60 -21
- package/build/cli/parse-metadata.js +10 -4
- package/build/cli/parse-selectors.js +22 -4
- package/build/cli/parse.js +43 -30
- package/build/cli/projection-address.js +7 -7
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +33 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/addressing.js +3 -3
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +46 -33
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +43 -6
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +60 -27
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +19 -14
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +132 -76
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +24 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +8 -8
- package/build/core/projection/projection-kill.js +20 -10
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +48 -29
- package/build/core/projection/projection-wake.js +28 -88
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/claim-evidence.js +1 -1
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +464 -349
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +57 -9
- package/build/core/repository-ledger/write-transaction.js +24 -17
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/run-control/sqlite-process-lifetime-lock.js +174 -0
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +328 -0
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +4 -3
- package/build/core/task/settlement-git.js +70 -30
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-git-store.js +1 -0
- package/build/core/task/task-store-repository.js +2 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +15 -2
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +2 -2
- package/skills/keiyaku-akuma/SKILL.md +27 -21
- package/skills/keiyaku-task/SKILL.md +61 -5
- package/skills/keiyaku-workflow/SKILL.md +81 -26
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -382
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -10,7 +10,7 @@ export function launchIfSettled(database, readOnly, input) {
|
|
|
10
10
|
const executionId = assertExecutionId(input.executionId);
|
|
11
11
|
const facts = validateLaunchFacts(input.facts, "launch facts");
|
|
12
12
|
return writeTransition(database, readOnly, () => {
|
|
13
|
-
const history = readValidatedHistory(database);
|
|
13
|
+
const history = readValidatedHistory(database, readOnly);
|
|
14
14
|
if (history.records.some((record) => record.kind === "launch" && record.executionId === executionId)) {
|
|
15
15
|
return rejected("execution-id-already-used", history.current);
|
|
16
16
|
}
|
|
@@ -27,7 +27,7 @@ export function failInterruptWakeIfSettled(database, readOnly, input) {
|
|
|
27
27
|
const successorFacts = validateLaunchFacts(input.successorFacts, "interrupt wake successor launch facts");
|
|
28
28
|
const terminalAt = assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
29
29
|
return writeTransition(database, readOnly, () => {
|
|
30
|
-
const history = readValidatedHistory(database);
|
|
30
|
+
const history = readValidatedHistory(database, readOnly);
|
|
31
31
|
const current = history.current;
|
|
32
32
|
if (!current)
|
|
33
33
|
return rejected("no-current-launch", null);
|
|
@@ -56,7 +56,7 @@ export function adoptionIfCurrent(database, readOnly, input) {
|
|
|
56
56
|
const executionId = assertExecutionId(input.executionId);
|
|
57
57
|
const facts = validateAdoptionFacts(input.facts, "adoption facts");
|
|
58
58
|
return writeTransition(database, readOnly, () => {
|
|
59
|
-
const history = readValidatedHistory(database);
|
|
59
|
+
const history = readValidatedHistory(database, readOnly);
|
|
60
60
|
const current = history.current;
|
|
61
61
|
if (!current)
|
|
62
62
|
return rejected("no-current-launch", null);
|
|
@@ -73,7 +73,7 @@ export function requestKillIfOpen(database, readOnly, input) {
|
|
|
73
73
|
const executionId = assertExecutionId(input.executionId);
|
|
74
74
|
const killedAt = assertIsoTimestamp(input.killedAt, "killedAt");
|
|
75
75
|
return writeTransition(database, readOnly, () => {
|
|
76
|
-
const history = readValidatedHistory(database);
|
|
76
|
+
const history = readValidatedHistory(database, readOnly);
|
|
77
77
|
const current = history.current;
|
|
78
78
|
if (!current)
|
|
79
79
|
return rejected("no-current-launch", null);
|
|
@@ -93,7 +93,7 @@ export function requestInterruptIfOpen(database, readOnly, input) {
|
|
|
93
93
|
const executionId = assertExecutionId(input.executionId);
|
|
94
94
|
const interruptedAt = assertIsoTimestamp(input.interruptedAt, "interruptedAt");
|
|
95
95
|
return writeTransition(database, readOnly, () => {
|
|
96
|
-
const history = readValidatedHistory(database);
|
|
96
|
+
const history = readValidatedHistory(database, readOnly);
|
|
97
97
|
const current = history.current;
|
|
98
98
|
if (!current)
|
|
99
99
|
return rejected("no-current-launch", null);
|
|
@@ -111,6 +111,7 @@ export function requestInterruptIfOpen(database, readOnly, input) {
|
|
|
111
111
|
}
|
|
112
112
|
export function runnerKilledIfRequested(database, readOnly, input) {
|
|
113
113
|
const executionId = assertExecutionId(input.executionId);
|
|
114
|
+
const terminalAt = assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
114
115
|
const callerFacts = cloneFacts(input.facts ?? {}, "killed verdict facts");
|
|
115
116
|
if ("state" in callerFacts) {
|
|
116
117
|
throw new ProjectionGenerationStoreError("killed verdict facts.state is reserved");
|
|
@@ -121,8 +122,11 @@ export function runnerKilledIfRequested(database, readOnly, input) {
|
|
|
121
122
|
if ("killedAt" in callerFacts) {
|
|
122
123
|
throw new ProjectionGenerationStoreError("killed verdict facts.killedAt is reserved");
|
|
123
124
|
}
|
|
125
|
+
if ("terminalAt" in callerFacts) {
|
|
126
|
+
throw new ProjectionGenerationStoreError("killed verdict facts.terminalAt is reserved");
|
|
127
|
+
}
|
|
124
128
|
return writeTransition(database, readOnly, () => {
|
|
125
|
-
const history = readValidatedHistory(database);
|
|
129
|
+
const history = readValidatedHistory(database, readOnly);
|
|
126
130
|
const current = history.current;
|
|
127
131
|
if (!current)
|
|
128
132
|
return rejected("no-current-launch", null);
|
|
@@ -137,6 +141,7 @@ export function runnerKilledIfRequested(database, readOnly, input) {
|
|
|
137
141
|
state: "killed",
|
|
138
142
|
killIntentSeq: current.killIntent.seq,
|
|
139
143
|
killedAt: current.killIntent.facts.killedAt,
|
|
144
|
+
terminalAt,
|
|
140
145
|
}, "killed verdict facts");
|
|
141
146
|
return committed([appendRecord(database, "verdict", executionId, facts, history.records.length + 1)]);
|
|
142
147
|
});
|
|
@@ -153,7 +158,7 @@ export function runnerInterruptedIfRequested(database, readOnly, input) {
|
|
|
153
158
|
? undefined
|
|
154
159
|
: assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
155
160
|
return writeTransition(database, readOnly, () => {
|
|
156
|
-
const history = readValidatedHistory(database);
|
|
161
|
+
const history = readValidatedHistory(database, readOnly);
|
|
157
162
|
const current = history.current;
|
|
158
163
|
if (!current)
|
|
159
164
|
return rejected("no-current-launch", null);
|
|
@@ -184,7 +189,7 @@ export function runnerVerdictIfOpen(database, readOnly, input) {
|
|
|
184
189
|
const callerFacts = cloneFacts(input.facts ?? {}, "runner verdict facts");
|
|
185
190
|
const terminalAt = assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
186
191
|
return writeTransition(database, readOnly, () => {
|
|
187
|
-
const history = readValidatedHistory(database);
|
|
192
|
+
const history = readValidatedHistory(database, readOnly);
|
|
188
193
|
const current = history.current;
|
|
189
194
|
if (!current)
|
|
190
195
|
return rejected("no-current-launch", null);
|
|
@@ -221,6 +226,7 @@ export function runnerVerdictIfOpen(database, readOnly, input) {
|
|
|
221
226
|
}
|
|
222
227
|
export function settleKillIfRunnerDead(database, readOnly, input) {
|
|
223
228
|
const executionId = assertExecutionId(input.executionId);
|
|
229
|
+
const terminalAt = assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
224
230
|
const releasedLockEvidence = cloneFacts(input.releasedLockEvidence, "released runner-lock evidence");
|
|
225
231
|
if (Object.keys(releasedLockEvidence).length === 0) {
|
|
226
232
|
throw new ProjectionGenerationStoreError("released runner-lock evidence must not be empty");
|
|
@@ -235,8 +241,11 @@ export function settleKillIfRunnerDead(database, readOnly, input) {
|
|
|
235
241
|
if ("killedAt" in callerFacts) {
|
|
236
242
|
throw new ProjectionGenerationStoreError("killed verdict facts.killedAt is reserved");
|
|
237
243
|
}
|
|
244
|
+
if ("terminalAt" in callerFacts) {
|
|
245
|
+
throw new ProjectionGenerationStoreError("killed verdict facts.terminalAt is reserved");
|
|
246
|
+
}
|
|
238
247
|
return writeTransition(database, readOnly, () => {
|
|
239
|
-
const history = readValidatedHistory(database);
|
|
248
|
+
const history = readValidatedHistory(database, readOnly);
|
|
240
249
|
const current = history.current;
|
|
241
250
|
if (!current)
|
|
242
251
|
return rejected("no-current-launch", null);
|
|
@@ -251,6 +260,7 @@ export function settleKillIfRunnerDead(database, readOnly, input) {
|
|
|
251
260
|
state: "killed",
|
|
252
261
|
killIntentSeq: current.killIntent.seq,
|
|
253
262
|
killedAt: current.killIntent.facts.killedAt,
|
|
263
|
+
terminalAt,
|
|
254
264
|
releasedLockEvidence,
|
|
255
265
|
}, "killed verdict facts");
|
|
256
266
|
return committed([appendRecord(database, "verdict", executionId, facts, history.records.length + 1)]);
|
|
@@ -264,7 +274,7 @@ export function verdictIfOpen(database, readOnly, input) {
|
|
|
264
274
|
throw new ProjectionGenerationStoreError(`verdict '${input.verdict}' is reserved to its runner-owned transition`);
|
|
265
275
|
}
|
|
266
276
|
return writeTransition(database, readOnly, () => {
|
|
267
|
-
const history = readValidatedHistory(database);
|
|
277
|
+
const history = readValidatedHistory(database, readOnly);
|
|
268
278
|
const current = history.current;
|
|
269
279
|
if (!current)
|
|
270
280
|
return rejected("no-current-launch", null);
|
|
@@ -298,7 +308,7 @@ export function supersedeStartupTimeout(database, readOnly, input) {
|
|
|
298
308
|
? undefined
|
|
299
309
|
: assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
300
310
|
return writeTransition(database, readOnly, () => {
|
|
301
|
-
const history = readValidatedHistory(database);
|
|
311
|
+
const history = readValidatedHistory(database, readOnly);
|
|
302
312
|
const current = history.current;
|
|
303
313
|
if (!current)
|
|
304
314
|
return rejected("no-current-launch", null);
|
|
@@ -346,7 +356,7 @@ export function settleStartupFailure(database, readOnly, input) {
|
|
|
346
356
|
? undefined
|
|
347
357
|
: assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
348
358
|
return writeTransition(database, readOnly, () => {
|
|
349
|
-
const history = readValidatedHistory(database);
|
|
359
|
+
const history = readValidatedHistory(database, readOnly);
|
|
350
360
|
const current = history.current;
|
|
351
361
|
if (!current)
|
|
352
362
|
return rejected("no-current-launch", null);
|
|
@@ -385,7 +395,7 @@ export function replaceDeadRunner(database, readOnly, input) {
|
|
|
385
395
|
? undefined
|
|
386
396
|
: assertIsoTimestamp(input.terminalAt, "terminalAt");
|
|
387
397
|
return writeTransition(database, readOnly, () => {
|
|
388
|
-
const history = readValidatedHistory(database);
|
|
398
|
+
const history = readValidatedHistory(database, readOnly);
|
|
389
399
|
const current = history.current;
|
|
390
400
|
if (!current)
|
|
391
401
|
return rejected("no-current-launch", null);
|
|
@@ -426,7 +436,7 @@ export function handoff(database, readOnly, input) {
|
|
|
426
436
|
const tellFence = validateTellFence([...input.tellFence], "handoff tellFence");
|
|
427
437
|
const successorFacts = validateLaunchFacts({ ...successorFactsBase, tellFence }, "handoff successor launch facts");
|
|
428
438
|
return writeTransition(database, readOnly, () => {
|
|
429
|
-
const history = readValidatedHistory(database);
|
|
439
|
+
const history = readValidatedHistory(database, readOnly);
|
|
430
440
|
const current = history.current;
|
|
431
441
|
if (!current)
|
|
432
442
|
return rejected("no-current-launch", null);
|
|
@@ -10,18 +10,18 @@ export { resolveProjectionCoordinate } from "./projection-coordinate.js";
|
|
|
10
10
|
export { appendGenerationEvent, executionEventsPath, isProjectionId, isPublishedProjection, isoFromNowMs, projectionDir, projectionDirAtCoordinate, projectionRoot, readProjectionEvents, readGenerationEventsWithRetention, } from "./projection-core.js";
|
|
11
11
|
export { readProjectionIdentity } from "./projection-identity.js";
|
|
12
12
|
export { interruptProjectionGeneration, killProjectionGeneration } from "./projection-kill.js";
|
|
13
|
+
export { openProjectionGenerationDoorbell, projectionGenerationDoorbellAddress, ringProjectionGenerationDoorbell, } from "./generation/projection-generation-doorbell.js";
|
|
13
14
|
export { observeProjectionLife, observeProjectionLifeSnapshot, } from "./projection-life-observer.js";
|
|
14
15
|
export { mintProjection, mintRepositoryProjection, ProjectionMintCollisionError, } from "./projection-mint.js";
|
|
15
|
-
export {
|
|
16
|
-
export { hasStrandableProjectionGeneration, prioritizeProjectionStatusRows, readAkumaNonterminalProjectionStatus, readExactProjectionStatus, readOperationalProjectionStatusBoard, } from "./projection-status.js";
|
|
16
|
+
export { hasStrandableProjectionGeneration, prioritizeProjectionStatusRows, paginateProjectionStatusRoster, triageProjectionStatusRows, readAkumaNonterminalProjectionStatus, readExactProjectionStatus, readOperationalProjectionStatusBoard, readProjectionRosterStatusBoard, selectProjectionStatus, } from "./projection-status.js";
|
|
17
17
|
export { PROJECTION_HISTORY_FILTER_COMPLETION, PROJECTION_HISTORY_FILTER_FIELDS, PROJECTION_HISTORY_FILTER_GRAMMAR, parseProjectionHistoryFilter, parseProjectionHistorySort, readProjectionHistoryAddress, readExactProjectionHistory, readProjectionHistory, resolveProjectionHistoryAddress, selectProjectionHistory, } from "./projection-history.js";
|
|
18
18
|
export { projectTerminalFailure, } from "./projection-terminal-failure.js";
|
|
19
19
|
export { snapshotCommissionProjectionIds, snapshotCommissionProjectionIdsAtCoordinate, waitForProjection, waitForProjectionSet, } from "./projection-wait.js";
|
|
20
20
|
export { assertTellEffortSupported, assertProjectionTellable, tellProjection, } from "./projection-wake.js";
|
|
21
21
|
export { openProjectionGenerationStore, openProjectionGenerationStoreReadOnly, ProjectionGenerationStore, } from "./generation/store.js";
|
|
22
|
-
export { createProjectionExecutionId } from "./generation/projection-generation-identity.js";
|
|
23
|
-
export {
|
|
24
|
-
export { projectionGenerationRunnerArgv,
|
|
22
|
+
export { createProjectionExecutionId, projectionRunnerLeasePath, } from "./generation/projection-generation-identity.js";
|
|
23
|
+
export { startProjectionGeneration } from "./generation/projection-generation-launcher.js";
|
|
24
|
+
export { projectionGenerationRunnerArgv, prepareProjectionGenerationRunner, } from "./generation/projection-generation-process.js";
|
|
25
25
|
export { runProjectionGeneration, } from "./generation/projection-generation-runner.js";
|
|
26
26
|
export { executeProjectionGeneration, prepareProjectionGenerationExecution, } from "./generation/projection-generation-execution.js";
|
|
27
27
|
export { runProjectionGenerationRuntime } from "./generation/projection-generation-runtime.js";
|
|
@@ -3,14 +3,18 @@ import * as path from "node:path";
|
|
|
3
3
|
import { isErrnoException } from "../../errno.js";
|
|
4
4
|
import { resolveProjectionCoordinate } from "./projection-coordinate.js";
|
|
5
5
|
import { ProjectionStateError, } from "../atomic-publish.js";
|
|
6
|
+
import { isAkumaName } from "./akuma-name.js";
|
|
6
7
|
export { atomicPublishFile, ProjectionStateError, randomHex8 } from "../atomic-publish.js";
|
|
7
8
|
export { isTellCausallyConsumed, } from "./tell/model.js";
|
|
8
9
|
export { bindTellLaunchReservation, claimAllInbox, claimAndReserveFencedTells, claimFencedTells, listTellLaunchBatch, listTellLaunchAdmissions, isTellLaunchAdmissionMember, claimTellToInflight, listTellIds, markTellSubmitted, recordTellReceiptCapabilities, recordTellReceipt, readTellReceiptCapabilities, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./tell/store.js";
|
|
9
10
|
/** Relative segments for `.keiyaku/projection`. */
|
|
10
11
|
export const PROJECTION_ROOT_SEGMENTS = [".keiyaku", "projection"];
|
|
11
|
-
|
|
12
|
+
const PROJECTION_HEX_PATTERN = /^[0-9a-f]{8}$/;
|
|
12
13
|
export function isProjectionId(value) {
|
|
13
|
-
|
|
14
|
+
const slash = value.indexOf("/");
|
|
15
|
+
if (slash <= 0 || slash !== value.lastIndexOf("/"))
|
|
16
|
+
return false;
|
|
17
|
+
return isAkumaName(value.slice(0, slash)) && PROJECTION_HEX_PATTERN.test(value.slice(slash + 1));
|
|
14
18
|
}
|
|
15
19
|
/** Derive one projection storage root from already-resolved repository authority. */
|
|
16
20
|
export function projectionRootAtCoordinate(coordinate) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { claimAllInbox, isTellLaunchAdmissionMember, listTellLaunchAdmissions, listTellIds, markTellSubmitted, bindTellLaunchReservation, recordTellReceiptCapabilities, recordTellReceipt, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
|
|
2
|
-
import { openProjectionTellObserver
|
|
2
|
+
import { openProjectionTellObserver } from "./generation/database.js";
|
|
3
3
|
const LIVE_TELL_DELIVERY_THROTTLE_MS = 200;
|
|
4
4
|
export function renderProjectionTellFrame(tellId, text) {
|
|
5
5
|
void tellId;
|
|
@@ -8,18 +8,17 @@ import { parseStructuredQuery, STRUCTURED_QUERY_COMPLETION, STRUCTURED_QUERY_GRA
|
|
|
8
8
|
import { selectValidStoredAgentEvents } from "../stored-agent-event.js";
|
|
9
9
|
import { extractTerminalTimestamp } from "./generation/model.js";
|
|
10
10
|
import { readProjectionAlias } from "./projection-alias.js";
|
|
11
|
-
import { foldPublicProjectionActivity } from "./projection-activity.js";
|
|
11
|
+
import { foldPublicProjectionActivity, } from "./projection-activity.js";
|
|
12
12
|
import { isProjectionId, projectionDir, projectionRoot, readGenerationEventsWithRetention, readProjectionEvents } from "./projection-core.js";
|
|
13
13
|
import { projectionCoordinateRoot } from "./projection-coordinate.js";
|
|
14
14
|
import { readProjectionIdentity } from "./projection-identity.js";
|
|
15
15
|
import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
|
|
16
|
-
import { selectCurrentGeneration } from "./
|
|
17
|
-
import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
|
|
16
|
+
import { selectCurrentGeneration } from "./generation/protocol.js";
|
|
18
17
|
export const PROJECTION_HISTORY_FILTER_FIELDS = ["contract", "akuma", "role", "outcome", "created", "updated"];
|
|
19
18
|
export const PROJECTION_HISTORY_FILTER_GRAMMAR = STRUCTURED_QUERY_GRAMMAR;
|
|
20
19
|
export const PROJECTION_HISTORY_FILTER_COMPLETION = STRUCTURED_QUERY_COMPLETION;
|
|
21
20
|
const HISTORY_FIELDS = new Set(PROJECTION_HISTORY_FILTER_FIELDS);
|
|
22
|
-
const HISTORY_ROLES = new Set(["call", "
|
|
21
|
+
const HISTORY_ROLES = new Set(["call", "fork", "unknown"]);
|
|
23
22
|
const HISTORY_OUTCOMES = new Set(["minting", "startup-timeout", "unknown", "out", "lost", "done", "killed", "dismissed", "failed", "dead", "incompatible"]);
|
|
24
23
|
const RELATIVE_TIME = /^now-([1-9][0-9]*)([smhdw])$/;
|
|
25
24
|
const UNIT_MS = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000 };
|
|
@@ -119,7 +118,7 @@ function outcomeFromLife(phase) {
|
|
|
119
118
|
function roleFromRecords(records) {
|
|
120
119
|
const inputs = records?.find((record) => record.kind === "launch")?.facts.inputs;
|
|
121
120
|
return inputs && typeof inputs === "object" && !Array.isArray(inputs) && typeof inputs.sourceArtifactId === "string"
|
|
122
|
-
? "
|
|
121
|
+
? "fork"
|
|
123
122
|
: records?.some((record) => record.kind === "launch") ? "call" : "unknown";
|
|
124
123
|
}
|
|
125
124
|
async function artifactFromVerdict(cwd, artifactId, executionId, index) {
|
|
@@ -235,10 +234,9 @@ async function readRun(artifactRoot, directory, id, alias, artifactIndex, addres
|
|
|
235
234
|
const diagnostics = [];
|
|
236
235
|
const snapshot = observeProjectionLifeSnapshot(directory, {
|
|
237
236
|
nowMs: Date.now(),
|
|
238
|
-
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
239
237
|
});
|
|
240
238
|
if (snapshot.compatibility === "incompatible") {
|
|
241
|
-
return { id, ...(alias ? { alias } : {}), akuma: null, provider: null, pid: null, role: "unknown", contractId: null, outcome: "incompatible", mintedAt: null, terminalAt: null, generation: null, activityAt: null, artifact: null, artifacts: [], activityRetention: "incompatible", activityCount: 0, activityRange: null, telemetry: null, consequences: null, diagnostics: [snapshot.diagnostic], activity: null };
|
|
239
|
+
return { id, ...(alias ? { alias } : {}), akuma: null, provider: null, pid: null, role: "unknown", contractId: null, outcome: "incompatible", mintedAt: null, terminalAt: null, generation: null, activityAt: null, artifact: null, artifacts: [], activityRetention: "incompatible", activityCount: 0, activityRange: null, telemetry: null, consequences: null, diagnostics: [snapshot.diagnostic], terminalAnswer: null, activity: null };
|
|
242
240
|
}
|
|
243
241
|
let identity;
|
|
244
242
|
try {
|
|
@@ -296,6 +294,9 @@ async function readRun(artifactRoot, directory, id, alias, artifactIndex, addres
|
|
|
296
294
|
telemetry: telemetryFromActivity(activity),
|
|
297
295
|
consequences: null,
|
|
298
296
|
diagnostics,
|
|
297
|
+
terminalAnswer: typeof current?.verdict?.facts.finalMessage === "string"
|
|
298
|
+
? { kind: "text", text: current.verdict.facts.finalMessage }
|
|
299
|
+
: null,
|
|
299
300
|
activity,
|
|
300
301
|
};
|
|
301
302
|
}
|
|
@@ -384,7 +385,6 @@ async function retainedArtifactRelationships(root, artifactId, preferredAkuma) {
|
|
|
384
385
|
return [];
|
|
385
386
|
const snapshot = observeProjectionLifeSnapshot(directory, {
|
|
386
387
|
nowMs: Date.now(),
|
|
387
|
-
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
388
388
|
});
|
|
389
389
|
if (snapshot.compatibility === "incompatible" || !snapshot.records)
|
|
390
390
|
return [];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { openProjectionGenerationStore } from "./generation/store.js";
|
|
2
|
-
import {
|
|
2
|
+
import { acquireRunnerLostProof, observeRunnerLease, RunnerLeaseUnavailableError, } from "../run-control/runner-lease.js";
|
|
3
|
+
import { projectionRunnerLeasePath } from "./generation/projection-generation-identity.js";
|
|
4
|
+
import { ringProjectionGenerationDoorbell } from "./generation/projection-generation-doorbell.js";
|
|
3
5
|
/** The runner observes this durable intent and owns native termination. */
|
|
4
6
|
export function interruptProjectionGeneration(projectionDirectory, nowMs = Date.now()) {
|
|
5
7
|
const store = openProjectionGenerationStore(projectionDirectory);
|
|
@@ -11,19 +13,23 @@ export function interruptProjectionGeneration(projectionDirectory, nowMs = Date.
|
|
|
11
13
|
if (current.verdict)
|
|
12
14
|
return { status: "already-closed", executionId: current.launch.executionId };
|
|
13
15
|
const executionId = current.launch.executionId;
|
|
14
|
-
if (!current.adoption ||
|
|
16
|
+
if (!current.adoption || observeRunnerLease(projectionRunnerLeasePath(projectionDirectory)).state !== "held") {
|
|
15
17
|
return { status: "already-closed", executionId };
|
|
16
18
|
}
|
|
17
19
|
const request = store.requestInterruptIfOpen({
|
|
18
20
|
executionId,
|
|
19
21
|
interruptedAt: new Date(nowMs).toISOString(),
|
|
20
22
|
});
|
|
21
|
-
if (request.status === "committed")
|
|
23
|
+
if (request.status === "committed") {
|
|
24
|
+
ringProjectionGenerationDoorbell(store.readIdentity(), executionId);
|
|
22
25
|
return { status: "accepted", executionId };
|
|
26
|
+
}
|
|
23
27
|
if (request.reason === "not-current")
|
|
24
28
|
continue;
|
|
25
|
-
if (request.reason === "interrupt-requested")
|
|
29
|
+
if (request.reason === "interrupt-requested") {
|
|
30
|
+
ringProjectionGenerationDoorbell(store.readIdentity(), executionId);
|
|
26
31
|
return { status: "accepted", executionId };
|
|
32
|
+
}
|
|
27
33
|
return { status: "already-closed", executionId };
|
|
28
34
|
}
|
|
29
35
|
}
|
|
@@ -31,7 +37,7 @@ export function interruptProjectionGeneration(projectionDirectory, nowMs = Date.
|
|
|
31
37
|
store.close();
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
|
-
function settleReleasedLeash(store, projectionDirectory, executionId) {
|
|
40
|
+
function settleReleasedLeash(store, projectionDirectory, executionId, terminalAt) {
|
|
35
41
|
const current = store.readCurrentGeneration();
|
|
36
42
|
if (current?.launch.executionId !== executionId
|
|
37
43
|
|| current.verdict) {
|
|
@@ -39,10 +45,10 @@ function settleReleasedLeash(store, projectionDirectory, executionId) {
|
|
|
39
45
|
}
|
|
40
46
|
let deathEvidence;
|
|
41
47
|
try {
|
|
42
|
-
deathEvidence =
|
|
48
|
+
deathEvidence = acquireRunnerLostProof(projectionRunnerLeasePath(projectionDirectory));
|
|
43
49
|
}
|
|
44
50
|
catch (error) {
|
|
45
|
-
if (error instanceof
|
|
51
|
+
if (error instanceof RunnerLeaseUnavailableError) {
|
|
46
52
|
return { status: "accepted", executionId };
|
|
47
53
|
}
|
|
48
54
|
return { status: "accepted", executionId };
|
|
@@ -50,7 +56,8 @@ function settleReleasedLeash(store, projectionDirectory, executionId) {
|
|
|
50
56
|
try {
|
|
51
57
|
store.settleKillIfRunnerDead({
|
|
52
58
|
executionId,
|
|
53
|
-
releasedLockEvidence: { state: "acquired", executionId },
|
|
59
|
+
releasedLockEvidence: { state: "acquired", executionId, observedAt: terminalAt },
|
|
60
|
+
terminalAt,
|
|
54
61
|
});
|
|
55
62
|
}
|
|
56
63
|
finally {
|
|
@@ -78,11 +85,14 @@ export function killProjectionGeneration(projectionDirectory, nowMs = Date.now()
|
|
|
78
85
|
const executionId = current.launch.executionId;
|
|
79
86
|
const request = store.requestKillIfOpen({ executionId, killedAt });
|
|
80
87
|
if (request.status === "committed") {
|
|
81
|
-
|
|
88
|
+
// A missing, stale, or rejected endpoint has no lifecycle meaning.
|
|
89
|
+
ringProjectionGenerationDoorbell(store.readIdentity(), executionId);
|
|
90
|
+
return settleReleasedLeash(store, projectionDirectory, executionId, new Date(nowMs).toISOString());
|
|
82
91
|
}
|
|
83
92
|
if (request.reason === "kill-requested") {
|
|
84
93
|
// Idempotent first intent: still accepted, then one leash observation.
|
|
85
|
-
|
|
94
|
+
ringProjectionGenerationDoorbell(store.readIdentity(), executionId);
|
|
95
|
+
return settleReleasedLeash(store, projectionDirectory, executionId, new Date(nowMs).toISOString());
|
|
86
96
|
}
|
|
87
97
|
if (request.reason === "already-terminal") {
|
|
88
98
|
return { status: "already-closed", executionId };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { isIncompatibleProjectionGenerationStoreError, openProjectionGenerationStoreReadOnly, } from "./generation/store.js";
|
|
2
|
-
import { deriveProjectionLife, selectCurrentGeneration, } from "./
|
|
3
|
-
import {
|
|
2
|
+
import { deriveProjectionLife, selectCurrentGeneration, } from "./generation/protocol.js";
|
|
3
|
+
import { observeRunnerLease } from "../run-control/runner-lease.js";
|
|
4
|
+
import { projectionRunnerLeasePath } from "./generation/projection-generation-identity.js";
|
|
4
5
|
function errorDetail(error) {
|
|
5
6
|
return error instanceof Error ? error.message : String(error);
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* Observe the current generation without creating, repairing, or mutating
|
|
9
10
|
* projection storage. Generation history is the ordering authority; the
|
|
10
|
-
* runner lock is consulted
|
|
11
|
+
* runner lock is consulted for every non-terminal generation. Launch age is
|
|
12
|
+
* not lifecycle authority.
|
|
11
13
|
*/
|
|
12
14
|
export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
13
15
|
const readRecords = () => {
|
|
@@ -37,18 +39,17 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
const current = selectCurrentGeneration(records);
|
|
40
|
-
if (!current || current.verdict
|
|
42
|
+
if (!current || current.verdict) {
|
|
41
43
|
return {
|
|
42
44
|
compatibility: "current",
|
|
43
45
|
life: deriveProjectionLife({
|
|
44
46
|
records,
|
|
45
47
|
nowMs: options.nowMs,
|
|
46
|
-
startupTimeoutMs: options.startupTimeoutMs,
|
|
47
48
|
}),
|
|
48
49
|
records,
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
|
-
const lock =
|
|
52
|
+
const lock = observeRunnerLease(projectionRunnerLeasePath(projectionDirectory));
|
|
52
53
|
let after;
|
|
53
54
|
try {
|
|
54
55
|
after = readRecords();
|
|
@@ -74,13 +75,12 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
74
75
|
compatibility: "current",
|
|
75
76
|
life: deriveProjectionLife({
|
|
76
77
|
records,
|
|
77
|
-
|
|
78
|
+
runnerLease: lock.state === "held"
|
|
78
79
|
? "held"
|
|
79
80
|
: lock.state === "released"
|
|
80
81
|
? "released"
|
|
81
82
|
: "unknown",
|
|
82
83
|
nowMs: options.nowMs,
|
|
83
|
-
startupTimeoutMs: options.startupTimeoutMs,
|
|
84
84
|
}),
|
|
85
85
|
records,
|
|
86
86
|
};
|
|
@@ -5,7 +5,9 @@ import { ProjectionStateError, randomHex8 } from "../atomic-publish.js";
|
|
|
5
5
|
import { isoFromNowMs, PROJECTION_ROOT_SEGMENTS, projectionDir, projectionRoot, } from "./projection-core.js";
|
|
6
6
|
import { parseExecutionAuthoritySelection, parseExecutionPact, parseExecutionPactFields, } from "../execution-pact.js";
|
|
7
7
|
import { openProjectionGenerationStore } from "./generation/store.js";
|
|
8
|
-
import {
|
|
8
|
+
import { initializeRunnerLeaseDatabase } from "../run-control/runner-lease.js";
|
|
9
|
+
import { projectionRunnerLeasePath } from "./generation/projection-generation-identity.js";
|
|
10
|
+
import { isAkumaName } from "./akuma-name.js";
|
|
9
11
|
export class ProjectionMintCollisionError extends Error {
|
|
10
12
|
code = "PROJECTION_MINT_COLLISION";
|
|
11
13
|
constructor(message, options) {
|
|
@@ -15,14 +17,10 @@ export class ProjectionMintCollisionError extends Error {
|
|
|
15
17
|
}
|
|
16
18
|
const DEFAULT_MINT_ATTEMPTS = 16;
|
|
17
19
|
function assertMintSlug(slug) {
|
|
18
|
-
|
|
19
|
-
if (!normalized) {
|
|
20
|
-
throw new ProjectionStateError("projection slug cannot be empty");
|
|
21
|
-
}
|
|
22
|
-
if (!/^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/.test(normalized)) {
|
|
20
|
+
if (!isAkumaName(slug)) {
|
|
23
21
|
throw new ProjectionStateError(`invalid projection slug: ${slug}`);
|
|
24
22
|
}
|
|
25
|
-
return
|
|
23
|
+
return slug;
|
|
26
24
|
}
|
|
27
25
|
function removeEmptyAkumaRoot(dir) {
|
|
28
26
|
try {
|
|
@@ -115,7 +113,7 @@ function publishReservedProjection(pact, reserved) {
|
|
|
115
113
|
finally {
|
|
116
114
|
heart.close();
|
|
117
115
|
}
|
|
118
|
-
|
|
116
|
+
initializeRunnerLeaseDatabase(projectionRunnerLeasePath(reserved.stagingDir));
|
|
119
117
|
fs.renameSync(reserved.stagingDir, reserved.dir);
|
|
120
118
|
published = true;
|
|
121
119
|
return { id: reserved.id, dir: reserved.dir, pact };
|
|
@@ -7,12 +7,51 @@ import { foldPublicProjectionActivity } from "./projection-activity.js";
|
|
|
7
7
|
import { readProjectionIdentity } from "./projection-identity.js";
|
|
8
8
|
import { readPendingTellWindow, readProjectionTellCounts } from "./tell/store.js";
|
|
9
9
|
import { ProjectionGenerationStoreError } from "./generation/store.js";
|
|
10
|
-
import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
|
|
11
10
|
import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
|
|
12
|
-
import { selectCurrentGeneration } from "./
|
|
11
|
+
import { selectCurrentGeneration } from "./generation/protocol.js";
|
|
13
12
|
import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "../stored-agent-event.js";
|
|
14
13
|
import { projectTerminalFailure } from "./projection-terminal-failure.js";
|
|
15
14
|
import { extractTerminalTimestamp } from "./generation/model.js";
|
|
15
|
+
function latestActivityRow(activity, kind, predicate) {
|
|
16
|
+
const rows = activity?.retainedTimeline.filter((row) => row.kind === kind) ?? [];
|
|
17
|
+
return [...rows].reverse().find((row) => predicate?.(row) ?? true);
|
|
18
|
+
}
|
|
19
|
+
export function deriveProjectionStatusReadFacts(input) {
|
|
20
|
+
const warning = latestActivityRow(input.activity, "warning");
|
|
21
|
+
const runningTool = latestActivityRow(input.activity, "ran", (row) => row.state === "running");
|
|
22
|
+
const said = latestActivityRow(input.activity, "said");
|
|
23
|
+
const answer = typeof input.verdictFacts?.finalMessage === "string"
|
|
24
|
+
? { kind: "text", text: input.verdictFacts.finalMessage }
|
|
25
|
+
: undefined;
|
|
26
|
+
const responsePath = typeof input.verdictFacts?.responsePath === "string" ? input.verdictFacts.responsePath : undefined;
|
|
27
|
+
const artifactId = typeof input.verdictFacts?.artifactId === "string" ? input.verdictFacts.artifactId : undefined;
|
|
28
|
+
const launchInputs = input.launchFacts?.inputs;
|
|
29
|
+
const incognito = launchInputs !== null && typeof launchInputs === "object" && !Array.isArray(launchInputs)
|
|
30
|
+
&& launchInputs.persistProjectionResponse === false;
|
|
31
|
+
const persistenceFailed = input.verdictFacts?.responsePersistError !== undefined;
|
|
32
|
+
const snapshot = warning
|
|
33
|
+
? { kind: "warning", row: warning }
|
|
34
|
+
: runningTool
|
|
35
|
+
? { kind: "running-tool", row: runningTool }
|
|
36
|
+
: said
|
|
37
|
+
? { kind: "said", row: said }
|
|
38
|
+
: input.activity?.plan
|
|
39
|
+
? { kind: "plan", plan: input.activity.plan }
|
|
40
|
+
: undefined;
|
|
41
|
+
const responseArtifact = input.verdictFacts === undefined
|
|
42
|
+
? undefined
|
|
43
|
+
: responsePath
|
|
44
|
+
? { state: "retained", path: responsePath, ...(artifactId ? { artifactId } : {}) }
|
|
45
|
+
: incognito
|
|
46
|
+
? { state: "none", reason: "incognito" }
|
|
47
|
+
: { state: "unavailable", reason: persistenceFailed ? "persistence-failed" : "not-observed" };
|
|
48
|
+
return {
|
|
49
|
+
...(answer ? { terminalAnswer: answer } : {}),
|
|
50
|
+
...(responseArtifact ? { responseArtifact } : {}),
|
|
51
|
+
attention: { ...(warning ? { latestWarning: warning } : {}) },
|
|
52
|
+
...(snapshot ? { snapshot } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
16
55
|
const MAX_STATUS_FILE_BYTES = 256 * 1024;
|
|
17
56
|
class ProjectionStatusFileError extends Error {
|
|
18
57
|
diagnostic;
|
|
@@ -233,11 +272,19 @@ function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallb
|
|
|
233
272
|
function terminalTimestamp(current) {
|
|
234
273
|
return extractTerminalTimestamp(current?.verdict?.facts);
|
|
235
274
|
}
|
|
275
|
+
function terminalAgeAt(terminalAt, observedAtMs) {
|
|
276
|
+
const terminalAtMs = Date.parse(terminalAt ?? "");
|
|
277
|
+
return Number.isFinite(terminalAtMs) ? Math.max(0, observedAtMs - terminalAtMs) : undefined;
|
|
278
|
+
}
|
|
279
|
+
function projectionStatusState(life) {
|
|
280
|
+
if (life.verdict === "lost-runner")
|
|
281
|
+
return "dead";
|
|
282
|
+
return life.phase === "active" || life.phase === "quiescent" ? "out" : life.phase;
|
|
283
|
+
}
|
|
236
284
|
export function readProjectionSummary(projectionDir, id, alias, nowMs, options) {
|
|
237
285
|
const diagnostics = [];
|
|
238
286
|
const snapshot = observeProjectionLifeSnapshot(projectionDir, {
|
|
239
287
|
nowMs,
|
|
240
|
-
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
241
288
|
});
|
|
242
289
|
if (snapshot.compatibility === "incompatible") {
|
|
243
290
|
return {
|
|
@@ -251,8 +298,7 @@ export function readProjectionSummary(projectionDir, id, alias, nowMs, options)
|
|
|
251
298
|
};
|
|
252
299
|
}
|
|
253
300
|
const life = snapshot.life;
|
|
254
|
-
const
|
|
255
|
-
const state = phase === "active" || phase === "quiescent" ? "out" : phase;
|
|
301
|
+
const state = projectionStatusState(life);
|
|
256
302
|
const identityFacts = options?.requiresIdentity?.(state) === false
|
|
257
303
|
? null
|
|
258
304
|
: readIdentityFacts(projectionDir, diagnostics);
|
|
@@ -268,27 +314,38 @@ export function readProjectionSummary(projectionDir, id, alias, nowMs, options)
|
|
|
268
314
|
}
|
|
269
315
|
export function readProjectionRow(summary, nowMs, readDetails) {
|
|
270
316
|
const { id, alias, projectionDir, snapshot } = summary;
|
|
317
|
+
const identity = { canonicalId: id };
|
|
271
318
|
const diagnostics = [...summary.diagnostics];
|
|
272
319
|
if (snapshot.compatibility === "incompatible") {
|
|
273
320
|
return {
|
|
274
321
|
id,
|
|
322
|
+
identity,
|
|
275
323
|
observedAtMs: nowMs,
|
|
276
324
|
...(alias ? { alias } : {}),
|
|
277
325
|
state: "incompatible",
|
|
278
326
|
observation: snapshot,
|
|
279
327
|
tellCounts: emptyTellCounts(),
|
|
328
|
+
attention: {},
|
|
280
329
|
diagnostics,
|
|
281
330
|
};
|
|
282
331
|
}
|
|
283
332
|
const identityFacts = summary.identityFacts;
|
|
284
333
|
if (identityFacts === null && !readDetails) {
|
|
334
|
+
const current = snapshot.records ? selectCurrentGeneration(snapshot.records) : null;
|
|
335
|
+
const terminalAt = terminalTimestamp(current);
|
|
336
|
+
const terminalAgeMs = current?.verdict ? terminalAgeAt(terminalAt, nowMs) : undefined;
|
|
337
|
+
const readFacts = deriveProjectionStatusReadFacts({ state: summary.state });
|
|
285
338
|
return {
|
|
286
339
|
id,
|
|
340
|
+
identity,
|
|
287
341
|
observedAtMs: nowMs,
|
|
288
342
|
...(alias ? { alias } : {}),
|
|
289
343
|
state: summary.state,
|
|
290
344
|
observation: { compatibility: "current", lifecycle: snapshot.life },
|
|
345
|
+
...(terminalAt ? { terminalAt } : {}),
|
|
346
|
+
...(terminalAgeMs !== undefined ? { terminalAgeMs } : {}),
|
|
291
347
|
tellCounts: emptyTellCounts(),
|
|
348
|
+
...readFacts,
|
|
292
349
|
diagnostics,
|
|
293
350
|
};
|
|
294
351
|
}
|
|
@@ -297,6 +354,7 @@ export function readProjectionRow(summary, nowMs, readDetails) {
|
|
|
297
354
|
?? "projection identity could not be observed";
|
|
298
355
|
return {
|
|
299
356
|
id,
|
|
357
|
+
identity,
|
|
300
358
|
observedAtMs: nowMs,
|
|
301
359
|
...(alias ? { alias } : {}),
|
|
302
360
|
state: "unknown",
|
|
@@ -305,6 +363,7 @@ export function readProjectionRow(summary, nowMs, readDetails) {
|
|
|
305
363
|
lifecycle: { phase: "unknown", diagnostic },
|
|
306
364
|
},
|
|
307
365
|
tellCounts: emptyTellCounts(),
|
|
366
|
+
attention: {},
|
|
308
367
|
diagnostics,
|
|
309
368
|
};
|
|
310
369
|
}
|
|
@@ -325,8 +384,10 @@ export function readProjectionRow(summary, nowMs, readDetails) {
|
|
|
325
384
|
: {};
|
|
326
385
|
const mintedAtMs = Date.parse(identityFacts.mintedAt ?? "");
|
|
327
386
|
const terminalAt = terminalTimestamp(current);
|
|
387
|
+
const terminalAtMs = Date.parse(terminalAt ?? "");
|
|
388
|
+
const terminalAgeMs = current?.verdict ? terminalAgeAt(terminalAt, nowMs) : undefined;
|
|
328
389
|
const durationAnchorMs = current?.verdict
|
|
329
|
-
?
|
|
390
|
+
? terminalAtMs
|
|
330
391
|
: nowMs;
|
|
331
392
|
const durationMs = Number.isFinite(mintedAtMs) && Number.isFinite(durationAnchorMs)
|
|
332
393
|
? Math.max(0, durationAnchorMs - mintedAtMs)
|
|
@@ -336,13 +397,19 @@ export function readProjectionRow(summary, nowMs, readDetails) {
|
|
|
336
397
|
if (life.phase === "unknown" && life.diagnostic) {
|
|
337
398
|
diagnostics.push({ kind: "unknown-lifecycle", detail: life.diagnostic });
|
|
338
399
|
}
|
|
339
|
-
const
|
|
340
|
-
const state = phase === "active" || phase === "quiescent" ? "out" : phase;
|
|
400
|
+
const state = projectionStatusState(life);
|
|
341
401
|
const terminalFailure = current?.verdict
|
|
342
402
|
? projectTerminalFailure(current.verdict.facts.state, current.verdict.facts)
|
|
343
403
|
: undefined;
|
|
404
|
+
const readFacts = deriveProjectionStatusReadFacts({
|
|
405
|
+
state,
|
|
406
|
+
activity: activityFacts.activity,
|
|
407
|
+
verdictFacts: current?.verdict?.facts,
|
|
408
|
+
launchFacts: current?.launch.facts,
|
|
409
|
+
});
|
|
344
410
|
return {
|
|
345
411
|
id,
|
|
412
|
+
identity,
|
|
346
413
|
observedAtMs: nowMs,
|
|
347
414
|
...(alias ? { alias } : {}),
|
|
348
415
|
state,
|
|
@@ -353,11 +420,13 @@ export function readProjectionRow(summary, nowMs, readDetails) {
|
|
|
353
420
|
pid: identityFacts.pid,
|
|
354
421
|
mintedAt: identityFacts.mintedAt,
|
|
355
422
|
...(terminalAt ? { terminalAt } : {}),
|
|
423
|
+
...(terminalAgeMs !== undefined ? { terminalAgeMs } : {}),
|
|
356
424
|
...(durationMs !== undefined ? { durationMs } : {}),
|
|
357
425
|
...(identityFacts.workspaceRoot ? { workspaceRoot: identityFacts.workspaceRoot } : {}),
|
|
358
426
|
...activityFacts,
|
|
359
427
|
...(displayAgeMs !== undefined ? { activityAgeMs: displayAgeMs } : {}),
|
|
360
428
|
...(terminalFailure ? { terminalFailure } : {}),
|
|
429
|
+
...readFacts,
|
|
361
430
|
tellCounts,
|
|
362
431
|
...(pendingTells.length > 0 ? { pendingTells } : {}),
|
|
363
432
|
diagnostics,
|