@astrosheep/keiyaku 2.9.4 → 2.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/harness/execution-handle.js +1 -0
- package/build/agents/harness/outcome.js +31 -5
- package/build/agents/harness/projection.js +151 -78
- package/build/agents/harness/router.js +2 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +13 -1
- package/build/agents/providers/claude-agent-sdk/registration.js +2 -1
- package/build/agents/providers/codex-app-server/adapter.js +43 -10
- package/build/agents/providers/codex-app-server/events.js +23 -0
- package/build/agents/providers/codex-app-server/registration.js +2 -1
- package/build/agents/providers/opencode-sdk/adapter.js +7 -0
- package/build/agents/providers/opencode-sdk/registration.js +2 -1
- package/build/agents/providers/pi/adapter.js +10 -0
- package/build/agents/providers/pi/events.js +4 -3
- package/build/agents/providers/pi/registration.js +3 -1
- package/build/agents/types.js +2 -0
- package/build/cli/commands/akuma/akuma/meta.js +1 -1
- package/build/cli/commands/akuma/list/meta-ls.js +1 -1
- package/build/cli/commands/akuma/view/meta.js +1 -1
- package/build/cli/commands/contract/amend/meta.js +4 -4
- package/build/cli/commands/contract/arc/handler.js +1 -1
- package/build/cli/commands/contract/arc/meta.js +4 -4
- package/build/cli/commands/contract/audit/handler.js +16 -0
- package/build/cli/commands/contract/audit/meta.js +16 -0
- package/build/cli/commands/contract/bind/meta.js +20 -11
- package/build/cli/commands/contract/catalog.js +2 -0
- package/build/cli/commands/contract/forfeit/meta.js +1 -1
- package/build/cli/commands/contract/log/meta.js +1 -1
- package/build/cli/commands/contract/petition/handler.js +1 -2
- package/build/cli/commands/contract/petition/meta.js +6 -6
- package/build/cli/commands/contract/renew/handler.js +2 -2
- package/build/cli/commands/contract/renew/meta.js +4 -4
- package/build/cli/commands/metadata.js +3 -2
- package/build/cli/commands/projection/call/meta.js +4 -1
- package/build/cli/commands/projection/kill/meta.js +1 -1
- package/build/cli/commands/projection/revive/meta.js +4 -1
- package/build/cli/commands/projection/status/meta.js +1 -1
- package/build/cli/commands/projection/tell/handler.js +1 -1
- package/build/cli/commands/projection/tell/meta.js +4 -1
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/skills/install/meta.js +1 -1
- package/build/cli/commands/skills/skills/meta.js +1 -1
- package/build/cli/commands/system/completion/meta.js +1 -1
- package/build/cli/commands/system/dump-env/meta.js +1 -1
- package/build/cli/commands/system/guide/meta.js +1 -1
- package/build/cli/commands/task/add/handler.js +2 -2
- package/build/cli/commands/task/add/meta.js +4 -1
- package/build/cli/commands/task/doctor/meta.js +1 -1
- package/build/cli/commands/task/done/meta.js +1 -1
- package/build/cli/commands/task/drop/meta.js +1 -1
- package/build/cli/commands/task/ls/handler.js +10 -4
- package/build/cli/commands/task/ls/meta.js +3 -3
- package/build/cli/commands/task/show/meta.js +1 -1
- package/build/cli/commands/task/start/meta.js +1 -1
- package/build/cli/commands/task/stop/meta.js +1 -1
- package/build/cli/commands/task/task/meta.js +1 -1
- package/build/cli/commands/task/update/handler.js +2 -3
- package/build/cli/commands/task/update/meta.js +4 -1
- package/build/cli/completion.js +9 -2
- package/build/cli/flags.js +19 -3
- package/build/cli/help.js +3 -3
- package/build/cli/index.js +59 -75
- package/build/cli/output-stream-error-policy.js +19 -5
- package/build/cli/parse-flags.js +19 -2
- package/build/cli/parse-metadata.js +1 -7
- package/build/cli/parse-selectors.js +1 -0
- package/build/cli/parse.js +16 -7
- package/build/cli/projection-address.js +6 -1
- package/build/cli/render/audit.js +121 -0
- package/build/cli/render/errors.js +18 -1
- package/build/cli/render/format.js +5 -2
- package/build/cli/render/misc.js +3 -1
- package/build/cli/render/projection-activity.js +12 -6
- package/build/cli/render/status.js +28 -18
- package/build/cli/render/success-response.js +6 -5
- package/build/cli/render/task.js +12 -43
- package/build/cli/render/terminal-failure.js +7 -2
- package/build/cli/render/wait.js +36 -8
- package/build/config/env-keys.js +2 -0
- package/build/config/env.js +3 -0
- package/build/core/amend.js +20 -94
- package/build/core/arc.js +14 -6
- package/build/core/audit/candidate.js +48 -0
- package/build/core/audit/coordinates.js +180 -0
- package/build/core/audit/evidence.js +158 -0
- package/build/core/audit/facade.js +42 -0
- package/build/core/audit/readiness.js +4 -0
- package/build/core/audit/report.js +38 -0
- package/build/core/audit.js +1 -0
- package/build/core/bind.js +1 -1
- package/build/core/call/execution.js +1 -0
- package/build/core/contract-view.js +2 -3
- package/build/core/draft.js +33 -20
- package/build/core/entry.js +3 -4
- package/build/core/markdown/lex.js +2 -1
- package/build/core/projection/generation/database.js +41 -16
- package/build/core/projection/generation/model.js +22 -2
- package/build/core/projection/generation/projection-generation-execution.js +20 -7
- package/build/core/projection/generation/projection-generation-launcher.js +4 -4
- package/build/core/projection/generation/projection-generation-runner.js +1 -0
- package/build/core/projection/generation/store.js +1 -1
- package/build/core/projection/generation/transitions.js +19 -3
- package/build/core/projection/index.js +2 -2
- package/build/core/projection/projection-execution-observer.js +35 -21
- package/build/core/projection/projection-life-observer.js +16 -2
- package/build/core/projection/projection-status.js +67 -22
- package/build/core/projection/projection-terminal-failure.js +2 -0
- package/build/core/projection/projection-wait.js +6 -9
- package/build/core/projection/projection-wake.js +30 -6
- package/build/core/renew.js +41 -22
- package/build/core/scope.js +129 -2
- package/build/core/seal.js +36 -15
- package/build/core/settlement/petition-claim-gates.js +4 -33
- package/build/core/settlement/petition-preview.js +1 -1
- package/build/core/settlement/petition.js +10 -3
- package/build/core/settlement/settlement.js +3 -10
- package/build/core/settlement/verification.js +59 -0
- package/build/core/status/board.js +48 -28
- package/build/core/stored-agent-event.js +3 -2
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +20 -12
- package/build/core/task/document.js +5 -0
- package/build/core/task/index.js +1 -1
- package/build/core/task/model.js +8 -1
- package/build/core/task/query.js +214 -0
- package/build/core/task/settlement-git.js +115 -16
- package/build/core/task/settlement-policy.js +17 -9
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-contract.js +125 -51
- package/build/core/task/task-store-repository.js +1 -0
- package/build/core/task/task.js +2 -0
- package/build/flow-error.js +2 -4
- package/build/generated/version.js +2 -2
- package/build/git/diff/parsers.js +23 -13
- package/build/git/diff/structured.js +127 -0
- package/build/index.js +7 -7
- package/build/telemetry/logger.js +4 -2
- package/package.json +1 -1
- package/skills/keiyaku/SKILL.md +2 -2
- package/skills/keiyaku-akuma/SKILL.md +17 -6
- package/skills/keiyaku-task/SKILL.md +16 -7
- package/skills/keiyaku-workflow/SKILL.md +48 -13
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import { claimAllInbox, listTellIds, markTellSubmitted, markTellConsumed, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
|
|
2
2
|
import { openProjectionTellObserver, } from "./tell/database.js";
|
|
3
3
|
const LIVE_TELL_DELIVERY_THROTTLE_MS = 200;
|
|
4
|
-
function
|
|
4
|
+
export function renderProjectionTellFrame(tellId, text) {
|
|
5
|
+
return `--- tell ${tellId} ---\n${text}`;
|
|
6
|
+
}
|
|
7
|
+
function submitEligible(projectionDirectory, executionId, control, requireInbox, submitting, submissions, excludedTellIds, observer, pendingCompletionEvidence) {
|
|
5
8
|
if (requireInbox && !observer?.hasInbox())
|
|
6
9
|
return;
|
|
7
10
|
claimAllInbox(projectionDirectory);
|
|
8
11
|
for (const tellId of listTellIds(projectionDirectory, "inflight")) {
|
|
12
|
+
if (excludedTellIds.has(tellId))
|
|
13
|
+
continue;
|
|
9
14
|
if (submitting.has(tellId))
|
|
10
15
|
continue;
|
|
11
16
|
// Construct before pin: a sync read/build failure must not leave submitting stuck.
|
|
12
17
|
const original = readTellOriginal(projectionDirectory, "inflight", tellId);
|
|
13
18
|
const tell = {
|
|
14
19
|
id: tellId,
|
|
15
|
-
text:
|
|
20
|
+
text: renderProjectionTellFrame(tellId, original.text),
|
|
16
21
|
...(original.effort ? { effort: original.effort } : {}),
|
|
17
22
|
};
|
|
18
23
|
submitting.add(tellId);
|
|
19
|
-
|
|
24
|
+
const submission = Promise.resolve()
|
|
20
25
|
.then(() => control.enqueue(tell))
|
|
21
26
|
.then((ack) => {
|
|
22
27
|
if (!ack)
|
|
@@ -39,6 +44,8 @@ function submitEligible(projectionDirectory, executionId, control, requireInbox,
|
|
|
39
44
|
pendingCompletionEvidence.delete(tellId);
|
|
40
45
|
})
|
|
41
46
|
.finally(() => submitting.delete(tellId));
|
|
47
|
+
submissions.add(submission);
|
|
48
|
+
void submission.finally(() => submissions.delete(submission));
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
export function createLiveTellDeliveryController(projectionDirectory, executionId, dependencies = {}) {
|
|
@@ -47,8 +54,10 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
|
|
|
47
54
|
const clearTimer = dependencies.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
48
55
|
const openObserver = dependencies.openObserver ?? openProjectionTellObserver;
|
|
49
56
|
const submitting = new Set();
|
|
57
|
+
const submissions = new Set();
|
|
50
58
|
// In-memory only: bridges completion-before-ack when a tell is still submitting.
|
|
51
59
|
const pendingCompletionEvidence = new Map();
|
|
60
|
+
const initial = new Set(dependencies.initialTellIds);
|
|
52
61
|
let observer;
|
|
53
62
|
let timer;
|
|
54
63
|
let nextOpportunityAt = 0;
|
|
@@ -93,7 +102,7 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
|
|
|
93
102
|
try {
|
|
94
103
|
observer ??= openObserver(projectionDirectory);
|
|
95
104
|
if (observer.hasDataVersionChanged()) {
|
|
96
|
-
submitEligible(projectionDirectory, executionId, control, true, submitting, observer, pendingCompletionEvidence);
|
|
105
|
+
submitEligible(projectionDirectory, executionId, control, true, submitting, submissions, initial, observer, pendingCompletionEvidence);
|
|
97
106
|
}
|
|
98
107
|
}
|
|
99
108
|
catch {
|
|
@@ -131,7 +140,14 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
|
|
|
131
140
|
return;
|
|
132
141
|
latestControl = control;
|
|
133
142
|
cancelOpportunity();
|
|
134
|
-
|
|
143
|
+
for (const tellId of initial) {
|
|
144
|
+
markTellSubmitted(projectionDirectory, tellId, {
|
|
145
|
+
executionId,
|
|
146
|
+
fence: checkpoint.fence,
|
|
147
|
+
});
|
|
148
|
+
initial.delete(tellId);
|
|
149
|
+
}
|
|
150
|
+
submitEligible(projectionDirectory, executionId, control, false, submitting, submissions, initial, observer, pendingCompletionEvidence);
|
|
135
151
|
if (!checkpoint.consumptionEvidence)
|
|
136
152
|
return;
|
|
137
153
|
for (const tellId of listTellIds(projectionDirectory, "submitted")) {
|
|
@@ -149,13 +165,14 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
|
|
|
149
165
|
pendingCompletionEvidence.set(tellId, [checkpoint.fence]);
|
|
150
166
|
}
|
|
151
167
|
},
|
|
152
|
-
close() {
|
|
153
|
-
if (closed)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
168
|
+
async close() {
|
|
169
|
+
if (!closed) {
|
|
170
|
+
closed = true;
|
|
171
|
+
cancelOpportunity();
|
|
172
|
+
discardObserver();
|
|
173
|
+
}
|
|
158
174
|
// Do not drop pendingCompletionEvidence: in-flight acks may still submit/consume.
|
|
175
|
+
await Promise.all([...submissions]);
|
|
159
176
|
},
|
|
160
177
|
};
|
|
161
178
|
}
|
|
@@ -174,8 +191,8 @@ export function buildSyncCheckpointCallback(projectionDirectory, executionId) {
|
|
|
174
191
|
*/
|
|
175
192
|
export function createProjectionExecutionObserver(input, dependencies = {}) {
|
|
176
193
|
const appendEvent = dependencies.appendGenerationEvent ?? appendGenerationEvent;
|
|
177
|
-
const tellController = createLiveTellDeliveryController(input.projectionDirectory, input.executionId);
|
|
178
|
-
let
|
|
194
|
+
const tellController = createLiveTellDeliveryController(input.projectionDirectory, input.executionId, { initialTellIds: input.initialTellIds });
|
|
195
|
+
let closePromise;
|
|
179
196
|
const observer = {
|
|
180
197
|
onEvent(event) {
|
|
181
198
|
appendEvent(input.projectionDirectory, input.executionId, event);
|
|
@@ -190,15 +207,12 @@ export function createProjectionExecutionObserver(input, dependencies = {}) {
|
|
|
190
207
|
return {
|
|
191
208
|
observer,
|
|
192
209
|
close() {
|
|
193
|
-
if (
|
|
194
|
-
return;
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
tellController.close();
|
|
198
|
-
}
|
|
199
|
-
catch {
|
|
210
|
+
if (closePromise)
|
|
211
|
+
return closePromise;
|
|
212
|
+
closePromise = tellController.close().catch(() => {
|
|
200
213
|
// Controller cleanup cannot change provider completion.
|
|
201
|
-
}
|
|
214
|
+
});
|
|
215
|
+
return closePromise;
|
|
202
216
|
},
|
|
203
217
|
};
|
|
204
218
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { openProjectionGenerationStoreReadOnly, } from "./generation/store.js";
|
|
1
|
+
import { isIncompatibleProjectionGenerationStoreError, openProjectionGenerationStoreReadOnly, } from "./generation/store.js";
|
|
2
2
|
import { deriveProjectionLife, selectCurrentGeneration, } from "./projection-life-protocol.js";
|
|
3
3
|
import { observeProjectionRunnerLock } from "./projection-runner-lock.js";
|
|
4
4
|
function errorDetail(error) {
|
|
@@ -25,7 +25,11 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
25
25
|
records = readRecords();
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
|
+
if (isIncompatibleProjectionGenerationStoreError(error)) {
|
|
29
|
+
return { compatibility: "incompatible", diagnostic: errorDetail(error) };
|
|
30
|
+
}
|
|
28
31
|
return {
|
|
32
|
+
compatibility: "current",
|
|
29
33
|
life: {
|
|
30
34
|
phase: "unknown",
|
|
31
35
|
diagnostic: `generation history could not be observed: ${errorDetail(error)}`,
|
|
@@ -35,6 +39,7 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
35
39
|
const current = selectCurrentGeneration(records);
|
|
36
40
|
if (!current || current.verdict || !current.adoption) {
|
|
37
41
|
return {
|
|
42
|
+
compatibility: "current",
|
|
38
43
|
life: deriveProjectionLife({
|
|
39
44
|
records,
|
|
40
45
|
nowMs: options.nowMs,
|
|
@@ -49,7 +54,11 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
49
54
|
after = readRecords();
|
|
50
55
|
}
|
|
51
56
|
catch (error) {
|
|
57
|
+
if (isIncompatibleProjectionGenerationStoreError(error)) {
|
|
58
|
+
return { compatibility: "incompatible", diagnostic: errorDetail(error) };
|
|
59
|
+
}
|
|
52
60
|
return {
|
|
61
|
+
compatibility: "current",
|
|
53
62
|
life: {
|
|
54
63
|
phase: "unknown",
|
|
55
64
|
executionId: current.launch.executionId,
|
|
@@ -62,6 +71,7 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
62
71
|
if (records.length !== after.length || beforeTail !== afterTail)
|
|
63
72
|
continue;
|
|
64
73
|
return {
|
|
74
|
+
compatibility: "current",
|
|
65
75
|
life: deriveProjectionLife({
|
|
66
76
|
records,
|
|
67
77
|
runnerLock: lock.state === "held"
|
|
@@ -76,6 +86,7 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
76
86
|
};
|
|
77
87
|
}
|
|
78
88
|
return {
|
|
89
|
+
compatibility: "current",
|
|
79
90
|
life: {
|
|
80
91
|
phase: "unknown",
|
|
81
92
|
diagnostic: "generation history changed during runner-lock observation",
|
|
@@ -83,5 +94,8 @@ export function observeProjectionLifeSnapshot(projectionDirectory, options) {
|
|
|
83
94
|
};
|
|
84
95
|
}
|
|
85
96
|
export function observeProjectionLife(projectionDirectory, options) {
|
|
86
|
-
|
|
97
|
+
const snapshot = observeProjectionLifeSnapshot(projectionDirectory, options);
|
|
98
|
+
return snapshot.compatibility === "current"
|
|
99
|
+
? snapshot.life
|
|
100
|
+
: { phase: "unknown", diagnostic: snapshot.diagnostic };
|
|
87
101
|
}
|
|
@@ -10,10 +10,11 @@ import { readProjectionTellCounts } from "./tell/store.js";
|
|
|
10
10
|
import { ProjectionGenerationStoreError, } from "./generation/store.js";
|
|
11
11
|
import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
|
|
12
12
|
import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
|
|
13
|
-
import { selectCurrentGeneration } from "./projection-life-protocol.js";
|
|
13
|
+
import { selectCurrentGeneration, } from "./projection-life-protocol.js";
|
|
14
14
|
import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "../stored-agent-event.js";
|
|
15
15
|
import { worktreeContainsCoordinate } from "../worktree-path.js";
|
|
16
16
|
import { projectTerminalFailure, } from "./projection-terminal-failure.js";
|
|
17
|
+
import { extractTerminalTimestamp } from "./generation/model.js";
|
|
17
18
|
const MAX_STATUS_FILE_BYTES = 256 * 1024;
|
|
18
19
|
const STATUS_GROUP = {
|
|
19
20
|
"startup-timeout": 0,
|
|
@@ -26,13 +27,15 @@ const STATUS_GROUP = {
|
|
|
26
27
|
done: 3,
|
|
27
28
|
killed: 4,
|
|
28
29
|
dismissed: 4,
|
|
30
|
+
incompatible: 5,
|
|
29
31
|
};
|
|
30
32
|
function isOmissibleProjectionStatusState(state) {
|
|
31
33
|
return state === "failed"
|
|
32
34
|
|| state === "dead"
|
|
33
35
|
|| state === "done"
|
|
34
36
|
|| state === "killed"
|
|
35
|
-
|| state === "dismissed"
|
|
37
|
+
|| state === "dismissed"
|
|
38
|
+
|| state === "incompatible";
|
|
36
39
|
}
|
|
37
40
|
function rowSortTime(row) {
|
|
38
41
|
if (row.activityAtMs !== undefined && Number.isFinite(row.activityAtMs)) {
|
|
@@ -71,6 +74,7 @@ export function prioritizeProjectionStatusRows(rows, showAll) {
|
|
|
71
74
|
done: 0,
|
|
72
75
|
killed: 0,
|
|
73
76
|
dismissed: 0,
|
|
77
|
+
incompatible: 0,
|
|
74
78
|
};
|
|
75
79
|
for (const row of ordered) {
|
|
76
80
|
if (isOmissibleProjectionStatusState(row.state))
|
|
@@ -239,64 +243,99 @@ function readStoredEvents(projectionDir, executionId, diagnostics) {
|
|
|
239
243
|
}
|
|
240
244
|
return values;
|
|
241
245
|
}
|
|
242
|
-
function
|
|
246
|
+
function latestValidEvent(events) {
|
|
243
247
|
let latest;
|
|
244
248
|
for (const event of events) {
|
|
245
249
|
const atMs = Date.parse(event.at);
|
|
246
250
|
if (!Number.isFinite(atMs))
|
|
247
251
|
continue;
|
|
248
|
-
if (latest === undefined || atMs > latest)
|
|
249
|
-
latest = atMs;
|
|
252
|
+
if (latest === undefined || atMs > latest.atMs)
|
|
253
|
+
latest = { at: event.at, atMs };
|
|
250
254
|
}
|
|
251
255
|
return latest;
|
|
252
256
|
}
|
|
253
|
-
function readActivityFacts(projectionDir, executionId, nowMs, diagnostics,
|
|
254
|
-
|
|
255
|
-
|
|
257
|
+
function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallbackAt) {
|
|
258
|
+
const fallbackAtMs = fallbackAt === undefined ? undefined : Date.parse(fallbackAt);
|
|
259
|
+
const validFallback = fallbackAt !== undefined && Number.isFinite(fallbackAtMs)
|
|
260
|
+
? { at: fallbackAt, atMs: fallbackAtMs }
|
|
261
|
+
: undefined;
|
|
262
|
+
if (!executionId) {
|
|
263
|
+
return validFallback
|
|
264
|
+
? { activityAt: validFallback.at, activityAtMs: validFallback.atMs, activityAgeMs: Math.max(0, nowMs - validFallback.atMs) }
|
|
265
|
+
: {};
|
|
266
|
+
}
|
|
256
267
|
const stored = readStoredEvents(projectionDir, executionId, diagnostics);
|
|
257
268
|
const { events, malformed } = selectValidStoredAgentEvents(stored);
|
|
258
269
|
const invalidActivity = invalidActivityEventDiagnostic(malformed);
|
|
259
270
|
if (invalidActivity)
|
|
260
271
|
diagnostics.push(invalidActivity);
|
|
261
|
-
const
|
|
272
|
+
const latest = latestValidEvent(events) ?? validFallback;
|
|
262
273
|
return {
|
|
263
274
|
...(events.length > 0 ? { activity: foldPublicProjectionActivity(events) } : {}),
|
|
264
|
-
...(
|
|
275
|
+
...(latest !== undefined
|
|
265
276
|
? {
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
activityAt: latest.at,
|
|
278
|
+
activityAtMs: latest.atMs,
|
|
279
|
+
activityAgeMs: Math.max(0, nowMs - latest.atMs),
|
|
268
280
|
}
|
|
269
281
|
: {}),
|
|
270
282
|
};
|
|
271
283
|
}
|
|
284
|
+
function terminalTimestamp(current) {
|
|
285
|
+
return extractTerminalTimestamp(current?.verdict?.facts);
|
|
286
|
+
}
|
|
272
287
|
function readProjectionRow(projectionDir, id, alias, nowMs) {
|
|
273
288
|
const diagnostics = [];
|
|
289
|
+
const snapshot = observeProjectionLifeSnapshot(projectionDir, {
|
|
290
|
+
nowMs,
|
|
291
|
+
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
292
|
+
});
|
|
293
|
+
if (snapshot.compatibility === "incompatible") {
|
|
294
|
+
return {
|
|
295
|
+
id,
|
|
296
|
+
...(alias ? { alias } : {}),
|
|
297
|
+
state: "incompatible",
|
|
298
|
+
observation: snapshot,
|
|
299
|
+
tellCounts: emptyTellCounts(),
|
|
300
|
+
diagnostics,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
274
303
|
const identityFacts = readIdentityFacts(projectionDir, diagnostics);
|
|
275
304
|
const tellCounts = identityFacts.identity
|
|
276
305
|
? readHeartTellCounts(projectionDir, diagnostics)
|
|
277
306
|
: emptyTellCounts();
|
|
278
307
|
if (!identityFacts.identity) {
|
|
308
|
+
const diagnostic = diagnostics.map((entry) => entry.detail).find((detail) => detail !== undefined)
|
|
309
|
+
?? "projection identity could not be observed";
|
|
279
310
|
return {
|
|
280
311
|
id,
|
|
312
|
+
...(alias ? { alias } : {}),
|
|
281
313
|
state: "unknown",
|
|
314
|
+
observation: {
|
|
315
|
+
compatibility: "current",
|
|
316
|
+
lifecycle: { phase: "unknown", diagnostic },
|
|
317
|
+
},
|
|
282
318
|
tellCounts,
|
|
283
319
|
diagnostics,
|
|
284
320
|
};
|
|
285
321
|
}
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
const life = observation.life;
|
|
291
|
-
const current = observation.records
|
|
292
|
-
? selectCurrentGeneration(observation.records)
|
|
322
|
+
const life = snapshot.life;
|
|
323
|
+
const current = snapshot.records
|
|
324
|
+
? selectCurrentGeneration(snapshot.records)
|
|
293
325
|
: null;
|
|
294
326
|
const adoptedAt = current?.adoption?.facts.adoptedAt;
|
|
295
327
|
const adoptedAtMs = typeof adoptedAt === "string"
|
|
296
328
|
? Date.parse(adoptedAt)
|
|
297
329
|
: Number.NaN;
|
|
298
|
-
const activityFacts = readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, Number.isFinite(adoptedAtMs) ?
|
|
330
|
+
const activityFacts = readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, typeof adoptedAt === "string" && Number.isFinite(adoptedAtMs) ? adoptedAt : identityFacts.mintedAt);
|
|
299
331
|
const mintedAtMs = Date.parse(identityFacts.mintedAt ?? "");
|
|
332
|
+
const terminalAt = terminalTimestamp(current);
|
|
333
|
+
const durationAnchorMs = current?.verdict
|
|
334
|
+
? (terminalAt === undefined ? Number.NaN : Date.parse(terminalAt))
|
|
335
|
+
: nowMs;
|
|
336
|
+
const durationMs = Number.isFinite(mintedAtMs) && Number.isFinite(durationAnchorMs)
|
|
337
|
+
? Math.max(0, durationAnchorMs - mintedAtMs)
|
|
338
|
+
: undefined;
|
|
300
339
|
const displayAgeMs = activityFacts.activityAgeMs
|
|
301
340
|
?? (Number.isFinite(mintedAtMs) ? Math.max(0, nowMs - mintedAtMs) : undefined);
|
|
302
341
|
if (life.phase === "unknown" && life.diagnostic) {
|
|
@@ -311,10 +350,13 @@ function readProjectionRow(projectionDir, id, alias, nowMs) {
|
|
|
311
350
|
id,
|
|
312
351
|
...(alias ? { alias } : {}),
|
|
313
352
|
state,
|
|
353
|
+
observation: { compatibility: "current", lifecycle: life },
|
|
314
354
|
akuma: identityFacts.akuma,
|
|
315
355
|
provider: identityFacts.provider,
|
|
316
356
|
pid: identityFacts.pid,
|
|
317
357
|
mintedAt: identityFacts.mintedAt,
|
|
358
|
+
...(terminalAt ? { terminalAt } : {}),
|
|
359
|
+
...(durationMs !== undefined ? { durationMs } : {}),
|
|
318
360
|
...(identityFacts.workspaceRoot ? { workspaceRoot: identityFacts.workspaceRoot } : {}),
|
|
319
361
|
...activityFacts,
|
|
320
362
|
...(displayAgeMs !== undefined ? { activityAgeMs: displayAgeMs } : {}),
|
|
@@ -448,10 +490,13 @@ export async function hasStrandableProjectionGeneration(input) {
|
|
|
448
490
|
|| !worktreeContainsCoordinate(input.deliveryWorktree, identity.workspaceRoot)) {
|
|
449
491
|
continue;
|
|
450
492
|
}
|
|
451
|
-
const
|
|
493
|
+
const observation = observeProjectionLifeSnapshot(projectionDir, {
|
|
452
494
|
nowMs: input.nowMs,
|
|
453
495
|
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
454
|
-
})
|
|
496
|
+
});
|
|
497
|
+
if (observation.compatibility !== "current")
|
|
498
|
+
continue;
|
|
499
|
+
const phase = observation.life.phase;
|
|
455
500
|
if (phase === "active"
|
|
456
501
|
|| phase === "minting"
|
|
457
502
|
|| phase === "startup-timeout"
|
|
@@ -3,6 +3,8 @@ import { RESPONSE_ARTIFACT_ID_RE } from "../response-artifact-id.js";
|
|
|
3
3
|
import { OUTCOME_ERROR_VERSION } from "../../agents/types.js";
|
|
4
4
|
const HARNESS_FAILURE_CODES = new Set([
|
|
5
5
|
"SUBAGENT_TIMEOUT",
|
|
6
|
+
"SUBAGENT_TRANSPORT_ERROR",
|
|
7
|
+
"SUBAGENT_POLICY_BUDGET",
|
|
6
8
|
"SUBAGENT_NON_ZERO_EXIT",
|
|
7
9
|
"SUBAGENT_SPAWN_ERROR",
|
|
8
10
|
"SUBAGENT_EXEC_ERROR",
|
|
@@ -4,6 +4,7 @@ import { foldPublicProjectionActivity } from "./projection-activity.js";
|
|
|
4
4
|
import { readGenerationEvents, isProjectionId, projectionRoot, } from "./projection-core.js";
|
|
5
5
|
import { readProjectionIdentity } from "./projection-identity.js";
|
|
6
6
|
import { RESPONSE_ARTIFACT_ID_RE } from "../response-artifact-id.js";
|
|
7
|
+
import { extractTerminalTimestamp } from "./generation/model.js";
|
|
7
8
|
import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
|
|
8
9
|
import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
|
|
9
10
|
import { selectCurrentGeneration } from "./projection-life-protocol.js";
|
|
@@ -100,13 +101,7 @@ function relativeEventLedgerFile(executionId) {
|
|
|
100
101
|
return `${executionId}.events.jsonl`;
|
|
101
102
|
}
|
|
102
103
|
function timestampFact(current, nowMs) {
|
|
103
|
-
|
|
104
|
-
for (const key of ["completedAt", "failedAt", "dismissedAt", "killedAt"]) {
|
|
105
|
-
const value = facts?.[key];
|
|
106
|
-
if (typeof value === "string" && Number.isFinite(Date.parse(value)))
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
return new Date(nowMs).toISOString();
|
|
104
|
+
return extractTerminalTimestamp(current.verdict?.facts) ?? new Date(nowMs).toISOString();
|
|
110
105
|
}
|
|
111
106
|
function verdictFromCurrent(current, nowMs) {
|
|
112
107
|
if (!current.verdict)
|
|
@@ -180,10 +175,12 @@ export async function waitForProjection(input) {
|
|
|
180
175
|
nowMs: observedAtMs,
|
|
181
176
|
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
182
177
|
});
|
|
183
|
-
const current = observation.records
|
|
178
|
+
const current = observation.compatibility === "current" && observation.records
|
|
184
179
|
? selectCurrentGeneration(observation.records)
|
|
185
180
|
: null;
|
|
186
|
-
const life = observation.
|
|
181
|
+
const life = observation.compatibility === "current"
|
|
182
|
+
? observation.life
|
|
183
|
+
: { phase: "unknown", diagnostic: observation.diagnostic };
|
|
187
184
|
const verdict = current ? verdictFromCurrent(current, observedAtMs) : undefined;
|
|
188
185
|
const terminal = verdict !== undefined;
|
|
189
186
|
const terminalAtMs = verdict === undefined ? undefined : Date.parse(verdict.at);
|
|
@@ -6,9 +6,25 @@ import { applyContinuationEffort, snapshotPendingTells, } from "./generation/pro
|
|
|
6
6
|
import { createProjectionExecutionId } from "./generation/projection-generation-identity.js";
|
|
7
7
|
import { PROJECTION_ADOPTION_POLL_MS, PROJECTION_ADOPTION_TIMEOUT_MS, startProjectionGeneration, } from "./generation/projection-generation-launcher.js";
|
|
8
8
|
import { openProjectionGenerationStore, openProjectionGenerationStoreReadOnly, } from "./generation/store.js";
|
|
9
|
-
import {
|
|
9
|
+
import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
|
|
10
10
|
import { observeProjectionRunnerLock } from "./projection-runner-lock.js";
|
|
11
11
|
import { setTimeout as delay } from "node:timers/promises";
|
|
12
|
+
function incompatibleProjectionError(projectionId) {
|
|
13
|
+
const subject = projectionId ? `Projection '${projectionId}'` : "Projection";
|
|
14
|
+
return new FlowError("PROJECTION_INCOMPATIBLE", `${subject} has incompatible historical storage; keiyaku status --all can inspect it, but tell cannot change it.`);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tell is a current-schema mutation only. This gate runs before the handler
|
|
18
|
+
* writes an inbox row, so historical storage never acquires pending mail.
|
|
19
|
+
*/
|
|
20
|
+
export function assertProjectionTellable(projectionDirectory) {
|
|
21
|
+
const observation = observeProjectionLifeSnapshot(projectionDirectory, {
|
|
22
|
+
nowMs: Date.now(),
|
|
23
|
+
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
24
|
+
});
|
|
25
|
+
if (observation.compatibility === "incompatible")
|
|
26
|
+
throw incompatibleProjectionError();
|
|
27
|
+
}
|
|
12
28
|
function readCurrentGeneration(projectionDirectory) {
|
|
13
29
|
const store = openProjectionGenerationStoreReadOnly(projectionDirectory);
|
|
14
30
|
try {
|
|
@@ -28,7 +44,7 @@ function successorFacts(current, snapshot, nowMs, requireContinuation) {
|
|
|
28
44
|
: {};
|
|
29
45
|
const session = verdictFacts?.session ?? inputs.session;
|
|
30
46
|
const launchSnapshot = parseResolvedAkumaLaunchSnapshot(inputs.launchSnapshot);
|
|
31
|
-
const recoverableSession = isRecoverableSessionEnvelope(launchSnapshot.provider.
|
|
47
|
+
const recoverableSession = isRecoverableSessionEnvelope(launchSnapshot.provider.kind, session);
|
|
32
48
|
if (requireContinuation && !recoverableSession) {
|
|
33
49
|
throw new FlowError("PROJECTION_WAKE_FAILED", "The tell was saved, but the successor could not be started because its provider session is not recoverable.");
|
|
34
50
|
}
|
|
@@ -86,7 +102,7 @@ async function startCommittedSuccessor(projectionDirectory, projectionId, execut
|
|
|
86
102
|
const started = await startGeneration(projectionDirectory, executionId);
|
|
87
103
|
if (started.status === "adopted")
|
|
88
104
|
return "woken";
|
|
89
|
-
throw new FlowError("PROJECTION_WAKE_FAILED", `The tell was saved for '${projectionId}', but its successor could not be started.`, {
|
|
105
|
+
throw new FlowError("PROJECTION_WAKE_FAILED", `The tell was saved for '${projectionId}', but its successor could not be started (cause: ${started.cause}).`, {
|
|
90
106
|
facts: {
|
|
91
107
|
kind: "projection_wake_failure",
|
|
92
108
|
projectionId,
|
|
@@ -96,7 +112,7 @@ async function startCommittedSuccessor(projectionDirectory, projectionId, execut
|
|
|
96
112
|
});
|
|
97
113
|
}
|
|
98
114
|
function sessionWakeFailure(projectionId, cause) {
|
|
99
|
-
return new FlowError("PROJECTION_WAKE_FAILED", `The tell was saved for '${projectionId}', but its successor could not be started.`, {
|
|
115
|
+
return new FlowError("PROJECTION_WAKE_FAILED", `The tell was saved for '${projectionId}', but its successor could not be started (cause: session_unrecoverable).`, {
|
|
100
116
|
cause,
|
|
101
117
|
facts: {
|
|
102
118
|
kind: "projection_wake_failure",
|
|
@@ -112,10 +128,14 @@ export async function tellProjection(projectionDirectory, projectionId, effort,
|
|
|
112
128
|
const observationStartedAt = Date.now();
|
|
113
129
|
for (;;) {
|
|
114
130
|
const nowMs = Date.now();
|
|
115
|
-
const
|
|
131
|
+
const observation = observeProjectionLifeSnapshot(projectionDirectory, {
|
|
116
132
|
nowMs,
|
|
117
133
|
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
118
134
|
});
|
|
135
|
+
if (observation.compatibility === "incompatible") {
|
|
136
|
+
throw incompatibleProjectionError(projectionId);
|
|
137
|
+
}
|
|
138
|
+
const life = observation.life;
|
|
119
139
|
if (life.phase === "active")
|
|
120
140
|
return "active";
|
|
121
141
|
if (life.phase === "minting" || life.phase === "quiescent") {
|
|
@@ -148,6 +168,7 @@ export async function tellProjection(projectionDirectory, projectionId, effort,
|
|
|
148
168
|
const store = openProjectionGenerationStore(projectionDirectory);
|
|
149
169
|
try {
|
|
150
170
|
if (life.phase === "startup-timeout") {
|
|
171
|
+
const terminalAt = new Date(nowMs).toISOString();
|
|
151
172
|
const transition = store.supersedeStartupTimeout({
|
|
152
173
|
executionId: current.launch.executionId,
|
|
153
174
|
observedAtMs: nowMs,
|
|
@@ -155,6 +176,7 @@ export async function tellProjection(projectionDirectory, projectionId, effort,
|
|
|
155
176
|
diagnostic: { detail: "runner did not adopt before startup timeout" },
|
|
156
177
|
successorExecutionId,
|
|
157
178
|
successorFacts: facts,
|
|
179
|
+
terminalAt,
|
|
158
180
|
});
|
|
159
181
|
if (transition.status === "committed") {
|
|
160
182
|
return await startCommittedSuccessor(projectionDirectory, projectionId, successorExecutionId, startGeneration);
|
|
@@ -168,11 +190,13 @@ export async function tellProjection(projectionDirectory, projectionId, effort,
|
|
|
168
190
|
const lock = observeProjectionRunnerLock(projectionDirectory);
|
|
169
191
|
if (lock.state !== "released")
|
|
170
192
|
return lock.state === "held" ? "active" : "pending";
|
|
193
|
+
const terminalAt = new Date(nowMs).toISOString();
|
|
171
194
|
const transition = store.replaceDeadRunner({
|
|
172
195
|
executionId: current.launch.executionId,
|
|
173
|
-
releasedLockEvidence: { observedAt:
|
|
196
|
+
releasedLockEvidence: { observedAt: terminalAt },
|
|
174
197
|
successorExecutionId,
|
|
175
198
|
successorFacts: facts,
|
|
199
|
+
terminalAt,
|
|
176
200
|
});
|
|
177
201
|
if (transition.status === "committed") {
|
|
178
202
|
return await startCommittedSuccessor(projectionDirectory, projectionId, successorExecutionId, startGeneration);
|