@akagilnc/pi-workflow-roles 0.1.5598 → 0.1.5614
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/dist/acp-host/production-host.js +406 -366
- package/dist/compliance-transport.js +2 -5
- package/dist/doctor-auditor.js +1 -1
- package/dist/gatekeeper-role.js +1 -1
- package/dist/headless-host/production-host.js +370 -330
- package/dist/migrate-book-topology.js +301 -291
- package/dist/navigator-public-session.js +4 -4
- package/dist/navigator-work-base.js +5 -30
- package/dist/packaged-role-registry.js +4 -2
- package/dist/pi/role-turn-host.js +10 -1
- package/dist/public-cli/instruction-seat-run.js +133 -38
- package/dist/public-cli/invocation.js +23 -26
- package/dist/public-cli/main.js +250 -179
- package/dist/public-cli/option-definitions.js +19 -0
- package/dist/public-cli/post-admission.js +23 -22
- package/dist/public-cli/run-lifecycle.js +2 -14
- package/dist/public-cli/turn-request.js +4 -9
- package/dist/public-role-summons.js +5 -3
- package/dist/role-run-placement.js +7 -1
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -3
- package/src/audited-run-identity.ts +30 -0
- package/src/compliance-transport.ts +1 -4
- package/src/doctor-auditor.ts +1 -1
- package/src/gatekeeper-role.ts +1 -1
- package/src/host-contracts.ts +6 -1
- package/src/navigator-public-session.ts +9 -5
- package/src/navigator-work-base.ts +19 -34
- package/src/notary-role.ts +12 -21
- package/src/packaged-role-registry.ts +4 -4
- package/src/pi/role-turn-host.ts +11 -1
- package/src/public-cli/instruction-seat-run.ts +162 -42
- package/src/public-cli/invocation.ts +35 -26
- package/src/public-cli/option-definitions.ts +20 -0
- package/src/public-cli/post-admission.ts +28 -22
- package/src/public-cli/run-lifecycle.ts +2 -17
- package/src/public-cli/turn-request.ts +9 -8
- package/src/public-role-summons.ts +17 -6
- package/src/role-envelope.ts +4 -0
- package/src/role-run-placement.ts +10 -1
- package/src/role-runtime.ts +54 -31
- package/dist/auditor-dossier-tool.js +0 -47
- package/src/auditor-dossier-tool.ts +0 -63
|
@@ -514,6 +514,24 @@ const NOTARY_OPTIONS = [
|
|
|
514
514
|
},
|
|
515
515
|
},
|
|
516
516
|
];
|
|
517
|
+
/** 台院: optional --source-run binds the audited leg; gate summons always pass it (#1166). */
|
|
518
|
+
const INSPECTOR_OPTIONS = [
|
|
519
|
+
...projectAndAttach("inspector"),
|
|
520
|
+
{
|
|
521
|
+
id: "source-run",
|
|
522
|
+
owner: "inspector",
|
|
523
|
+
canonical: "--source-run",
|
|
524
|
+
aliases: [],
|
|
525
|
+
valueMetavar: "runId@role|path",
|
|
526
|
+
required: false,
|
|
527
|
+
repeatable: false,
|
|
528
|
+
form: "option",
|
|
529
|
+
description: {
|
|
530
|
+
en: "Optional audited source-run locator (runId@role or path). Gate summons always supply it; identity rides startup materials.",
|
|
531
|
+
zh: "可选被审腿定位符(runId@role 或路径)。闸传召必带;身份在启动材料里。",
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
];
|
|
517
535
|
const MERGER_OPTIONS = [
|
|
518
536
|
// Merger project face: worktree whose Git materials are handed to the role.
|
|
519
537
|
{
|
|
@@ -677,6 +695,7 @@ const SEAT_OPTION_ROWS = {
|
|
|
677
695
|
merger: MERGER_OPTIONS,
|
|
678
696
|
notary: NOTARY_OPTIONS,
|
|
679
697
|
auditor: AUDITOR_OPTIONS,
|
|
698
|
+
inspector: INSPECTOR_OPTIONS,
|
|
680
699
|
};
|
|
681
700
|
function seatOptionRows(role) {
|
|
682
701
|
return SEAT_OPTION_ROWS[role] ?? projectAndAttach(role);
|
|
@@ -10,7 +10,7 @@ import { writeFile } from "node:fs/promises";
|
|
|
10
10
|
import { buildAutoResumeContinuationPrompt, findRunDirectoryById, readRoleRunState, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
|
|
11
11
|
import { CliUsageError } from "./cli-errors.js";
|
|
12
12
|
import { projectPublicTurnAxes } from "./turn-request.js";
|
|
13
|
-
import {
|
|
13
|
+
import { appendCallerFileFlagPaths, bindAdmittedTicketNumber, relocateAdmittedRunToTicket, } from "./invocation.js";
|
|
14
14
|
import { readRecordedSubmissionRows } from "../submission-ledger.js";
|
|
15
15
|
import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.js";
|
|
16
16
|
import { readStoredHostSessionId } from "../session-identity.js";
|
|
@@ -906,8 +906,8 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
906
906
|
* Shared resume continuation projection (#471 / #600 / #633 / #637 / #755 / #879):
|
|
907
907
|
* seat-table model/engine/timeout axes, restored correlation, and either
|
|
908
908
|
* - manual resume (no same-ticket summons): caller message bytes, or
|
|
909
|
-
* - same-ticket summons (审核循环续话): caller/peer words + optional
|
|
910
|
-
*
|
|
909
|
+
* - same-ticket summons (审核循环续话): caller/peer words + optional caller file-flag
|
|
910
|
+
* paths only — no「请重读」、no code-authored content substitute,
|
|
911
911
|
* no engine handbook packaging (#750/#755/#879).
|
|
912
912
|
* Caller message wins as prompt base when supplied (bytes unchanged, including
|
|
913
913
|
* blank/whitespace); else summons instruction (parent payload or reask).
|
|
@@ -916,22 +916,17 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
916
916
|
* first when request.summons carries instruction or attachment paths.
|
|
917
917
|
*/
|
|
918
918
|
export function resumeTurnRequestProjectionOptions(admitted, request, env, summonsPrepared) {
|
|
919
|
-
// #879: officer dialogue
|
|
920
|
-
//
|
|
921
|
-
// Binding pointer stays on summons sourceRunPath / activation / attachments.
|
|
919
|
+
// #879: officer dialogue is caller/peer words; #1166 J11 merges caller file-flag
|
|
920
|
+
// paths when present. Gate with no file flags stays body-only (ADR 0085).
|
|
922
921
|
const officerDialogue = isStationChildOfficerDialogue(admitted.role, env);
|
|
922
|
+
const fileFlags = summonsPrepared?.attachments ?? [];
|
|
923
923
|
let prompt;
|
|
924
924
|
if (request.message !== undefined) {
|
|
925
925
|
if (summonsPrepared !== undefined) {
|
|
926
|
-
// #755: same-ticket review / open-court — caller words.
|
|
927
|
-
|
|
928
|
-
prompt = officerDialogue
|
|
926
|
+
// #755: same-ticket review / open-court — caller words + caller file flags.
|
|
927
|
+
prompt = officerDialogue && fileFlags.length === 0
|
|
929
928
|
? request.message
|
|
930
|
-
:
|
|
931
|
-
instruction: request.message,
|
|
932
|
-
instructionEmpty: false,
|
|
933
|
-
attachments: summonsPrepared.attachments,
|
|
934
|
-
});
|
|
929
|
+
: appendCallerFileFlagPaths(request.message, fileFlags);
|
|
935
930
|
}
|
|
936
931
|
else if (request.summons !== undefined) {
|
|
937
932
|
// #755: same-ticket summons without prepared materials — caller words only.
|
|
@@ -942,13 +937,11 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
942
937
|
}
|
|
943
938
|
}
|
|
944
939
|
else if (summonsPrepared !== undefined) {
|
|
945
|
-
|
|
946
|
-
//
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
? summonsPrepared.instruction
|
|
951
|
-
: buildInstructionTransportPrompt(summonsPrepared);
|
|
940
|
+
const body = summonsPrepared.instruction;
|
|
941
|
+
// No file flags: body alone (gate peer words). With flags: merge delivery.
|
|
942
|
+
prompt = officerDialogue && fileFlags.length === 0
|
|
943
|
+
? body
|
|
944
|
+
: appendCallerFileFlagPaths(body, fileFlags);
|
|
952
945
|
}
|
|
953
946
|
else if (request.summons !== undefined) {
|
|
954
947
|
// #879: same-ticket summons with no instruction (e.g. notary source-run binding
|
|
@@ -971,6 +964,10 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
971
964
|
...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
|
|
972
965
|
// #1132: one configured ceiling, already resolved by the caller (#422).
|
|
973
966
|
deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
|
|
967
|
+
...(env.navigatorWorkContextPath === undefined
|
|
968
|
+
|| env.navigatorWorkContextPath.trim() === ""
|
|
969
|
+
? {}
|
|
970
|
+
: { navigatorWorkContextPath: env.navigatorWorkContextPath }),
|
|
974
971
|
};
|
|
975
972
|
}
|
|
976
973
|
/** Shared new-turn and in-call auto-resume projection. Seat code supplies prompt and activation. */
|
|
@@ -986,6 +983,10 @@ export function roleTurnOptions(env, admitted, continuation) {
|
|
|
986
983
|
...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
|
|
987
984
|
// #1132: one configured ceiling, already resolved by the caller (#422).
|
|
988
985
|
deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
|
|
986
|
+
...(env.navigatorWorkContextPath === undefined
|
|
987
|
+
|| env.navigatorWorkContextPath.trim() === ""
|
|
988
|
+
? {}
|
|
989
|
+
: { navigatorWorkContextPath: env.navigatorWorkContextPath }),
|
|
989
990
|
};
|
|
990
991
|
}
|
|
991
992
|
/**
|
|
@@ -1070,7 +1071,7 @@ export async function prepareSummonsResumeMaterials(_runDirectory, summons) {
|
|
|
1070
1071
|
* validation, turn builder, and adapters stay on the seat.
|
|
1071
1072
|
*
|
|
1072
1073
|
* Court handling (#637): public manual resume reads only the open court's
|
|
1073
|
-
* settlement identity; internal re-summons
|
|
1074
|
+
* settlement identity; internal re-summons passes caller file-flag paths as-is.
|
|
1074
1075
|
*/
|
|
1075
1076
|
export async function runPostAdmissionSeatResume(input) {
|
|
1076
1077
|
let request = input.request;
|
|
@@ -631,18 +631,9 @@ export async function findRunDirectoryById(home, runId, onlyBookKey, onlyRole) {
|
|
|
631
631
|
.filter((bookKey) => onlyBookKey === undefined || bookKey === onlyBookKey)
|
|
632
632
|
.map((bookKey) => activationBookDirectory(ledgerHome, bookKey)), runId, onlyRole);
|
|
633
633
|
}
|
|
634
|
-
/** Code-owned gate inspector summons prefix (public-role-summons / #747). */
|
|
635
|
-
export const GATE_DOSSIER_POINTER_PREFIX = "卷宗指针:";
|
|
636
|
-
/** Parent path from a code-owned inspector 卷宗指针 instruction; else undefined. */
|
|
637
|
-
export function parentRunPathFromGatePointerInstruction(instruction) {
|
|
638
|
-
if (!instruction.startsWith(GATE_DOSSIER_POINTER_PREFIX))
|
|
639
|
-
return undefined;
|
|
640
|
-
const path = instruction.slice(GATE_DOSSIER_POINTER_PREFIX.length).trim();
|
|
641
|
-
return path === "" ? undefined : path;
|
|
642
|
-
}
|
|
643
634
|
/**
|
|
644
|
-
* Parent-run binding on a retained officer run (#747).
|
|
645
|
-
*
|
|
635
|
+
* Parent-run binding on a retained officer run (#747 / #1166).
|
|
636
|
+
* Typed sourceRunPath only — instruction bytes are never a path protocol.
|
|
646
637
|
* Missing page → undefined; damage / non-ENOENT IO propagates.
|
|
647
638
|
*/
|
|
648
639
|
export async function readRunParentPath(runDirectory) {
|
|
@@ -652,9 +643,6 @@ export async function readRunParentPath(runDirectory) {
|
|
|
652
643
|
if (typeof record.sourceRunPath === "string" && record.sourceRunPath.trim() !== "") {
|
|
653
644
|
return record.sourceRunPath;
|
|
654
645
|
}
|
|
655
|
-
if (typeof record.instruction === "string") {
|
|
656
|
-
return parentRunPathFromGatePointerInstruction(record.instruction);
|
|
657
|
-
}
|
|
658
646
|
return undefined;
|
|
659
647
|
}
|
|
660
648
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { pickEngineAxis } from "../package-resources/engine-material.js";
|
|
2
2
|
import { packagedRoleActivationFlags, packagedRoleMetadata, } from "../packaged-role-registry.js";
|
|
3
3
|
import { installedMethodSkillPath } from "./machine-method-skills.js";
|
|
4
|
-
import { parentRunPathFromGatePointerInstruction } from "./run-lifecycle.js";
|
|
5
4
|
/**
|
|
6
5
|
* Env axes shared by every public turn projection.
|
|
7
6
|
* Callers add correlation, continuation, host, and station-only fields.
|
|
@@ -49,6 +48,10 @@ export function projectRoleTurnRequest(admitted, roleDetails, options) {
|
|
|
49
48
|
...(options.deliveryRequestLimit === undefined
|
|
50
49
|
? {}
|
|
51
50
|
: { deliveryRequestLimit: options.deliveryRequestLimit }),
|
|
51
|
+
...(options.navigatorWorkContextPath === undefined
|
|
52
|
+
|| options.navigatorWorkContextPath.trim() === ""
|
|
53
|
+
? {}
|
|
54
|
+
: { navigatorWorkContextPath: options.navigatorWorkContextPath }),
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
/**
|
|
@@ -83,14 +86,6 @@ function activationForAdmitted(admitted) {
|
|
|
83
86
|
const activation = { role: admitted.role };
|
|
84
87
|
for (const spec of packagedRoleActivationFlags(admitted.role)) {
|
|
85
88
|
let value = readAdmittedPath(admitted, spec.from ?? spec.field);
|
|
86
|
-
if (spec.fallback === "gate-pointer") {
|
|
87
|
-
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
88
|
-
value = trimmed !== ""
|
|
89
|
-
? trimmed
|
|
90
|
-
: parentRunPathFromGatePointerInstruction(admitted.instruction);
|
|
91
|
-
if (typeof value !== "string" || value === "")
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
89
|
if (value === undefined)
|
|
95
90
|
continue;
|
|
96
91
|
if (spec.text === true) {
|
|
@@ -197,7 +197,8 @@ async function summonPublicRole(options) {
|
|
|
197
197
|
...options.boundTicketNumber === void 0 ? {} : { boundTicketNumber: options.boundTicketNumber },
|
|
198
198
|
...options.parentRunPath === void 0 || options.parentRunPath.trim() === "" ? {} : { parentRunPath: options.parentRunPath },
|
|
199
199
|
...options.correlationId === void 0 || options.correlationId.trim() === "" ? {} : { correlationId: options.correlationId },
|
|
200
|
-
...options.createRunId === void 0 ? {} : { createRunId: options.createRunId }
|
|
200
|
+
...options.createRunId === void 0 ? {} : { createRunId: options.createRunId },
|
|
201
|
+
...options.navigatorWorkContextPath === void 0 || options.navigatorWorkContextPath.trim() === "" ? {} : { navigatorWorkContextPath: options.navigatorWorkContextPath }
|
|
201
202
|
}
|
|
202
203
|
};
|
|
203
204
|
} catch (error) {
|
|
@@ -454,7 +455,8 @@ async function summonGateOfficer(options) {
|
|
|
454
455
|
"judge",
|
|
455
456
|
"--source-run",
|
|
456
457
|
options.sourceRunDirectory,
|
|
457
|
-
|
|
458
|
+
"--project",
|
|
459
|
+
options.cwd
|
|
458
460
|
],
|
|
459
461
|
...common
|
|
460
462
|
});
|
|
@@ -482,7 +484,7 @@ async function summonGateOfficer(options) {
|
|
|
482
484
|
}
|
|
483
485
|
return summonPublicRole({
|
|
484
486
|
role: options.officer,
|
|
485
|
-
argv: [
|
|
487
|
+
argv: ["--source-run", options.sourceRunDirectory, "--project", options.cwd],
|
|
486
488
|
...common
|
|
487
489
|
});
|
|
488
490
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
|
-
import { basename, dirname, join } from "node:path";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
activationBookDirectory,
|
|
5
5
|
ensureRealDirectoryTree
|
|
@@ -27,6 +27,11 @@ function sessionFileOf(runDirectory) {
|
|
|
27
27
|
function runDirectoryOfSessionFile(sessionFile) {
|
|
28
28
|
return dirname(dirname(sessionFile));
|
|
29
29
|
}
|
|
30
|
+
function auditorRunDirectory(context) {
|
|
31
|
+
const sessionFile = context.sessionManager?.getSessionFile?.();
|
|
32
|
+
if (sessionFile === void 0) return void 0;
|
|
33
|
+
return resolve(runDirectoryOfSessionFile(sessionFile));
|
|
34
|
+
}
|
|
30
35
|
function runDirectoryFromSessionDirectory(sessionDirectory) {
|
|
31
36
|
return dirname(sessionDirectory);
|
|
32
37
|
}
|
|
@@ -107,6 +112,7 @@ function ensureRoleRunPlacement(ledgerHome, placement) {
|
|
|
107
112
|
}
|
|
108
113
|
export {
|
|
109
114
|
ROLE_RUN_SESSION_FILENAME,
|
|
115
|
+
auditorRunDirectory,
|
|
110
116
|
ensureRoleRunDirectory,
|
|
111
117
|
ensureRoleRunPlacement,
|
|
112
118
|
findRoleRunDirectory,
|
package/package.json
CHANGED
|
@@ -32,9 +32,8 @@ const entries = [
|
|
|
32
32
|
"archivist-record-topology",
|
|
33
33
|
// Session material loaders used by published non-bundle roots.
|
|
34
34
|
"session-opening-materials",
|
|
35
|
-
// Value-import closure of published non-bundle roots (build-package-only loadable)
|
|
36
|
-
|
|
37
|
-
// Static import of auditor-dossier-tool / placement path authority (#859).
|
|
35
|
+
// Value-import closure of published non-bundle roots (build-package-only loadable);
|
|
36
|
+
// placement path authority (#859).
|
|
38
37
|
"role-run-placement",
|
|
39
38
|
// Static import of role-run-placement (safe-positive ticket invariant).
|
|
40
39
|
"run-ticket-number",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared audited-run identity for review-officer startup materials (#1166 / ADR 0087).
|
|
3
|
+
* Value form is `<runId>@<席>` only — no directory path.
|
|
4
|
+
*/
|
|
5
|
+
import { basename } from "node:path";
|
|
6
|
+
|
|
7
|
+
import { formatRunLeaf, parseRunLeaf } from "./role-run-placement.ts";
|
|
8
|
+
|
|
9
|
+
export const AUDITED_RUN_IDENTITY_KIND = "audited-run-identity" as const;
|
|
10
|
+
|
|
11
|
+
export type AuditedRunIdentityMaterial = {
|
|
12
|
+
readonly kind: typeof AUDITED_RUN_IDENTITY_KIND;
|
|
13
|
+
readonly identity: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Identity leaf from a run directory basename; undefined when the leaf is not `runId@role`. */
|
|
17
|
+
export function auditedRunIdentityFromDirectory(runDirectory: string): string | undefined {
|
|
18
|
+
const parsed = parseRunLeaf(basename(runDirectory));
|
|
19
|
+
if (parsed === undefined) return undefined;
|
|
20
|
+
return formatRunLeaf(parsed.runId, parsed.role);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function auditedRunIdentityMaterial(identity: string): AuditedRunIdentityMaterial {
|
|
24
|
+
return { kind: AUDITED_RUN_IDENTITY_KIND, identity };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Identity from locator fields already in hand (notary source-run). */
|
|
28
|
+
export function auditedRunIdentityFromParts(runId: string, role: string): string {
|
|
29
|
+
return formatRunLeaf(runId, role);
|
|
30
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Usage } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { AuditorSoulRole } from "./auditor-soul.ts";
|
|
3
|
-
import { auditorRunDirectory } from "./
|
|
3
|
+
import { auditorRunDirectory } from "./role-run-placement.ts";
|
|
4
4
|
import type { HostContext } from "./host-contracts.ts";
|
|
5
5
|
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
6
6
|
import type { PublicSummonResult } from "./public-role-summons.ts";
|
|
@@ -40,8 +40,6 @@ export type ComplianceDecision =
|
|
|
40
40
|
| ComplianceReceived
|
|
41
41
|
| ComplianceTransportFailure;
|
|
42
42
|
/** Source-run pointer remains the binding locator; parent body rides the separate instruction. */
|
|
43
|
-
export const AUDITOR_DOSSIER_PROMPT = "卷宗指针:" as const;
|
|
44
|
-
|
|
45
43
|
export const COMPLIANCE_RESPONSE_ENTRY_TYPE = "ak_compliance_response" as const;
|
|
46
44
|
export const AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE = "ak_auditor_parent_attempt_binding" as const;
|
|
47
45
|
export const AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE = "ak_auditor_compliance_failure" as const;
|
|
@@ -215,7 +213,6 @@ export async function runComplianceAudit(options: RunComplianceAuditOptions): Pr
|
|
|
215
213
|
auditSubject,
|
|
216
214
|
"--source-run",
|
|
217
215
|
sourceRunDirectory,
|
|
218
|
-
`${AUDITOR_DOSSIER_PROMPT}${sourceRunDirectory}`,
|
|
219
216
|
],
|
|
220
217
|
cwd: options.context.cwd ?? process.cwd(),
|
|
221
218
|
home,
|
package/src/doctor-auditor.ts
CHANGED
package/src/gatekeeper-role.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { HostContext } from "./host-contracts.ts";
|
|
3
3
|
|
|
4
|
-
import { auditorRunDirectory } from "./
|
|
4
|
+
import { auditorRunDirectory } from "./role-run-placement.ts";
|
|
5
5
|
import { packagedGateStageLabel } from "./packaged-role-registry.ts";
|
|
6
6
|
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
7
7
|
import { GatekeeperDecisionError, receivedDiscriminator, unreadableDiscriminatorNotice } from "./submission-errors.ts";
|
package/src/host-contracts.ts
CHANGED
|
@@ -200,6 +200,11 @@ export type RoleTurnRequest = {
|
|
|
200
200
|
* number. Absent = package default. Never re-read from disk downstream.
|
|
201
201
|
*/
|
|
202
202
|
readonly deliveryRequestLimit?: number;
|
|
203
|
+
/**
|
|
204
|
+
* Navigator nest work-context locator for startup materials (#1166).
|
|
205
|
+
* Request-scoped — never parent process.env across await.
|
|
206
|
+
*/
|
|
207
|
+
readonly navigatorWorkContextPath?: string;
|
|
203
208
|
};
|
|
204
209
|
|
|
205
210
|
/** Turn result — only fields upper layers currently consume. */
|
|
@@ -258,7 +263,7 @@ export interface DurablePrincipalAuthority {
|
|
|
258
263
|
type HostSessionManager = { getLeafEntry(): HostSessionEntry | undefined; getLeafId(): string | null | undefined; getEntries(): Iterable<HostSessionEntry>; getSessionDir(): string; getSessionFile(): string | undefined; getHeader?(): { readonly type: string; readonly id?: string } | null; setSessionFile?(path: string): void; appendCustomEntry?(customType: string, data?: unknown): unknown; };
|
|
259
264
|
|
|
260
265
|
/** Context supplied by a host for one activation and its interceptable events. */
|
|
261
|
-
export type HostContext = { cwd: string; mode: string; model: { readonly provider: string } | undefined; sessionManager: HostSessionManager; /** Per-turn admitted run directory (#879); never process-global env. */ runDirectory?: string; /** Per-turn court attempt (#879); never process-global env. */ courtAttemptId?: string; /** Public-invocation scope (#537); never process-global env. */ invocationScopeId?: string; /** Selected host axis (#537 / ADR 0082); never process-global env invent. */ host?: string; signal?: AbortSignal | undefined; ui?: { notify?(message: string, type?: "info" | "warning" | "error"): void }; transcript?(): string; abort(): void; };
|
|
266
|
+
export type HostContext = { cwd: string; mode: string; model: { readonly provider: string } | undefined; sessionManager: HostSessionManager; /** Per-turn admitted run directory (#879); never process-global env. */ runDirectory?: string; /** Per-turn court attempt (#879); never process-global env. */ courtAttemptId?: string; /** Public-invocation scope (#537); never process-global env. */ invocationScopeId?: string; /** Selected host axis (#537 / ADR 0082); never process-global env invent. */ host?: string; /** Navigator nest work-context locator (#1166); never process-global env. */ navigatorWorkContextPath?: string; signal?: AbortSignal | undefined; ui?: { notify?(message: string, type?: "info" | "warning" | "error"): void }; transcript?(): string; abort(): void; };
|
|
262
267
|
|
|
263
268
|
/** Per-turn run directory; adapters must project any child-process identity. */
|
|
264
269
|
export function runDirectoryFromHostContext(context: HostContext): string | undefined {
|
|
@@ -26,7 +26,6 @@ import type { PublicSummonResult } from "./public-role-summons.ts";
|
|
|
26
26
|
import { CliUsageError } from "./public-cli/cli-errors.ts";
|
|
27
27
|
import { isNavigatorSeat } from "./packaged-role-registry.ts";
|
|
28
28
|
import {
|
|
29
|
-
attachWorkContextPointer,
|
|
30
29
|
navigatorWorkContextFile,
|
|
31
30
|
} from "./navigator-work-base.ts";
|
|
32
31
|
|
|
@@ -157,19 +156,24 @@ export function createNativeNavigatorSessionFactory(deps?: {
|
|
|
157
156
|
// (ADR 0018 / #959 — legal HostContext may omit signal).
|
|
158
157
|
if (disposed) return;
|
|
159
158
|
const resumeRunId = hostRunId;
|
|
159
|
+
// Prompt stays caller/summoner bytes (#1166). Nest work-context path is an
|
|
160
|
+
// internal locator for startup materials only — never folded into argv,
|
|
161
|
+
// never written onto shared parent process.env across await.
|
|
160
162
|
const workContextPath = navigatorWorkContextFile(sessionManager.getSessionDir());
|
|
161
|
-
const
|
|
162
|
-
?
|
|
163
|
-
: text;
|
|
163
|
+
const workContextLocator =
|
|
164
|
+
existsSync(workContextPath) ? workContextPath : undefined;
|
|
164
165
|
|
|
165
166
|
// Call contract only: forward shared-lifecycle signal; do not own AbortController here
|
|
166
167
|
// (ADR 0018 / #959 — cancel ownership stays on the attendance/envelope seam).
|
|
167
168
|
const baseSummon = {
|
|
168
169
|
role: "navigator" as const,
|
|
169
|
-
argv: [
|
|
170
|
+
argv: [text] as const,
|
|
170
171
|
cwd: context.cwd,
|
|
171
172
|
...(summonHome === undefined ? {} : { home: summonHome }),
|
|
172
173
|
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
174
|
+
...(workContextLocator === undefined
|
|
175
|
+
? {}
|
|
176
|
+
: { navigatorWorkContextPath: workContextLocator }),
|
|
173
177
|
};
|
|
174
178
|
|
|
175
179
|
// Prefer host CLI resume so prior advice stays on the host session.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Durable work subject and controlling authority for one navigator nest.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Startup materials load these bytes from the nest session directory; the
|
|
4
|
+
* caller/summon prompt must not receive a package-injected path (#1166).
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, realpathSync } from "node:fs";
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
@@ -12,6 +12,9 @@ import { isEnoent, isRecord } from "./unknown-value.ts";
|
|
|
12
12
|
|
|
13
13
|
export const NAVIGATOR_WORK_CONTEXT_BASENAME = "work-context.json";
|
|
14
14
|
|
|
15
|
+
/** Child-process / in-process locator for nest work-context bytes — not prompt material (#1166). */
|
|
16
|
+
export const AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV = "AK_ROLE_NAVIGATOR_WORK_CONTEXT" as const;
|
|
17
|
+
|
|
15
18
|
export type NavigatorWorkBase = {
|
|
16
19
|
readonly subject: string;
|
|
17
20
|
readonly authority: string;
|
|
@@ -81,38 +84,20 @@ export async function readNavigatorWorkBase(path: string): Promise<NavigatorWork
|
|
|
81
84
|
return { subject: record.subject, authority: record.authority };
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const path = record?.workContextPath;
|
|
99
|
-
if (typeof path !== "string" || path.trim() === "") return undefined;
|
|
100
|
-
return isNavigatorWorkContextFile(path) ? path : undefined;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Settlement JSON gains the nest pointer. Non-JSON prompts stay unchanged. */
|
|
104
|
-
export function attachWorkContextPointer(text: string, workContextPath: string): string {
|
|
105
|
-
const record = jsonObjectFromPrompt(text);
|
|
106
|
-
if (record === undefined) return text;
|
|
107
|
-
if (typeof record.workContextPath === "string" && record.workContextPath.trim() !== "") return text;
|
|
108
|
-
const head = text.trimStart().split("\n\n", 1)[0] ?? "";
|
|
109
|
-
const rest = text.trimStart().slice(head.length);
|
|
110
|
-
return `${JSON.stringify({ ...record, workContextPath })}${rest}`;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** System-prompt base for one agent start. Absent or unreadable file yields undefined. */
|
|
114
|
-
export async function loadNavigatorWorkBaseSuffix(prompt: string): Promise<string | undefined> {
|
|
115
|
-
const path = workContextPathFromPrompt(prompt);
|
|
87
|
+
/** System-prompt base for one agent start. Prefer request locator, else nest session dir. */
|
|
88
|
+
export async function loadNavigatorWorkBaseSuffix(
|
|
89
|
+
sessionDir: string | undefined,
|
|
90
|
+
locatorPath?: string,
|
|
91
|
+
): Promise<string | undefined> {
|
|
92
|
+
const path =
|
|
93
|
+
typeof locatorPath === "string" && locatorPath.trim() !== ""
|
|
94
|
+
? locatorPath.trim()
|
|
95
|
+
: typeof process.env[AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV] === "string"
|
|
96
|
+
&& process.env[AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV].trim() !== ""
|
|
97
|
+
? process.env[AK_ROLE_NAVIGATOR_WORK_CONTEXT_ENV].trim()
|
|
98
|
+
: sessionDir !== undefined && sessionDir.trim() !== ""
|
|
99
|
+
? navigatorWorkContextFile(sessionDir)
|
|
100
|
+
: undefined;
|
|
116
101
|
if (path === undefined) return undefined;
|
|
117
102
|
const body = await readNavigatorWorkBase(path);
|
|
118
103
|
if (body === undefined) return undefined;
|
package/src/notary-role.ts
CHANGED
|
@@ -9,6 +9,10 @@ import { isSafePositiveTicketNumber } from "./run-ticket-number.ts";
|
|
|
9
9
|
* This module keeps evidence assembly + projection only.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
auditedRunIdentityFromParts,
|
|
14
|
+
auditedRunIdentityMaterial,
|
|
15
|
+
} from "./audited-run-identity.ts";
|
|
12
16
|
import {
|
|
13
17
|
NOTARY_OUTPUT_TOOL_NAME,
|
|
14
18
|
NOTARY_SOURCE_RUN_FLAG,
|
|
@@ -53,25 +57,15 @@ export function readNotaryTicketFlag(flag: unknown): number | undefined {
|
|
|
53
57
|
return n;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
* Sole production projection for agent-start material + session custom entry.
|
|
59
|
-
*/
|
|
60
|
-
export function projectNotarySessionBound(input: {
|
|
61
|
-
readonly sourceRun: NotarySourceRunLocator;
|
|
62
|
-
readonly ticketNumber?: number;
|
|
63
|
-
}): {
|
|
60
|
+
/** Agent-start reading material: identity `<runId>@<席>` only — no directory path. */
|
|
61
|
+
export function projectNotaryAuditedRunIdentity(input: {
|
|
64
62
|
readonly sourceRun: NotarySourceRunLocator;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
...(input.ticketNumber === undefined ? {} : { ticketNumber: input.ticketNumber }),
|
|
70
|
-
};
|
|
63
|
+
}) {
|
|
64
|
+
return auditedRunIdentityMaterial(
|
|
65
|
+
auditedRunIdentityFromParts(input.sourceRun.runId, input.sourceRun.role),
|
|
66
|
+
);
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
export type NotarySessionBound = ReturnType<typeof projectNotarySessionBound>;
|
|
74
|
-
|
|
75
69
|
/**
|
|
76
70
|
* Session custom-entry type for typed notary bound (written by shared envelope).
|
|
77
71
|
* Role module only projects; lifecycle write is envelope-owned (ADR 0018).
|
|
@@ -102,7 +96,7 @@ export function projectNotaryBoundFromFlags(
|
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
/** Assemble systemPrompt body from base + soul only (NO embedded bound JSON).
|
|
105
|
-
*
|
|
99
|
+
* Audited-run identity travels separately as `readingMaterial`; adapters fold
|
|
106
100
|
* it into the provider-visible prompt at the send boundary. */
|
|
107
101
|
export function assembleNotaryAgentStartPrompt(input: {
|
|
108
102
|
readonly baseSystemPrompt: string;
|
|
@@ -172,11 +166,8 @@ export function createNotaryRoleRuntime(
|
|
|
172
166
|
baseSystemPrompt: event.systemPrompt,
|
|
173
167
|
soul: activation.soul,
|
|
174
168
|
}),
|
|
175
|
-
readingMaterial:
|
|
169
|
+
readingMaterial: projectNotaryAuditedRunIdentity({
|
|
176
170
|
sourceRun: activation.sourceRun,
|
|
177
|
-
...(activation.ticketNumber === undefined
|
|
178
|
-
? {}
|
|
179
|
-
: { ticketNumber: activation.ticketNumber }),
|
|
180
171
|
}),
|
|
181
172
|
};
|
|
182
173
|
});
|
|
@@ -390,7 +390,8 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
390
390
|
presentSettled: "always",
|
|
391
391
|
summonResume: false,
|
|
392
392
|
admission: "instruction",
|
|
393
|
-
|
|
393
|
+
/** Structured --source-run only; instruction stays opaque caller/peer bytes (#1166). */
|
|
394
|
+
sameParent: "source-run",
|
|
394
395
|
/** Resume restores an optional source-run path onto the admitted face. */
|
|
395
396
|
resumeSourcePath: true,
|
|
396
397
|
/** 台院 is a review officer and inherits the gatekeeper model. */
|
|
@@ -403,11 +404,12 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
403
404
|
/** Court reask replaces the initial prompt. */
|
|
404
405
|
reaskPrompt: true,
|
|
405
406
|
gateStageLabel: "台院",
|
|
407
|
+
/** Gate summon binds --source-run; identity is startup material (#1166). */
|
|
408
|
+
gateSummon: "source-run",
|
|
406
409
|
activationFlags: [
|
|
407
410
|
{
|
|
408
411
|
field: "sourceRun",
|
|
409
412
|
from: "sourceRunPath",
|
|
410
|
-
fallback: "gate-pointer",
|
|
411
413
|
flag: "ak-inspector-source-run",
|
|
412
414
|
binds: "input",
|
|
413
415
|
},
|
|
@@ -554,7 +556,6 @@ export function packagedModelParent(role: string): PackagedRole | undefined {
|
|
|
554
556
|
* One seat's admitted → activation copy.
|
|
555
557
|
* `field` is the activation key. `from` is the admitted path when it differs.
|
|
556
558
|
* `text` writes a number as decimal text on the activation object.
|
|
557
|
-
* `fallback: "gate-pointer"` is the inspector instruction pointer when sourceRunPath is blank.
|
|
558
559
|
* `flag` omitted: the value stays on the activation object and is not a host flag
|
|
559
560
|
* (secretariat and countersign ticketNumber).
|
|
560
561
|
*/
|
|
@@ -562,7 +563,6 @@ export type PackagedActivationFlag = {
|
|
|
562
563
|
readonly field: string;
|
|
563
564
|
readonly from?: string;
|
|
564
565
|
readonly text?: true;
|
|
565
|
-
readonly fallback?: "gate-pointer";
|
|
566
566
|
readonly flag?: string;
|
|
567
567
|
readonly binds?: "input" | "phase";
|
|
568
568
|
};
|
package/src/pi/role-turn-host.ts
CHANGED
|
@@ -429,7 +429,7 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
429
429
|
if (request.deliveryRequestLimit === undefined) delete env[RECEIPT_DELIVERY_LIMIT_ENV];
|
|
430
430
|
else env[RECEIPT_DELIVERY_LIMIT_ENV] = String(request.deliveryRequestLimit);
|
|
431
431
|
applyEngineChildEnv(env, request.engine);
|
|
432
|
-
// Nested auditor
|
|
432
|
+
// Nested auditor binds the audited source-run for identity / soul selection.
|
|
433
433
|
if (
|
|
434
434
|
typeof process.env.AK_ROLE_AUDITOR_SOURCE_RUN === "string"
|
|
435
435
|
&& process.env.AK_ROLE_AUDITOR_SOURCE_RUN.trim() !== ""
|
|
@@ -443,6 +443,16 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
443
443
|
) {
|
|
444
444
|
env.AK_ROLE_AUDITOR_SUBJECT = process.env.AK_ROLE_AUDITOR_SUBJECT;
|
|
445
445
|
}
|
|
446
|
+
// Navigator nest work-context locator for startup materials (#1166) —
|
|
447
|
+
// child-process scoped only from this turn request; never parent process.env.
|
|
448
|
+
if (
|
|
449
|
+
typeof request.navigatorWorkContextPath === "string"
|
|
450
|
+
&& request.navigatorWorkContextPath.trim() !== ""
|
|
451
|
+
) {
|
|
452
|
+
env.AK_ROLE_NAVIGATOR_WORK_CONTEXT = request.navigatorWorkContextPath.trim();
|
|
453
|
+
} else {
|
|
454
|
+
delete env.AK_ROLE_NAVIGATOR_WORK_CONTEXT;
|
|
455
|
+
}
|
|
446
456
|
if (
|
|
447
457
|
config.recordLaunchedRolePackageIdentity !== undefined &&
|
|
448
458
|
config.observeLaunchedRolePackageIdentity !== undefined
|