@akagilnc/pi-workflow-roles 0.1.4021 → 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 +682 -355
- 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 +709 -369
- package/dist/host-contracts.js +16 -0
- package/dist/inspector-contracts.js +8 -0
- package/dist/navigator-attendance.js +2 -0
- package/dist/navigator-public-session.js +18 -34
- package/dist/navigator-session-contracts.js +19 -0
- package/dist/packaged-role-registry.js +1 -1
- package/dist/pi/known-failure.js +3 -4
- package/dist/pi/role-turn-host.js +43 -13
- package/dist/public-cli/auto-resume.js +20 -7
- package/dist/public-cli/case-dossier-delivery.js +60 -1
- package/dist/public-cli/diarist-run.js +9 -23
- package/dist/public-cli/inspector-run.js +17 -3
- package/dist/public-cli/instruction-seat-run.js +12 -7
- package/dist/public-cli/judge-run.js +3 -1
- package/dist/public-cli/main.js +440 -222
- 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 +78 -58
- package/dist/public-cli/terminal.js +1 -11
- package/dist/public-role-summons.js +8 -0
- package/dist/submission-ledger.js +139 -19
- 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 +30 -7
- package/src/inspector-contracts.ts +7 -0
- package/src/judge-role.ts +4 -0
- package/src/navigator-attendance.ts +2 -0
- package/src/navigator-public-session.ts +19 -55
- package/src/navigator-session-contracts.ts +39 -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/known-failure.ts +3 -4
- package/src/pi/role-turn-host.ts +48 -14
- package/src/public-cli/auto-resume.ts +57 -20
- package/src/public-cli/case-dossier-delivery.ts +86 -1
- package/src/public-cli/cli.ts +2 -2
- package/src/public-cli/collector-run.ts +3 -1
- package/src/public-cli/countersign-run.ts +34 -27
- package/src/public-cli/diarist-run.ts +10 -27
- package/src/public-cli/inspector-run.ts +26 -2
- package/src/public-cli/instruction-seat-run.ts +20 -10
- package/src/public-cli/invocation.ts +2 -0
- package/src/public-cli/judge-run.ts +3 -1
- package/src/public-cli/notary-run.ts +13 -4
- package/src/public-cli/post-admission.ts +74 -65
- package/src/public-cli/reviewer-run.ts +3 -1
- package/src/public-cli/run-lifecycle.ts +3 -0
- package/src/public-cli/settlement.ts +105 -80
- package/src/public-cli/terminal.ts +7 -16
- 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 +195 -26
- package/src/user-dialogue-stdin.ts +37 -0
- package/src/worker-role.ts +4 -0
package/dist/host-contracts.js
CHANGED
|
@@ -15,3 +15,19 @@ export class ExplicitInternalActivationError extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/** Gate review officers (察院 / 符宝郎 / 审刑院). Single authority for seat checks. */
|
|
19
|
+
export function isOfficerReviewSeat(role) {
|
|
20
|
+
return role === "notary" || role === "inspector" || role === "auditor";
|
|
21
|
+
}
|
|
22
|
+
/** Per-turn run directory; adapters must project any child-process identity. */
|
|
23
|
+
export function runDirectoryFromHostContext(context) {
|
|
24
|
+
return typeof context.runDirectory === "string" && context.runDirectory.trim() !== ""
|
|
25
|
+
? context.runDirectory
|
|
26
|
+
: undefined;
|
|
27
|
+
}
|
|
28
|
+
/** Per-turn court attempt; absence never inherits ambient process identity. */
|
|
29
|
+
export function courtAttemptIdFromHostContext(context) {
|
|
30
|
+
return typeof context.courtAttemptId === "string" && context.courtAttemptId.trim() !== ""
|
|
31
|
+
? context.courtAttemptId
|
|
32
|
+
: undefined;
|
|
33
|
+
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
const INSPECTOR_OUTPUT_TOOL_NAME = "ak_inspector_output";
|
|
2
2
|
const INSPECTOR_ACCEPTED_TEXT = "\u5BDF\u9662\u56DE\u6267\u5DF2\u63A5\u53D7";
|
|
3
|
+
const INSPECTOR_SOURCE_RUN_FLAG = {
|
|
4
|
+
name: "ak-inspector-source-run",
|
|
5
|
+
definition: {
|
|
6
|
+
description: "\u5BDF\u9662\u521D\u94F8\u7ED1\u5B9A\u7684\u7236 run \u7EDD\u5BF9\u8DEF\u5F84",
|
|
7
|
+
type: "string"
|
|
8
|
+
}
|
|
9
|
+
};
|
|
3
10
|
function validateRecordedInspectorOutput(value) {
|
|
4
11
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
5
12
|
throw new Error("Inspector output has no execution discriminator");
|
|
@@ -18,5 +25,6 @@ function validateRecordedInspectorOutput(value) {
|
|
|
18
25
|
export {
|
|
19
26
|
INSPECTOR_ACCEPTED_TEXT,
|
|
20
27
|
INSPECTOR_OUTPUT_TOOL_NAME,
|
|
28
|
+
INSPECTOR_SOURCE_RUN_FLAG,
|
|
21
29
|
validateRecordedInspectorOutput
|
|
22
30
|
};
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
navigatorProviderFailure,
|
|
23
23
|
navigatorProviderFailureFromDiagnostics,
|
|
24
24
|
navigatorProviderFailureFromError,
|
|
25
|
+
navigatorProviderFailureFromPublicTerminal,
|
|
25
26
|
navigatorProviderFailureFromStatus,
|
|
26
27
|
navigatorUnavailableError,
|
|
27
28
|
parseNavigatorModelSetting,
|
|
@@ -698,6 +699,7 @@ export {
|
|
|
698
699
|
navigatorProviderFailure,
|
|
699
700
|
navigatorProviderFailureFromDiagnostics,
|
|
700
701
|
navigatorProviderFailureFromError,
|
|
702
|
+
navigatorProviderFailureFromPublicTerminal,
|
|
701
703
|
navigatorProviderFailureFromStatus,
|
|
702
704
|
navigatorSubjectKey,
|
|
703
705
|
navigatorSubjectKeyForInput,
|
|
@@ -1,30 +1,12 @@
|
|
|
1
1
|
import { sitianReport } from "./sitian-facade.js";
|
|
2
2
|
import {
|
|
3
3
|
NavigatorUnavailableError,
|
|
4
|
-
|
|
4
|
+
navigatorProviderFailureFromPublicTerminal,
|
|
5
5
|
navigatorProviderFailureFromError,
|
|
6
|
-
navigatorProviderFailureFromStatus,
|
|
7
6
|
navigatorUnavailableError,
|
|
8
7
|
parseNavigatorModelSetting,
|
|
9
8
|
resolveNavigatorSeatSelection
|
|
10
9
|
} from "./navigator-session-contracts.js";
|
|
11
|
-
import { lastRolePayloadRecord } from "./public-cli/terminal.js";
|
|
12
|
-
function providerFailureFromPublicTerminal(outcome) {
|
|
13
|
-
const facts = outcome.decisiveFacts;
|
|
14
|
-
const secondary = typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null ? facts.secondaryEvidence : void 0;
|
|
15
|
-
const httpStatus = typeof secondary?.httpStatus === "number" ? secondary.httpStatus : typeof facts.httpStatus === "number" ? facts.httpStatus : typeof facts.errorCode === "number" ? facts.errorCode : void 0;
|
|
16
|
-
const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
|
|
17
|
-
if (fromStatus !== void 0) return fromStatus;
|
|
18
|
-
const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
|
|
19
|
-
const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
|
|
20
|
-
if (fromDiagnostics !== void 0) return fromDiagnostics;
|
|
21
|
-
const fromCode = navigatorProviderFailureFromError({
|
|
22
|
-
code: secondary?.code ?? facts.errorCode
|
|
23
|
-
});
|
|
24
|
-
if (fromCode !== void 0) return fromCode;
|
|
25
|
-
if (outcome.cause === "provider") return { source: "transport", cause: "transport" };
|
|
26
|
-
return { source: "session", cause: "session" };
|
|
27
|
-
}
|
|
28
10
|
function createNativeNavigatorSessionFactory() {
|
|
29
11
|
return async ({ context, subject, tool }) => {
|
|
30
12
|
const resolved = await resolveNavigatorSeatSelection(context);
|
|
@@ -80,14 +62,15 @@ function createNativeNavigatorSessionFactory() {
|
|
|
80
62
|
const outcome = summoned.terminal?.roleOutcome;
|
|
81
63
|
if (outcome === void 0) {
|
|
82
64
|
const detail = summoned.stderr?.trim() || `exit ${summoned.exitCode}`;
|
|
83
|
-
providerFailure = { source: "transport", cause: "
|
|
65
|
+
providerFailure = { source: "transport", cause: "unknown" };
|
|
84
66
|
throw navigatorUnavailableError(
|
|
85
|
-
|
|
86
|
-
new Error(`Navigator public summon produced no terminal (${detail})`)
|
|
67
|
+
providerFailure.source,
|
|
68
|
+
new Error(`Navigator public summon produced no terminal (${detail})`),
|
|
69
|
+
providerFailure.cause
|
|
87
70
|
);
|
|
88
71
|
}
|
|
89
72
|
if (outcome.kind === "failure") {
|
|
90
|
-
providerFailure =
|
|
73
|
+
providerFailure = navigatorProviderFailureFromPublicTerminal(outcome);
|
|
91
74
|
throw navigatorUnavailableError(
|
|
92
75
|
providerFailure.source,
|
|
93
76
|
new Error(outcome.diagnostic),
|
|
@@ -101,21 +84,22 @@ function createNativeNavigatorSessionFactory() {
|
|
|
101
84
|
if (outcome.kind !== "accepted") {
|
|
102
85
|
return;
|
|
103
86
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
87
|
+
for (const payload of outcome.payloads ?? []) {
|
|
88
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) continue;
|
|
89
|
+
const candidates = payload.candidates;
|
|
90
|
+
if (!Array.isArray(candidates)) continue;
|
|
91
|
+
await tool.execute(
|
|
92
|
+
"navigator-public-prepare",
|
|
93
|
+
{ candidates },
|
|
94
|
+
void 0,
|
|
95
|
+
void 0,
|
|
96
|
+
context
|
|
97
|
+
);
|
|
107
98
|
}
|
|
108
|
-
await tool.execute(
|
|
109
|
-
"navigator-public-prepare",
|
|
110
|
-
{ candidates },
|
|
111
|
-
void 0,
|
|
112
|
-
void 0,
|
|
113
|
-
context
|
|
114
|
-
);
|
|
115
99
|
} catch (error) {
|
|
116
100
|
if (error instanceof NavigatorUnavailableError) throw error;
|
|
117
101
|
const fact = navigatorProviderFailureFromError(error);
|
|
118
|
-
providerFailure = fact ?? { source: "transport", cause: "
|
|
102
|
+
providerFailure = fact ?? { source: "transport", cause: "unknown" };
|
|
119
103
|
throw navigatorUnavailableError(providerFailure.source, error, providerFailure.cause);
|
|
120
104
|
}
|
|
121
105
|
})();
|
|
@@ -92,6 +92,24 @@ function navigatorProviderFailureFromDiagnostics(diagnostics) {
|
|
|
92
92
|
}
|
|
93
93
|
return void 0;
|
|
94
94
|
}
|
|
95
|
+
function navigatorProviderFailureFromPublicTerminal(outcome) {
|
|
96
|
+
const facts = outcome.decisiveFacts;
|
|
97
|
+
const secondary = typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null ? facts.secondaryEvidence : void 0;
|
|
98
|
+
const httpStatus = typeof secondary?.httpStatus === "number" ? secondary.httpStatus : typeof facts.httpStatus === "number" ? facts.httpStatus : typeof facts.errorCode === "number" ? facts.errorCode : void 0;
|
|
99
|
+
const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
|
|
100
|
+
if (fromStatus !== void 0) return fromStatus;
|
|
101
|
+
const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
|
|
102
|
+
const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
|
|
103
|
+
if (fromDiagnostics !== void 0) return fromDiagnostics;
|
|
104
|
+
const fromCode = navigatorProviderFailureFromError({
|
|
105
|
+
code: secondary?.code ?? facts.errorCode
|
|
106
|
+
});
|
|
107
|
+
if (fromCode !== void 0) return fromCode;
|
|
108
|
+
if (outcome.cause === "provider") return { source: "transport", cause: "unknown" };
|
|
109
|
+
const typed = navigatorUnavailableKey(outcome.cause);
|
|
110
|
+
if (typed !== void 0) return { source: typed, cause: typed };
|
|
111
|
+
return { source: "unknown", cause: "unknown" };
|
|
112
|
+
}
|
|
95
113
|
const navigatorProviderFailureSchema = Type.Object({
|
|
96
114
|
source: Type.Union([
|
|
97
115
|
Type.Literal("context"),
|
|
@@ -208,6 +226,7 @@ export {
|
|
|
208
226
|
navigatorProviderFailure,
|
|
209
227
|
navigatorProviderFailureFromDiagnostics,
|
|
210
228
|
navigatorProviderFailureFromError,
|
|
229
|
+
navigatorProviderFailureFromPublicTerminal,
|
|
211
230
|
navigatorProviderFailureFromStatus,
|
|
212
231
|
navigatorUnavailableError,
|
|
213
232
|
parseNavigatorModelSetting,
|
|
@@ -158,7 +158,7 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
158
158
|
role: "inspector",
|
|
159
159
|
phases: [null],
|
|
160
160
|
outputTool: INSPECTOR_OUTPUT_TOOL_NAME,
|
|
161
|
-
inputFlag:
|
|
161
|
+
inputFlag: "ak-inspector-source-run",
|
|
162
162
|
phaseFlag: void 0,
|
|
163
163
|
activationStage: "load-and-install",
|
|
164
164
|
sessionMaterials: INSPECTOR_SESSION_MATERIALS
|
package/dist/pi/known-failure.js
CHANGED
|
@@ -35,9 +35,8 @@ function sessionStopDetails(input) {
|
|
|
35
35
|
/**
|
|
36
36
|
* Project a native session assistant stop onto the existing knownFailure chain.
|
|
37
37
|
* Classification follows two-way testimony: typed HTTP status or SDK structure
|
|
38
|
-
* keeps provider; stopReason
|
|
39
|
-
*
|
|
40
|
-
* in details without rewriting; missing fields are omitted.
|
|
38
|
+
* keeps provider; stopReason / errorMessage prose alone never invents a class (#881).
|
|
39
|
+
* Present upstream payload is preserved in details without rewriting; missing fields are omitted.
|
|
41
40
|
*/
|
|
42
41
|
export function knownFailureFromProviderStop(input) {
|
|
43
42
|
if (input.stopReason !== "error" && input.stopReason !== "aborted")
|
|
@@ -45,7 +44,7 @@ export function knownFailureFromProviderStop(input) {
|
|
|
45
44
|
const diagnostic = nonEmptyString(input.errorMessage);
|
|
46
45
|
const details = sessionStopDetails(input);
|
|
47
46
|
return {
|
|
48
|
-
|
|
47
|
+
...(hasUpstreamErrorTestimony(input) ? { cause: "provider" } : {}),
|
|
49
48
|
...(diagnostic === undefined ? {} : { diagnostic }),
|
|
50
49
|
...(Object.keys(details).length === 0 ? {} : { details }),
|
|
51
50
|
};
|
|
@@ -10,9 +10,10 @@ import { basename, delimiter, dirname, isAbsolute, join, resolve } from "node:pa
|
|
|
10
10
|
import { platform } from "node:process";
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
12
|
import { randomUUID } from "node:crypto";
|
|
13
|
-
import { ExplicitInternalActivationError } from "../host-contracts.js";
|
|
14
|
-
import { applyEngineChildEnv } from "../engine-detour.js";
|
|
13
|
+
import { ExplicitInternalActivationError, isOfficerReviewSeat } from "../host-contracts.js";
|
|
14
|
+
import { applyEngineChildEnv, ENGINE_MODEL_FLAG_NAME, normalizeEngineName } from "../engine-detour.js";
|
|
15
15
|
import { projectActivationFlags } from "../role-activation-flags.js";
|
|
16
|
+
import { encodeUserDialogueStdin } from "../user-dialogue-stdin.js";
|
|
16
17
|
/** Package-relative Internal role entrypoint (ADR 0052; same path as public-cli registry). */
|
|
17
18
|
const INTERNAL_ROLE_ENTRYPOINT_RELATIVE = "extensions/role-runtime.ts";
|
|
18
19
|
export function resolveInternalRoleEntrypoint(packageRoot) {
|
|
@@ -95,17 +96,10 @@ export function applyPiNativeSkillInvocation(methods, prompt) {
|
|
|
95
96
|
* Pi last-hop argv after `--no-extensions -e entry` (#819).
|
|
96
97
|
* Activation flag membership comes from middle-layer projectActivationFlags;
|
|
97
98
|
* this function only renders session coords, controlled constants, and pairs.
|
|
98
|
-
*
|
|
99
|
+
* User dialogue is not an argv element (#879): it rides spawn stdin.
|
|
99
100
|
*/
|
|
100
101
|
export function buildPiTurnExtraArgs(request, authority, extraPiArgs = []) {
|
|
101
102
|
const { sessionFile, sessionDirectory } = authority.decode(request.principal);
|
|
102
|
-
const rawPrompt = request.continuation.kind === "initial" || request.continuation.kind === "resume"
|
|
103
|
-
? request.continuation.prompt
|
|
104
|
-
: (() => {
|
|
105
|
-
const _exhaustive = request.continuation;
|
|
106
|
-
return _exhaustive;
|
|
107
|
-
})();
|
|
108
|
-
const prompt = applyPiNativeSkillInvocation(request.methods, rawPrompt);
|
|
109
103
|
return [
|
|
110
104
|
"--no-skills",
|
|
111
105
|
...buildMethodArgs(request.methods),
|
|
@@ -119,12 +113,28 @@ export function buildPiTurnExtraArgs(request, authority, extraPiArgs = []) {
|
|
|
119
113
|
...extraPiArgs,
|
|
120
114
|
// Envelope assembly = projectActivationFlags; pi only renders argv pairs.
|
|
121
115
|
...activationFlagsToPiArgv(projectActivationFlags(request)),
|
|
116
|
+
...piEngineModelArgs(request),
|
|
122
117
|
"--mode",
|
|
123
118
|
"json",
|
|
124
119
|
...buildSeatModelCliArgs(request.model),
|
|
125
|
-
prompt,
|
|
126
120
|
];
|
|
127
121
|
}
|
|
122
|
+
/** Engine name stays on child env; model has no env fallback (#883 / #879). */
|
|
123
|
+
function piEngineModelArgs(request) {
|
|
124
|
+
const model = normalizeEngineName(request.engineModel);
|
|
125
|
+
if (model === undefined)
|
|
126
|
+
return [];
|
|
127
|
+
return [`--${ENGINE_MODEL_FLAG_NAME}`, model];
|
|
128
|
+
}
|
|
129
|
+
function piUserDialogueBody(request) {
|
|
130
|
+
const rawPrompt = request.continuation.kind === "initial" || request.continuation.kind === "resume"
|
|
131
|
+
? request.continuation.prompt
|
|
132
|
+
: (() => {
|
|
133
|
+
const _exhaustive = request.continuation;
|
|
134
|
+
return _exhaustive;
|
|
135
|
+
})();
|
|
136
|
+
return applyPiNativeSkillInvocation(request.methods, rawPrompt);
|
|
137
|
+
}
|
|
128
138
|
const execFileAsync = promisify(execFile);
|
|
129
139
|
async function resolveSelectedPi(command, cwd, env) {
|
|
130
140
|
const searchPath = env.PATH ?? (platform === "win32" ? (process.env.PATH ?? "") : "/usr/bin:/bin");
|
|
@@ -173,11 +183,22 @@ export function createDefaultPiSpawnRunner(options) {
|
|
|
173
183
|
const child = spawn(piIdentity.executable, [...args], {
|
|
174
184
|
cwd: spawnOptions.cwd,
|
|
175
185
|
env: spawnOptions.env,
|
|
176
|
-
stdio: ["
|
|
186
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
177
187
|
});
|
|
188
|
+
if (child.stdin === null) {
|
|
189
|
+
throw new Error("Pi child stdin pipe was not created");
|
|
190
|
+
}
|
|
178
191
|
if (child.stderr === null) {
|
|
179
192
|
throw new Error("Pi child stderr pipe was not created");
|
|
180
193
|
}
|
|
194
|
+
let stdinDeliveryError;
|
|
195
|
+
child.stdin.on("error", (error) => {
|
|
196
|
+
stdinDeliveryError ??= error;
|
|
197
|
+
});
|
|
198
|
+
if (spawnOptions.stdin !== undefined) {
|
|
199
|
+
child.stdin.write(spawnOptions.stdin);
|
|
200
|
+
}
|
|
201
|
+
child.stdin.end();
|
|
181
202
|
let stderr = "";
|
|
182
203
|
let timedOut = false;
|
|
183
204
|
// No default wall clock. Only an explicit caller budget arms a timer (ADR 0010).
|
|
@@ -248,6 +269,10 @@ export function createDefaultPiSpawnRunner(options) {
|
|
|
248
269
|
reject(executionError);
|
|
249
270
|
return;
|
|
250
271
|
}
|
|
272
|
+
if (stdinDeliveryError !== undefined) {
|
|
273
|
+
reject(stdinDeliveryError);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
251
276
|
resolveResult({
|
|
252
277
|
code,
|
|
253
278
|
stderr,
|
|
@@ -275,8 +300,11 @@ export function createPiRoleTurnHost(config) {
|
|
|
275
300
|
async executeTurn(request) {
|
|
276
301
|
// #617 DK-7: Pi argv gets projected native paths once; never record bytes.
|
|
277
302
|
// Pi already owns its own session file, so only sitian prior volume rides in.
|
|
303
|
+
// #879: station-child officer dialogue keeps peer words — do not splice
|
|
304
|
+
// host-transition priorNativePaths into the review prompt body.
|
|
278
305
|
let turnRequest = request;
|
|
279
|
-
const
|
|
306
|
+
const officerStationChild = request.stationChild === true && isOfficerReviewSeat(request.activation.role);
|
|
307
|
+
const paths = !officerStationChild && request.hostTransition?.priorNativeKind === "sitian"
|
|
280
308
|
? request.hostTransition.priorNativePaths
|
|
281
309
|
: undefined;
|
|
282
310
|
if (request.continuation.kind === "resume"
|
|
@@ -293,6 +321,7 @@ export function createPiRoleTurnHost(config) {
|
|
|
293
321
|
const roleEntry = await realpath(resolveInternalRoleEntrypoint(config.packageRoot));
|
|
294
322
|
const extraArgs = buildPiTurnExtraArgs(turnRequest, config.principalAuthority, config.extraPiArgs ?? []);
|
|
295
323
|
const args = buildExplicitInternalActivationArgs(roleEntry, extraArgs);
|
|
324
|
+
const stdin = encodeUserDialogueStdin(piUserDialogueBody(turnRequest));
|
|
296
325
|
// Shared envelope isolates this call's court identity: omitting courtAttemptId
|
|
297
326
|
// must not inherit a parent process.env.AK_ROLE_COURT_ATTEMPT (#637).
|
|
298
327
|
const env = {
|
|
@@ -330,6 +359,7 @@ export function createPiRoleTurnHost(config) {
|
|
|
330
359
|
return await spawnRunner(args, {
|
|
331
360
|
cwd: request.cwd,
|
|
332
361
|
env,
|
|
362
|
+
stdin,
|
|
333
363
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
334
364
|
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
335
365
|
});
|
|
@@ -18,7 +18,7 @@ import { join } from "node:path";
|
|
|
18
18
|
import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunResumable, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
|
|
19
19
|
import { parseAutoResumeLimit } from "./config.js";
|
|
20
20
|
import { isLawfulTypedTerminalOutcome, formatTerminalResult } from "./terminal.js";
|
|
21
|
-
import { presentFailureTerminal, presentStructuralRejection, resolveControlledFailureResumeObservation, } from "./settlement.js";
|
|
21
|
+
import { attachRecordedSubmissions, presentFailureTerminal, presentStructuralRejection, resolveControlledFailureResumeObservation, } from "./settlement.js";
|
|
22
22
|
const dummyIo = { stdout: () => { }, stderr: () => { } };
|
|
23
23
|
/**
|
|
24
24
|
* Persist run-state after a host-turn result, outside the retried dispatch try.
|
|
@@ -280,6 +280,20 @@ function unwrapTurnDispatchedFailure(error) {
|
|
|
280
280
|
}
|
|
281
281
|
return current;
|
|
282
282
|
}
|
|
283
|
+
async function attachDispatchExceptionTerminal(admitted, terminal, io) {
|
|
284
|
+
try {
|
|
285
|
+
return await attachRecordedSubmissions({
|
|
286
|
+
projectRoot: admitted.projectRoot,
|
|
287
|
+
runId: admitted.runId,
|
|
288
|
+
runDirectory: admitted.runDirectory,
|
|
289
|
+
}, terminal);
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
// Existing diagnostic seam: attach true cause on stderr, original Terminal stays.
|
|
293
|
+
io.stderr(`dispatch exception ledger attach failed (best-effort continue): ${describeErrorIdentity(error)}\n`);
|
|
294
|
+
return terminal;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
283
297
|
/**
|
|
284
298
|
* Typed failure terminal for a retry path that ended with only exceptions:
|
|
285
299
|
* loud, non-lawful, carrying the last true cause and the pointers to the
|
|
@@ -293,8 +307,8 @@ function dispatchExceptionFailureTerminal(input) {
|
|
|
293
307
|
? "dispatch threw an exception on every attempt"
|
|
294
308
|
: "the final dispatch threw an exception";
|
|
295
309
|
const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(causeError)}`;
|
|
310
|
+
// #881: no fabricated cause class — original error identity + error-file pointers carry the fact.
|
|
296
311
|
const decisiveFacts = {
|
|
297
|
-
cause: "unrecognized",
|
|
298
312
|
diagnostic,
|
|
299
313
|
resumesUsed: input.autoResumeAttempts,
|
|
300
314
|
dispatchErrorFiles: [...input.errorFiles],
|
|
@@ -316,7 +330,6 @@ function dispatchExceptionFailureTerminal(input) {
|
|
|
316
330
|
roleOutcome: {
|
|
317
331
|
kind: "failure",
|
|
318
332
|
role: input.role,
|
|
319
|
-
cause: "unrecognized",
|
|
320
333
|
diagnostic,
|
|
321
334
|
decisiveFacts,
|
|
322
335
|
},
|
|
@@ -431,7 +444,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
431
444
|
else {
|
|
432
445
|
// Exception path: continue through the identical budget/session gates.
|
|
433
446
|
if (autoResumeAttempts >= limit) {
|
|
434
|
-
const terminal = dispatchExceptionFailureTerminal({
|
|
447
|
+
const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
|
|
435
448
|
role: options.admitted.role,
|
|
436
449
|
runId: options.admitted.runId,
|
|
437
450
|
causeError: lastThrownError,
|
|
@@ -439,7 +452,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
439
452
|
autoResumeAttempts,
|
|
440
453
|
endReason: "auto-resume budget exhausted",
|
|
441
454
|
everyAttemptThrew,
|
|
442
|
-
});
|
|
455
|
+
}), options.io);
|
|
443
456
|
await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
|
|
444
457
|
presentTerminal(terminal, options.io);
|
|
445
458
|
return {
|
|
@@ -448,7 +461,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
448
461
|
};
|
|
449
462
|
}
|
|
450
463
|
if (!(await isPrincipalAvailable(options.admitted.principal))) {
|
|
451
|
-
const terminal = dispatchExceptionFailureTerminal({
|
|
464
|
+
const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
|
|
452
465
|
role: options.admitted.role,
|
|
453
466
|
runId: options.admitted.runId,
|
|
454
467
|
causeError: lastThrownError,
|
|
@@ -456,7 +469,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
456
469
|
autoResumeAttempts,
|
|
457
470
|
endReason: "session principal unavailable before further resume",
|
|
458
471
|
everyAttemptThrew,
|
|
459
|
-
});
|
|
472
|
+
}), options.io);
|
|
460
473
|
await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
|
|
461
474
|
presentTerminal(terminal, options.io);
|
|
462
475
|
return {
|
|
@@ -5,11 +5,23 @@
|
|
|
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";
|
|
10
17
|
import { resolveTicketProvenanceVolume } from "../ticket-provenance.js";
|
|
18
|
+
import { freezeAttachmentsIntoRun, } from "./invocation.js";
|
|
11
19
|
/** Section heading of the system-delivered dossier pointer (presentation only). */
|
|
12
20
|
const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)";
|
|
21
|
+
/** Stable freeze key under run/attachments/ for station-child 0081 delivery. */
|
|
22
|
+
const CASE_DOSSIER_ATTACH_KEY = "case-dossier";
|
|
23
|
+
/** Leaf name of the frozen pointer section (content = purpose + paths). */
|
|
24
|
+
const CASE_DOSSIER_ATTACH_FILE = "case-dossier-pointer.md";
|
|
13
25
|
/** Pointer only — presence/absence is for the role to observe at the path. */
|
|
14
26
|
function describeDossierFile(path) {
|
|
15
27
|
return path;
|
|
@@ -32,3 +44,50 @@ export async function projectCaseDossierPointerSection(input) {
|
|
|
32
44
|
`记录卷宗:${describeDossierFile(volume.recordFile)}`,
|
|
33
45
|
].join("\n");
|
|
34
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* ADR 0081 delivery for station-child officer turns (#879): freeze the same
|
|
49
|
+
* pointer section through the existing attachments seam. Role-runtime loads
|
|
50
|
+
* that freeze via loadCaseDossierReadingMaterial onto readingMaterial; the
|
|
51
|
+
* envelope then folds it into systemPrompt.materials. Peer dialogue instruction
|
|
52
|
+
* stays the parent payload; never RoleTurnRequest.materials.
|
|
53
|
+
* Returns frozen attachments, or undefined when unbound (no dossier).
|
|
54
|
+
*/
|
|
55
|
+
export async function deliverCaseDossierAsAttachment(input) {
|
|
56
|
+
const section = await projectCaseDossierPointerSection({
|
|
57
|
+
ticketNumber: input.ticketNumber,
|
|
58
|
+
projectRoot: input.projectRoot,
|
|
59
|
+
home: input.home,
|
|
60
|
+
});
|
|
61
|
+
if (section === undefined)
|
|
62
|
+
return undefined;
|
|
63
|
+
// Stage in OS temp only — never leave a run-local .case-dossier-stage copy.
|
|
64
|
+
const stagingDir = await mkdtemp(join(tmpdir(), "ak-case-dossier-"));
|
|
65
|
+
try {
|
|
66
|
+
const stagingPath = join(stagingDir, CASE_DOSSIER_ATTACH_FILE);
|
|
67
|
+
await writeFile(stagingPath, `${section}\n`, "utf8");
|
|
68
|
+
return await freezeAttachmentsIntoRun([stagingPath], input.runDirectory, CASE_DOSSIER_ATTACH_KEY);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Load a previously frozen case-dossier attachment as reading material
|
|
76
|
+
* (existing agent-start / systemPrompt.materials fold — not dialogue prompt,
|
|
77
|
+
* not RoleTurnRequest.materials). Undefined when the run has no such freeze.
|
|
78
|
+
*/
|
|
79
|
+
export async function loadCaseDossierReadingMaterial(runDirectory) {
|
|
80
|
+
const frozenPath = join(runDirectory, "attachments", CASE_DOSSIER_ATTACH_KEY, `00-${CASE_DOSSIER_ATTACH_FILE}`);
|
|
81
|
+
let section;
|
|
82
|
+
try {
|
|
83
|
+
section = await readFile(frozenPath, "utf8");
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error.code === "ENOENT")
|
|
87
|
+
return undefined;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
if (section.trim() === "")
|
|
91
|
+
return undefined;
|
|
92
|
+
return { kind: "case-dossier-pointer", frozenPath, section };
|
|
93
|
+
}
|
|
@@ -5,8 +5,8 @@ import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissio
|
|
|
5
5
|
import { loadResumableDiaristRun, markRunAdmitted, } from "./run-lifecycle.js";
|
|
6
6
|
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
|
|
7
7
|
import { presentStructuralRejection, trySettleDiaristTerminalResult, } from "./settlement.js";
|
|
8
|
-
import { lastRolePayloadRecord } from "./terminal.js";
|
|
9
8
|
import { projectRoleTurnRequest, } from "./turn-request.js";
|
|
9
|
+
import { readRunTicketNumber } from "../run-ticket-number.js";
|
|
10
10
|
/** Project admitted invocation onto the host-neutral turn request. */
|
|
11
11
|
export function buildDiaristTurnRequest(admitted, options) {
|
|
12
12
|
return projectRoleTurnRequest(admitted, { activation: { role: "diarist" } }, options);
|
|
@@ -110,29 +110,15 @@ export async function runPublicDiarist(argv, env, io, parseDiaristArgv) {
|
|
|
110
110
|
adapters: diaristAdapters(),
|
|
111
111
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
112
112
|
}).then(async (result) => {
|
|
113
|
-
// Accept
|
|
114
|
-
//
|
|
115
|
-
//
|
|
113
|
+
// Accept hook binds ticket onto durable pages (#771). Mirror that page fact
|
|
114
|
+
// onto the caller-visible admitted object — never pick a ticketNumber out of
|
|
115
|
+
// the payload sequence (#881 sole-collapse ban on ticket/escalate).
|
|
116
116
|
if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
roleOutcome.kind === "accepted" &&
|
|
123
|
-
facts?.status !== "escalate") {
|
|
124
|
-
const raw = typeof facts?.ticketNumber === "number"
|
|
125
|
-
? facts.ticketNumber
|
|
126
|
-
: typeof facts?.sitian === "object"
|
|
127
|
-
&& facts.sitian !== null
|
|
128
|
-
&& typeof facts.sitian.ticketNumber === "number"
|
|
129
|
-
? facts.sitian.ticketNumber
|
|
130
|
-
: undefined;
|
|
131
|
-
if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) {
|
|
132
|
-
admitted.ticketNumber = raw;
|
|
133
|
-
if (result.admitted.ticketNumber === undefined) {
|
|
134
|
-
result.admitted.ticketNumber = raw;
|
|
135
|
-
}
|
|
117
|
+
const fromPages = await readRunTicketNumber(admitted.runDirectory);
|
|
118
|
+
if (fromPages !== undefined) {
|
|
119
|
+
await bindAdmittedTicketNumber(admitted, fromPages);
|
|
120
|
+
if (result.admitted.ticketNumber === undefined) {
|
|
121
|
+
result.admitted.ticketNumber = fromPages;
|
|
136
122
|
}
|
|
137
123
|
}
|
|
138
124
|
}
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { engineSessionMaterialFromOptions, pickEngineAxis } from "../package-resources/engine-material.js";
|
|
2
2
|
import { CliUsageError } from "./cli-errors.js";
|
|
3
3
|
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
|
|
4
|
-
import { admitInspectorInvocation, buildInspectorTransportPrompt, } from "./invocation.js";
|
|
4
|
+
import { admitInspectorInvocation, buildInspectorTransportPrompt, persistAdmittedSourceRunPath, } from "./invocation.js";
|
|
5
5
|
import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissionSeatResume, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
|
|
6
6
|
import { loadResumableInspectorRun, markRunAdmitted, parentRunPathFromGatePointerInstruction, } from "./run-lifecycle.js";
|
|
7
7
|
import { presentStructuralRejection, trySettleInspectorTerminalResult, } from "./settlement.js";
|
|
8
8
|
import { projectRoleTurnRequest, } from "./turn-request.js";
|
|
9
|
+
function inspectorParentRunPath(admitted) {
|
|
10
|
+
if (typeof admitted.sourceRunPath === "string" && admitted.sourceRunPath.trim() !== "") {
|
|
11
|
+
return admitted.sourceRunPath;
|
|
12
|
+
}
|
|
13
|
+
return parentRunPathFromGatePointerInstruction(admitted.instruction);
|
|
14
|
+
}
|
|
9
15
|
/** Project admitted invocation onto the host-neutral turn request. */
|
|
10
16
|
export function buildInspectorTurnRequest(admitted, options) {
|
|
17
|
+
const sourceRun = inspectorParentRunPath(admitted);
|
|
11
18
|
return projectRoleTurnRequest(admitted, {
|
|
12
19
|
activation: {
|
|
13
20
|
role: "inspector",
|
|
21
|
+
...(sourceRun === undefined ? {} : { sourceRun }),
|
|
14
22
|
},
|
|
15
23
|
}, options);
|
|
16
24
|
}
|
|
@@ -34,7 +42,7 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
|
|
|
34
42
|
// #753/#786: gate re-ask / verbatim submission body ride summons.instruction on resume.
|
|
35
43
|
const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction);
|
|
36
44
|
if (parentRunPath !== undefined) {
|
|
37
|
-
const resumeInstruction = env.reviewReask;
|
|
45
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
38
46
|
const summons = {
|
|
39
47
|
sourceRunPath: parentRunPath,
|
|
40
48
|
...(resumeInstruction === undefined
|
|
@@ -79,6 +87,10 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
|
|
|
79
87
|
throw error;
|
|
80
88
|
}
|
|
81
89
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
90
|
+
if (parentRunPath !== undefined) {
|
|
91
|
+
await persistAdmittedSourceRunPath(admitted, parentRunPath);
|
|
92
|
+
admitted = { ...admitted, sourceRunPath: parentRunPath };
|
|
93
|
+
}
|
|
82
94
|
const engineMaterial = engineSessionMaterialFromOptions({
|
|
83
95
|
...pickEngineAxis(env),
|
|
84
96
|
packageRoot: env.packageRoot,
|
|
@@ -95,7 +107,9 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
|
|
|
95
107
|
: { correlationId: env.correlationId }),
|
|
96
108
|
continuation: {
|
|
97
109
|
kind: "initial",
|
|
98
|
-
|
|
110
|
+
// #879: gate first mint uses parent payload as content; binding stays typed activation.
|
|
111
|
+
prompt: (env.reviewReask ?? env.gateReviewInstruction)
|
|
112
|
+
?? buildInspectorTransportPrompt(admitted, engineMaterial),
|
|
99
113
|
},
|
|
100
114
|
});
|
|
101
115
|
return await runPostAdmissionOneShot({
|
|
@@ -36,7 +36,9 @@ function instructionSeatAdapters(options) {
|
|
|
36
36
|
return infrastructureFailure === undefined
|
|
37
37
|
? result.knownFailure
|
|
38
38
|
: {
|
|
39
|
-
|
|
39
|
+
...(infrastructureFailure.cause === undefined
|
|
40
|
+
? {}
|
|
41
|
+
: { cause: infrastructureFailure.cause }),
|
|
40
42
|
diagnostic: infrastructureFailure.diagnostic,
|
|
41
43
|
...(infrastructureFailure.identity === undefined
|
|
42
44
|
? {}
|
|
@@ -150,8 +152,9 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
150
152
|
return { exitCode: 2 };
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
|
-
// #756/#
|
|
154
|
-
|
|
155
|
+
// #756/#879: auditor reask / verbatim submission body ride summons.instruction.
|
|
156
|
+
// Binding pointer stays --source-run / argv 卷宗指针; content is body or reask.
|
|
157
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
155
158
|
const summons = {
|
|
156
159
|
...(resumeInstruction === undefined
|
|
157
160
|
? {
|
|
@@ -223,10 +226,12 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
223
226
|
: { correlationId: env.correlationId }),
|
|
224
227
|
continuation: {
|
|
225
228
|
kind: "initial",
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
// #879: gate first mint uses parent payload as content when present.
|
|
230
|
+
prompt: (env.reviewReask ?? env.gateReviewInstruction)
|
|
231
|
+
?? buildInstructionTransportPrompt(admitted, engineSessionMaterialFromOptions({
|
|
232
|
+
...pickEngineAxis(env),
|
|
233
|
+
packageRoot: env.packageRoot,
|
|
234
|
+
})),
|
|
230
235
|
},
|
|
231
236
|
});
|
|
232
237
|
return await runPostAdmissionOneShot({
|
|
@@ -18,7 +18,9 @@ function judgeAdapters() {
|
|
|
18
18
|
(infrastructureFailure === undefined
|
|
19
19
|
? undefined
|
|
20
20
|
: {
|
|
21
|
-
|
|
21
|
+
...(infrastructureFailure.cause === undefined
|
|
22
|
+
? {}
|
|
23
|
+
: { cause: infrastructureFailure.cause }),
|
|
22
24
|
diagnostic: infrastructureFailure.diagnostic,
|
|
23
25
|
...(infrastructureFailure.identity === undefined
|
|
24
26
|
? {}
|