@astrosheep/keiyaku 2.9.8 → 2.9.10
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 +50 -182
- package/build/.tsbuildinfo +1 -1
- package/build/agents/harness/control-types.js +18 -1
- package/build/agents/harness/index.js +2 -0
- package/build/agents/harness/outcome-error.js +81 -0
- package/build/agents/harness/projection.js +10 -7
- package/build/agents/harness/router.js +5 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
- package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
- package/build/agents/providers/claude-agent-sdk/session.js +19 -5
- package/build/agents/providers/codex-app-server/adapter.js +11 -6
- package/build/agents/providers/codex-app-server/events.js +67 -6
- package/build/agents/providers/codex-app-server/registration.js +11 -1
- package/build/agents/providers/opencode-sdk/adapter.js +8 -1
- package/build/agents/providers/opencode-sdk/events.js +24 -2
- package/build/agents/providers/opencode-sdk/registration.js +11 -1
- package/build/agents/providers/pi/checkpoint.js +9 -1
- package/build/agents/providers/pi/events.js +2 -0
- package/build/agents/providers/pi/registration.js +14 -1
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/akuma/meta.js +3 -3
- package/build/cli/commands/akuma/catalog.js +2 -2
- package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
- package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +5 -4
- package/build/cli/commands/contract/arc/handler.js +5 -4
- package/build/cli/commands/contract/audit/handler.js +2 -2
- package/build/cli/commands/contract/bind/handler.js +12 -3
- package/build/cli/commands/contract/bind/meta.js +45 -15
- package/build/cli/commands/contract/forfeit/handler.js +9 -5
- package/build/cli/commands/contract/log/handler.js +2 -2
- package/build/cli/commands/contract/petition/handler.js +34 -7
- package/build/cli/commands/contract/renew/handler.js +7 -7
- package/build/cli/commands/contract/renew/meta.js +3 -3
- package/build/cli/commands/metadata.js +10 -6
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -0
- package/build/cli/commands/projection/history/handler.js +21 -0
- package/build/cli/commands/projection/history/meta.js +9 -0
- package/build/cli/commands/projection/shared.js +3 -4
- package/build/cli/commands/projection/status/handler.js +3 -3
- package/build/cli/commands/projection/wait/handler.js +20 -11
- package/build/cli/commands/task/add/handler.js +14 -5
- package/build/cli/commands/task/add/meta.js +2 -2
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/done/handler.js +1 -4
- 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/hold/handler.js +3 -0
- package/build/cli/commands/task/hold/meta.js +8 -0
- package/build/cli/commands/task/log/handler.js +1 -1
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/resume/handler.js +3 -0
- package/build/cli/commands/task/resume/meta.js +8 -0
- package/build/cli/commands/task/shared.js +39 -14
- package/build/cli/commands/task/show/handler.js +1 -1
- 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/handler.js +1 -4
- package/build/cli/commands/task/stop/meta.js +1 -1
- package/build/cli/commands/task/update/handler.js +11 -3
- package/build/cli/commands/task/update/meta.js +3 -3
- package/build/cli/completion.js +3 -2
- package/build/cli/flags.js +51 -14
- package/build/cli/help.js +4 -1
- package/build/cli/index.js +40 -25
- package/build/cli/parse-flags.js +40 -7
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +2 -3
- package/build/cli/parse.js +40 -6
- package/build/cli/projection-address.js +12 -10
- package/build/cli/render/amendment-warning.js +8 -0
- package/build/cli/render/arc.js +6 -2
- package/build/cli/render/audit.js +400 -81
- package/build/cli/render/kanshi.js +242 -79
- package/build/cli/render/lifecycle.js +56 -0
- package/build/cli/render/misc.js +24 -28
- package/build/cli/render/petition.js +5 -0
- package/build/cli/render/projection-activity.js +65 -29
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +13 -35
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +45 -69
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +3 -1
- package/build/config/env.js +24 -1
- package/build/config/settings/schema.js +3 -1
- package/build/core/address-carrier.js +89 -0
- package/build/core/addressing.js +255 -196
- package/build/core/amend.js +115 -38
- package/build/core/amendment-criteria.js +17 -0
- package/build/core/amendment-warning.js +10 -0
- package/build/core/arc.js +184 -83
- package/build/core/audit/candidate.js +110 -9
- package/build/core/audit/coordinates.js +120 -65
- package/build/core/audit/evidence.js +19 -23
- package/build/core/audit/facade.js +34 -11
- package/build/core/audit/report.js +5 -5
- package/build/core/bind-reconciliation.js +390 -0
- package/build/core/bind-workspace.js +12 -20
- package/build/core/bind.js +242 -273
- package/build/core/call/call.js +1 -0
- package/build/core/call/context.js +46 -71
- package/build/core/call/prompt.js +6 -3
- package/build/core/context.js +49 -35
- package/build/core/contract-carrier-runtime.js +408 -0
- package/build/core/contract-carrier.js +270 -0
- package/build/core/contract-view.js +33 -27
- package/build/core/contract.js +5 -1
- package/build/core/derived-replay.js +207 -0
- package/build/core/draft.js +125 -57
- package/build/core/forfeit.js +113 -56
- package/build/core/identifier-slug.js +63 -13
- package/build/core/ids.js +4 -4
- package/build/core/lifecycle-history.js +35 -0
- package/build/core/lifecycle-recovery.js +117 -0
- package/build/core/lifecycle-runner.js +377 -0
- package/build/core/log.js +37 -11
- package/build/core/outcome-base.js +181 -0
- package/build/core/process-group.js +139 -0
- package/build/core/projection/generation/database.js +82 -7
- package/build/core/projection/generation/model.js +9 -36
- package/build/core/projection/generation/projection-generation-continuation.js +10 -22
- package/build/core/projection/generation/projection-generation-execution.js +13 -6
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +29 -8
- package/build/core/projection/projection-alias.js +23 -10
- package/build/core/projection/projection-core.js +21 -7
- package/build/core/projection/projection-execution-observer.js +91 -39
- package/build/core/projection/projection-history.js +474 -0
- package/build/core/projection/projection-runner-lock.js +12 -0
- package/build/core/projection/projection-status-observation.js +365 -0
- package/build/core/projection/projection-status.js +35 -337
- package/build/core/projection/projection-terminal-failure.js +13 -8
- package/build/core/projection/projection-wait.js +7 -2
- package/build/core/projection/tell/launch-store.js +244 -0
- package/build/core/projection/tell/model.js +3 -3
- package/build/core/projection/tell/store.js +70 -118
- package/build/core/registry.js +22 -87
- package/build/core/renew-build.js +424 -0
- package/build/core/renew-plan.js +239 -0
- package/build/core/renew-rewrite.js +77 -0
- package/build/core/renew.js +295 -520
- package/build/core/repository-ledger/accepted-fold-read.js +19 -0
- package/build/core/repository-ledger/accepted-tail.js +116 -0
- package/build/core/repository-ledger/admission.js +38 -0
- package/build/core/repository-ledger/authoritative-transaction.js +1 -0
- package/build/core/repository-ledger/claim-evidence.js +147 -0
- package/build/core/repository-ledger/codec.js +297 -0
- package/build/core/repository-ledger/current-state-store.js +853 -0
- package/build/core/repository-ledger/fold-repository.js +375 -0
- package/build/core/repository-ledger/fold.js +248 -0
- package/build/core/repository-ledger/git-object-store.js +243 -0
- package/build/core/repository-ledger/history-read.js +15 -0
- package/build/core/repository-ledger/incremental-read.js +153 -0
- package/build/core/repository-ledger/index.js +15 -0
- package/build/core/repository-ledger/inventory-plan.js +75 -0
- package/build/core/repository-ledger/inventory.js +310 -0
- package/build/core/repository-ledger/publication-admission.js +125 -0
- package/build/core/repository-ledger/publication.js +353 -0
- package/build/core/repository-ledger/read-model.js +469 -0
- package/build/core/repository-ledger/transaction-membership.js +78 -0
- package/build/core/repository-ledger/traversal.js +404 -0
- package/build/core/repository-ledger/write-transaction.js +339 -0
- package/build/core/seal.js +168 -161
- package/build/core/settlement/claim-delivery.js +42 -215
- package/build/core/settlement/claim.js +235 -153
- package/build/core/settlement/index.js +4 -4
- package/build/core/settlement/petition-claim-gates.js +52 -33
- package/build/core/settlement/petition-forfeit.js +12 -5
- package/build/core/settlement/petition-head-guard.js +8 -5
- package/build/core/settlement/petition-preview.js +68 -56
- package/build/core/settlement/petition-window.js +50 -0
- package/build/core/settlement/petition.js +252 -131
- package/build/core/settlement/queue-read-model.js +72 -95
- package/build/core/settlement/queue-seat-allocation.js +35 -126
- package/build/core/settlement/queue.js +1 -1
- package/build/core/settlement/settlement.js +172 -80
- package/build/core/settlement/verdict.js +112 -66
- package/build/core/settlement/verification-coordination.js +305 -0
- package/build/core/settlement/verification-supervisor.js +275 -0
- package/build/core/settlement/verification.js +398 -31
- package/build/core/status/board.js +125 -127
- package/build/core/status/lifecycle.js +7 -80
- package/build/core/structured-query.js +23 -0
- package/build/core/target-ref.js +1 -1
- package/build/core/task/board.js +59 -24
- package/build/core/task/commands.js +181 -33
- package/build/core/task/coordinate.js +56 -0
- package/build/core/task/document.js +25 -9
- package/build/core/task/index.js +7 -4
- package/build/core/task/query.js +17 -27
- package/build/core/task/settlement-git.js +164 -179
- package/build/core/task/settlement-policy.js +15 -11
- package/build/core/task/source-board.js +31 -13
- package/build/core/task/task-bind-preparation.js +193 -0
- package/build/core/task/task-contract.js +102 -193
- package/build/core/task/task-file-transaction.js +171 -0
- package/build/core/task/task-git-runtime.js +76 -14
- package/build/core/task/task-git-store.js +68 -169
- package/build/core/task/task-store-repository.js +13 -6
- package/build/core/task/task-worktree.js +6 -33
- package/build/core/task/task.js +1 -1
- package/build/core/task/tree.js +3 -3
- package/build/core/task/validation.js +4 -3
- package/build/core/transcripts.js +93 -1
- package/build/core/worktree-bootstrap.js +44 -36
- package/build/core/worktree-path.js +210 -77
- package/build/flow-error.js +7 -7
- package/build/generated/version.js +2 -2
- package/build/git/core.js +76 -13
- package/build/git/refs.js +168 -46
- package/build/git/streaming-batch.js +73 -13
- package/package.json +1 -1
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +3 -3
- package/skills/keiyaku-task/SKILL.md +4 -2
- package/skills/keiyaku-workflow/SKILL.md +88 -203
- package/build/core/entry.js +0 -305
- package/build/core/ledger-batch.js +0 -194
- package/build/core/ledger.js +0 -84
- package/build/core/projection/leash.js +0 -87
- package/build/core/ref-log.js +0 -119
- package/build/core/renew-session.js +0 -128
- package/build/core/status/ledger-batch.js +0 -1
- package/build/core/status/reconciliation.js +0 -137
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { isErrnoException } from "../../errno.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { foldPublicProjectionActivity, } from "./projection-activity.js";
|
|
4
|
+
import { isProjectionId, projectionRoot, projectionRootAtCoordinate } from "./projection-core.js";
|
|
5
|
+
import { projectionAliasRootAtCoordinate, readProjectionAliasAtRoot, } from "./projection-alias.js";
|
|
6
|
+
import { resolveProjectionCoordinate } from "./projection-coordinate.js";
|
|
8
7
|
import { readProjectionIdentity } from "./projection-identity.js";
|
|
9
|
-
import { readProjectionTellCounts } from "./tell/store.js";
|
|
10
|
-
import { ProjectionGenerationStoreError, } from "./generation/store.js";
|
|
11
8
|
import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
|
|
12
9
|
import { observeProjectionLifeSnapshot, } from "./projection-life-observer.js";
|
|
13
|
-
import { selectCurrentGeneration, } from "./projection-life-protocol.js";
|
|
14
|
-
import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "../stored-agent-event.js";
|
|
15
10
|
import { worktreeContainsCoordinate } from "../worktree-path.js";
|
|
16
|
-
import {
|
|
17
|
-
import { extractTerminalTimestamp } from "./generation/model.js";
|
|
11
|
+
import { readProjectionRow, readProjectionSummary } from "./projection-status-observation.js";
|
|
18
12
|
const MAX_STATUS_FILE_BYTES = 256 * 1024;
|
|
19
13
|
const STATUS_GROUP = {
|
|
20
14
|
"startup-timeout": 0,
|
|
@@ -84,324 +78,13 @@ export function prioritizeProjectionStatusRows(rows) {
|
|
|
84
78
|
export function requiresProjectionStatusDetails(state) {
|
|
85
79
|
return !isOmissibleProjectionStatusState(state);
|
|
86
80
|
}
|
|
87
|
-
|
|
88
|
-
diagnostic;
|
|
89
|
-
constructor(kind, filePath) {
|
|
90
|
-
const label = path.basename(filePath);
|
|
91
|
-
const detail = `${label} is not a regular file`;
|
|
92
|
-
super(detail);
|
|
93
|
-
this.name = "ProjectionStatusFileError";
|
|
94
|
-
this.diagnostic = { kind, detail };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function oversizedProjectionFileDiagnostic(filePath) {
|
|
98
|
-
return {
|
|
99
|
-
kind: "oversized-projection-file",
|
|
100
|
-
fileName: path.basename(filePath),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function recordProjectionStatusFileError(error, diagnostics) {
|
|
104
|
-
if (!(error instanceof ProjectionStatusFileError))
|
|
105
|
-
return false;
|
|
106
|
-
diagnostics.push(error.diagnostic);
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
function readBoundedRegularFileTail(filePath) {
|
|
110
|
-
const before = fs.lstatSync(filePath);
|
|
111
|
-
if (!before.isFile())
|
|
112
|
-
throw new ProjectionStatusFileError("non-regular-projection-file", filePath);
|
|
113
|
-
let fd;
|
|
114
|
-
try {
|
|
115
|
-
fd = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK | fs.constants.O_NOFOLLOW);
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
if (isErrnoException(error) && error.code === "ELOOP") {
|
|
119
|
-
throw new ProjectionStatusFileError("non-regular-projection-file", filePath);
|
|
120
|
-
}
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
const opened = fs.fstatSync(fd);
|
|
125
|
-
if (!opened.isFile())
|
|
126
|
-
throw new ProjectionStatusFileError("non-regular-projection-file", filePath);
|
|
127
|
-
const snapshotSize = opened.size;
|
|
128
|
-
const truncated = snapshotSize > MAX_STATUS_FILE_BYTES;
|
|
129
|
-
const readLength = Math.min(snapshotSize, MAX_STATUS_FILE_BYTES);
|
|
130
|
-
const readOffset = Math.max(0, snapshotSize - MAX_STATUS_FILE_BYTES);
|
|
131
|
-
const buffer = Buffer.allocUnsafe(readLength);
|
|
132
|
-
let total = 0;
|
|
133
|
-
while (total < readLength) {
|
|
134
|
-
const bytesRead = fs.readSync(fd, buffer, total, readLength - total, readOffset + total);
|
|
135
|
-
if (bytesRead === 0)
|
|
136
|
-
break;
|
|
137
|
-
total += bytesRead;
|
|
138
|
-
}
|
|
139
|
-
let tail = buffer.subarray(0, total);
|
|
140
|
-
if (truncated) {
|
|
141
|
-
const firstLf = tail.indexOf(0x0a);
|
|
142
|
-
if (firstLf < 0) {
|
|
143
|
-
return { content: "", truncated: true };
|
|
144
|
-
}
|
|
145
|
-
tail = tail.subarray(firstLf + 1);
|
|
146
|
-
}
|
|
147
|
-
return { content: tail.toString("utf8"), truncated };
|
|
148
|
-
}
|
|
149
|
-
finally {
|
|
150
|
-
fs.closeSync(fd);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function displayedTellCounts(counts) {
|
|
154
|
-
return {
|
|
155
|
-
inbox: counts.inbox,
|
|
156
|
-
inflight: counts.inflight,
|
|
157
|
-
submitted: counts.submitted,
|
|
158
|
-
consumed: counts.consumed,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
function emptyTellCounts() {
|
|
162
|
-
return { inbox: 0, inflight: 0, submitted: 0, consumed: 0 };
|
|
163
|
-
}
|
|
164
|
-
function readIdentityFacts(projectionDir, diagnostics) {
|
|
165
|
-
try {
|
|
166
|
-
const identity = readProjectionIdentity(projectionDir);
|
|
167
|
-
return {
|
|
168
|
-
identity,
|
|
169
|
-
pid: identity.pid,
|
|
170
|
-
akuma: identity.akuma,
|
|
171
|
-
provider: identity.provider,
|
|
172
|
-
mintedAt: identity.mintedAt,
|
|
173
|
-
workspaceRoot: identity.workspaceRoot,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
178
|
-
diagnostics.push({ kind: "missing-identity", detail: "heart identity missing" });
|
|
179
|
-
return { identity: null, pid: undefined, akuma: undefined, provider: undefined, mintedAt: undefined, workspaceRoot: undefined };
|
|
180
|
-
}
|
|
181
|
-
if (error instanceof ProjectionGenerationStoreError) {
|
|
182
|
-
diagnostics.push({ kind: "invalid-identity", detail: error.message });
|
|
183
|
-
return { identity: null, pid: undefined, akuma: undefined, provider: undefined, mintedAt: undefined, workspaceRoot: undefined };
|
|
184
|
-
}
|
|
185
|
-
if (error instanceof ProjectionStateError) {
|
|
186
|
-
diagnostics.push({ kind: "invalid-identity", detail: error.message });
|
|
187
|
-
return { identity: null, pid: undefined, akuma: undefined, provider: undefined, mintedAt: undefined, workspaceRoot: undefined };
|
|
188
|
-
}
|
|
189
|
-
diagnostics.push({ kind: "unreadable-identity", detail: `heart identity unreadable: ${String(error)}` });
|
|
190
|
-
return { identity: null, pid: undefined, akuma: undefined, provider: undefined, mintedAt: undefined, workspaceRoot: undefined };
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function readHeartTellCounts(projectionDir, diagnostics) {
|
|
194
|
-
try {
|
|
195
|
-
return displayedTellCounts(readProjectionTellCounts(projectionDir));
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
if (error instanceof ProjectionGenerationStoreError || error instanceof ProjectionStateError) {
|
|
199
|
-
diagnostics.push({ kind: "unreadable-tells", detail: error.message });
|
|
200
|
-
return emptyTellCounts();
|
|
201
|
-
}
|
|
202
|
-
diagnostics.push({ kind: "unreadable-tells", detail: `heart tells unreadable: ${String(error)}` });
|
|
203
|
-
return emptyTellCounts();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function readStoredEvents(projectionDir, executionId, diagnostics) {
|
|
207
|
-
let content;
|
|
208
|
-
let truncated = false;
|
|
209
|
-
const eventsPath = executionEventsPath(projectionDir, executionId);
|
|
210
|
-
try {
|
|
211
|
-
const read = readBoundedRegularFileTail(eventsPath);
|
|
212
|
-
content = read.content;
|
|
213
|
-
truncated = read.truncated;
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
if (isErrnoException(error) && error.code === "ENOENT")
|
|
217
|
-
return [];
|
|
218
|
-
if (recordProjectionStatusFileError(error, diagnostics))
|
|
219
|
-
return [];
|
|
220
|
-
diagnostics.push({ kind: "unreadable-activity", detail: `activity ledger unreadable: ${String(error)}` });
|
|
221
|
-
return [];
|
|
222
|
-
}
|
|
223
|
-
if (truncated) {
|
|
224
|
-
diagnostics.push(oversizedProjectionFileDiagnostic(eventsPath));
|
|
225
|
-
}
|
|
226
|
-
// Parse only LF-terminated records. An unterminated final line is ignored even
|
|
227
|
-
// when it happens to parse: it may be a concurrent append fragment.
|
|
228
|
-
const lines = content.split("\n");
|
|
229
|
-
const values = [];
|
|
230
|
-
let malformed = 0;
|
|
231
|
-
for (const line of lines.slice(0, -1)) {
|
|
232
|
-
try {
|
|
233
|
-
values.push(JSON.parse(line));
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
malformed += 1;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (malformed > 0) {
|
|
240
|
-
diagnostics.push({
|
|
241
|
-
kind: "invalid-activity-event",
|
|
242
|
-
detail: `${malformed} persisted activity event${malformed === 1 ? "" : "s"} malformed`,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
return values;
|
|
246
|
-
}
|
|
247
|
-
function latestValidEvent(events) {
|
|
248
|
-
let latest;
|
|
249
|
-
for (const event of events) {
|
|
250
|
-
const atMs = Date.parse(event.at);
|
|
251
|
-
if (!Number.isFinite(atMs))
|
|
252
|
-
continue;
|
|
253
|
-
if (latest === undefined || atMs > latest.atMs)
|
|
254
|
-
latest = { at: event.at, atMs };
|
|
255
|
-
}
|
|
256
|
-
return latest;
|
|
257
|
-
}
|
|
258
|
-
function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallbackAt) {
|
|
259
|
-
const fallbackAtMs = fallbackAt === undefined ? undefined : Date.parse(fallbackAt);
|
|
260
|
-
const validFallback = fallbackAt !== undefined && Number.isFinite(fallbackAtMs)
|
|
261
|
-
? { at: fallbackAt, atMs: fallbackAtMs }
|
|
262
|
-
: undefined;
|
|
263
|
-
if (!executionId) {
|
|
264
|
-
return validFallback
|
|
265
|
-
? { activityAt: validFallback.at, activityAtMs: validFallback.atMs, activityAgeMs: Math.max(0, nowMs - validFallback.atMs) }
|
|
266
|
-
: {};
|
|
267
|
-
}
|
|
268
|
-
const stored = readStoredEvents(projectionDir, executionId, diagnostics);
|
|
269
|
-
const { events, malformed } = selectValidStoredAgentEvents(stored);
|
|
270
|
-
const invalidActivity = invalidActivityEventDiagnostic(malformed);
|
|
271
|
-
if (invalidActivity)
|
|
272
|
-
diagnostics.push(invalidActivity);
|
|
273
|
-
const latest = latestValidEvent(events) ?? validFallback;
|
|
274
|
-
return {
|
|
275
|
-
...(events.length > 0 ? { activity: foldPublicProjectionActivity(events) } : {}),
|
|
276
|
-
...(latest !== undefined
|
|
277
|
-
? {
|
|
278
|
-
activityAt: latest.at,
|
|
279
|
-
activityAtMs: latest.atMs,
|
|
280
|
-
activityAgeMs: Math.max(0, nowMs - latest.atMs),
|
|
281
|
-
}
|
|
282
|
-
: {}),
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
function terminalTimestamp(current) {
|
|
286
|
-
return extractTerminalTimestamp(current?.verdict?.facts);
|
|
287
|
-
}
|
|
288
|
-
function readProjectionSummary(projectionDir, id, alias, nowMs) {
|
|
289
|
-
const diagnostics = [];
|
|
290
|
-
const snapshot = observeProjectionLifeSnapshot(projectionDir, {
|
|
291
|
-
nowMs,
|
|
292
|
-
startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
|
|
293
|
-
});
|
|
294
|
-
if (snapshot.compatibility === "incompatible") {
|
|
295
|
-
return {
|
|
296
|
-
id,
|
|
297
|
-
...(alias ? { alias } : {}),
|
|
298
|
-
projectionDir,
|
|
299
|
-
snapshot,
|
|
300
|
-
identityFacts: null,
|
|
301
|
-
diagnostics,
|
|
302
|
-
state: "incompatible",
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
const identityFacts = readIdentityFacts(projectionDir, diagnostics);
|
|
306
|
-
const life = snapshot.life;
|
|
307
|
-
const phase = life.phase;
|
|
308
|
-
const state = phase === "active" || phase === "quiescent" ? "out" : phase;
|
|
309
|
-
return {
|
|
310
|
-
id,
|
|
311
|
-
...(alias ? { alias } : {}),
|
|
312
|
-
projectionDir,
|
|
313
|
-
snapshot,
|
|
314
|
-
identityFacts,
|
|
315
|
-
diagnostics,
|
|
316
|
-
state,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
function readProjectionRow(summary, nowMs, readDetails) {
|
|
320
|
-
const { id, alias, projectionDir, snapshot } = summary;
|
|
321
|
-
const diagnostics = [...summary.diagnostics];
|
|
322
|
-
if (snapshot.compatibility === "incompatible") {
|
|
323
|
-
return {
|
|
324
|
-
id,
|
|
325
|
-
...(alias ? { alias } : {}),
|
|
326
|
-
state: "incompatible",
|
|
327
|
-
observation: snapshot,
|
|
328
|
-
tellCounts: emptyTellCounts(),
|
|
329
|
-
diagnostics,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
const identityFacts = summary.identityFacts;
|
|
333
|
-
if (!identityFacts.identity) {
|
|
334
|
-
const diagnostic = diagnostics.map((entry) => entry.detail).find((detail) => detail !== undefined)
|
|
335
|
-
?? "projection identity could not be observed";
|
|
336
|
-
return {
|
|
337
|
-
id,
|
|
338
|
-
...(alias ? { alias } : {}),
|
|
339
|
-
state: "unknown",
|
|
340
|
-
observation: {
|
|
341
|
-
compatibility: "current",
|
|
342
|
-
lifecycle: { phase: "unknown", diagnostic },
|
|
343
|
-
},
|
|
344
|
-
tellCounts: emptyTellCounts(),
|
|
345
|
-
diagnostics,
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
const life = snapshot.life;
|
|
349
|
-
const current = snapshot.records ? selectCurrentGeneration(snapshot.records) : null;
|
|
350
|
-
const adoptedAt = current?.adoption?.facts.adoptedAt;
|
|
351
|
-
const adoptedAtMs = typeof adoptedAt === "string"
|
|
352
|
-
? Date.parse(adoptedAt)
|
|
353
|
-
: Number.NaN;
|
|
354
|
-
const tellCounts = readDetails
|
|
355
|
-
? readHeartTellCounts(projectionDir, diagnostics)
|
|
356
|
-
: emptyTellCounts();
|
|
357
|
-
const activityFacts = readDetails
|
|
358
|
-
? readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, typeof adoptedAt === "string" && Number.isFinite(adoptedAtMs) ? adoptedAt : identityFacts.mintedAt)
|
|
359
|
-
: {};
|
|
360
|
-
const mintedAtMs = Date.parse(identityFacts.mintedAt ?? "");
|
|
361
|
-
const terminalAt = terminalTimestamp(current);
|
|
362
|
-
const durationAnchorMs = current?.verdict
|
|
363
|
-
? (terminalAt === undefined ? Number.NaN : Date.parse(terminalAt))
|
|
364
|
-
: nowMs;
|
|
365
|
-
const durationMs = Number.isFinite(mintedAtMs) && Number.isFinite(durationAnchorMs)
|
|
366
|
-
? Math.max(0, durationAnchorMs - mintedAtMs)
|
|
367
|
-
: undefined;
|
|
368
|
-
const displayAgeMs = activityFacts.activityAgeMs
|
|
369
|
-
?? (Number.isFinite(mintedAtMs) ? Math.max(0, nowMs - mintedAtMs) : undefined);
|
|
370
|
-
if (life.phase === "unknown" && life.diagnostic) {
|
|
371
|
-
diagnostics.push({ kind: "unknown-lifecycle", detail: life.diagnostic });
|
|
372
|
-
}
|
|
373
|
-
const phase = life.phase;
|
|
374
|
-
const state = phase === "active" || phase === "quiescent" ? "out" : phase;
|
|
375
|
-
const terminalFailure = current?.verdict
|
|
376
|
-
? projectTerminalFailure(current.verdict.facts.state, current.verdict.facts)
|
|
377
|
-
: undefined;
|
|
378
|
-
return {
|
|
379
|
-
id,
|
|
380
|
-
...(alias ? { alias } : {}),
|
|
381
|
-
state,
|
|
382
|
-
observation: { compatibility: "current", lifecycle: life },
|
|
383
|
-
akuma: identityFacts.akuma,
|
|
384
|
-
provider: identityFacts.provider,
|
|
385
|
-
pid: identityFacts.pid,
|
|
386
|
-
mintedAt: identityFacts.mintedAt,
|
|
387
|
-
...(terminalAt ? { terminalAt } : {}),
|
|
388
|
-
...(durationMs !== undefined ? { durationMs } : {}),
|
|
389
|
-
...(identityFacts.workspaceRoot ? { workspaceRoot: identityFacts.workspaceRoot } : {}),
|
|
390
|
-
...activityFacts,
|
|
391
|
-
...(displayAgeMs !== undefined ? { activityAgeMs: displayAgeMs } : {}),
|
|
392
|
-
...(terminalFailure ? { terminalFailure } : {}),
|
|
393
|
-
tellCounts,
|
|
394
|
-
diagnostics,
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
async function readProjectionStatusAliases(cwd, root) {
|
|
81
|
+
async function readProjectionStatusAliases(root) {
|
|
398
82
|
const aliases = new Map();
|
|
399
|
-
const aliasRoot = path.join(path.dirname(path.dirname(root)), ".keiyaku", "projection-alias");
|
|
400
83
|
try {
|
|
401
|
-
for (const alias of fs.readdirSync(
|
|
84
|
+
for (const alias of fs.readdirSync(root).sort()) {
|
|
402
85
|
if (alias.startsWith("."))
|
|
403
86
|
continue;
|
|
404
|
-
const id = await
|
|
87
|
+
const id = await readProjectionAliasAtRoot(root, alias);
|
|
405
88
|
if (id)
|
|
406
89
|
aliases.set(id, alias);
|
|
407
90
|
}
|
|
@@ -424,8 +107,8 @@ function readProjectionTopologyEntries(directory) {
|
|
|
424
107
|
throw error;
|
|
425
108
|
}
|
|
426
109
|
}
|
|
427
|
-
|
|
428
|
-
const root =
|
|
110
|
+
function readProjectionRootTopology(coordinate) {
|
|
111
|
+
const root = projectionRootAtCoordinate(coordinate);
|
|
429
112
|
const akumas = readProjectionTopologyEntries(root);
|
|
430
113
|
return akumas ? { root, akumas } : null;
|
|
431
114
|
}
|
|
@@ -433,7 +116,7 @@ function readAkumaProjectionStatusSummaries(root, akuma, aliases, nowMs) {
|
|
|
433
116
|
const akumaDir = path.join(root, akuma);
|
|
434
117
|
const hexes = readProjectionTopologyEntries(akumaDir);
|
|
435
118
|
if (!hexes)
|
|
436
|
-
return
|
|
119
|
+
return null;
|
|
437
120
|
const summaries = [];
|
|
438
121
|
for (const hex of hexes) {
|
|
439
122
|
const id = `${akuma}/${hex}`;
|
|
@@ -447,7 +130,9 @@ function readAkumaProjectionStatusSummaries(root, akuma, aliases, nowMs) {
|
|
|
447
130
|
catch {
|
|
448
131
|
continue;
|
|
449
132
|
}
|
|
450
|
-
summaries.push(readProjectionSummary(dir, id, aliases.get(id), nowMs
|
|
133
|
+
summaries.push(readProjectionSummary(dir, id, aliases.get(id), nowMs, {
|
|
134
|
+
requiresIdentity: requiresProjectionStatusDetails,
|
|
135
|
+
}));
|
|
451
136
|
}
|
|
452
137
|
return summaries;
|
|
453
138
|
}
|
|
@@ -456,24 +141,33 @@ function isNonterminalProjectionStatusSummary(summary) {
|
|
|
456
141
|
&& requiresProjectionStatusDetails(summary.state);
|
|
457
142
|
}
|
|
458
143
|
export async function readOperationalProjectionStatusBoard(cwd, nowMs) {
|
|
459
|
-
const
|
|
144
|
+
const coordinate = await resolveProjectionCoordinate(cwd);
|
|
145
|
+
const topology = readProjectionRootTopology(coordinate);
|
|
460
146
|
if (!topology)
|
|
461
147
|
return null;
|
|
462
148
|
const { root, akumas } = topology;
|
|
463
|
-
const aliases = await readProjectionStatusAliases(
|
|
149
|
+
const aliases = await readProjectionStatusAliases(projectionAliasRootAtCoordinate(coordinate));
|
|
464
150
|
const summaries = [];
|
|
465
151
|
for (const akuma of akumas) {
|
|
466
152
|
if (akuma.startsWith("."))
|
|
467
153
|
continue;
|
|
468
|
-
|
|
154
|
+
const scoped = readAkumaProjectionStatusSummaries(root, akuma, aliases, nowMs);
|
|
155
|
+
// Seeing an entry and then being unable to observe its namespace is not an
|
|
156
|
+
// observable zero. Keep the whole aggregate surface unavailable.
|
|
157
|
+
if (scoped === null)
|
|
158
|
+
return null;
|
|
159
|
+
summaries.push(...scoped);
|
|
469
160
|
}
|
|
470
161
|
const rows = summaries.map((summary) => readProjectionRow(summary, nowMs, requiresProjectionStatusDetails(summary.state)));
|
|
471
|
-
|
|
162
|
+
// A published root is an observable surface even when it currently has no
|
|
163
|
+
// rows. The caller must distinguish that fact from an unavailable root.
|
|
164
|
+
return prioritizeProjectionStatusRows(rows);
|
|
472
165
|
}
|
|
473
166
|
export async function readExactProjectionStatus(cwd, nowMs, projectionId) {
|
|
474
167
|
if (!isProjectionId(projectionId))
|
|
475
168
|
return null;
|
|
476
|
-
const
|
|
169
|
+
const coordinate = await resolveProjectionCoordinate(cwd);
|
|
170
|
+
const root = projectionRootAtCoordinate(coordinate);
|
|
477
171
|
try {
|
|
478
172
|
if (!fs.lstatSync(root).isDirectory())
|
|
479
173
|
return null;
|
|
@@ -494,13 +188,14 @@ export async function readExactProjectionStatus(cwd, nowMs, projectionId) {
|
|
|
494
188
|
return null;
|
|
495
189
|
throw error;
|
|
496
190
|
}
|
|
497
|
-
const aliases = await readProjectionStatusAliases(
|
|
191
|
+
const aliases = await readProjectionStatusAliases(projectionAliasRootAtCoordinate(coordinate));
|
|
498
192
|
return readProjectionRow(readProjectionSummary(dir, projectionId, aliases.get(projectionId), nowMs), nowMs, true);
|
|
499
193
|
}
|
|
500
194
|
export async function readAkumaNonterminalProjectionStatus(cwd, nowMs, akuma) {
|
|
501
195
|
if (!isProjectionId(`${akuma}/00000000`))
|
|
502
196
|
return null;
|
|
503
|
-
const
|
|
197
|
+
const coordinate = await resolveProjectionCoordinate(cwd);
|
|
198
|
+
const root = projectionRootAtCoordinate(coordinate);
|
|
504
199
|
try {
|
|
505
200
|
if (!fs.lstatSync(root).isDirectory())
|
|
506
201
|
return null;
|
|
@@ -510,8 +205,11 @@ export async function readAkumaNonterminalProjectionStatus(cwd, nowMs, akuma) {
|
|
|
510
205
|
return null;
|
|
511
206
|
throw error;
|
|
512
207
|
}
|
|
513
|
-
const aliases = await readProjectionStatusAliases(
|
|
514
|
-
const
|
|
208
|
+
const aliases = await readProjectionStatusAliases(projectionAliasRootAtCoordinate(coordinate));
|
|
209
|
+
const summaries = readAkumaProjectionStatusSummaries(root, akuma, aliases, nowMs);
|
|
210
|
+
if (summaries === null)
|
|
211
|
+
return null;
|
|
212
|
+
const rows = summaries
|
|
515
213
|
.filter(isNonterminalProjectionStatusSummary)
|
|
516
214
|
.map((summary) => readProjectionRow(summary, nowMs, true))
|
|
517
215
|
.sort(compareProjectionStatusRows);
|
|
@@ -43,15 +43,19 @@ function artifactId(source) {
|
|
|
43
43
|
}
|
|
44
44
|
function providerFailureCause(value) {
|
|
45
45
|
const source = objectValue(value);
|
|
46
|
-
return source
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
return source && isHarnessFailureValue(source) ? source : undefined;
|
|
47
|
+
}
|
|
48
|
+
function isHarnessFailureValue(value) {
|
|
49
|
+
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
50
|
+
return true;
|
|
51
|
+
if (typeof value === "number")
|
|
52
|
+
return Number.isFinite(value);
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.every(isHarnessFailureValue);
|
|
55
|
+
const object = objectValue(value);
|
|
56
|
+
return object !== undefined && Object.values(object).every(isHarnessFailureValue);
|
|
49
57
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Convert artifact-rich verdict facts into the strict public allowlist. Raw
|
|
52
|
-
* messages, output, diagnostics, sessions, provider payloads, and unknown
|
|
53
|
-
* strings have no path through this function.
|
|
54
|
-
*/
|
|
58
|
+
/** Convert the public harness terminal failure into the shared projection face. */
|
|
55
59
|
export function projectTerminalFailure(state, facts) {
|
|
56
60
|
if (state !== "failed" && state !== "lost-runner" && state !== "launch-failed")
|
|
57
61
|
return undefined;
|
|
@@ -97,6 +101,7 @@ export function projectTerminalFailure(state, facts) {
|
|
|
97
101
|
class: "harness",
|
|
98
102
|
code,
|
|
99
103
|
phase: source.phase === "adoption" ? "adoption" : "runtime",
|
|
104
|
+
...(typeof error.message === "string" ? { message: error.message } : {}),
|
|
100
105
|
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
101
106
|
...(publicExitCode !== undefined ? { exitCode: publicExitCode } : {}),
|
|
102
107
|
...(publicSignal ? { signal: publicSignal } : {}),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { foldPublicProjectionActivity } from "./projection-activity.js";
|
|
4
|
-
import { readGenerationEvents, isProjectionId,
|
|
4
|
+
import { readGenerationEvents, isProjectionId, projectionRootAtCoordinate, } from "./projection-core.js";
|
|
5
|
+
import { resolveProjectionCoordinate } from "./projection-coordinate.js";
|
|
5
6
|
import { readProjectionIdentity } from "./projection-identity.js";
|
|
6
7
|
import { RESPONSE_ARTIFACT_ID_RE } from "../response-artifact-id.js";
|
|
7
8
|
import { extractTerminalTimestamp } from "./generation/model.js";
|
|
@@ -15,7 +16,11 @@ import { tellProjection } from "./projection-wake.js";
|
|
|
15
16
|
import { FlowError } from "../../flow-error.js";
|
|
16
17
|
/** Return the commission-bound projection IDs visible at one admission instant. */
|
|
17
18
|
export async function snapshotCommissionProjectionIds(cwd, commissionId) {
|
|
18
|
-
|
|
19
|
+
return snapshotCommissionProjectionIdsAtCoordinate(await resolveProjectionCoordinate(cwd), commissionId);
|
|
20
|
+
}
|
|
21
|
+
/** Scan one already-resolved projection coordinate for commission-bound IDs. */
|
|
22
|
+
export async function snapshotCommissionProjectionIdsAtCoordinate(coordinate, commissionId) {
|
|
23
|
+
const root = projectionRootAtCoordinate(coordinate);
|
|
19
24
|
let akumas;
|
|
20
25
|
try {
|
|
21
26
|
akumas = fs.readdirSync(root).sort();
|