@akagilnc/pi-workflow-roles 0.1.5079 → 0.1.5122
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 +545 -334
- package/dist/atomic-write.js +13 -19
- package/dist/auditor-soul.js +2 -1
- package/dist/doctor-contracts.js +1 -1
- package/dist/gatekeeper-role.js +2 -16
- package/dist/headless-host/production-host.js +577 -366
- package/dist/merger-contracts.js +1 -1
- package/dist/migrate-book-topology.js +18 -14
- package/dist/navigator-attendance.js +28 -143
- package/dist/navigator-invocation-identity.js +2 -0
- package/dist/navigator-public-session.js +13 -1
- package/dist/navigator-work-base.js +113 -0
- package/dist/notary-contracts.js +0 -2
- package/dist/package-contracts/fixer-output.js +1 -1
- package/dist/package-contracts/gatekeeper-output.js +1 -1
- package/dist/package-contracts/navigator-output.js +1 -1
- package/dist/package-contracts/terminating-infrastructure.js +3 -3
- package/dist/packaged-role-registry.js +9 -5
- package/dist/public-cli/cli.js +24 -1
- package/dist/public-cli/countersign-run.js +1 -1
- package/dist/public-cli/instruction-seat-run.js +8 -6
- package/dist/public-cli/invocation.js +6 -3
- package/dist/public-cli/main.js +6711 -6523
- package/dist/public-cli/post-admission.js +125 -26
- package/dist/public-cli/settlement.js +48 -5
- package/dist/receipt-delivery-policy.js +9 -2
- package/dist/review-submission.js +3 -3
- package/docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md +9 -0
- package/extensions/role-runtime.ts +0 -27
- package/package.json +2 -1
- package/resources/836-deleted-machine-instruction-inventory.md +3 -3
- package/resources/collector-bot-handbook.md +4 -0
- package/resources/navigator-route-playbook.md +4 -6
- package/scripts/build-package.mjs +4 -0
- package/souls/coder-output-guide.md +1 -0
- package/src/auditor-soul.ts +2 -1
- package/src/collector-role.ts +6 -7
- package/src/collector-tool-schemas.ts +5 -5
- package/src/diarist-role.ts +2 -2
- package/src/doctor-contracts.ts +1 -1
- package/src/gatekeeper-role.ts +1 -18
- package/src/gleaner-left-role.ts +2 -2
- package/src/inspector-role.ts +1 -1
- package/src/judge-role.ts +1 -1
- package/src/merger-contracts.ts +1 -1
- package/src/navigator-attendance.ts +38 -164
- package/src/navigator-invocation-identity.ts +2 -0
- package/src/navigator-public-session.ts +17 -1
- package/src/navigator-role.ts +4 -0
- package/src/navigator-session-contracts.ts +2 -0
- package/src/navigator-work-base.ts +123 -0
- package/src/notary-contracts.ts +0 -4
- package/src/package-contracts/fixer-output.ts +1 -1
- package/src/package-contracts/gatekeeper-output.ts +3 -3
- package/src/package-contracts/navigator-output.ts +1 -1
- package/src/package-contracts/terminating-infrastructure.ts +3 -6
- package/src/packaged-role-registry.ts +5 -1
- package/src/public-cli/cli-io.ts +2 -0
- package/src/public-cli/cli.ts +38 -4
- package/src/public-cli/countersign-run.ts +1 -1
- package/src/public-cli/instruction-seat-run.ts +10 -6
- package/src/public-cli/invocation.ts +12 -4
- package/src/public-cli/post-admission.ts +189 -34
- package/src/public-cli/settlement.ts +58 -4
- package/src/receipt-delivery-policy.ts +9 -1
- package/src/review-submission.ts +3 -3
- package/src/reviewer-role.ts +3 -3
- package/src/role-runtime-dependencies.ts +1 -5
- package/src/role-runtime.ts +52 -38
- package/src/secretariat-role.ts +3 -3
- package/src/worker-role.ts +3 -3
|
@@ -66,10 +66,10 @@ export const collectorOpenWaitWindowArgsSchema = Type.Object({
|
|
|
66
66
|
*/
|
|
67
67
|
export const collectorBindTargetArgsSchema = Type.Object({
|
|
68
68
|
prNumber: Type.Optional(Type.Unknown({
|
|
69
|
-
description: "
|
|
69
|
+
description: "本仓 PR 号(正整数)。",
|
|
70
70
|
})),
|
|
71
71
|
issueNumber: Type.Optional(Type.Unknown({
|
|
72
|
-
description: "
|
|
72
|
+
description: "本仓 issue 号(正整数)。",
|
|
73
73
|
})),
|
|
74
74
|
}, { additionalProperties: true });
|
|
75
75
|
|
|
@@ -96,7 +96,7 @@ const collectorFindingItemDeclaration = (() => {
|
|
|
96
96
|
{
|
|
97
97
|
additionalProperties: true,
|
|
98
98
|
description:
|
|
99
|
-
"单条 finding 指针:evidenceId
|
|
99
|
+
"单条 finding 指针:evidenceId,以及可选 category、summary。",
|
|
100
100
|
},
|
|
101
101
|
);
|
|
102
102
|
(item as unknown as { required: string[] }).required = [];
|
|
@@ -113,12 +113,12 @@ export const collectorOutputBaseSchema = openToolObject(
|
|
|
113
113
|
// Nested item declarations ride `items` for registration preservation (ADR 0057).
|
|
114
114
|
findings: Type.Unsafe({
|
|
115
115
|
description:
|
|
116
|
-
"本次收集到的逐条 findings
|
|
116
|
+
"本次收集到的逐条 findings 指针。",
|
|
117
117
|
items: collectorFindingItemDeclaration,
|
|
118
118
|
}),
|
|
119
119
|
unfinishedReasons: Type.Unknown({
|
|
120
120
|
description:
|
|
121
|
-
"
|
|
121
|
+
"未完成原因。",
|
|
122
122
|
}),
|
|
123
123
|
}),
|
|
124
124
|
);
|
package/src/diarist-role.ts
CHANGED
|
@@ -16,14 +16,14 @@ export type { DiaristOutput };
|
|
|
16
16
|
export { validateRecordedDiaristOutput };
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* 起居郎交卷形状。
|
|
20
20
|
* #901:交边界(sessions);正文与不可解析原字节由机械投影。
|
|
21
21
|
*/
|
|
22
22
|
export const diaristOutputSchema = withTerminatingOutputDeclarations(
|
|
23
23
|
openToolObject(
|
|
24
24
|
Type.Object({
|
|
25
25
|
status: Type.Unknown({
|
|
26
|
-
description: "completed | escalate
|
|
26
|
+
description: "completed | escalate",
|
|
27
27
|
}),
|
|
28
28
|
ticketNumber: Type.Optional(
|
|
29
29
|
Type.Unknown({
|
package/src/doctor-contracts.ts
CHANGED
|
@@ -73,7 +73,7 @@ const caseIdentity = Type.Object({ issueNumber: Type.Optional(Type.Integer()), r
|
|
|
73
73
|
// (src/countersign-role.ts) — one shared description across both variants
|
|
74
74
|
// so openToolObjectFromUnion's identical-declaration collapse drops none of it.
|
|
75
75
|
const DOCTOR_STATUS_DESCRIPTION =
|
|
76
|
-
"completed | refused
|
|
76
|
+
"completed | refused。completed 允许空 findings;refused 仅当证据不足以支撑如实案证词" as const;
|
|
77
77
|
const doctorSubmissionVariants = Type.Union([
|
|
78
78
|
Type.Object({
|
|
79
79
|
status: Type.Unknown({ description: DOCTOR_STATUS_DESCRIPTION }),
|
package/src/gatekeeper-role.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
|
|
|
9
9
|
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
10
10
|
import {
|
|
11
11
|
GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
12
|
-
gatekeeperDecisionSchema,
|
|
13
12
|
gatekeeperOutputSchema,
|
|
14
13
|
} from "./package-contracts/gatekeeper-output.ts";
|
|
15
14
|
import type { PublicSummonResult } from "./public-role-summons.ts";
|
|
@@ -161,7 +160,7 @@ export type SubmissionGateHostActions = {
|
|
|
161
160
|
export const GATEKEEPER_TOOL_SPEC = {
|
|
162
161
|
name: GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
163
162
|
label: "门下省决议",
|
|
164
|
-
description: "
|
|
163
|
+
description: "门下省终局决议。status 为 dispatch 或 pass。",
|
|
165
164
|
promptSnippet: "门下省决议",
|
|
166
165
|
parameters: gatekeeperOutputSchema,
|
|
167
166
|
} as const;
|
|
@@ -170,22 +169,6 @@ export type GatekeeperRuntimeDependencies = {
|
|
|
170
169
|
loadSoul(): Promise<string>;
|
|
171
170
|
};
|
|
172
171
|
|
|
173
|
-
function result(content: string, details: unknown) {
|
|
174
|
-
return { content: [{ type: "text" as const, text: content }], details };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/** Gatekeeper province decision tool — open transport; package-contract projection owns legality. */
|
|
178
|
-
export function createGatekeeperOutputTool() {
|
|
179
|
-
return {
|
|
180
|
-
name: GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
181
|
-
description: "提交门下省派官决定。",
|
|
182
|
-
parameters: gatekeeperDecisionSchema,
|
|
183
|
-
async execute(_id: string, args: unknown) {
|
|
184
|
-
return result(`已收 ${String((args as { status?: unknown })?.status)}`, args);
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
172
|
function failureReason(error: unknown): string {
|
|
190
173
|
if (error instanceof AggregateError) return error.errors.map(failureReason).join("; ");
|
|
191
174
|
return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
package/src/gleaner-left-role.ts
CHANGED
|
@@ -17,12 +17,12 @@ export { validateRecordedGleanerLeftOutput };
|
|
|
17
17
|
|
|
18
18
|
// #836 r16 class 1: pointer/statement are LLM/human-read narrative content — no
|
|
19
19
|
// code branches on their presence.
|
|
20
|
-
/**
|
|
20
|
+
/** 左拾遗弹章交卷形状。 */
|
|
21
21
|
export const gleanerLeftOutputSchema = withTerminatingOutputDeclarations(
|
|
22
22
|
openToolObject(
|
|
23
23
|
Type.Object({
|
|
24
24
|
status: Type.Unknown({
|
|
25
|
-
description: "completed
|
|
25
|
+
description: "completed",
|
|
26
26
|
}),
|
|
27
27
|
findings: Type.Array(
|
|
28
28
|
Type.Object(
|
package/src/inspector-role.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { INSPECTOR_OUTPUT_TOOL_NAME as INSPECTOR_OUTPUT_TOOL };
|
|
|
13
13
|
export type { InspectorOutput };
|
|
14
14
|
export { validateRecordedInspectorOutput };
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/** 台院事后察举交卷形状。 */
|
|
17
17
|
export const inspectorOutputSchema = reviewSubmissionSchema;
|
|
18
18
|
|
|
19
19
|
export type InspectorOutputParameters = Static<typeof inspectorOutputSchema>;
|
package/src/judge-role.ts
CHANGED
|
@@ -48,7 +48,7 @@ export function createJudgeRoleRuntime(
|
|
|
48
48
|
pi.registerTool({
|
|
49
49
|
name: JUDGE_OUTPUT_TOOL_NAME,
|
|
50
50
|
label: "大理寺输出",
|
|
51
|
-
description: "
|
|
51
|
+
description: "提交大理寺终局判词。",
|
|
52
52
|
promptSnippet: "提交大理寺终局判词",
|
|
53
53
|
parameters: judgeVerdictSchema,
|
|
54
54
|
async execute(toolCallId: string, parameters: Static<typeof judgeVerdictSchema>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> {
|
package/src/merger-contracts.ts
CHANGED
|
@@ -20,7 +20,7 @@ export const mergerInputSchema = Type.Object({
|
|
|
20
20
|
// #836 (ADR 0003 Amendment): status kept open like countersignStatus
|
|
21
21
|
// (src/countersign-role.ts) — one shared description across both variants
|
|
22
22
|
// so openToolObjectFromUnion's identical-declaration collapse drops none of it.
|
|
23
|
-
const MERGER_STATUS_DESCRIPTION = "completed | escalate
|
|
23
|
+
const MERGER_STATUS_DESCRIPTION = "completed | escalate" as const;
|
|
24
24
|
const mergerOutputVariants = Type.Union([
|
|
25
25
|
Type.Object({ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }), attemptId: Type.String({ description: "已受理合并 attempt 身份" }), report: Type.String({ description: "如实结果报告" }), mergeCommitId: Type.String({ description: "完成合并 commit object ID" }) }, { additionalProperties: false }),
|
|
26
26
|
Type.Object({ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }), attemptId: Type.String({ description: "已受理合并 attempt 身份" }), diagnosis: Type.String({ description: "合并无法或不应由本席完成的原因(含无进行中合并、无活可干、需新的产品/权力决定)" }), report: Type.String({ description: "如实结果报告" }) }, { additionalProperties: false }),
|
|
@@ -8,6 +8,7 @@ import { Type, type Static } from "typebox";
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
NAVIGATOR_INVOCATION_ENTRY,
|
|
11
|
+
NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY,
|
|
11
12
|
mintNavigatorInvocationId,
|
|
12
13
|
} from "./navigator-invocation-identity.ts";
|
|
13
14
|
import { PACKAGED_ROLE_REGISTRY, type PackagedRole } from "./packaged-role-registry.ts";
|
|
@@ -59,11 +60,12 @@ export {
|
|
|
59
60
|
export { createNativeNavigatorSessionFactory };
|
|
60
61
|
export { resolveNavigatorSeatSelection };
|
|
61
62
|
import { issueRoot, subjectPath } from "./work-subject-identity.ts";
|
|
62
|
-
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE
|
|
63
|
+
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE } from "./receipt-delivery-policy.ts";
|
|
63
64
|
import { navigatorProseFromUnknown } from "./package-contracts/navigator-output.ts";
|
|
65
|
+
import { persistNavigatorWorkBase } from "./navigator-work-base.ts";
|
|
64
66
|
|
|
65
67
|
export const NAVIGATOR_EVENT_TYPE = "ak-navigator-attendance" as const;
|
|
66
|
-
export
|
|
68
|
+
export { NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY };
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* Role-input document bytes win verbatim over work-root file authority when non-empty.
|
|
@@ -131,23 +133,11 @@ export type NavigatorEvent = {
|
|
|
131
133
|
arrivalMessage?: string;
|
|
132
134
|
};
|
|
133
135
|
|
|
134
|
-
export type NavigatorSettlementFact = NavigatorSettlement & { invocationId: string; subjectKey: string };
|
|
135
|
-
export type NavigatorContextProjection = {
|
|
136
|
-
subjectKey: string;
|
|
137
|
-
subject: string;
|
|
138
|
-
authority: string;
|
|
139
|
-
currentRole: { role: string; phase: NavigatorPhase };
|
|
140
|
-
/** Present on the unique settlement-bound advice prompt. */
|
|
141
|
-
currentSettlement?: NavigatorSettlement;
|
|
142
|
-
publicSettlementHistory: NavigatorSettlementFact[];
|
|
143
|
-
liveRoleHelp: Array<{ role: NavigatorTargetRole; help: string }>;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
136
|
// #959: prepare is a prose vehicle. Object root only (ADR 0060); nested shape is
|
|
147
137
|
// never a gate — every object root reaches execute exactly once (Rule 0).
|
|
148
138
|
const prepareSchema = Type.Object({
|
|
149
139
|
prose: Type.Optional(Type.Unknown({
|
|
150
|
-
description: "游奕使散文建议,原样呈现。不要求 candidates/next
|
|
140
|
+
description: "游奕使散文建议,原样呈现。不要求 candidates/next 结构。",
|
|
151
141
|
})),
|
|
152
142
|
}, { additionalProperties: true });
|
|
153
143
|
type PrepareOutput = Static<typeof prepareSchema>;
|
|
@@ -157,9 +147,6 @@ export type NavigatorAttendanceOptions = {
|
|
|
157
147
|
role: string;
|
|
158
148
|
phase: NavigatorPhase;
|
|
159
149
|
subjectKey: string;
|
|
160
|
-
loadSoul: () => Promise<string>;
|
|
161
|
-
loadRoutePlaybook?: () => Promise<string>;
|
|
162
|
-
loadRoleHelp: (role: NavigatorTargetRole) => Promise<string>;
|
|
163
150
|
createSession: NavigatorSessionFactory;
|
|
164
151
|
modelSettingPath?: string;
|
|
165
152
|
subject: string;
|
|
@@ -170,7 +157,6 @@ export type NavigatorAttendanceOptions = {
|
|
|
170
157
|
onEvent: (event: NavigatorEvent, report: NavigatorReport) => void | Promise<void>;
|
|
171
158
|
};
|
|
172
159
|
|
|
173
|
-
const CONTEXT_ENTRY = "ak-navigator-context";
|
|
174
160
|
const INVOCATION_ENTRY = NAVIGATOR_INVOCATION_ENTRY;
|
|
175
161
|
const SETTLEMENT_ENTRY = "ak-navigator-settlement";
|
|
176
162
|
const unavailableKeys = new Set<NavigatorUnavailableKey>(["context", "session", "model", "thinking", "auth", "quota", "transport", "unknown"]);
|
|
@@ -263,7 +249,7 @@ export function createNavigatorPrepareTool(onOutput: (value: PrepareOutput) => v
|
|
|
263
249
|
async execute(_id, value) {
|
|
264
250
|
// Rule 0: the unique prepare submission is accepted once. Shape is never a gate.
|
|
265
251
|
onOutput(value as PrepareOutput);
|
|
266
|
-
return { content: [
|
|
252
|
+
return { content: [], details: value, terminate: true as const };
|
|
267
253
|
},
|
|
268
254
|
};
|
|
269
255
|
}
|
|
@@ -271,7 +257,7 @@ export function createNavigatorPrepareTool(onOutput: (value: PrepareOutput) => v
|
|
|
271
257
|
export function formatNavigatorReport(report: NavigatorReport): string {
|
|
272
258
|
const playbookFailure = report.routePlaybookReadFailure === undefined
|
|
273
259
|
? []
|
|
274
|
-
: [
|
|
260
|
+
: [report.routePlaybookReadFailure];
|
|
275
261
|
if (report.disposition === "no-advice") return playbookFailure.join("\n");
|
|
276
262
|
if (report.disposition === "unavailable") {
|
|
277
263
|
return [...playbookFailure, ...(report.unavailableReason ? [report.unavailableReason] : [])].join("\n");
|
|
@@ -300,16 +286,13 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
300
286
|
let settlementTail: Promise<void> = Promise.resolve();
|
|
301
287
|
let settlementFailure: unknown;
|
|
302
288
|
let preparationFailure: unknown;
|
|
303
|
-
let
|
|
289
|
+
let observedRoutePlaybookFailure: string | undefined;
|
|
304
290
|
let disposed = false;
|
|
305
291
|
/** In-flight session teardown; repeat dispose returns the same promise (#959 mutation proof). */
|
|
306
292
|
let closing: Promise<void> | undefined;
|
|
307
293
|
// Shared attendance seam owns nested summon cancel (ADR 0018 / #959).
|
|
308
294
|
// Role session factory only forwards HostContext.signal — never its own controller.
|
|
309
295
|
const nestCancel = new AbortController();
|
|
310
|
-
/** One-shot live-help warm; consumed by the next prepare so later prepares reread live help. */
|
|
311
|
-
let warmedHelp: Promise<Array<{ role: NavigatorTargetRole; help: string }>> | undefined;
|
|
312
|
-
|
|
313
296
|
const sessionHostContext = (): HostContext => {
|
|
314
297
|
const parentSignal = options.context.signal;
|
|
315
298
|
const signal = parentSignal === undefined
|
|
@@ -318,19 +301,6 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
318
301
|
return { ...options.context, signal };
|
|
319
302
|
};
|
|
320
303
|
|
|
321
|
-
const loadLiveHelp = async (): Promise<Array<{ role: NavigatorTargetRole; help: string }>> => {
|
|
322
|
-
try {
|
|
323
|
-
return await Promise.all(
|
|
324
|
-
NAVIGATOR_TARGETS.map(async ({ role }) => ({
|
|
325
|
-
role,
|
|
326
|
-
help: await options.loadRoleHelp(role),
|
|
327
|
-
})),
|
|
328
|
-
);
|
|
329
|
-
} catch (error) {
|
|
330
|
-
throw navigatorUnavailableError("transport", error);
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
|
|
334
304
|
const unavailable = (invocationId: string, reason: unknown): NavigatorReport => {
|
|
335
305
|
const failure = reason instanceof NavigatorUnavailableError
|
|
336
306
|
? reason
|
|
@@ -342,24 +312,13 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
342
312
|
unavailableCause: failure.unavailableCause,
|
|
343
313
|
};
|
|
344
314
|
};
|
|
345
|
-
let routePlaybookSettlement: Promise<void> | undefined;
|
|
346
315
|
/**
|
|
347
|
-
* Settlement
|
|
348
|
-
*
|
|
349
|
-
* lets navigator speak. No candidates/next parse (owner 2026-09-17 #959).
|
|
316
|
+
* Settlement is the only model round. Standby records attendance and does not prompt.
|
|
317
|
+
* Soul and route playbook stay on the navigator system prompt, not this user turn.
|
|
350
318
|
*/
|
|
351
319
|
let prepareBoundSettlement: NavigatorSettlement | undefined;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
routePlaybook: string;
|
|
355
|
-
helpContext: string;
|
|
356
|
-
modelSetting: string;
|
|
357
|
-
model: ReturnType<typeof parseNavigatorModelSetting>;
|
|
358
|
-
help: Array<{ role: NavigatorTargetRole; help: string }>;
|
|
359
|
-
activeSession: NavigatorPreparationSession;
|
|
360
|
-
};
|
|
361
|
-
/** Materials + session only — no host prompt (cold settle book-before-feed). */
|
|
362
|
-
const loadMaterialsAndSession = async (invocationId: string): Promise<MaterialsBundle> => {
|
|
320
|
+
/** Session only — no host prompt (standby attendance, or cold settle book-before-feed). */
|
|
321
|
+
const loadMaterialsAndSession = async (invocationId: string): Promise<NavigatorPreparationSession> => {
|
|
363
322
|
if (contextError !== undefined) throw navigatorUnavailableError("context", contextError);
|
|
364
323
|
if (typeof authority !== "string" || authority.trim() === "") {
|
|
365
324
|
throw navigatorUnavailableError(
|
|
@@ -367,30 +326,6 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
367
326
|
new Error("controlling authority content was not supplied as typed work context"),
|
|
368
327
|
);
|
|
369
328
|
}
|
|
370
|
-
let soul: string;
|
|
371
|
-
let modelSetting: string;
|
|
372
|
-
let help: Array<{ role: NavigatorTargetRole; help: string }>;
|
|
373
|
-
let routePlaybook = "";
|
|
374
|
-
routePlaybookReadFailure = undefined;
|
|
375
|
-
const soulPromise = (async () => {
|
|
376
|
-
try {
|
|
377
|
-
const text = (await options.loadSoul()).trim();
|
|
378
|
-
if (!text) throw new Error("Navigator soul is empty");
|
|
379
|
-
return text;
|
|
380
|
-
} catch (error) {
|
|
381
|
-
throw navigatorUnavailableError("context", error);
|
|
382
|
-
}
|
|
383
|
-
})();
|
|
384
|
-
const routePlaybookPromise = (async () => {
|
|
385
|
-
if (options.loadRoutePlaybook === undefined) return "";
|
|
386
|
-
try {
|
|
387
|
-
return await options.loadRoutePlaybook();
|
|
388
|
-
} catch (error) {
|
|
389
|
-
routePlaybookReadFailure = error instanceof Error ? error.message : String(error);
|
|
390
|
-
return "";
|
|
391
|
-
}
|
|
392
|
-
})();
|
|
393
|
-
routePlaybookSettlement = routePlaybookPromise.then(() => undefined);
|
|
394
329
|
const modelPromise = (async () => {
|
|
395
330
|
try {
|
|
396
331
|
const resolved = await resolveNavigatorSeatSelection(options.context);
|
|
@@ -400,21 +335,13 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
400
335
|
throw navigatorUnavailableError("model", error);
|
|
401
336
|
}
|
|
402
337
|
})();
|
|
403
|
-
const
|
|
404
|
-
warmedHelp = undefined;
|
|
405
|
-
[soul, routePlaybook, modelSetting, help] = await Promise.all([
|
|
406
|
-
soulPromise,
|
|
407
|
-
routePlaybookPromise,
|
|
408
|
-
modelPromise,
|
|
409
|
-
helpPromise,
|
|
410
|
-
]);
|
|
338
|
+
const modelSetting = await modelPromise;
|
|
411
339
|
let model: ReturnType<typeof parseNavigatorModelSetting>;
|
|
412
340
|
try {
|
|
413
341
|
model = parseNavigatorModelSetting(modelSetting);
|
|
414
342
|
} catch (error) {
|
|
415
343
|
throw navigatorUnavailableError("model", error);
|
|
416
344
|
}
|
|
417
|
-
const helpContext = help.map(({ role, help: text }) => `<role_help role="${role}">\n${text}\n</role_help>`).join("\n");
|
|
418
345
|
const tool = createNavigatorPrepareTool((value) => { outputSink?.(value); });
|
|
419
346
|
if (session === undefined) {
|
|
420
347
|
sessionReady = (async () => {
|
|
@@ -457,9 +384,9 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
457
384
|
}
|
|
458
385
|
if (disposed) throw navigatorUnavailableError("session", new Error("Navigator attendance was disposed"));
|
|
459
386
|
if (session === undefined) throw new Error("Navigator session was not created");
|
|
460
|
-
return
|
|
387
|
+
return session;
|
|
461
388
|
};
|
|
462
|
-
const prepare = async (
|
|
389
|
+
const prepare = async (existingSession?: NavigatorPreparationSession): Promise<string | undefined> => {
|
|
463
390
|
// Exact principal is owned by shared lifecycle (or one mint per attendance).
|
|
464
391
|
// Model/tool/advice paths cannot override it; role-session persistence is
|
|
465
392
|
// pi.appendEntry at lifecycle start — not optional sessionManager probing.
|
|
@@ -480,53 +407,14 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
480
407
|
const merged = [prior, next].filter((part) => part.trim() !== "").join("\n\n");
|
|
481
408
|
output = { prose: merged };
|
|
482
409
|
};
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
// Prior advice is the host session's own history (CLI resume), not a package ledger.
|
|
490
|
-
const projection: NavigatorContextProjection = {
|
|
410
|
+
const activeSession = existingSession ?? await loadMaterialsAndSession(invocationId);
|
|
411
|
+
// Standby records the invocation entry and does not call the model.
|
|
412
|
+
if (boundSettlement === undefined) return undefined;
|
|
413
|
+
const request = JSON.stringify({
|
|
414
|
+
...boundSettlement,
|
|
415
|
+
invocationId,
|
|
491
416
|
subjectKey,
|
|
492
|
-
|
|
493
|
-
authority,
|
|
494
|
-
currentRole: { role: options.role, phase: options.phase },
|
|
495
|
-
...(boundSettlement === undefined ? {} : { currentSettlement: boundSettlement }),
|
|
496
|
-
publicSettlementHistory,
|
|
497
|
-
liveRoleHelp: help,
|
|
498
|
-
};
|
|
499
|
-
activeSession.appendEntry(CONTEXT_ENTRY, projection);
|
|
500
|
-
// Early (parent start): full host round — ready and wait. Settlement-bound:
|
|
501
|
-
// feed the result and let navigator speak (owner 2026-09-17 #959).
|
|
502
|
-
// ADR 0073: phase behavior text lives in resources/navigator-route-playbook.md;
|
|
503
|
-
// code only binds a neutral phase pointer + typed materials.
|
|
504
|
-
const request = boundSettlement === undefined
|
|
505
|
-
? [
|
|
506
|
-
"本轮为待命轮。",
|
|
507
|
-
`<navigator_soul>\n${soul}\n</navigator_soul>`,
|
|
508
|
-
...(routePlaybookReadFailure === undefined
|
|
509
|
-
? [`<route_playbook>\n${routePlaybook}\n</route_playbook>`]
|
|
510
|
-
: ["可选路线手册未能读取。"]),
|
|
511
|
-
`<work_subject>\n${subject}\n</work_subject>`,
|
|
512
|
-
`<controlling_authority>\n${authority}\n</controlling_authority>`,
|
|
513
|
-
`<current_role>\n${JSON.stringify({ role: options.role, phase: options.phase })}\n</current_role>`,
|
|
514
|
-
`<public_settlement_history>\n${JSON.stringify(projection.publicSettlementHistory)}\n</public_settlement_history>`,
|
|
515
|
-
`<live_role_help>\n${helpContext}\n</live_role_help>`,
|
|
516
|
-
].join("\n\n")
|
|
517
|
-
: [
|
|
518
|
-
"本轮为结算投喂轮。",
|
|
519
|
-
`<navigator_soul>\n${soul}\n</navigator_soul>`,
|
|
520
|
-
...(routePlaybookReadFailure === undefined
|
|
521
|
-
? [`<route_playbook>\n${routePlaybook}\n</route_playbook>`]
|
|
522
|
-
: ["可选路线手册未能读取。"]),
|
|
523
|
-
`<work_subject>\n${subject}\n</work_subject>`,
|
|
524
|
-
`<controlling_authority>\n${authority}\n</controlling_authority>`,
|
|
525
|
-
`<current_role>\n${JSON.stringify({ role: options.role, phase: options.phase })}\n</current_role>`,
|
|
526
|
-
`<current_settlement>\n${JSON.stringify(boundSettlement)}\n</current_settlement>`,
|
|
527
|
-
`<public_settlement_history>\n${JSON.stringify(projection.publicSettlementHistory)}\n</public_settlement_history>`,
|
|
528
|
-
`<live_role_help>\n${helpContext}\n</live_role_help>`,
|
|
529
|
-
].join("\n\n");
|
|
417
|
+
});
|
|
530
418
|
try {
|
|
531
419
|
try {
|
|
532
420
|
if (disposed) throw navigatorUnavailableError("session", new Error("Navigator attendance was disposed"));
|
|
@@ -535,6 +423,7 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
535
423
|
// (navigator-public-session). No assistant-entry harvest — entries() is
|
|
536
424
|
// archivist custom-only on the wired factory (#959).
|
|
537
425
|
const promptAllowingRejectedPrepare = async (text: string, deliveryRequest: boolean) => {
|
|
426
|
+
await persistNavigatorWorkBase(activeSession.recordPointer(), { subject, authority });
|
|
538
427
|
const entryStart = activeSession.entries().length;
|
|
539
428
|
prepareBatchRejected = false;
|
|
540
429
|
let promptFailure: unknown;
|
|
@@ -572,7 +461,7 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
572
461
|
// does not 催交 final advice (owner: prepare then wait for settlement feed).
|
|
573
462
|
if (boundSettlement !== undefined) {
|
|
574
463
|
while (output === undefined && prepareBatchRejected && delivery.nextAction() === "request-delivery") {
|
|
575
|
-
await promptAllowingRejectedPrepare(
|
|
464
|
+
await promptAllowingRejectedPrepare(JSON.stringify(delivery.deliveryState()), true);
|
|
576
465
|
}
|
|
577
466
|
if (output === undefined && delivery.nextAction() === "request-delivery") {
|
|
578
467
|
while (delivery.nextAction() === "request-delivery") {
|
|
@@ -618,6 +507,10 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
618
507
|
preparedProse = normalizePrepareProse(output);
|
|
619
508
|
return preparedProse;
|
|
620
509
|
} finally {
|
|
510
|
+
const playbookFailure = activeSession.routePlaybookReadFailure?.();
|
|
511
|
+
observedRoutePlaybookFailure = typeof playbookFailure === "string" && playbookFailure.trim() !== ""
|
|
512
|
+
? playbookFailure
|
|
513
|
+
: undefined;
|
|
621
514
|
outputSink = undefined;
|
|
622
515
|
}
|
|
623
516
|
};
|
|
@@ -636,17 +529,6 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
636
529
|
contextError = next.contextError;
|
|
637
530
|
return closing;
|
|
638
531
|
},
|
|
639
|
-
/**
|
|
640
|
-
* Start live-help subprocesses during activation without beginning full
|
|
641
|
-
* preparation. Next prepare() consumes the warm result; a later prepare
|
|
642
|
-
* reloads so live help edits remain visible.
|
|
643
|
-
*/
|
|
644
|
-
warmHelp(): void {
|
|
645
|
-
if (disposed || warmedHelp !== undefined || preparation !== undefined) return;
|
|
646
|
-
warmedHelp = loadLiveHelp();
|
|
647
|
-
// Prevent unhandled rejection if attendance is disposed before prepare.
|
|
648
|
-
void warmedHelp.catch(() => undefined);
|
|
649
|
-
},
|
|
650
532
|
prepare(): void {
|
|
651
533
|
if (disposed || preparation !== undefined) return;
|
|
652
534
|
preparationFailure = undefined;
|
|
@@ -662,9 +544,6 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
662
544
|
isPreparing(): boolean {
|
|
663
545
|
return preparationInFlight;
|
|
664
546
|
},
|
|
665
|
-
knownRoutePlaybookReadFailure(): string | undefined {
|
|
666
|
-
return routePlaybookReadFailure;
|
|
667
|
-
},
|
|
668
547
|
settle(settlement: NavigatorSettlement): Promise<void> {
|
|
669
548
|
const next = settlementTail.then(() => settleOnce(settlement));
|
|
670
549
|
// Contract: README.md#Navigator-attendance — rejected attendance settlements are drained only to serialize later attendance; retain the exact rejection for the caller/audit path.
|
|
@@ -698,10 +577,11 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
698
577
|
async function settleOnce(settlement: NavigatorSettlement): Promise<void> {
|
|
699
578
|
// Dispose during post-role grace must ignore late completion entirely (#675).
|
|
700
579
|
if (disposed) return;
|
|
580
|
+
observedRoutePlaybookFailure = undefined;
|
|
701
581
|
const invocationId = activeInvocationId ?? invocationPrincipal;
|
|
702
582
|
let report: NavigatorReport;
|
|
703
|
-
// Drain in-flight
|
|
704
|
-
//
|
|
583
|
+
// Drain in-flight standby attendance (record only). Then feed the typed
|
|
584
|
+
// settlement as the only model round on the same host session.
|
|
705
585
|
if (sessionReady !== undefined) {
|
|
706
586
|
try { await sessionReady; } catch (error) { preparationFailure ??= error; }
|
|
707
587
|
}
|
|
@@ -740,12 +620,12 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
740
620
|
// session already exists so the output prompt can run (owner: 准备好了就等着,
|
|
741
621
|
// 结果出来喂给它让它自己输出).
|
|
742
622
|
if (session !== undefined) preparationFailure = undefined;
|
|
743
|
-
// Cold settle:
|
|
744
|
-
// Pass the
|
|
745
|
-
let
|
|
623
|
+
// Cold settle: session only (no unbound host prompt), then book + feed.
|
|
624
|
+
// Pass the session into prepare so setModel/INVOCATION run once.
|
|
625
|
+
let coldSession: NavigatorPreparationSession | undefined;
|
|
746
626
|
if (session === undefined && preparationFailure === undefined) {
|
|
747
627
|
try {
|
|
748
|
-
|
|
628
|
+
coldSession = await loadMaterialsAndSession(invocationId);
|
|
749
629
|
} catch (error) {
|
|
750
630
|
preparationFailure = error;
|
|
751
631
|
}
|
|
@@ -753,7 +633,7 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
753
633
|
session?.appendEntry(SETTLEMENT_ENTRY, settlementFact);
|
|
754
634
|
if (preparationFailure === undefined) {
|
|
755
635
|
prepareBoundSettlement = settlement;
|
|
756
|
-
preparation = prepare(
|
|
636
|
+
preparation = prepare(coldSession);
|
|
757
637
|
void preparation.catch((error) => { preparationFailure = error; });
|
|
758
638
|
try {
|
|
759
639
|
drainedProse = await preparation;
|
|
@@ -777,12 +657,8 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
777
657
|
report = { disposition: "no-advice" };
|
|
778
658
|
}
|
|
779
659
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
// waiting for, and independently attaching, the routebook diagnostic.
|
|
783
|
-
await routePlaybookSettlement;
|
|
784
|
-
if (routePlaybookReadFailure !== undefined) {
|
|
785
|
-
report = { ...report, routePlaybookReadFailure };
|
|
660
|
+
if (observedRoutePlaybookFailure !== undefined) {
|
|
661
|
+
report = { ...report, routePlaybookReadFailure: observedRoutePlaybookFailure };
|
|
786
662
|
}
|
|
787
663
|
const event: NavigatorEvent = {
|
|
788
664
|
version: 1,
|
|
@@ -813,8 +689,6 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
813
689
|
sessionReady = undefined;
|
|
814
690
|
preparedProse = undefined;
|
|
815
691
|
preparationFailure = undefined;
|
|
816
|
-
routePlaybookSettlement = undefined;
|
|
817
|
-
routePlaybookReadFailure = undefined;
|
|
818
692
|
}
|
|
819
693
|
}
|
|
820
694
|
|
|
@@ -29,6 +29,8 @@ import { isUuidV7, uuidv7 } from "./uuidv7.ts";
|
|
|
29
29
|
import { workSubjectKeysEqual } from "./work-subject-identity.ts";
|
|
30
30
|
|
|
31
31
|
export const NAVIGATOR_INVOCATION_ENTRY = "ak-navigator-invocation" as const;
|
|
32
|
+
/** Native playbook read failure recorded by the navigator process that read the file. */
|
|
33
|
+
export const NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY = "ak-navigator-route-playbook-failure" as const;
|
|
32
34
|
|
|
33
35
|
/** Typed durable infrastructure-failure fact on a packaged role output toolResult. */
|
|
34
36
|
export const NAVIGATOR_INFRASTRUCTURE_FAILURE_KIND = "role_infrastructure_failure" as const;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* host conversation continuity uses CLI resume (`resumeRunId`), not a package
|
|
7
7
|
* advice ledger (development-closure: do not invent package-level memory).
|
|
8
8
|
*/
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
9
10
|
import { basename } from "node:path";
|
|
10
11
|
|
|
11
12
|
import { sitianReport } from "./sitian-facade.ts";
|
|
@@ -23,6 +24,10 @@ import { runDirectoryFromHostContext, type HostContext } from "./host-contracts.
|
|
|
23
24
|
import type { PublicSummonResult } from "./public-role-summons.ts";
|
|
24
25
|
import { CliUsageError } from "./public-cli/cli-errors.ts";
|
|
25
26
|
import { isNavigatorSeat } from "./packaged-role-registry.ts";
|
|
27
|
+
import {
|
|
28
|
+
attachWorkContextPointer,
|
|
29
|
+
navigatorWorkContextFile,
|
|
30
|
+
} from "./navigator-work-base.ts";
|
|
26
31
|
|
|
27
32
|
/**
|
|
28
33
|
* Ledger process home for navigator attendance: admitted HostContext.runDirectory
|
|
@@ -130,6 +135,7 @@ export function createNativeNavigatorSessionFactory(deps?: {
|
|
|
130
135
|
|
|
131
136
|
let providerFailure: NavigatorProviderFailureFact | undefined;
|
|
132
137
|
let noReceipt: NoReceiptLifecycleFacts | undefined;
|
|
138
|
+
let routePlaybookReadFailure: string | undefined;
|
|
133
139
|
let disposed = false;
|
|
134
140
|
/** In-factory host run id for CLI resume; durable pointer also lives on the nest. */
|
|
135
141
|
let hostRunId = readNavigatorHostRunPointer(sessionManager.getEntries() as readonly unknown[]);
|
|
@@ -147,18 +153,23 @@ export function createNativeNavigatorSessionFactory(deps?: {
|
|
|
147
153
|
}
|
|
148
154
|
providerFailure = undefined;
|
|
149
155
|
noReceipt = undefined;
|
|
156
|
+
routePlaybookReadFailure = undefined;
|
|
150
157
|
try {
|
|
151
158
|
const summonHome = await resolveNavigatorLedgerHome(context);
|
|
152
159
|
// Admission after every await: dispose during preflight must not start summon
|
|
153
160
|
// (ADR 0018 / #959 — legal HostContext may omit signal).
|
|
154
161
|
if (disposed) return;
|
|
155
162
|
const resumeRunId = hostRunId;
|
|
163
|
+
const workContextPath = navigatorWorkContextFile(sessionManager.getSessionDir());
|
|
164
|
+
const argvText = existsSync(workContextPath)
|
|
165
|
+
? attachWorkContextPointer(text, workContextPath)
|
|
166
|
+
: text;
|
|
156
167
|
|
|
157
168
|
// Call contract only: forward shared-lifecycle signal; do not own AbortController here
|
|
158
169
|
// (ADR 0018 / #959 — cancel ownership stays on the attendance/envelope seam).
|
|
159
170
|
const baseSummon = {
|
|
160
171
|
role: "navigator" as const,
|
|
161
|
-
argv: [
|
|
172
|
+
argv: [argvText] as const,
|
|
162
173
|
cwd: context.cwd,
|
|
163
174
|
...(summonHome === undefined ? {} : { home: summonHome }),
|
|
164
175
|
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
@@ -186,6 +197,10 @@ export function createNativeNavigatorSessionFactory(deps?: {
|
|
|
186
197
|
// Cancel ownership stays on attendance/envelope; this only closes side effects (#959).
|
|
187
198
|
if (disposed) return;
|
|
188
199
|
|
|
200
|
+
const playbookFailure = summoned.terminal?.navigator?.advisoryDiagnostic;
|
|
201
|
+
routePlaybookReadFailure = typeof playbookFailure === "string" && playbookFailure.trim() !== ""
|
|
202
|
+
? playbookFailure
|
|
203
|
+
: undefined;
|
|
189
204
|
const outcome = summoned.terminal?.roleOutcome;
|
|
190
205
|
if (outcome === undefined) {
|
|
191
206
|
const detail = summoned.stderr?.trim() || `exit ${summoned.exitCode}`;
|
|
@@ -258,6 +273,7 @@ export function createNativeNavigatorSessionFactory(deps?: {
|
|
|
258
273
|
},
|
|
259
274
|
providerFailure: () => providerFailure,
|
|
260
275
|
noReceipt: () => noReceipt,
|
|
276
|
+
routePlaybookReadFailure: () => routePlaybookReadFailure,
|
|
261
277
|
appendEntry: (customType, data) => {
|
|
262
278
|
sessionManager.appendCustomEntry(customType, data);
|
|
263
279
|
try {
|
package/src/navigator-role.ts
CHANGED
|
@@ -14,4 +14,8 @@ export const NAVIGATOR_TOOL_SPEC = {
|
|
|
14
14
|
|
|
15
15
|
export type NavigatorRuntimeDependencies = {
|
|
16
16
|
loadSoul(): Promise<string>;
|
|
17
|
+
/** Standing route playbook. Production omits this and reads the package file once. */
|
|
18
|
+
loadRoutePlaybook?(): Promise<string>;
|
|
19
|
+
/** Same read's native failure, for the existing routePlaybookReadFailure outlet. */
|
|
20
|
+
recordRoutePlaybookReadFailure?(message: string | undefined): void;
|
|
17
21
|
};
|
|
@@ -203,6 +203,8 @@ export type NavigatorPreparationSession = {
|
|
|
203
203
|
* independent summon rather than press the session that owes the receipt.
|
|
204
204
|
*/
|
|
205
205
|
noReceipt?(): NoReceiptLifecycleFacts | undefined;
|
|
206
|
+
/** Native message from the nested playbook read, when that read failed. */
|
|
207
|
+
routePlaybookReadFailure?(): string | undefined;
|
|
206
208
|
setModel?(model: string, thinkingLevel?: string): Promise<void>;
|
|
207
209
|
getThinkingLevel?(): string | undefined;
|
|
208
210
|
recordPointer(): string;
|