@akagilnc/pi-workflow-roles 0.1.4035 → 0.1.4062
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 +476 -250
- package/dist/doctor-auditor.js +1 -1
- package/dist/dossier-resolution.js +21 -8
- package/dist/gatekeeper-role.js +34 -27
- package/dist/headless-host/description.js +7 -8
- package/dist/headless-host/production-host.js +503 -264
- package/dist/host-contracts.js +16 -0
- package/dist/inspector-contracts.js +8 -0
- package/dist/packaged-role-registry.js +1 -1
- package/dist/pi/role-turn-host.js +43 -13
- package/dist/public-cli/case-dossier-delivery.js +60 -1
- package/dist/public-cli/inspector-run.js +17 -3
- package/dist/public-cli/instruction-seat-run.js +9 -6
- package/dist/public-cli/main.js +239 -112
- package/dist/public-cli/notary-run.js +7 -4
- package/dist/public-cli/post-admission.js +66 -59
- package/dist/public-cli/run-lifecycle.js +3 -0
- package/dist/public-cli/settlement.js +24 -7
- package/dist/public-role-summons.js +8 -0
- package/dist/submission-ledger.js +30 -8
- package/dist/user-dialogue-stdin.js +33 -0
- package/extensions/role-runtime.ts +1 -0
- package/package.json +1 -1
- package/resources/836-deleted-machine-instruction-inventory.md +1 -1
- package/src/doctor-auditor.ts +1 -1
- package/src/dossier-resolution.ts +26 -8
- package/src/external-host-turn-loop.ts +9 -0
- package/src/gatekeeper-pass-envelope.ts +6 -0
- package/src/gatekeeper-role.ts +47 -28
- package/src/headless-host/description.ts +8 -11
- package/src/headless-host/role-turn-host.ts +20 -5
- package/src/host-contracts.ts +22 -3
- package/src/inspector-contracts.ts +7 -0
- package/src/judge-role.ts +4 -0
- package/src/navigator-work-context.ts +3 -4
- package/src/notary-role.ts +1 -0
- package/src/packaged-role-registry.ts +1 -1
- package/src/pi/adapter.ts +19 -4
- package/src/pi/role-turn-host.ts +48 -14
- package/src/public-cli/case-dossier-delivery.ts +86 -1
- package/src/public-cli/inspector-run.ts +26 -2
- package/src/public-cli/instruction-seat-run.ts +17 -9
- package/src/public-cli/invocation.ts +2 -0
- package/src/public-cli/notary-run.ts +13 -4
- package/src/public-cli/post-admission.ts +74 -65
- package/src/public-cli/run-lifecycle.ts +3 -0
- package/src/public-cli/settlement.ts +32 -6
- package/src/public-role-summons.ts +31 -7
- package/src/role-envelope.ts +9 -30
- package/src/role-runtime-dependencies.ts +1 -0
- package/src/role-runtime.ts +95 -19
- package/src/submission-ledger.ts +48 -13
- package/src/user-dialogue-stdin.ts +37 -0
- package/src/worker-role.ts +4 -0
package/src/pi/role-turn-host.ts
CHANGED
|
@@ -21,9 +21,10 @@ import type {
|
|
|
21
21
|
RoleTurnRequest,
|
|
22
22
|
RoleTurnResult,
|
|
23
23
|
} from "../host-contracts.ts";
|
|
24
|
-
import { ExplicitInternalActivationError } from "../host-contracts.ts";
|
|
25
|
-
import { applyEngineChildEnv } from "../engine-detour.ts";
|
|
24
|
+
import { ExplicitInternalActivationError, isOfficerReviewSeat } from "../host-contracts.ts";
|
|
25
|
+
import { applyEngineChildEnv, ENGINE_MODEL_FLAG_NAME, normalizeEngineName } from "../engine-detour.ts";
|
|
26
26
|
import { projectActivationFlags } from "../role-activation-flags.ts";
|
|
27
|
+
import { encodeUserDialogueStdin } from "../user-dialogue-stdin.ts";
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
/** Package-relative Internal role entrypoint (ADR 0052; same path as public-cli registry). */
|
|
@@ -119,7 +120,7 @@ export function applyPiNativeSkillInvocation(
|
|
|
119
120
|
* Pi last-hop argv after `--no-extensions -e entry` (#819).
|
|
120
121
|
* Activation flag membership comes from middle-layer projectActivationFlags;
|
|
121
122
|
* this function only renders session coords, controlled constants, and pairs.
|
|
122
|
-
*
|
|
123
|
+
* User dialogue is not an argv element (#879): it rides spawn stdin.
|
|
123
124
|
*/
|
|
124
125
|
export function buildPiTurnExtraArgs(
|
|
125
126
|
request: RoleTurnRequest,
|
|
@@ -127,14 +128,6 @@ export function buildPiTurnExtraArgs(
|
|
|
127
128
|
extraPiArgs: readonly string[] = [],
|
|
128
129
|
): string[] {
|
|
129
130
|
const { sessionFile, sessionDirectory } = authority.decode(request.principal);
|
|
130
|
-
const rawPrompt =
|
|
131
|
-
request.continuation.kind === "initial" || request.continuation.kind === "resume"
|
|
132
|
-
? request.continuation.prompt
|
|
133
|
-
: (() => {
|
|
134
|
-
const _exhaustive: never = request.continuation;
|
|
135
|
-
return _exhaustive;
|
|
136
|
-
})();
|
|
137
|
-
const prompt = applyPiNativeSkillInvocation(request.methods, rawPrompt);
|
|
138
131
|
return [
|
|
139
132
|
"--no-skills",
|
|
140
133
|
...buildMethodArgs(request.methods),
|
|
@@ -148,13 +141,31 @@ export function buildPiTurnExtraArgs(
|
|
|
148
141
|
...extraPiArgs,
|
|
149
142
|
// Envelope assembly = projectActivationFlags; pi only renders argv pairs.
|
|
150
143
|
...activationFlagsToPiArgv(projectActivationFlags(request)),
|
|
144
|
+
...piEngineModelArgs(request),
|
|
151
145
|
"--mode",
|
|
152
146
|
"json",
|
|
153
147
|
...buildSeatModelCliArgs(request.model),
|
|
154
|
-
prompt,
|
|
155
148
|
];
|
|
156
149
|
}
|
|
157
150
|
|
|
151
|
+
/** Engine name stays on child env; model has no env fallback (#883 / #879). */
|
|
152
|
+
function piEngineModelArgs(request: RoleTurnRequest): string[] {
|
|
153
|
+
const model = normalizeEngineName(request.engineModel);
|
|
154
|
+
if (model === undefined) return [];
|
|
155
|
+
return [`--${ENGINE_MODEL_FLAG_NAME}`, model];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function piUserDialogueBody(request: RoleTurnRequest): string {
|
|
159
|
+
const rawPrompt =
|
|
160
|
+
request.continuation.kind === "initial" || request.continuation.kind === "resume"
|
|
161
|
+
? request.continuation.prompt
|
|
162
|
+
: (() => {
|
|
163
|
+
const _exhaustive: never = request.continuation;
|
|
164
|
+
return _exhaustive;
|
|
165
|
+
})();
|
|
166
|
+
return applyPiNativeSkillInvocation(request.methods, rawPrompt);
|
|
167
|
+
}
|
|
168
|
+
|
|
158
169
|
export type PiSpawnRunner = (
|
|
159
170
|
args: readonly string[],
|
|
160
171
|
options: {
|
|
@@ -163,6 +174,8 @@ export type PiSpawnRunner = (
|
|
|
163
174
|
timeoutMs?: number;
|
|
164
175
|
/** Parent cancellation; the child gets the same graceful SIGTERM as a budget. */
|
|
165
176
|
signal?: AbortSignal;
|
|
177
|
+
/** User dialogue body; omitted from argv so execve cannot E2BIG (#879). */
|
|
178
|
+
stdin?: string;
|
|
166
179
|
},
|
|
167
180
|
) => Promise<{
|
|
168
181
|
code: number | null;
|
|
@@ -271,11 +284,22 @@ export function createDefaultPiSpawnRunner(options: {
|
|
|
271
284
|
const child = spawn(piIdentity.executable, [...args], {
|
|
272
285
|
cwd: spawnOptions.cwd,
|
|
273
286
|
env: spawnOptions.env,
|
|
274
|
-
stdio: ["
|
|
287
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
275
288
|
});
|
|
289
|
+
if (child.stdin === null) {
|
|
290
|
+
throw new Error("Pi child stdin pipe was not created");
|
|
291
|
+
}
|
|
276
292
|
if (child.stderr === null) {
|
|
277
293
|
throw new Error("Pi child stderr pipe was not created");
|
|
278
294
|
}
|
|
295
|
+
let stdinDeliveryError: Error | undefined;
|
|
296
|
+
child.stdin.on("error", (error) => {
|
|
297
|
+
stdinDeliveryError ??= error;
|
|
298
|
+
});
|
|
299
|
+
if (spawnOptions.stdin !== undefined) {
|
|
300
|
+
child.stdin.write(spawnOptions.stdin);
|
|
301
|
+
}
|
|
302
|
+
child.stdin.end();
|
|
279
303
|
let stderr = "";
|
|
280
304
|
let timedOut = false;
|
|
281
305
|
// No default wall clock. Only an explicit caller budget arms a timer (ADR 0010).
|
|
@@ -344,6 +368,10 @@ export function createDefaultPiSpawnRunner(options: {
|
|
|
344
368
|
reject(executionError);
|
|
345
369
|
return;
|
|
346
370
|
}
|
|
371
|
+
if (stdinDeliveryError !== undefined) {
|
|
372
|
+
reject(stdinDeliveryError);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
347
375
|
resolveResult({
|
|
348
376
|
code,
|
|
349
377
|
stderr,
|
|
@@ -375,9 +403,13 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
375
403
|
async executeTurn(request: RoleTurnRequest): Promise<RoleTurnResult> {
|
|
376
404
|
// #617 DK-7: Pi argv gets projected native paths once; never record bytes.
|
|
377
405
|
// Pi already owns its own session file, so only sitian prior volume rides in.
|
|
406
|
+
// #879: station-child officer dialogue keeps peer words — do not splice
|
|
407
|
+
// host-transition priorNativePaths into the review prompt body.
|
|
378
408
|
let turnRequest = request;
|
|
409
|
+
const officerStationChild =
|
|
410
|
+
request.stationChild === true && isOfficerReviewSeat(request.activation.role);
|
|
379
411
|
const paths =
|
|
380
|
-
request.hostTransition?.priorNativeKind === "sitian"
|
|
412
|
+
!officerStationChild && request.hostTransition?.priorNativeKind === "sitian"
|
|
381
413
|
? request.hostTransition.priorNativePaths
|
|
382
414
|
: undefined;
|
|
383
415
|
if (
|
|
@@ -400,6 +432,7 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
400
432
|
config.extraPiArgs ?? [],
|
|
401
433
|
);
|
|
402
434
|
const args = buildExplicitInternalActivationArgs(roleEntry, extraArgs);
|
|
435
|
+
const stdin = encodeUserDialogueStdin(piUserDialogueBody(turnRequest));
|
|
403
436
|
// Shared envelope isolates this call's court identity: omitting courtAttemptId
|
|
404
437
|
// must not inherit a parent process.env.AK_ROLE_COURT_ATTEMPT (#637).
|
|
405
438
|
const env: NodeJS.ProcessEnv = {
|
|
@@ -444,6 +477,7 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
444
477
|
return await spawnRunner(args, {
|
|
445
478
|
cwd: request.cwd,
|
|
446
479
|
env,
|
|
480
|
+
stdin,
|
|
447
481
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
448
482
|
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
449
483
|
});
|
|
@@ -5,13 +5,31 @@
|
|
|
5
5
|
* 只读已有案卷:不刷新、不生成、不校验内容、不新增拒收或停工条件。
|
|
6
6
|
* 机器文本仅中立标识材料(ADR 0073),用途说明归角色材料所有。
|
|
7
7
|
*
|
|
8
|
-
* 递送挂载点唯一:`post-admission` 在 beforeDispatch
|
|
8
|
+
* 递送挂载点唯一:`post-admission` 在 beforeDispatch 之后为每个公共入口挂载。
|
|
9
|
+
* 普通入口把本段追加进 continuation;station-child 审核轮次走 attachments
|
|
10
|
+
* 冻结 + role-runtime `loadCaseDossierReadingMaterial` → readingMaterial →
|
|
11
|
+
* systemPrompt.materials fold(#879:对话 instruction 保持父腿 payload 原文;
|
|
12
|
+
* 起居录作独立附件面,不新造 RoleTurnRequest.materials)。
|
|
9
13
|
*/
|
|
14
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
|
|
10
18
|
import { resolveTicketProvenanceVolume } from "../ticket-provenance.ts";
|
|
19
|
+
import {
|
|
20
|
+
freezeAttachmentsIntoRun,
|
|
21
|
+
type FrozenAttachment,
|
|
22
|
+
} from "./invocation.ts";
|
|
11
23
|
|
|
12
24
|
/** Section heading of the system-delivered dossier pointer (presentation only). */
|
|
13
25
|
const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)" as const;
|
|
14
26
|
|
|
27
|
+
/** Stable freeze key under run/attachments/ for station-child 0081 delivery. */
|
|
28
|
+
const CASE_DOSSIER_ATTACH_KEY = "case-dossier" as const;
|
|
29
|
+
|
|
30
|
+
/** Leaf name of the frozen pointer section (content = purpose + paths). */
|
|
31
|
+
const CASE_DOSSIER_ATTACH_FILE = "case-dossier-pointer.md" as const;
|
|
32
|
+
|
|
15
33
|
/** Pointer only — presence/absence is for the role to observe at the path. */
|
|
16
34
|
function describeDossierFile(path: string): string {
|
|
17
35
|
return path;
|
|
@@ -42,3 +60,70 @@ export async function projectCaseDossierPointerSection(input: {
|
|
|
42
60
|
`记录卷宗:${describeDossierFile(volume.recordFile)}`,
|
|
43
61
|
].join("\n");
|
|
44
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* ADR 0081 delivery for station-child officer turns (#879): freeze the same
|
|
66
|
+
* pointer section through the existing attachments seam. Role-runtime loads
|
|
67
|
+
* that freeze via loadCaseDossierReadingMaterial onto readingMaterial; the
|
|
68
|
+
* envelope then folds it into systemPrompt.materials. Peer dialogue instruction
|
|
69
|
+
* stays the parent payload; never RoleTurnRequest.materials.
|
|
70
|
+
* Returns frozen attachments, or undefined when unbound (no dossier).
|
|
71
|
+
*/
|
|
72
|
+
export async function deliverCaseDossierAsAttachment(input: {
|
|
73
|
+
readonly ticketNumber: number | undefined;
|
|
74
|
+
readonly projectRoot: string;
|
|
75
|
+
readonly home: string;
|
|
76
|
+
readonly runDirectory: string;
|
|
77
|
+
}): Promise<readonly FrozenAttachment[] | undefined> {
|
|
78
|
+
const section = await projectCaseDossierPointerSection({
|
|
79
|
+
ticketNumber: input.ticketNumber,
|
|
80
|
+
projectRoot: input.projectRoot,
|
|
81
|
+
home: input.home,
|
|
82
|
+
});
|
|
83
|
+
if (section === undefined) return undefined;
|
|
84
|
+
// Stage in OS temp only — never leave a run-local .case-dossier-stage copy.
|
|
85
|
+
const stagingDir = await mkdtemp(join(tmpdir(), "ak-case-dossier-"));
|
|
86
|
+
try {
|
|
87
|
+
const stagingPath = join(stagingDir, CASE_DOSSIER_ATTACH_FILE);
|
|
88
|
+
await writeFile(stagingPath, `${section}\n`, "utf8");
|
|
89
|
+
return await freezeAttachmentsIntoRun(
|
|
90
|
+
[stagingPath],
|
|
91
|
+
input.runDirectory,
|
|
92
|
+
CASE_DOSSIER_ATTACH_KEY,
|
|
93
|
+
);
|
|
94
|
+
} finally {
|
|
95
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Typed reading-material face for a frozen station-child 0081 attachment. */
|
|
100
|
+
export type CaseDossierReadingMaterial = {
|
|
101
|
+
readonly kind: "case-dossier-pointer";
|
|
102
|
+
readonly frozenPath: string;
|
|
103
|
+
readonly section: string;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Load a previously frozen case-dossier attachment as reading material
|
|
108
|
+
* (existing agent-start / systemPrompt.materials fold — not dialogue prompt,
|
|
109
|
+
* not RoleTurnRequest.materials). Undefined when the run has no such freeze.
|
|
110
|
+
*/
|
|
111
|
+
export async function loadCaseDossierReadingMaterial(
|
|
112
|
+
runDirectory: string,
|
|
113
|
+
): Promise<CaseDossierReadingMaterial | undefined> {
|
|
114
|
+
const frozenPath = join(
|
|
115
|
+
runDirectory,
|
|
116
|
+
"attachments",
|
|
117
|
+
CASE_DOSSIER_ATTACH_KEY,
|
|
118
|
+
`00-${CASE_DOSSIER_ATTACH_FILE}`,
|
|
119
|
+
);
|
|
120
|
+
let section: string;
|
|
121
|
+
try {
|
|
122
|
+
section = await readFile(frozenPath, "utf8");
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
if (section.trim() === "") return undefined;
|
|
128
|
+
return { kind: "case-dossier-pointer", frozenPath, section };
|
|
129
|
+
}
|
|
@@ -12,6 +12,7 @@ import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.ts";
|
|
|
12
12
|
import {
|
|
13
13
|
admitInspectorInvocation,
|
|
14
14
|
buildInspectorTransportPrompt,
|
|
15
|
+
persistAdmittedSourceRunPath,
|
|
15
16
|
type AdmittedInspectorInvocation,
|
|
16
17
|
type ParseInspectorArgvResult,
|
|
17
18
|
} from "./invocation.ts";
|
|
@@ -50,18 +51,35 @@ export type InspectorRunEnv = PostAdmissionEnv & {
|
|
|
50
51
|
* Never a public CLI argv — must not pollute the 卷宗指针 parentRunPath key (#747).
|
|
51
52
|
*/
|
|
52
53
|
reviewReask?: string;
|
|
54
|
+
/**
|
|
55
|
+
* #879 gate summons: verbatim parent-submission body for officer dialogue content.
|
|
56
|
+
* Rides summons.instruction / first-mint prompt when reviewReask is absent.
|
|
57
|
+
* Argv 卷宗指针 stays the binding/lookup key only.
|
|
58
|
+
*/
|
|
59
|
+
gateReviewInstruction?: string;
|
|
53
60
|
};
|
|
54
61
|
|
|
62
|
+
function inspectorParentRunPath(
|
|
63
|
+
admitted: AdmittedInspectorInvocation,
|
|
64
|
+
): string | undefined {
|
|
65
|
+
if (typeof admitted.sourceRunPath === "string" && admitted.sourceRunPath.trim() !== "") {
|
|
66
|
+
return admitted.sourceRunPath;
|
|
67
|
+
}
|
|
68
|
+
return parentRunPathFromGatePointerInstruction(admitted.instruction);
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
/** Project admitted invocation onto the host-neutral turn request. */
|
|
56
72
|
export function buildInspectorTurnRequest(
|
|
57
73
|
admitted: AdmittedInspectorInvocation,
|
|
58
74
|
options: RoleTurnRequestProjectionOptions,
|
|
59
75
|
): RoleTurnRequest {
|
|
76
|
+
const sourceRun = inspectorParentRunPath(admitted);
|
|
60
77
|
return projectRoleTurnRequest(
|
|
61
78
|
admitted,
|
|
62
79
|
{
|
|
63
80
|
activation: {
|
|
64
81
|
role: "inspector" as const,
|
|
82
|
+
...(sourceRun === undefined ? {} : { sourceRun }),
|
|
65
83
|
},
|
|
66
84
|
},
|
|
67
85
|
options,
|
|
@@ -97,7 +115,7 @@ export async function runPublicInspector(
|
|
|
97
115
|
// #753/#786: gate re-ask / verbatim submission body ride summons.instruction on resume.
|
|
98
116
|
const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction);
|
|
99
117
|
if (parentRunPath !== undefined) {
|
|
100
|
-
const resumeInstruction = env.reviewReask;
|
|
118
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
101
119
|
const summons: SameTicketSummonsMaterials = {
|
|
102
120
|
sourceRunPath: parentRunPath,
|
|
103
121
|
...(resumeInstruction === undefined
|
|
@@ -147,6 +165,10 @@ export async function runPublicInspector(
|
|
|
147
165
|
}
|
|
148
166
|
|
|
149
167
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
168
|
+
if (parentRunPath !== undefined) {
|
|
169
|
+
await persistAdmittedSourceRunPath(admitted, parentRunPath);
|
|
170
|
+
admitted = { ...admitted, sourceRunPath: parentRunPath };
|
|
171
|
+
}
|
|
150
172
|
|
|
151
173
|
const engineMaterial = engineSessionMaterialFromOptions({
|
|
152
174
|
...pickEngineAxis(env),
|
|
@@ -164,7 +186,9 @@ export async function runPublicInspector(
|
|
|
164
186
|
: { correlationId: env.correlationId }),
|
|
165
187
|
continuation: {
|
|
166
188
|
kind: "initial",
|
|
167
|
-
|
|
189
|
+
// #879: gate first mint uses parent payload as content; binding stays typed activation.
|
|
190
|
+
prompt: (env.reviewReask ?? env.gateReviewInstruction)
|
|
191
|
+
?? buildInspectorTransportPrompt(admitted, engineMaterial),
|
|
168
192
|
},
|
|
169
193
|
});
|
|
170
194
|
|
|
@@ -69,6 +69,11 @@ export type InstructionSeatRunEnv = PostAdmissionEnv & {
|
|
|
69
69
|
* Never a public CLI argv — must not pollute the --source-run parent lookup key (#747).
|
|
70
70
|
*/
|
|
71
71
|
reviewReask?: string;
|
|
72
|
+
/**
|
|
73
|
+
* #879 gate summons: verbatim parent-submission body for officer dialogue content.
|
|
74
|
+
* Rides summons.instruction / first-mint prompt when reviewReask is absent.
|
|
75
|
+
*/
|
|
76
|
+
gateReviewInstruction?: string;
|
|
72
77
|
};
|
|
73
78
|
|
|
74
79
|
/** Project an admitted instruction-seat invocation onto the host-neutral turn request. */
|
|
@@ -291,8 +296,9 @@ export async function runPublicInstructionSeat(
|
|
|
291
296
|
}
|
|
292
297
|
}
|
|
293
298
|
|
|
294
|
-
// #756/#
|
|
295
|
-
|
|
299
|
+
// #756/#879: auditor reask / verbatim submission body ride summons.instruction.
|
|
300
|
+
// Binding pointer stays --source-run / argv 卷宗指针; content is body or reask.
|
|
301
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
296
302
|
const summons: SameTicketSummonsMaterials = {
|
|
297
303
|
...(resumeInstruction === undefined
|
|
298
304
|
? {
|
|
@@ -372,13 +378,15 @@ export async function runPublicInstructionSeat(
|
|
|
372
378
|
: { correlationId: env.correlationId }),
|
|
373
379
|
continuation: {
|
|
374
380
|
kind: "initial",
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
381
|
+
// #879: gate first mint uses parent payload as content when present.
|
|
382
|
+
prompt: (env.reviewReask ?? env.gateReviewInstruction)
|
|
383
|
+
?? buildInstructionTransportPrompt(
|
|
384
|
+
admitted,
|
|
385
|
+
engineSessionMaterialFromOptions({
|
|
386
|
+
...pickEngineAxis(env),
|
|
387
|
+
packageRoot: env.packageRoot,
|
|
388
|
+
}),
|
|
389
|
+
),
|
|
382
390
|
},
|
|
383
391
|
});
|
|
384
392
|
|
|
@@ -131,6 +131,8 @@ export type AdmittedGleanerLeftInvocation = AdmittedRoleInvocationBase & {
|
|
|
131
131
|
|
|
132
132
|
export type AdmittedInspectorInvocation = AdmittedRoleInvocationBase & {
|
|
133
133
|
readonly role: "inspector";
|
|
134
|
+
/** Parent run directory (#747 / #879); independent of dialogue instruction. */
|
|
135
|
+
readonly sourceRunPath?: string;
|
|
134
136
|
};
|
|
135
137
|
|
|
136
138
|
export type AdmittedGatekeeperInvocation = AdmittedRoleInvocationBase & {
|
|
@@ -52,6 +52,12 @@ export type NotaryRunEnv = PostAdmissionEnv & {
|
|
|
52
52
|
* Never a public CLI argv — external callers still have zero prompt.
|
|
53
53
|
*/
|
|
54
54
|
reviewReask?: string;
|
|
55
|
+
/**
|
|
56
|
+
* #879 gate summons: verbatim parent-submission body for officer dialogue content.
|
|
57
|
+
* Rides summons.instruction / first-mint prompt when reviewReask is absent.
|
|
58
|
+
* Binding pointer stays --source-run (independent machine material).
|
|
59
|
+
*/
|
|
60
|
+
gateReviewInstruction?: string;
|
|
55
61
|
};
|
|
56
62
|
|
|
57
63
|
/** Project admitted invocation onto the host-neutral turn request. */
|
|
@@ -115,10 +121,10 @@ export async function runPublicNotary(
|
|
|
115
121
|
throw error;
|
|
116
122
|
}
|
|
117
123
|
// #747: officer resume key is this parent source-run path (not ticket number).
|
|
118
|
-
// #753/#
|
|
119
|
-
// (reask wins when both present; no parallel stack).
|
|
124
|
+
// #753/#879: gate re-ask and verbatim submission body share summons.instruction
|
|
125
|
+
// (reask wins when both present; no parallel stack). Binding stays sourceRunPath.
|
|
120
126
|
{
|
|
121
|
-
const resumeInstruction = env.reviewReask;
|
|
127
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
122
128
|
const summons: SameTicketSummonsMaterials = {
|
|
123
129
|
sourceRunPath: source.runDirectory,
|
|
124
130
|
sourceRun: source,
|
|
@@ -181,7 +187,10 @@ export async function runPublicNotary(
|
|
|
181
187
|
: { correlationId: env.correlationId }),
|
|
182
188
|
continuation: {
|
|
183
189
|
kind: "initial",
|
|
184
|
-
|
|
190
|
+
// #879: gate path first mint carries parent payload as dialogue content;
|
|
191
|
+
// external zero-prompt kickoff unchanged when no body/reask.
|
|
192
|
+
prompt: (env.reviewReask ?? env.gateReviewInstruction)
|
|
193
|
+
?? buildNotaryTransportPrompt(admitted, engineMaterial),
|
|
185
194
|
},
|
|
186
195
|
});
|
|
187
196
|
|
|
@@ -11,7 +11,6 @@ import { isAbsolute, join, resolve } from "node:path";
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
buildResumeContinuationPrompt,
|
|
14
|
-
GATE_DOSSIER_POINTER_PREFIX,
|
|
15
14
|
RESUME_TRANSPORT_ENVELOPE,
|
|
16
15
|
type PublicResumeRequest,
|
|
17
16
|
type SameTicketSummonsMaterials,
|
|
@@ -37,7 +36,11 @@ import type {
|
|
|
37
36
|
RoleTurnResult,
|
|
38
37
|
SessionCustomEntryAppender,
|
|
39
38
|
} from "../host-contracts.ts";
|
|
40
|
-
import {
|
|
39
|
+
import { isOfficerReviewSeat } from "../host-contracts.ts";
|
|
40
|
+
import {
|
|
41
|
+
deliverCaseDossierAsAttachment,
|
|
42
|
+
projectCaseDossierPointerSection,
|
|
43
|
+
} from "./case-dossier-delivery.ts";
|
|
41
44
|
|
|
42
45
|
/** Original error bytes, never relabeled — a secondary fact riding beside a classified cause. */
|
|
43
46
|
function describeCaughtError(error: unknown): { name?: string; message: string; code?: string | number } {
|
|
@@ -58,6 +61,18 @@ function appendContinuationSection(
|
|
|
58
61
|
? { kind: "initial", prompt }
|
|
59
62
|
: { kind: "resume", prompt };
|
|
60
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Nested gate summons (station child) on an officer seat: dialogue content is
|
|
67
|
+
* peer words only (#879). ADR 0081 case dossier still hangs via the existing
|
|
68
|
+
* attachments freeze seam — never into the peer body, never RoleTurnRequest.materials.
|
|
69
|
+
*/
|
|
70
|
+
function isStationChildOfficerDialogue(
|
|
71
|
+
role: string,
|
|
72
|
+
env: { readonly stationChild?: boolean },
|
|
73
|
+
): boolean {
|
|
74
|
+
return env.stationChild === true && isOfficerReviewSeat(role);
|
|
75
|
+
}
|
|
61
76
|
import { projectHostTransitionPriorNative } from "../host-transition-prior-native.ts";
|
|
62
77
|
import type { CredentialProviders, SeatModelConfig } from "./config.ts";
|
|
63
78
|
import {
|
|
@@ -654,9 +669,9 @@ export async function dispatchPostAdmissionTurn<
|
|
|
654
669
|
// Turn request is assembled after beforeDispatch so this turn sees whatever it
|
|
655
670
|
// settled — the seat's ticket bind re-projection and any court diarist station
|
|
656
671
|
// writes (#742). Case dossier delivery (ADR 0081 / #709) rides here once for
|
|
657
|
-
// every public entry:
|
|
658
|
-
//
|
|
659
|
-
//
|
|
672
|
+
// every public entry. #879: station-child officer dialogue keeps peer body
|
|
673
|
+
// intact — 起居录 hangs via existing attachments freeze (not prompt wrap,
|
|
674
|
+
// not RoleTurnRequest.materials). Other entries keep the neutral prompt section.
|
|
660
675
|
let turnRequest: RoleTurnRequest =
|
|
661
676
|
env.signal === undefined ? request : { ...request, signal: env.signal };
|
|
662
677
|
if (env.stationChild !== undefined) {
|
|
@@ -665,19 +680,33 @@ export async function dispatchPostAdmissionTurn<
|
|
|
665
680
|
if (hostTransition !== undefined) {
|
|
666
681
|
turnRequest = { ...turnRequest, hostTransition };
|
|
667
682
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
683
|
+
if (isStationChildOfficerDialogue(admitted.role, env)) {
|
|
684
|
+
// 0081 non-body face: freeze pointer section under run/attachments/.
|
|
685
|
+
// Peer dialogue continuation.prompt stays parent payload only — the seat
|
|
686
|
+
// consumes the freeze via loadCaseDossierReadingMaterial → existing
|
|
687
|
+
// agent-start readingMaterial / systemPrompt.materials fold (not prompt splice,
|
|
688
|
+
// not RoleTurnRequest.materials).
|
|
689
|
+
await deliverCaseDossierAsAttachment({
|
|
690
|
+
ticketNumber: admitted.ticketNumber,
|
|
691
|
+
projectRoot: admitted.projectRoot,
|
|
692
|
+
home: env.home,
|
|
693
|
+
runDirectory: admitted.runDirectory,
|
|
694
|
+
});
|
|
695
|
+
} else {
|
|
696
|
+
const dossierSection = await projectCaseDossierPointerSection({
|
|
697
|
+
ticketNumber: admitted.ticketNumber,
|
|
698
|
+
projectRoot: admitted.projectRoot,
|
|
699
|
+
home: env.home,
|
|
700
|
+
});
|
|
701
|
+
if (dossierSection !== undefined) {
|
|
702
|
+
turnRequest = {
|
|
703
|
+
...turnRequest,
|
|
704
|
+
continuation: appendContinuationSection(
|
|
705
|
+
turnRequest.continuation,
|
|
706
|
+
dossierSection,
|
|
707
|
+
),
|
|
708
|
+
};
|
|
709
|
+
}
|
|
681
710
|
}
|
|
682
711
|
|
|
683
712
|
// Authoritative host write happens here, at the real dispatch boundary —
|
|
@@ -1065,16 +1094,16 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1065
1094
|
}
|
|
1066
1095
|
|
|
1067
1096
|
/**
|
|
1068
|
-
* Shared resume continuation projection (#471 / #600 / #633 / #637 / #755):
|
|
1097
|
+
* Shared resume continuation projection (#471 / #600 / #633 / #637 / #755 / #879):
|
|
1069
1098
|
* seat-table model/engine/timeout axes, restored correlation, and either
|
|
1070
1099
|
* - manual resume (no same-ticket summons): package envelope / optional caller
|
|
1071
1100
|
* message, with engine-axis handbook via buildResumeContinuationPrompt, or
|
|
1072
1101
|
* - same-ticket summons (审核循环续话): caller/peer words + optional frozen
|
|
1073
|
-
* attachment paths only — no
|
|
1074
|
-
* (#750/#755)
|
|
1102
|
+
* attachment paths only — no「请重读」、no code-authored content substitute,
|
|
1103
|
+
* no engine handbook packaging (#750/#755/#879).
|
|
1075
1104
|
* Caller message wins as prompt base when supplied (bytes unchanged, including
|
|
1076
|
-
* blank/whitespace); else summons instruction
|
|
1077
|
-
*
|
|
1105
|
+
* blank/whitespace); else summons instruction (parent payload or reask).
|
|
1106
|
+
* Binding pointer stays on summons.sourceRunPath / activation — not dialogue content.
|
|
1078
1107
|
* Seats add only their activation projection. Call prepareSummonsResumeMaterials
|
|
1079
1108
|
* first when request.summons carries instruction or attachment paths.
|
|
1080
1109
|
*/
|
|
@@ -1088,32 +1117,25 @@ export function resumeTurnRequestProjectionOptions(
|
|
|
1088
1117
|
readonly attachments: readonly { frozenPath: string }[];
|
|
1089
1118
|
},
|
|
1090
1119
|
): RoleTurnRequestProjectionOptions {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|| (admitted.role !== "notary"
|
|
1096
|
-
&& admitted.role !== "inspector"
|
|
1097
|
-
&& admitted.role !== "auditor")
|
|
1098
|
-
) {
|
|
1099
|
-
return body;
|
|
1100
|
-
}
|
|
1101
|
-
if (body.startsWith("请重读")) return body;
|
|
1102
|
-
return `请重读\n${body}`;
|
|
1103
|
-
};
|
|
1120
|
+
// #879: officer dialogue content is caller/peer words only — no「请重读」,
|
|
1121
|
+
// no code-authored constant substitute, no attachment-list wrap of peer body.
|
|
1122
|
+
// Binding pointer stays on summons sourceRunPath / activation / attachments.
|
|
1123
|
+
const officerDialogue = isStationChildOfficerDialogue(admitted.role, env);
|
|
1104
1124
|
let prompt: string;
|
|
1105
1125
|
if (request.message !== undefined) {
|
|
1106
1126
|
if (summonsPrepared !== undefined) {
|
|
1107
|
-
// #755: same-ticket review / open-court — caller words
|
|
1108
|
-
//
|
|
1109
|
-
prompt =
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1127
|
+
// #755: same-ticket review / open-court — caller words.
|
|
1128
|
+
// #879 station-child officer: words only (attachments are independent freeze).
|
|
1129
|
+
prompt = officerDialogue
|
|
1130
|
+
? request.message
|
|
1131
|
+
: buildInstructionTransportPrompt({
|
|
1132
|
+
instruction: request.message,
|
|
1133
|
+
instructionEmpty: false,
|
|
1134
|
+
attachments: summonsPrepared.attachments,
|
|
1135
|
+
});
|
|
1114
1136
|
} else if (request.summons !== undefined) {
|
|
1115
1137
|
// #755: same-ticket summons without prepared materials — caller words only.
|
|
1116
|
-
prompt =
|
|
1138
|
+
prompt = request.message;
|
|
1117
1139
|
} else {
|
|
1118
1140
|
// Bare manual resume — outsourcing engine axis keeps handbook (#600/#736).
|
|
1119
1141
|
prompt = buildResumeContinuationPrompt({
|
|
@@ -1123,28 +1145,15 @@ export function resumeTurnRequestProjectionOptions(
|
|
|
1123
1145
|
});
|
|
1124
1146
|
}
|
|
1125
1147
|
} else if (summonsPrepared !== undefined) {
|
|
1126
|
-
// #
|
|
1127
|
-
|
|
1148
|
+
// #879 station-child officer: instruction bytes === peer body/reask (no wrap).
|
|
1149
|
+
// Other seats keep #755 instruction + optional attachment path listing.
|
|
1150
|
+
prompt = officerDialogue
|
|
1151
|
+
? (summonsPrepared.instructionEmpty ? "" : summonsPrepared.instruction)
|
|
1152
|
+
: buildInstructionTransportPrompt(summonsPrepared);
|
|
1128
1153
|
} else if (request.summons !== undefined) {
|
|
1129
|
-
// #
|
|
1130
|
-
//
|
|
1131
|
-
|
|
1132
|
-
if (
|
|
1133
|
-
path !== undefined &&
|
|
1134
|
-
(admitted.role === "notary" ||
|
|
1135
|
-
admitted.role === "inspector" ||
|
|
1136
|
-
admitted.role === "auditor")
|
|
1137
|
-
) {
|
|
1138
|
-
prompt = `请重读\n${GATE_DOSSIER_POINTER_PREFIX}${path}`;
|
|
1139
|
-
} else if (
|
|
1140
|
-
admitted.role === "notary" ||
|
|
1141
|
-
admitted.role === "inspector" ||
|
|
1142
|
-
admitted.role === "auditor"
|
|
1143
|
-
) {
|
|
1144
|
-
prompt = "请重读";
|
|
1145
|
-
} else {
|
|
1146
|
-
prompt = "";
|
|
1147
|
-
}
|
|
1154
|
+
// #879: same-ticket summons with no instruction (e.g. notary source-run binding
|
|
1155
|
+
// only). Pointer is activation/sourceRun material — not dialogue content.
|
|
1156
|
+
prompt = "";
|
|
1148
1157
|
} else {
|
|
1149
1158
|
// Bare manual resume — outsourcing engine axis keeps handbook (#600/#736).
|
|
1150
1159
|
prompt = buildResumeContinuationPrompt({
|
|
@@ -2007,6 +2007,9 @@ export async function loadResumableInspectorRun(
|
|
|
2007
2007
|
const admitted: AdmittedInspectorInvocation = {
|
|
2008
2008
|
role: "inspector",
|
|
2009
2009
|
...resumedBaseAdmitted(loaded),
|
|
2010
|
+
...(loaded.admittedFields.sourceRunPath === undefined
|
|
2011
|
+
? {}
|
|
2012
|
+
: { sourceRunPath: loaded.admittedFields.sourceRunPath }),
|
|
2010
2013
|
};
|
|
2011
2014
|
return seatLoadedResult(loaded, admitted);
|
|
2012
2015
|
}
|