@akagilnc/pi-workflow-roles 0.1.5483 → 0.1.5571
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +6312 -6376
- package/dist/analyst-gate-cycles-read.js +28 -78
- package/dist/analyst-ledger.js +26 -42
- package/dist/analyst-metric-families/acceptance-success-rework.js +25 -1
- package/dist/analyst-metric-families/round-timeline.js +2 -5
- package/dist/archivist-record-entry.js +4 -3
- package/dist/archivist-record-pointer.js +71 -36
- package/dist/atomic-write.js +2 -2
- package/dist/auditor-dossier-tool.js +11 -6
- package/dist/auditor-soul.js +5 -12
- package/dist/collector-config.js +6 -5
- package/dist/doctor-evidence.js +8 -4
- package/dist/engine-detour-usage.js +8 -22
- package/dist/headless-host/production-host.js +5969 -6011
- package/dist/host-descriptions.js +0 -4
- package/dist/host-session-record.js +266 -267
- package/dist/ledger-session-read.js +5 -0
- package/dist/migrate-book-topology.js +1369 -722
- package/dist/navigator-public-session.js +13 -17
- package/dist/notary-source-run.js +8 -16
- package/dist/packaged-role-registry.js +5 -56
- package/dist/pi/durable-principal.js +2 -3
- package/dist/pi/role-turn-host.js +79 -15
- package/dist/public-cli/auto-resume.js +8 -47
- package/dist/public-cli/instruction-seat-run.js +9 -7
- package/dist/public-cli/invocation.js +67 -100
- package/dist/public-cli/main.js +2689 -2532
- package/dist/public-cli/post-admission.js +96 -55
- package/dist/public-cli/run-lifecycle.js +44 -100
- package/dist/public-cli/settlement.js +184 -429
- package/dist/public-cli/terminal.js +0 -12
- package/dist/role-run-placement.js +53 -16
- package/dist/role-run-relocation.js +111 -52
- package/dist/run-dossier-files.js +30 -0
- package/dist/run-dossier.js +322 -0
- package/dist/run-terminal-artifacts.js +69 -202
- package/dist/run-ticket-number.js +49 -7
- package/dist/session-assistant-usage.js +9 -18
- package/dist/session-identity.js +49 -19
- package/dist/sitian-appender.js +29 -18
- package/dist/sitian-facade.js +11 -0
- package/dist/sitian-reader.js +44 -29
- package/dist/submission-gate.js +1 -1
- package/dist/submission-ledger.js +4 -4
- package/dist/ticket-provenance-contracts.js +2 -8
- package/dist/ticket-provenance.js +25 -9
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -0
- package/src/acp-host/production-host.ts +1 -1
- package/src/acp-host/role-turn-host.ts +11 -4
- package/src/analyst-gate-cycles-read.ts +31 -88
- package/src/analyst-ledger.ts +36 -51
- package/src/analyst-metric-families/acceptance-success-rework.ts +24 -1
- package/src/analyst-metric-families/gate-cycles.ts +1 -1
- package/src/analyst-metric-families/round-timeline.ts +2 -5
- package/src/analyst-page.ts +3 -3
- package/src/archivist-record-entry.ts +6 -5
- package/src/archivist-record-pointer.ts +92 -42
- package/src/atomic-write.ts +3 -1
- package/src/auditor-dossier-tool.ts +14 -8
- package/src/auditor-soul.ts +5 -10
- package/src/book-topology-migration-placement.ts +16 -3
- package/src/book-topology-record-class-migrators.ts +4 -2
- package/src/book-topology-runs-migrator.ts +6 -3
- package/src/collector-config.ts +5 -3
- package/src/doctor-contracts.ts +1 -1
- package/src/doctor-evidence.ts +7 -3
- package/src/engine-detour-tool.ts +1 -1
- package/src/engine-detour-usage.ts +12 -25
- package/src/external-host-turn-loop.ts +25 -0
- package/src/factory-board.ts +16 -81
- package/src/headless-host/production-host.ts +1 -4
- package/src/headless-host/role-turn-host.ts +56 -21
- package/src/host-descriptions.ts +0 -6
- package/src/host-session-record.ts +224 -140
- package/src/ledger-session-read.ts +6 -0
- package/src/navigator-public-session.ts +13 -18
- package/src/navigator-work-context.ts +3 -1
- package/src/notary-role.ts +2 -1
- package/src/notary-source-run.ts +8 -18
- package/src/packaged-role-registry.ts +1 -65
- package/src/pi/adapter.ts +50 -7
- package/src/pi/durable-principal.ts +2 -4
- package/src/pi/role-turn-host.ts +85 -14
- package/src/public-cli/auto-resume.ts +7 -43
- package/src/public-cli/instruction-seat-run.ts +13 -6
- package/src/public-cli/invocation.ts +90 -144
- package/src/public-cli/post-admission.ts +111 -72
- package/src/public-cli/run-lifecycle.ts +47 -115
- package/src/public-cli/settlement.ts +221 -589
- package/src/public-cli/terminal.ts +1 -61
- package/src/role-envelope.ts +4 -2
- package/src/role-run-placement.ts +65 -17
- package/src/role-run-relocation.ts +110 -70
- package/src/role-runtime.ts +46 -69
- package/src/run-dossier-files.ts +33 -0
- package/src/run-dossier.ts +357 -0
- package/src/run-terminal-artifacts.ts +84 -240
- package/src/run-ticket-number.ts +82 -14
- package/src/session-assistant-usage.ts +8 -20
- package/src/session-identity.ts +52 -21
- package/src/sitian-appender.ts +33 -26
- package/src/sitian-facade.ts +22 -0
- package/src/sitian-reader.ts +62 -32
- package/src/submission-gate.ts +1 -1
- package/src/submission-ledger.ts +4 -4
- package/src/ticket-provenance-contracts.ts +3 -6
- package/src/ticket-provenance.ts +23 -11
- package/src/ticket-snapshot.ts +3 -2
- package/src/ticket-trajectory.ts +95 -109
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { sitianReportSafe } from "./host-session-record.js";
|
|
4
4
|
import {
|
|
5
5
|
NavigatorUnavailableError,
|
|
6
6
|
navigatorProviderFailureFromPublicTerminal,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
parseNavigatorModelSetting
|
|
10
10
|
} from "./navigator-session-contracts.js";
|
|
11
11
|
import { runDirectoryFromHostContext } from "./host-contracts.js";
|
|
12
|
+
import { parseRunLeaf } from "./role-run-placement.js";
|
|
12
13
|
import { CliUsageError } from "./public-cli/cli-errors.js";
|
|
13
14
|
import { isNavigatorSeat } from "./packaged-role-registry.js";
|
|
14
15
|
import {
|
|
@@ -32,11 +33,9 @@ async function resolveNavigatorLedgerHome(context) {
|
|
|
32
33
|
}
|
|
33
34
|
const NAVIGATOR_HOST_RUN_POINTER_ENTRY = "ak-navigator-host-run";
|
|
34
35
|
function runIdFromNavigatorDirectory(runDirectory) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const runId = entry.slice(0, entry.length - suffix.length);
|
|
39
|
-
return runId.length > 0 ? runId : void 0;
|
|
36
|
+
const parsed = parseRunLeaf(basename(runDirectory));
|
|
37
|
+
if (parsed === void 0 || parsed.role !== "navigator") return void 0;
|
|
38
|
+
return parsed.runId;
|
|
40
39
|
}
|
|
41
40
|
function readNavigatorHostRunPointer(entries) {
|
|
42
41
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
@@ -188,17 +187,14 @@ function createNativeNavigatorSessionFactory(deps) {
|
|
|
188
187
|
routePlaybookReadFailure: () => routePlaybookReadFailure,
|
|
189
188
|
appendEntry: (customType, data) => {
|
|
190
189
|
sessionManager.appendCustomEntry(customType, data);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
} catch {
|
|
201
|
-
}
|
|
190
|
+
sitianReportSafe({
|
|
191
|
+
level: "event",
|
|
192
|
+
kind: "attendance",
|
|
193
|
+
cwd: context.cwd,
|
|
194
|
+
sessionParent: sessionManager.getSessionFile(),
|
|
195
|
+
payload: { customType, data },
|
|
196
|
+
source: "navigator-public-session"
|
|
197
|
+
});
|
|
202
198
|
},
|
|
203
199
|
entries: () => sessionManager.getEntries(),
|
|
204
200
|
setModel: async (next, nextThinking) => {
|
|
@@ -8,6 +8,7 @@ import { lstat, realpath } from "node:fs/promises";
|
|
|
8
8
|
import { resolveBookKeyFromGit } from "./activation-ledger-git.js";
|
|
9
9
|
import { activationBookDirectory, physicalPathIdentity, resolveActivationLedgerHome, } from "./activation-ledger-topology.js";
|
|
10
10
|
import { findRunDirectoryById, readRoleRunIdentity } from "./public-cli/run-lifecycle.js";
|
|
11
|
+
import { formatRunLeaf, parseRunLeaf } from "./role-run-placement.js";
|
|
11
12
|
async function readRetainedIdentity(runDirectory) {
|
|
12
13
|
try {
|
|
13
14
|
return await readRoleRunIdentity(runDirectory);
|
|
@@ -20,21 +21,12 @@ async function readRetainedIdentity(runDirectory) {
|
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
// Run directory leaf: <runId>@<role>. Production uses uuidv7 runIds; offline tracers
|
|
24
|
-
// may use deterministic non-uuid leaves. Role token stays identifier-shaped.
|
|
25
|
-
const RUN_DIR_NAME = /^([A-Za-z0-9][A-Za-z0-9._-]*)@([A-Za-z][A-Za-z0-9_-]*)$/;
|
|
26
24
|
export class NotarySourceRunError extends Error {
|
|
27
25
|
constructor(message, options) {
|
|
28
26
|
super(message, options);
|
|
29
27
|
this.name = "NotarySourceRunError";
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
function parseRunDirectoryName(name) {
|
|
33
|
-
const match = RUN_DIR_NAME.exec(name);
|
|
34
|
-
if (match === null)
|
|
35
|
-
return undefined;
|
|
36
|
-
return { runId: match[1], role: match[2] };
|
|
37
|
-
}
|
|
38
30
|
async function requireRunDirectory(candidate, display) {
|
|
39
31
|
let real;
|
|
40
32
|
try {
|
|
@@ -53,7 +45,7 @@ async function requireRunDirectory(candidate, display) {
|
|
|
53
45
|
if (!stat.isDirectory()) {
|
|
54
46
|
throw new NotarySourceRunError(`notary --source-run must be a run directory: ${display}`);
|
|
55
47
|
}
|
|
56
|
-
const identity =
|
|
48
|
+
const identity = parseRunLeaf(basename(real));
|
|
57
49
|
if (identity === undefined) {
|
|
58
50
|
throw new NotarySourceRunError(`notary --source-run must be named <runId>@<role>: ${basename(real)}`);
|
|
59
51
|
}
|
|
@@ -66,7 +58,7 @@ async function requireRunDirectory(candidate, display) {
|
|
|
66
58
|
* - absolute/relative path that realpath's to that same book runs slot
|
|
67
59
|
*
|
|
68
60
|
* Rejects project-tree projections and any path outside the project's ledger book runs.
|
|
69
|
-
* Retained
|
|
61
|
+
* Retained state.jsonl run-state must match basename identity and book binding.
|
|
70
62
|
*/
|
|
71
63
|
export async function resolveNotarySourceRunLocator(options) {
|
|
72
64
|
const raw = options.sourceRun.trim();
|
|
@@ -78,14 +70,14 @@ export async function resolveNotarySourceRunLocator(options) {
|
|
|
78
70
|
const bookDirectory = activationBookDirectory(ledgerHome, bookKey);
|
|
79
71
|
const bookRunsRoot = join(bookDirectory, "runs");
|
|
80
72
|
let candidate;
|
|
81
|
-
const bare =
|
|
73
|
+
const bare = parseRunLeaf(raw);
|
|
82
74
|
if (bare !== undefined && !raw.includes("/") && !raw.includes("\\")) {
|
|
83
75
|
candidate = (await findRunDirectoryById(options.home, bare.runId, bookKey, bare.role))
|
|
84
|
-
?? join(bookRunsRoot,
|
|
76
|
+
?? join(bookRunsRoot, formatRunLeaf(bare.runId, bare.role));
|
|
85
77
|
}
|
|
86
78
|
else {
|
|
87
79
|
candidate = isAbsolute(raw) ? raw : resolve(options.projectRoot, raw);
|
|
88
|
-
const identity =
|
|
80
|
+
const identity = parseRunLeaf(basename(candidate));
|
|
89
81
|
const subjectDirectory = dirname(dirname(candidate));
|
|
90
82
|
const isLegacyUnboundLocator = identity !== undefined &&
|
|
91
83
|
basename(dirname(candidate)) === "runs" &&
|
|
@@ -96,7 +88,7 @@ export async function resolveNotarySourceRunLocator(options) {
|
|
|
96
88
|
}
|
|
97
89
|
}
|
|
98
90
|
const real = await requireRunDirectory(candidate, raw);
|
|
99
|
-
const identity =
|
|
91
|
+
const identity = parseRunLeaf(basename(real));
|
|
100
92
|
const bookIdentity = physicalPathIdentity(bookDirectory);
|
|
101
93
|
const parentIdentity = physicalPathIdentity(dirname(real));
|
|
102
94
|
const subjectBookIdentity = physicalPathIdentity(dirname(dirname(dirname(real))));
|
|
@@ -128,7 +120,7 @@ export async function resolveNotarySourceRunLocator(options) {
|
|
|
128
120
|
/** Internal activation loader: path is the admitted absolute source run directory. */
|
|
129
121
|
export async function loadNotarySourceRunLocator(path) {
|
|
130
122
|
const real = await requireRunDirectory(path, path);
|
|
131
|
-
const identity =
|
|
123
|
+
const identity = parseRunLeaf(basename(real));
|
|
132
124
|
const runState = await readRetainedIdentity(real);
|
|
133
125
|
if (runState === undefined) {
|
|
134
126
|
throw new NotarySourceRunError("notary source-run lacks retained run-state identity");
|
|
@@ -88,14 +88,7 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
88
88
|
outputTool: FIXER_OUTPUT_TOOL_NAME,
|
|
89
89
|
settlement: "sealed",
|
|
90
90
|
artifactFace: {
|
|
91
|
-
reportPhase: true
|
|
92
|
-
evidenceRole: true,
|
|
93
|
-
leaves: [
|
|
94
|
-
{ key: "phase" },
|
|
95
|
-
{ key: "packetPath" },
|
|
96
|
-
{ key: "prerequisitesPath", omitUndefined: true },
|
|
97
|
-
{ key: "prerequisites" }
|
|
98
|
-
]
|
|
91
|
+
reportPhase: true
|
|
99
92
|
},
|
|
100
93
|
boardPlacement: "marshal",
|
|
101
94
|
activationFlags: [
|
|
@@ -126,12 +119,7 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
126
119
|
outputTool: CODER_OUTPUT_TOOL_NAME,
|
|
127
120
|
settlement: "sealed",
|
|
128
121
|
artifactFace: {
|
|
129
|
-
reportPhase: true
|
|
130
|
-
evidenceRole: true,
|
|
131
|
-
leaves: [
|
|
132
|
-
{ key: "phase" },
|
|
133
|
-
{ key: "taskPath" }
|
|
134
|
-
]
|
|
122
|
+
reportPhase: true
|
|
135
123
|
},
|
|
136
124
|
activationFlags: [
|
|
137
125
|
{ field: "taskPath", flag: "ak-coder-task", binds: "input" },
|
|
@@ -165,15 +153,7 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
165
153
|
settlement: "sealed",
|
|
166
154
|
/** Publish only an accepted ledger outcome; audit escalation stays unsettled here. */
|
|
167
155
|
sealedAcceptedOnly: true,
|
|
168
|
-
artifactFace: {
|
|
169
|
-
evidenceRole: true,
|
|
170
|
-
leaves: [
|
|
171
|
-
{ key: "baseRevision" },
|
|
172
|
-
{ key: "lens" },
|
|
173
|
-
{ key: "authorityRefs", copyArray: true },
|
|
174
|
-
{ key: "callerProvenance", callerProvenance: true }
|
|
175
|
-
]
|
|
176
|
-
},
|
|
156
|
+
artifactFace: {},
|
|
177
157
|
/** Frozen base/lens/authority become the initial prompt; instruction follows. */
|
|
178
158
|
transportPrompt: "skill-args",
|
|
179
159
|
boardPlacement: "marshal",
|
|
@@ -204,14 +184,7 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
204
184
|
bareCommand: false,
|
|
205
185
|
outputTool: COLLECTOR_OUTPUT_TOOL,
|
|
206
186
|
settlement: "sealed",
|
|
207
|
-
artifactFace: {
|
|
208
|
-
evidenceRole: true,
|
|
209
|
-
leaves: [
|
|
210
|
-
{ key: "prNumber", omitUndefined: true },
|
|
211
|
-
{ key: "repository", from: "repository.canonical" },
|
|
212
|
-
{ key: "manifestDigest" }
|
|
213
|
-
]
|
|
214
|
-
},
|
|
187
|
+
artifactFace: {},
|
|
215
188
|
boardPlacement: "collector",
|
|
216
189
|
/** Taishi acceptance is a typed groups array, not a status leaf. */
|
|
217
190
|
analystTerminal: "groups",
|
|
@@ -240,12 +213,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
240
213
|
auditTool: DOCTOR_AUDIT_TOOL_NAME,
|
|
241
214
|
settlement: "sealed",
|
|
242
215
|
artifactFace: {
|
|
243
|
-
evidenceRole: true,
|
|
244
|
-
leaves: [
|
|
245
|
-
{ key: "issueNumber" },
|
|
246
|
-
{ key: "caseRunsPath" },
|
|
247
|
-
{ key: "caseIdentity" }
|
|
248
|
-
],
|
|
249
216
|
doctorReportFacts: true
|
|
250
217
|
},
|
|
251
218
|
/** Navigator subject is the doctor case, not the case-path file bytes. */
|
|
@@ -271,13 +238,7 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
271
238
|
phases: [null],
|
|
272
239
|
outputTool: MERGER_OUTPUT_TOOL_NAME,
|
|
273
240
|
settlement: "sealed",
|
|
274
|
-
artifactFace: {
|
|
275
|
-
evidenceRole: true,
|
|
276
|
-
leaves: [
|
|
277
|
-
{ key: "mergerInputPath" },
|
|
278
|
-
{ key: "derived" }
|
|
279
|
-
]
|
|
280
|
-
},
|
|
241
|
+
artifactFace: {},
|
|
281
242
|
activationFlags: [
|
|
282
243
|
{ field: "inputPath", from: "mergerInputPath", flag: "ak-merger-input", binds: "input" }
|
|
283
244
|
],
|
|
@@ -297,8 +258,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
297
258
|
sourceRunStored: "raw",
|
|
298
259
|
argvResult: "source-run",
|
|
299
260
|
sameParent: "source-locator",
|
|
300
|
-
/** Infrastructure failure at this stage has no accepted gate cycle to project. */
|
|
301
|
-
skipGateOnInfrastructureStage: true,
|
|
302
261
|
/** 符宝郎 is a review officer and inherits the gatekeeper model. */
|
|
303
262
|
reviewOfficer: true,
|
|
304
263
|
provinceConfig: true,
|
|
@@ -406,8 +365,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
406
365
|
sameParent: "gate-pointer",
|
|
407
366
|
/** Resume restores an optional source-run path onto the admitted face. */
|
|
408
367
|
resumeSourcePath: true,
|
|
409
|
-
/** Infrastructure failure at this stage has no accepted gate cycle to project. */
|
|
410
|
-
skipGateOnInfrastructureStage: true,
|
|
411
368
|
/** 台院 is a review officer and inherits the gatekeeper model. */
|
|
412
369
|
reviewOfficer: true,
|
|
413
370
|
provinceConfig: true,
|
|
@@ -439,8 +396,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
439
396
|
presentSettled: "always",
|
|
440
397
|
summonResume: true,
|
|
441
398
|
admission: "instruction",
|
|
442
|
-
/** Infrastructure failure at this stage has no accepted gate cycle to project. */
|
|
443
|
-
skipGateOnInfrastructureStage: true,
|
|
444
399
|
sameParent: "none",
|
|
445
400
|
/** Province model root. Officers name this seat via modelInheritsFrom. */
|
|
446
401
|
provinceConfig: true,
|
|
@@ -608,11 +563,6 @@ function packagedAuditToolName(role) {
|
|
|
608
563
|
if (record === void 0 || !("auditTool" in record)) return void 0;
|
|
609
564
|
return record.auditTool;
|
|
610
565
|
}
|
|
611
|
-
function packagedSkipsGateOnInfrastructureStage(stage) {
|
|
612
|
-
return typeof stage === "string" && PUBLIC_ROLE_RECORDS.some(
|
|
613
|
-
(record) => "skipGateOnInfrastructureStage" in record && record.skipGateOnInfrastructureStage === true && record.role === stage
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
566
|
function packagedDurableOfficerEntry(officer) {
|
|
617
567
|
return typeof officer === "string" && PUBLIC_ROLE_RECORDS.some(
|
|
618
568
|
(record) => "durableOfficerEntry" in record && record.durableOfficerEntry === true && record.role === officer
|
|
@@ -699,7 +649,6 @@ export {
|
|
|
699
649
|
packagedRoleOutputTool,
|
|
700
650
|
packagedRolePhaseFlag,
|
|
701
651
|
packagedRoleSessionMaterials,
|
|
702
|
-
packagedSkipsGateOnInfrastructureStage,
|
|
703
652
|
packagedStoresSourceRunRaw,
|
|
704
653
|
packagedSubjectChoices
|
|
705
654
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { resolveBookKeyFromGit } from "../activation-ledger-git.js";
|
|
3
2
|
import { resolveActivationLedgerHome } from "../activation-ledger-topology.js";
|
|
4
|
-
import { roleRunPlacement } from "../role-run-placement.js";
|
|
3
|
+
import { roleRunPlacement, sessionFileIn } from "../role-run-placement.js";
|
|
5
4
|
function encode(coordinates) {
|
|
6
5
|
return coordinates;
|
|
7
6
|
}
|
|
@@ -47,7 +46,7 @@ export const piDurablePrincipalAuthority = {
|
|
|
47
46
|
sessionDirectory: record.sessionDirectory,
|
|
48
47
|
sessionFile: typeof record.sessionFile === "string" && record.sessionFile.trim() !== ""
|
|
49
48
|
? record.sessionFile
|
|
50
|
-
:
|
|
49
|
+
: sessionFileIn(record.sessionDirectory),
|
|
51
50
|
};
|
|
52
51
|
},
|
|
53
52
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { execFile, spawn } from "node:child_process";
|
|
7
7
|
import { constants } from "node:fs";
|
|
8
|
-
import { access, appendFile,
|
|
8
|
+
import { access, appendFile, realpath } from "node:fs/promises";
|
|
9
9
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
import { platform } from "node:process";
|
|
11
11
|
import { promisify } from "node:util";
|
|
@@ -14,9 +14,13 @@ import { ExplicitInternalActivationError } from "../host-contracts.js";
|
|
|
14
14
|
import { applyEngineChildEnv, ENGINE_MODEL_FLAG_NAME, normalizeEngineName } from "../engine-detour.js";
|
|
15
15
|
import { projectActivationFlags } from "../role-activation-flags.js";
|
|
16
16
|
import { encodeUserDialogueStdin } from "../user-dialogue-stdin.js";
|
|
17
|
+
import { readLedgerSessionJsonlLines, readStrictPiSessionJsonl } from "../ledger-session-read.js";
|
|
18
|
+
import { copyAndRecordHostDossier } from "../host-session-record.js";
|
|
17
19
|
import { projectThrownFailureLeaf, retainPackageFault } from "../public-cli/settlement.js";
|
|
18
20
|
import { describeErrorIdentity } from "../public-cli/run-lifecycle.js";
|
|
19
21
|
import { RECEIPT_DELIVERY_LIMIT_ENV } from "../receipt-delivery-policy.js";
|
|
22
|
+
import { createSessionIdentityAuthority } from "../session-identity.js";
|
|
23
|
+
import { sitianReport } from "../sitian-facade.js";
|
|
20
24
|
/** Package-relative Internal role entrypoint (ADR 0052; same path as public-cli registry). */
|
|
21
25
|
const INTERNAL_ROLE_ENTRYPOINT_RELATIVE = "extensions/role-runtime.ts";
|
|
22
26
|
export function resolveInternalRoleEntrypoint(packageRoot) {
|
|
@@ -350,39 +354,99 @@ export function createPiRoleTurnHost(config) {
|
|
|
350
354
|
await config.recordLaunchedRolePackageIdentity(request.runDirectory, await config.observeLaunchedRolePackageIdentity(config.packageRoot, roleEntry));
|
|
351
355
|
}
|
|
352
356
|
const timeoutMs = request.timeoutMs ?? config.timeoutMs;
|
|
353
|
-
|
|
357
|
+
const result = await spawnRunner(args, {
|
|
354
358
|
cwd: request.cwd,
|
|
355
359
|
env,
|
|
356
360
|
stdin,
|
|
357
361
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
358
362
|
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
359
363
|
});
|
|
364
|
+
// Deliver already-written Pi host facts. Pi lands at session/session.jsonl;
|
|
365
|
+
// bind projects host.sessions; copy records the landing. Host adapters do
|
|
366
|
+
// not write current.json — public bind/settlement seams own that render.
|
|
367
|
+
try {
|
|
368
|
+
const { sessionFile, sessionDirectory } = config.principalAuthority.decode(request.principal);
|
|
369
|
+
const sessionId = await readPiSessionHeaderId(sessionFile);
|
|
370
|
+
if (sessionId !== undefined) {
|
|
371
|
+
await createSessionIdentityAuthority(config.principalAuthority, "pi").bind(request.principal, sessionId);
|
|
372
|
+
copyAndRecordHostDossier({
|
|
373
|
+
host: "pi",
|
|
374
|
+
sessionId,
|
|
375
|
+
cwd: request.cwd,
|
|
376
|
+
sessionDirectory,
|
|
377
|
+
sessionParent: sessionFile,
|
|
378
|
+
...(request.home !== undefined ? { home: request.home } : {}),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
await retainPackageFault({
|
|
384
|
+
runDirectory: request.runDirectory,
|
|
385
|
+
diagnostic: `pi host dossier record failed beside host terminal: ${describeErrorIdentity(error)}`,
|
|
386
|
+
error,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return result;
|
|
360
390
|
},
|
|
361
391
|
};
|
|
362
392
|
}
|
|
363
|
-
import { sitianReport } from "../sitian-facade.js";
|
|
364
393
|
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
394
|
+
* Session header id from the Pi volume the child (or fixture) already wrote.
|
|
395
|
+
* Reuses the ledger line kernel: a later malformed line must not erase a valid
|
|
396
|
+
* header already present (ADR 0086 originals are not validated whole-file).
|
|
367
397
|
*/
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
398
|
+
async function readPiSessionHeaderId(sessionFile) {
|
|
399
|
+
try {
|
|
400
|
+
for (const line of await readLedgerSessionJsonlLines(sessionFile)) {
|
|
401
|
+
const entry = line.row;
|
|
402
|
+
if (entry === undefined || entry.type !== "session")
|
|
403
|
+
continue;
|
|
404
|
+
if (typeof entry.id === "string" && entry.id.trim() !== "")
|
|
405
|
+
return entry.id;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
if (error.code === "ENOENT")
|
|
410
|
+
return undefined;
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
/** Last non-session entry id. Session headers are not parents of custom lines. */
|
|
416
|
+
export function piSessionCustomParentId(entries) {
|
|
371
417
|
let parentId = null;
|
|
372
|
-
for (const
|
|
373
|
-
const entry = JSON.parse(line);
|
|
418
|
+
for (const entry of entries) {
|
|
374
419
|
if (typeof entry.id === "string" && entry.type !== "session")
|
|
375
420
|
parentId = entry.id;
|
|
376
421
|
}
|
|
377
|
-
|
|
378
|
-
|
|
422
|
+
return parentId;
|
|
423
|
+
}
|
|
424
|
+
/** One Pi custom JSONL line: type, customType, data, id, parentId, timestamp. */
|
|
425
|
+
export function formatPiSessionCustomEntry(input) {
|
|
426
|
+
return `${JSON.stringify({
|
|
379
427
|
type: "custom",
|
|
428
|
+
customType: input.customType,
|
|
429
|
+
data: input.data,
|
|
430
|
+
id: randomUUID(),
|
|
431
|
+
parentId: input.parentId,
|
|
432
|
+
timestamp: input.timestamp,
|
|
433
|
+
})}\n`;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Append one custom JSONL entry to the durable principal's session file.
|
|
437
|
+
* Pi session codec only — AK artifact O_EXCL retention stays in public-cli.
|
|
438
|
+
* Read is raw JSON.parse (SyntaxError propagates). Sitian full-traversal stays elsewhere.
|
|
439
|
+
*/
|
|
440
|
+
export async function appendPiSessionCustomEntry(authority, principal, customType, data) {
|
|
441
|
+
const { sessionFile } = authority.decode(principal);
|
|
442
|
+
const entries = await readStrictPiSessionJsonl(sessionFile);
|
|
443
|
+
const timestamp = new Date().toISOString();
|
|
444
|
+
const pointerLine = formatPiSessionCustomEntry({
|
|
380
445
|
customType,
|
|
381
446
|
data,
|
|
382
|
-
|
|
383
|
-
parentId,
|
|
447
|
+
parentId: piSessionCustomParentId(entries),
|
|
384
448
|
timestamp,
|
|
385
|
-
})
|
|
449
|
+
});
|
|
386
450
|
await appendFile(sessionFile, pointerLine, "utf8");
|
|
387
451
|
sitianReport({
|
|
388
452
|
level: "event",
|
|
@@ -1,27 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single generic auto-resume loop for #416 (owner scope = single LLM call).
|
|
3
|
-
* Call-local retries, at most the effective autoResumeLimit times (injected once
|
|
4
|
-
* per call by the caller, #422 — never re-read from disk inside the loop),
|
|
5
|
-
* in-place (same runId/session).
|
|
6
|
-
* Unifies presentation: intermediate attempts use dummyIo, only final Terminal is presented.
|
|
7
|
-
*
|
|
8
|
-
* Owner 2026-08-23: a dispatch that exits by throwing used to bypass the entire
|
|
9
|
-
* retry mechanism (the throw escaped the while-loop before the count check ever
|
|
10
|
-
* ran). Every exception is now retained whole, in place, and the ordinary retry
|
|
11
|
-
* path continues: same budget, same call-local count semantics. No failure-type
|
|
12
|
-
* classification — every thrown value is treated identically.
|
|
13
|
-
*/
|
|
14
|
-
import { constants as fsConstants } from "node:fs";
|
|
15
|
-
import { randomUUID } from "node:crypto";
|
|
16
|
-
import { open } from "node:fs/promises";
|
|
17
|
-
import { join } from "node:path";
|
|
18
1
|
import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
|
|
19
2
|
import { parseAutoResumeLimit } from "./config.js";
|
|
20
3
|
import { processCancelSignalName } from "./process-cancel.js";
|
|
21
4
|
import { isLawfulTypedTerminalOutcome, formatTerminalResult } from "./terminal.js";
|
|
22
|
-
import { attachRecordedSubmissions,
|
|
23
|
-
export { ensureRealArtifactsDirectory };
|
|
5
|
+
import { attachRecordedSubmissions, presentFailureTerminal, retainPackageFault, } from "./settlement.js";
|
|
24
6
|
import { serializeThrownValue } from "../serialize-thrown-value.js";
|
|
7
|
+
import { reportRunRecord } from "../sitian-facade.js";
|
|
25
8
|
import { errorText } from "../unknown-value.js";
|
|
26
9
|
const dummyIo = { stdout: () => { }, stderr: () => { } };
|
|
27
10
|
/**
|
|
@@ -105,41 +88,19 @@ function jsonSafeReplacer() {
|
|
|
105
88
|
};
|
|
106
89
|
}
|
|
107
90
|
/**
|
|
108
|
-
* Retain one throwing dispatch attempt's complete exception as
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* attempt can never overwrite an earlier attempt's file.
|
|
91
|
+
* Retain one throwing dispatch attempt's complete exception as a line in the
|
|
92
|
+
* run's log.jsonl, then leave an addressable pointer in the session principal
|
|
93
|
+
* (custom entry). The log only appends (史必追加, #419): a later attempt can
|
|
94
|
+
* never overwrite an earlier attempt's record.
|
|
113
95
|
*/
|
|
114
|
-
/**
|
|
115
|
-
* Hardened create-once JSON write shared by every durable artifact this loop
|
|
116
|
-
* retains directly (dispatch-error dumps, lawful-persist-failure error/
|
|
117
|
-
* evidence records): O_EXCL (fail loud on a colliding name, never overwrite)
|
|
118
|
-
* + O_NOFOLLOW where the platform provides it (a planted symlink is never
|
|
119
|
-
* followed) — mirrors settlement.ts's own hardened artifact writers.
|
|
120
|
-
*/
|
|
121
|
-
export async function writeHardenedArtifactFile(artifactsDir, namePrefix, payload) {
|
|
122
|
-
const filePath = join(artifactsDir, `${namePrefix}-${randomUUID()}.json`);
|
|
123
|
-
const body = `${JSON.stringify(payload, jsonSafeReplacer(), 2)}\n`;
|
|
124
|
-
const noFollowFlag = typeof fsConstants.O_NOFOLLOW === "number" ? fsConstants.O_NOFOLLOW : 0;
|
|
125
|
-
const handle = await open(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollowFlag, 0o600);
|
|
126
|
-
try {
|
|
127
|
-
await handle.writeFile(body, "utf8");
|
|
128
|
-
}
|
|
129
|
-
finally {
|
|
130
|
-
await handle.close();
|
|
131
|
-
}
|
|
132
|
-
return filePath;
|
|
133
|
-
}
|
|
134
96
|
async function retainDispatchError(admitted, principalAuthority, sessionAppender, attempt, error) {
|
|
135
|
-
const artifactsDir = await ensureRealArtifactsDirectory(admitted.runDirectory);
|
|
136
97
|
// Whole-object dump: everything the thrown value carries, nothing picked.
|
|
137
|
-
const filePath =
|
|
98
|
+
const filePath = reportRunRecord(admitted.runDirectory, "dispatch-exception", JSON.parse(JSON.stringify({
|
|
138
99
|
version: 1,
|
|
139
100
|
attempt,
|
|
140
101
|
recordedAt: new Date().toISOString(),
|
|
141
102
|
error: serializeThrownValue(error),
|
|
142
|
-
});
|
|
103
|
+
}, jsonSafeReplacer())), "auto-resume").recordFile;
|
|
143
104
|
// Addressable pointer in the dossier (卷宗): Pi session custom-entry codec
|
|
144
105
|
// (appendPiSessionCustomEntry). Lease still owned here with run-writer.
|
|
145
106
|
let pointerLease;
|
|
@@ -29,11 +29,11 @@ import { packagedAdmitsCountersign, packagedBindsBoardTicket, packagedRebindSour
|
|
|
29
29
|
import { readAuditorResumeBinding } from "../auditor-soul.js";
|
|
30
30
|
import { CliUsageError } from "./cli-errors.js";
|
|
31
31
|
import { admitPublicRole, bindAdmittedTicketNumber, buildInstructionTransportPrompt, materializeCountersignInvocation, persistAdmittedSourceRunPath, recordAdmittedCorrelation, recordChildDiaristRun, relocateAdmittedRunToTicket, withPreparedAttachments, } from "./invocation.js";
|
|
32
|
-
import { presentControlledFailure, packageFaultNoteFor, prepareSummonsResumeMaterials, roleTurnOptions, runPostAdmissionOneShot, runPostAdmissionResumable, runPostAdmissionSeatResume, showResumeErrorPointer, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
|
|
32
|
+
import { bindDiaristAssertedTicketNumber, presentControlledFailure, packageFaultNoteFor, prepareSummonsResumeMaterials, roleTurnOptions, runPostAdmissionOneShot, runPostAdmissionResumable, runPostAdmissionSeatResume, showResumeErrorPointer, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
|
|
33
33
|
import { presentTerminal } from "./auto-resume.js";
|
|
34
34
|
import { buildAutoResumeContinuationPrompt, loadResumablePublicRole, markRunAdmitted, parentRunPathFromGatePointerInstruction, readCurrentCourt, } from "./run-lifecycle.js";
|
|
35
35
|
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
|
|
36
|
-
import { presentStructuralRejection, readBoundSessionEntries,
|
|
36
|
+
import { presentStructuralRejection, readBoundSessionEntries, attachPostAuditCountersignFact, trySettlePublicSeat, } from "./settlement.js";
|
|
37
37
|
import { warnMissingMethodSkills } from "./machine-method-skills.js";
|
|
38
38
|
import { formatTerminalResult, isLawfulTypedTerminalOutcome, } from "./terminal.js";
|
|
39
39
|
import { admittedSeatTurnDetails, projectRoleTurnRequest, requiredMethodSkills, } from "./turn-request.js";
|
|
@@ -184,6 +184,8 @@ function isBoardTicketSeat(seat) {
|
|
|
184
184
|
return packagedBindsBoardTicket(seat.role);
|
|
185
185
|
}
|
|
186
186
|
async function bindAndRelocateDiarist(admitted, authority, lease) {
|
|
187
|
+
// The diarist's asserted ticketNumber binds here, in the public call's process.
|
|
188
|
+
await bindDiaristAssertedTicketNumber(admitted);
|
|
187
189
|
const boardTicket = await readBoardTicketNumber(admitted.runDirectory);
|
|
188
190
|
if (boardTicket === undefined)
|
|
189
191
|
return undefined;
|
|
@@ -856,7 +858,7 @@ export async function continueParentAfterChild(parentRunId, child, env, io) {
|
|
|
856
858
|
if (stopped.exitCode === 0
|
|
857
859
|
&& heldUnreadableTerminal(stopped.terminal)
|
|
858
860
|
&& stopped.terminal !== undefined) {
|
|
859
|
-
const parentTerminal = await trySettlePublicSeat(admitted, env.principalAuthority,
|
|
861
|
+
const parentTerminal = await trySettlePublicSeat(admitted, env.principalAuthority, { ...await readCurrentCourt(admitted.runDirectory), ...packageFaultScope(admitted, env, io) });
|
|
860
862
|
if (parentTerminal?.roleOutcome.kind === "accepted"
|
|
861
863
|
|| parentTerminal?.roleOutcome.kind === "audit_escalation") {
|
|
862
864
|
const terminal = await presentUnsettledAudit(parentTerminal, stopped.terminal, io, admitted.runDirectory);
|
|
@@ -870,7 +872,7 @@ export async function continueParentAfterChild(parentRunId, child, env, io) {
|
|
|
870
872
|
}
|
|
871
873
|
if (resolved !== undefined && (resolved.status === "continue" || resolved.status === "converged")) {
|
|
872
874
|
if (AUDITED_ROLES.has(admitted.role) && resolved.status === "converged") {
|
|
873
|
-
const terminal = await trySettlePublicSeat(admitted, env.principalAuthority, packageFaultScope(admitted, env, io));
|
|
875
|
+
const terminal = await trySettlePublicSeat(admitted, env.principalAuthority, { ...await readCurrentCourt(admitted.runDirectory), ...packageFaultScope(admitted, env, io) });
|
|
874
876
|
if (terminal?.roleOutcome.kind !== "accepted")
|
|
875
877
|
throw new Error("pending submission is not recorded");
|
|
876
878
|
return auditSubmittedRole({ exitCode: 0, admitted, terminal }, env, io, resolved.admitted, latestQueuePayload(resolved.terminal));
|
|
@@ -1151,11 +1153,11 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
|
|
|
1151
1153
|
}
|
|
1152
1154
|
}
|
|
1153
1155
|
// The turn already settled this court (payloads, usage, autoResumeCount, history).
|
|
1154
|
-
// Audit only adds
|
|
1155
|
-
//
|
|
1156
|
+
// Audit only adds, for 中书省, the officer fact. A second settle would
|
|
1157
|
+
// republish the same attempt.
|
|
1156
1158
|
if (turn.terminal === undefined)
|
|
1157
1159
|
throw new Error(`audited ${admitted.role} submission did not settle`);
|
|
1158
|
-
const terminal = await
|
|
1160
|
+
const terminal = await attachPostAuditCountersignFact(admitted, env.principalAuthority, turn.terminal, packageFaultScope(admitted, env, io));
|
|
1159
1161
|
turn = { ...turn, terminal };
|
|
1160
1162
|
await presentTerminal(terminal, { ...io, omitFailureStderrDiagnostic: true }, admitted.runDirectory);
|
|
1161
1163
|
return turn;
|