@akagilnc/pi-workflow-roles 0.1.3565 → 0.1.3621
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +18477 -8099
- package/dist/analyst-gate-cycles-read.js +387 -0
- package/dist/archivist-record-entry.js +22 -1
- package/dist/audit-escalation.js +6 -0
- package/dist/auditor-soul.js +74 -0
- package/dist/collector-config.js +86 -0
- package/dist/collector-evidence.js +316 -0
- package/dist/collector-github.js +527 -0
- package/dist/collector-ledger.js +853 -0
- package/dist/collector-tool-schemas.js +33 -0
- package/dist/compliance-transport.js +104 -52
- package/dist/diarist-contracts.js +72 -0
- package/dist/diarist-mechanical.js +411 -0
- package/dist/diarist-ticket-resolution.js +177 -0
- package/dist/diarist.js +311 -0
- package/dist/doctor-auditor.js +25 -0
- package/dist/doctor-contracts.js +2 -0
- package/dist/doctor-evidence.js +142 -0
- package/dist/gatekeeper-role.js +104 -81
- package/dist/host-transition-prior-native.js +78 -0
- package/dist/institutional-resolution.js +1 -95
- package/dist/judge-auditor.js +26 -0
- package/dist/ledger-session-read.js +227 -0
- package/dist/merger-git-state.js +78 -0
- package/dist/navigator-attendance.js +15 -7
- package/dist/navigator-public-session.js +170 -0
- package/dist/navigator-session-contracts.js +36 -40
- package/dist/notary-source-run.js +122 -0
- package/dist/package-contracts/auditor-output.js +66 -0
- package/dist/package-contracts/evidence-child-output.js +34 -0
- package/dist/package-contracts/judge-output.js +2 -0
- package/dist/package-contracts/terminating-tools.js +44 -3
- package/dist/package-resources/method-skill.js +254 -0
- package/dist/packaged-role-registry.js +56 -0
- package/dist/pi/durable-principal.js +61 -0
- package/dist/pi/in-process-session.js +25 -6
- package/dist/pi/known-failure.js +52 -0
- package/dist/pi/role-turn-host.js +430 -0
- package/dist/public-cli/auto-resume.js +414 -0
- package/dist/public-cli/cli-errors.js +8 -0
- package/dist/public-cli/cli-io.js +1 -0
- package/dist/public-cli/command-renderer.js +5 -0
- package/dist/public-cli/doctor-run.js +84 -0
- package/dist/public-cli/inspector-run.js +136 -0
- package/dist/public-cli/instruction-seat-run.js +256 -0
- package/dist/public-cli/invocation.js +2225 -0
- package/dist/public-cli/judge-run.js +118 -0
- package/dist/public-cli/load-production-acp-host.js +40 -0
- package/dist/public-cli/main.js +1511 -1124
- package/dist/public-cli/notary-run.js +161 -0
- package/dist/public-cli/option-definitions.js +1192 -0
- package/dist/public-cli/post-admission.js +667 -0
- package/dist/public-cli/public-run-credentials.js +49 -0
- package/dist/public-cli/registry.js +8 -2
- package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
- package/dist/public-cli/run-lifecycle.js +1344 -0
- package/dist/public-cli/seat-ticket-binding.js +113 -0
- package/dist/public-cli/settlement.js +3441 -0
- package/dist/public-cli/terminal.js +135 -0
- package/dist/public-cli/turn-request.js +25 -0
- package/dist/public-role-summons.js +301 -0
- package/dist/receipt-delivery-policy.js +10 -0
- package/dist/reviewer-child-executor.js +80 -8
- package/dist/reviewer-execution-ledger.js +2 -1
- package/dist/run-terminal-artifacts.js +195 -0
- package/dist/run-ticket-number.js +40 -0
- package/dist/session-assistant-usage.js +107 -0
- package/dist/shape-unreadable-failure.js +34 -0
- package/dist/submission-errors.js +3 -0
- package/dist/submission-ledger.js +388 -0
- package/dist/ticket-provenance-contracts.js +115 -0
- package/dist/ticket-provenance.js +340 -0
- package/extensions/role-runtime.ts +8 -0
- package/package.json +1 -1
- package/resources/diarist-collect.md +6 -10
- package/scripts/build-package.mjs +2 -1
- package/souls/diarist.md +9 -0
- package/souls/doctor-auditor.md +1 -0
- package/souls/evidence-child.md +1 -0
- package/src/acp-host/production-host.ts +4 -0
- package/src/acp-host/role-envelope.ts +4 -3
- package/src/acp-host/role-turn-host.ts +9 -1
- package/src/analyst-gate-cycles-read.ts +37 -2
- package/src/archivist-record-entry.ts +45 -1
- package/src/audit-escalation.ts +17 -0
- package/src/auditor-role.ts +22 -0
- package/src/auditor-soul.ts +42 -0
- package/src/compliance-transport.ts +177 -66
- package/src/diarist-contracts.ts +88 -0
- package/src/diarist-role.ts +60 -0
- package/src/diarist.ts +253 -179
- package/src/doctor-auditor.ts +10 -14
- package/src/doctor-contracts.ts +1 -0
- package/src/doctor-role.ts +2 -2
- package/src/evidence-child-role.ts +22 -0
- package/src/gatekeeper-pass-envelope.ts +109 -0
- package/src/gatekeeper-role.ts +145 -104
- package/src/host-contracts.ts +15 -2
- package/src/institutional-resolution.ts +8 -163
- package/src/judge-auditor.ts +10 -15
- package/src/judge-role.ts +8 -0
- package/src/navigator-attendance.ts +25 -9
- package/src/navigator-public-session.ts +217 -0
- package/src/navigator-session-contracts.ts +61 -54
- package/src/notary-source-run.ts +3 -1
- package/src/package-contracts/auditor-output.ts +82 -0
- package/src/package-contracts/evidence-child-output.ts +51 -0
- package/src/package-contracts/judge-output.ts +1 -0
- package/src/package-contracts/reviewer-output.ts +2 -2
- package/src/package-contracts/terminating-tools.ts +53 -3
- package/src/packaged-role-registry.ts +63 -0
- package/src/pi/adapter.ts +2 -1
- package/src/pi/in-process-session.ts +38 -12
- package/src/pi/role-turn-host.ts +42 -0
- package/src/public-cli/cli.ts +43 -6
- package/src/public-cli/countersign-run.ts +9 -129
- package/src/public-cli/diarist-run.ts +312 -0
- package/src/public-cli/instruction-seat-run.ts +289 -50
- package/src/public-cli/invocation.ts +87 -25
- package/src/public-cli/option-definitions.ts +79 -0
- package/src/public-cli/post-admission.ts +9 -3
- package/src/public-cli/registry.ts +7 -1
- package/src/public-cli/run-lifecycle.ts +53 -6
- package/src/public-cli/settlement.ts +170 -7
- package/src/public-cli/terminal.ts +4 -1
- package/src/public-role-summons.ts +431 -0
- package/src/receipt-delivery-policy.ts +10 -0
- package/src/reviewer-agent.ts +1 -1
- package/src/reviewer-child-executor.ts +114 -12
- package/src/reviewer-execution-ledger.ts +3 -2
- package/src/role-runtime.ts +275 -23
- package/src/session-assistant-usage.ts +119 -0
- package/src/session-opening-materials.ts +1 -1
- package/src/shape-unreadable-failure.ts +49 -0
- package/src/submission-errors.ts +3 -0
- package/src/ticket-provenance-contracts.ts +1 -0
- package/src/ticket-provenance.ts +0 -23
- package/dist/evidence-child-executor.js +0 -829
- package/src/diarist-llm-collector.ts +0 -324
- package/src/evidence-child-executor.ts +0 -1140
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Province-officer model authority (#453/#620) and shared seatModelOnly projection.
|
|
3
|
+
* #675 deleted the independent institutional-resolution.json seat page and the
|
|
4
|
+
* child-session open path that consumed it. Seat resolution for every activation
|
|
5
|
+
* is the live public seat table (model/host/engine).
|
|
6
|
+
*/
|
|
4
7
|
import type {
|
|
5
8
|
GateOfficerSeat,
|
|
6
9
|
PublicCliConfig,
|
|
@@ -8,70 +11,20 @@ import type {
|
|
|
8
11
|
} from "./public-cli/config.ts";
|
|
9
12
|
import { seatModelOnly } from "./public-cli/registry.ts";
|
|
10
13
|
|
|
11
|
-
export const INSTITUTIONAL_RESOLUTION_FILE = "institutional-resolution.json" as const;
|
|
12
|
-
|
|
13
|
-
/** Non-secret per-seat model selection — alias of the single host-neutral RoleTurnModelConfig. */
|
|
14
|
-
export type InstitutionalSeatSelection = RoleTurnModelConfig;
|
|
15
|
-
|
|
16
14
|
/**
|
|
17
15
|
* Configured province-officer resolution (#453/#620 authority seam):
|
|
18
16
|
* officer persistent > gatekeeper persistent > unconfigured.
|
|
19
|
-
* No startup candidates and no parent-session fallback —
|
|
20
|
-
*
|
|
21
|
-
* this typed result as-is.
|
|
17
|
+
* No startup candidates and no parent-session fallback — direct call and config
|
|
18
|
+
* display consume this typed result as-is.
|
|
22
19
|
*/
|
|
23
20
|
export type ConfiguredProvinceOfficerResolution = {
|
|
24
21
|
readonly selection?: SeatModelConfig;
|
|
25
22
|
readonly source: "persistent" | "inherit-gatekeeper" | "unconfigured";
|
|
26
23
|
};
|
|
27
24
|
|
|
28
|
-
export type InstitutionalResolutionPage = {
|
|
29
|
-
readonly version: 1;
|
|
30
|
-
readonly seats: {
|
|
31
|
-
readonly gatekeeper?: InstitutionalSeatSelection;
|
|
32
|
-
readonly inspector?: InstitutionalSeatSelection;
|
|
33
|
-
readonly notary?: InstitutionalSeatSelection;
|
|
34
|
-
readonly auditor?: InstitutionalSeatSelection;
|
|
35
|
-
readonly evidenceChild?: InstitutionalSeatSelection;
|
|
36
|
-
readonly [seat: string]: InstitutionalSeatSelection | undefined;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Why institutional seat read failed — structured, not free-text. */
|
|
41
|
-
export type InstitutionalResolutionFailureReason =
|
|
42
|
-
| "missing-page"
|
|
43
|
-
| "missing-seat"
|
|
44
|
-
| "corrupted"
|
|
45
|
-
| "invalid-format";
|
|
46
|
-
|
|
47
|
-
export class InstitutionalResolutionError extends Error {
|
|
48
|
-
readonly reason: InstitutionalResolutionFailureReason;
|
|
49
|
-
constructor(
|
|
50
|
-
message: string,
|
|
51
|
-
reason: InstitutionalResolutionFailureReason,
|
|
52
|
-
options?: ErrorOptions,
|
|
53
|
-
) {
|
|
54
|
-
super(message, options);
|
|
55
|
-
this.name = "InstitutionalResolutionError";
|
|
56
|
-
this.reason = reason;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function cleanSelection(model?: { provider?: string; model?: string; thinking?: string }): InstitutionalSeatSelection | undefined {
|
|
61
|
-
if (model === undefined) return undefined;
|
|
62
|
-
if (typeof model.provider !== "string" || typeof model.model !== "string") return undefined;
|
|
63
|
-
return {
|
|
64
|
-
provider: model.provider,
|
|
65
|
-
model: model.model,
|
|
66
|
-
...(model.thinking === undefined ? {} : { thinking: model.thinking }),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
25
|
/**
|
|
71
26
|
* Authority for configured province-officer model+source (#453/#620):
|
|
72
27
|
* own persistent > gatekeeper persistent > unconfigured.
|
|
73
|
-
* Shared by resolveInstitutionalSeatSelections (plus parent fallback),
|
|
74
|
-
* resolveEffectiveSeat subordinate branch, and roles/config display.
|
|
75
28
|
* Model-axis projection stays seatModelOnly (single implementation in registry).
|
|
76
29
|
*/
|
|
77
30
|
export function resolveConfiguredProvinceOfficer(
|
|
@@ -91,111 +44,3 @@ export function resolveConfiguredProvinceOfficer(
|
|
|
91
44
|
}
|
|
92
45
|
return { source: "unconfigured" };
|
|
93
46
|
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Resolve effective per-seat institutional selections (#518 §2 Hop 1; #620):
|
|
97
|
-
* - gatekeeper / inspector / notary: resolveConfiguredProvinceOfficer then
|
|
98
|
-
* parent effective on the province path only
|
|
99
|
-
* - auditor / evidenceChild: parent effective
|
|
100
|
-
* - navigator: explicit config seat only. Navigator model authority stays
|
|
101
|
-
* `navigator-model.json`; the page never carries a parent-inherited navigator
|
|
102
|
-
* seat, so host-neutral transport cannot shadow that setting (#590 Out of Scope:
|
|
103
|
-
* 席位表/模型路由变更归 owner 域).
|
|
104
|
-
*/
|
|
105
|
-
export function resolveInstitutionalSeatSelections(
|
|
106
|
-
config: PublicCliConfig,
|
|
107
|
-
parentEffectiveModel?: { provider: string; model: string; thinking?: string },
|
|
108
|
-
): InstitutionalResolutionPage {
|
|
109
|
-
const parentSelection = cleanSelection(parentEffectiveModel);
|
|
110
|
-
|
|
111
|
-
const gatekeeper =
|
|
112
|
-
resolveConfiguredProvinceOfficer(config, "gatekeeper").selection ?? parentSelection;
|
|
113
|
-
const inspector =
|
|
114
|
-
resolveConfiguredProvinceOfficer(config, "inspector").selection ?? parentSelection;
|
|
115
|
-
const notary =
|
|
116
|
-
resolveConfiguredProvinceOfficer(config, "notary").selection ?? parentSelection;
|
|
117
|
-
const auditor = parentSelection;
|
|
118
|
-
const evidenceChild = parentSelection;
|
|
119
|
-
const navigator = cleanSelection(seatModelOnly(config.seats?.navigator));
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
version: 1,
|
|
123
|
-
seats: {
|
|
124
|
-
...(gatekeeper === undefined ? {} : { gatekeeper }),
|
|
125
|
-
...(inspector === undefined ? {} : { inspector }),
|
|
126
|
-
...(notary === undefined ? {} : { notary }),
|
|
127
|
-
...(auditor === undefined ? {} : { auditor }),
|
|
128
|
-
...(evidenceChild === undefined ? {} : { evidenceChild }),
|
|
129
|
-
...(navigator === undefined ? {} : { navigator }),
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Persist institutional resolution page to run directory (#518 §2 Hop 2).
|
|
136
|
-
* Rewritten on each dispatch and resume. Zero secrets inside page.
|
|
137
|
-
*/
|
|
138
|
-
export async function writeInstitutionalResolutionPage(
|
|
139
|
-
runDirectory: string,
|
|
140
|
-
page: InstitutionalResolutionPage,
|
|
141
|
-
): Promise<void> {
|
|
142
|
-
const filePath = join(runDirectory, INSTITUTIONAL_RESOLUTION_FILE);
|
|
143
|
-
await writeFile(filePath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Read institutional seat selection from run directory (#518 §2 Hop 3).
|
|
148
|
-
* Fails loud if page is missing, corrupted, or does not contain a resolution for the seat.
|
|
149
|
-
*/
|
|
150
|
-
export async function readInstitutionalSeatSelection(
|
|
151
|
-
runDirectory: string,
|
|
152
|
-
seat: string,
|
|
153
|
-
): Promise<InstitutionalSeatSelection> {
|
|
154
|
-
const filePath = join(runDirectory, INSTITUTIONAL_RESOLUTION_FILE);
|
|
155
|
-
let raw: string;
|
|
156
|
-
try {
|
|
157
|
-
raw = await readFile(filePath, "utf8");
|
|
158
|
-
} catch (error) {
|
|
159
|
-
throw new InstitutionalResolutionError(
|
|
160
|
-
`institutional resolution page is missing at ${filePath}`,
|
|
161
|
-
"missing-page",
|
|
162
|
-
{ cause: error },
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
let page: unknown;
|
|
167
|
-
try {
|
|
168
|
-
page = JSON.parse(raw);
|
|
169
|
-
} catch (error) {
|
|
170
|
-
throw new InstitutionalResolutionError(
|
|
171
|
-
`institutional resolution page is corrupted at ${filePath}`,
|
|
172
|
-
"corrupted",
|
|
173
|
-
{ cause: error },
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (typeof page !== "object" || page === null || (page as { version?: unknown }).version !== 1) {
|
|
178
|
-
throw new InstitutionalResolutionError(
|
|
179
|
-
`institutional resolution page format is invalid at ${filePath}`,
|
|
180
|
-
"invalid-format",
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const seats = (page as { seats?: Record<string, unknown> }).seats;
|
|
185
|
-
if (typeof seats !== "object" || seats === null) {
|
|
186
|
-
throw new InstitutionalResolutionError(
|
|
187
|
-
`institutional resolution page missing seats object at ${filePath}`,
|
|
188
|
-
"invalid-format",
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const selection = cleanSelection(seats[seat] as InstitutionalSeatSelection | undefined);
|
|
193
|
-
if (selection === undefined) {
|
|
194
|
-
throw new InstitutionalResolutionError(
|
|
195
|
-
`institutional resolution page has no resolution for seat "${seat}" at ${filePath}`,
|
|
196
|
-
"missing-seat",
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return selection;
|
|
201
|
-
}
|
package/src/judge-auditor.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createComplianceDecisionTool,
|
|
3
2
|
runComplianceAudit,
|
|
3
|
+
type AuditorSummon,
|
|
4
4
|
type ComplianceDecision,
|
|
5
5
|
} from "./compliance-transport.ts";
|
|
6
6
|
import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
|
|
7
|
-
import { loadAuditorSoul } from "./auditor-soul.ts";
|
|
8
7
|
import {
|
|
9
8
|
readJudgeAuditSubjects,
|
|
10
9
|
requireAuditMaterials,
|
|
@@ -18,17 +17,13 @@ export const SOUL_AUDIT_TOOL_NAME = JUDGE_AUDIT_TOOL_NAME;
|
|
|
18
17
|
export type JudgeAuditOptions = {
|
|
19
18
|
context: HostContext;
|
|
20
19
|
signal?: AbortSignal;
|
|
20
|
+
/** Same seam as runComplianceAudit options — offline tracers only. */
|
|
21
|
+
summonAuditor?: AuditorSummon;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
const auditDecisionTool = createComplianceDecisionTool(
|
|
24
|
-
JUDGE_AUDIT_TOOL_NAME,
|
|
25
|
-
"提交 typed pass/revise/escalate 决议(大理寺审刑)。",
|
|
26
|
-
);
|
|
27
|
-
|
|
28
24
|
/**
|
|
29
|
-
* Judge auditor
|
|
30
|
-
*
|
|
31
|
-
* proceeds without that pointer and self-locates per soul.
|
|
25
|
+
* Judge compliance via public auditor activation (#675 / ADR 0062 / owner r11).
|
|
26
|
+
* 审刑院 is an independent role; subject=judge selects souls/judge-auditor.md.
|
|
32
27
|
*/
|
|
33
28
|
export function createPiJudgeAuditor(): (options: JudgeAuditOptions) => Promise<ComplianceDecision> {
|
|
34
29
|
return async (options) => {
|
|
@@ -38,13 +33,13 @@ export function createPiJudgeAuditor(): (options: JudgeAuditOptions) => Promise<
|
|
|
38
33
|
requireAuditMaterials(subjects);
|
|
39
34
|
|
|
40
35
|
return runComplianceAudit({
|
|
41
|
-
|
|
42
|
-
systemPrompt: await loadAuditorSoul("judge"),
|
|
43
|
-
roleLabel: "Judge compliance audit",
|
|
44
|
-
invalidDecisionLabel: "invalid judge audit decision",
|
|
36
|
+
subject: "judge",
|
|
45
37
|
context: options.context,
|
|
46
|
-
...(auditorRunDirectory(options.context) === undefined
|
|
38
|
+
...(auditorRunDirectory(options.context) === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { runDirectory: auditorRunDirectory(options.context) }),
|
|
47
41
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
42
|
+
...(options.summonAuditor === undefined ? {} : { summonAuditor: options.summonAuditor }),
|
|
48
43
|
});
|
|
49
44
|
};
|
|
50
45
|
}
|
package/src/judge-role.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { withInfrastructureFailureDeclaration } from "./package-contracts/termin
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT,
|
|
11
|
+
JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT,
|
|
11
12
|
JUDGE_ACCEPTED_TEXT,
|
|
12
13
|
JUDGE_OUTPUT_TOOL_NAME,
|
|
13
14
|
validateAcceptedJudgeDetails,
|
|
@@ -127,6 +128,13 @@ export function createJudgeRoleRuntime(
|
|
|
127
128
|
terminate: true as const,
|
|
128
129
|
...usageProjection,
|
|
129
130
|
}),
|
|
131
|
+
// ADR 0055: parent stands with typed unreadable audit fact — not forged pass.
|
|
132
|
+
unreadable: (auditUnreadable, usageProjection) => ({
|
|
133
|
+
content: [{ type: "text" as const, text: JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT }],
|
|
134
|
+
details: { ...acceptedDetails, auditUnreadable },
|
|
135
|
+
terminate: true as const,
|
|
136
|
+
...usageProjection,
|
|
137
|
+
}),
|
|
130
138
|
revise: (violations) => {
|
|
131
139
|
throw new Error(
|
|
132
140
|
`大理寺回执违 soul:${violations.join("; ")}`,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
resolveActivationLedgerHome,
|
|
17
17
|
} from "./activation-ledger-topology.ts";
|
|
18
18
|
import type { HostContext } from "./host-contracts.ts";
|
|
19
|
-
import { createNativeNavigatorSessionFactory } from "./
|
|
19
|
+
import { createNativeNavigatorSessionFactory } from "./navigator-public-session.ts";
|
|
20
20
|
import {
|
|
21
21
|
NAVIGATOR_DEFAULT_MODEL,
|
|
22
22
|
NAVIGATOR_PREPARE_TOOL_NAME,
|
|
@@ -78,7 +78,9 @@ export function resolveNavigatorAuthorityMaterial(
|
|
|
78
78
|
return undefined;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
/** Every public role is a lawful navigator route target (#675 — no nested-only seats). */
|
|
82
|
+
export const NAVIGATOR_TARGETS = PACKAGED_ROLE_REGISTRY
|
|
83
|
+
.map(({ role, phases }) => ({ role, phases }));
|
|
82
84
|
|
|
83
85
|
export type NavigatorTargetRole = PackagedRole;
|
|
84
86
|
export type NavigatorPhase = "plan" | "apply" | null;
|
|
@@ -591,11 +593,7 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
591
593
|
const modelPromise = (async () => {
|
|
592
594
|
try {
|
|
593
595
|
// Same resolution authority as createNativeNavigatorSessionFactory (#590).
|
|
594
|
-
const resolved = await resolveNavigatorSeatSelection(
|
|
595
|
-
options.context,
|
|
596
|
-
options.modelSettingPath,
|
|
597
|
-
options.modelSettingPath ?? navigatorModelSettingPath(),
|
|
598
|
-
);
|
|
596
|
+
const resolved = await resolveNavigatorSeatSelection(options.context);
|
|
599
597
|
return resolved.configuredLabel;
|
|
600
598
|
} catch (error) {
|
|
601
599
|
if (error instanceof NavigatorUnavailableError) throw error;
|
|
@@ -735,6 +733,14 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
735
733
|
return;
|
|
736
734
|
}
|
|
737
735
|
if (promptFailure !== undefined) throw promptFailure;
|
|
736
|
+
const sessionNoReceipt = activeSession.noReceipt?.();
|
|
737
|
+
if (sessionNoReceipt !== undefined) {
|
|
738
|
+
// This session already settled without an accepted receipt on its own
|
|
739
|
+
// budget; one more prompt opens an independent summon, not a delivery
|
|
740
|
+
// request on the settled session (#675).
|
|
741
|
+
delivery.recordNestedNoReceipt(sessionNoReceipt);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
738
744
|
if (deliveryRequest && output === undefined) delivery.recordDeliveryRequest();
|
|
739
745
|
};
|
|
740
746
|
await promptAllowingRejectedPrepare(request, false);
|
|
@@ -831,6 +837,8 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
831
837
|
};
|
|
832
838
|
|
|
833
839
|
async function settleOnce(settlement: NavigatorSettlement): Promise<void> {
|
|
840
|
+
// Dispose during post-role grace must ignore late completion entirely (#675).
|
|
841
|
+
if (disposed) return;
|
|
834
842
|
const invocationId = activeInvocationId ?? invocationPrincipal;
|
|
835
843
|
let report: NavigatorReport;
|
|
836
844
|
if (settlement.kind === "human_decision" || settlement.kind === "role_infrastructure_failure") {
|
|
@@ -876,8 +884,10 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
876
884
|
// table. After selection (speculative or rebound), advice is passed
|
|
877
885
|
// through as-is (ADR 0010 / ADR 0061: caller may ignore).
|
|
878
886
|
if (selected?.candidate.next !== undefined && !selected.matchedToSettlement) {
|
|
887
|
+
if (disposed) return;
|
|
879
888
|
prepareBoundSettlement = settlement;
|
|
880
889
|
prepared = await prepare();
|
|
890
|
+
if (disposed) return;
|
|
881
891
|
selected = selectNavigatorCandidate(prepared, settlement);
|
|
882
892
|
}
|
|
883
893
|
// Budget exhaustion is affirmative typed no-advice; malformed submitted
|
|
@@ -933,10 +943,16 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
933
943
|
...(report.routePlaybookReadFailure === undefined ? {} : { routePlaybookReadFailure: report.routePlaybookReadFailure }),
|
|
934
944
|
...(report.arrivalMessage === undefined ? {} : { arrivalMessage: report.arrivalMessage }),
|
|
935
945
|
};
|
|
936
|
-
// Dispose during post-role grace must ignore late completion (ADR 0052 / #106).
|
|
946
|
+
// Dispose during post-role grace must ignore late completion (ADR 0052 / #106 / #675).
|
|
937
947
|
// Every settled disposition (recommendation | no-advice | unavailable | arrival) is affirmative.
|
|
938
|
-
|
|
948
|
+
// Only the disposed bit is typed authority to drop — never match free-text Error.message.
|
|
949
|
+
if (disposed) return;
|
|
950
|
+
try {
|
|
939
951
|
await options.onEvent(event, report);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
// Race: dispose landed between the check and onEvent; drop only then.
|
|
954
|
+
if (disposed) return;
|
|
955
|
+
throw error;
|
|
940
956
|
}
|
|
941
957
|
preparation = undefined;
|
|
942
958
|
sessionReady = undefined;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigator attendance session factory (#675 r3).
|
|
3
|
+
* Each prepare turn uses the public navigator activation path (summonPublicRole) —
|
|
4
|
+
* same seat table and shared envelope as `ak-role navigator`.
|
|
5
|
+
* Archivist createRecordSession only books route-memory nest under the parent;
|
|
6
|
+
* no openPiInProcessSession second lifecycle / no agentDir patch on the old path.
|
|
7
|
+
*/
|
|
8
|
+
import { sitianReport } from "./sitian-facade.ts";
|
|
9
|
+
import {
|
|
10
|
+
NavigatorUnavailableError,
|
|
11
|
+
navigatorProviderFailureFromDiagnostics,
|
|
12
|
+
navigatorProviderFailureFromError,
|
|
13
|
+
navigatorProviderFailureFromStatus,
|
|
14
|
+
navigatorUnavailableError,
|
|
15
|
+
parseNavigatorModelSetting,
|
|
16
|
+
resolveNavigatorSeatSelection,
|
|
17
|
+
type NavigatorProviderFailureFact,
|
|
18
|
+
type NavigatorSessionFactory,
|
|
19
|
+
} from "./navigator-session-contracts.ts";
|
|
20
|
+
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Classify public-navigator failure terminal from structured decisiveFacts only
|
|
24
|
+
* (httpStatus / diagnostics / secondaryEvidence on the terminal) — same fields
|
|
25
|
+
* settlement already stamps from typed HTTP observation / provider stop.
|
|
26
|
+
*/
|
|
27
|
+
function providerFailureFromPublicTerminal(outcome: {
|
|
28
|
+
readonly cause: string;
|
|
29
|
+
readonly diagnostic: string;
|
|
30
|
+
readonly decisiveFacts: Readonly<Record<string, unknown>>;
|
|
31
|
+
}): NavigatorProviderFailureFact {
|
|
32
|
+
const facts = outcome.decisiveFacts;
|
|
33
|
+
const secondary =
|
|
34
|
+
typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null
|
|
35
|
+
? (facts.secondaryEvidence as Record<string, unknown>)
|
|
36
|
+
: undefined;
|
|
37
|
+
const httpStatus =
|
|
38
|
+
typeof secondary?.httpStatus === "number"
|
|
39
|
+
? secondary.httpStatus
|
|
40
|
+
: typeof facts.httpStatus === "number"
|
|
41
|
+
? facts.httpStatus
|
|
42
|
+
: typeof facts.errorCode === "number"
|
|
43
|
+
? facts.errorCode
|
|
44
|
+
: undefined;
|
|
45
|
+
const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
|
|
46
|
+
if (fromStatus !== undefined) return fromStatus;
|
|
47
|
+
const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
|
|
48
|
+
const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
|
|
49
|
+
if (fromDiagnostics !== undefined) return fromDiagnostics;
|
|
50
|
+
const fromCode = navigatorProviderFailureFromError({
|
|
51
|
+
code: secondary?.code ?? facts.errorCode,
|
|
52
|
+
});
|
|
53
|
+
if (fromCode !== undefined) return fromCode;
|
|
54
|
+
if (outcome.cause === "provider") return { source: "transport", cause: "transport" };
|
|
55
|
+
return { source: "session", cause: "session" };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createNativeNavigatorSessionFactory(): NavigatorSessionFactory {
|
|
59
|
+
return async ({ context, subject, tool }) => {
|
|
60
|
+
const resolved = await resolveNavigatorSeatSelection(context);
|
|
61
|
+
let thinkingLevel = resolved.thinkingLevel;
|
|
62
|
+
|
|
63
|
+
// Archivist nest for attendance route memory only (ADR 0018 / 0065) — not a session open.
|
|
64
|
+
const { createRecordSession } = await import("./archivist-record-entry.ts");
|
|
65
|
+
const sessionManager = createRecordSession({
|
|
66
|
+
cwd: context.cwd,
|
|
67
|
+
kind: "navigator",
|
|
68
|
+
subject,
|
|
69
|
+
parent: context.sessionManager,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
let providerFailure: NavigatorProviderFailureFact | undefined;
|
|
73
|
+
let noReceipt: NoReceiptLifecycleFacts | undefined;
|
|
74
|
+
let disposed = false;
|
|
75
|
+
let inFlightPrompt: Promise<unknown> | undefined;
|
|
76
|
+
|
|
77
|
+
const resolveHome = async (): Promise<string | undefined> => {
|
|
78
|
+
const parentFile = context.sessionManager?.getSessionFile?.();
|
|
79
|
+
if (typeof parentFile === "string" && parentFile.trim() !== "") {
|
|
80
|
+
try {
|
|
81
|
+
const { tryHomeFromAkRolesPath, homeFromRunDirectory } = await import(
|
|
82
|
+
"./activation-ledger-topology.ts"
|
|
83
|
+
);
|
|
84
|
+
const fromParent = tryHomeFromAkRolesPath(parentFile);
|
|
85
|
+
if (fromParent !== undefined && fromParent.length > 0) return fromParent;
|
|
86
|
+
const runDir = parentFile.replace(/\/session\/session\.jsonl$/, "");
|
|
87
|
+
if (runDir !== parentFile) {
|
|
88
|
+
try {
|
|
89
|
+
return homeFromRunDirectory(runDir);
|
|
90
|
+
} catch {
|
|
91
|
+
// fall through
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch {
|
|
95
|
+
// fall through
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const envHome = process.env.HOME;
|
|
99
|
+
return typeof envHome === "string" && envHome.trim() !== "" ? envHome : undefined;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
prompt: async (text) => {
|
|
104
|
+
if (disposed) {
|
|
105
|
+
throw navigatorUnavailableError("session", new Error("Navigator attendance was disposed"));
|
|
106
|
+
}
|
|
107
|
+
providerFailure = undefined;
|
|
108
|
+
noReceipt = undefined;
|
|
109
|
+
const run = (async () => {
|
|
110
|
+
try {
|
|
111
|
+
const { summonPublicRole } = await import("./public-role-summons.ts");
|
|
112
|
+
const home = await resolveHome();
|
|
113
|
+
// Public activation — same face as `ak-role navigator <instruction>` (#675).
|
|
114
|
+
const summoned = await summonPublicRole({
|
|
115
|
+
role: "navigator",
|
|
116
|
+
argv: [text],
|
|
117
|
+
cwd: context.cwd,
|
|
118
|
+
...(home === undefined ? {} : { home }),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const outcome = summoned.terminal?.roleOutcome;
|
|
122
|
+
if (outcome === undefined) {
|
|
123
|
+
const detail = summoned.stderr?.trim() || `exit ${summoned.exitCode}`;
|
|
124
|
+
providerFailure = { source: "transport", cause: "transport" };
|
|
125
|
+
throw navigatorUnavailableError(
|
|
126
|
+
"transport",
|
|
127
|
+
new Error(`Navigator public summon produced no terminal (${detail})`),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (outcome.kind === "failure") {
|
|
131
|
+
providerFailure = providerFailureFromPublicTerminal(outcome);
|
|
132
|
+
throw navigatorUnavailableError(
|
|
133
|
+
providerFailure.source,
|
|
134
|
+
new Error(outcome.diagnostic),
|
|
135
|
+
providerFailure.cause,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (outcome.kind === "no_receipt") {
|
|
139
|
+
// The nested session spent its own delivery budget; attendance settles
|
|
140
|
+
// no-receipt on these facts instead of opening another summon (#675).
|
|
141
|
+
noReceipt = outcome;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (outcome.kind !== "accepted") {
|
|
145
|
+
providerFailure = { source: "session", cause: "session" };
|
|
146
|
+
throw navigatorUnavailableError(
|
|
147
|
+
"session",
|
|
148
|
+
new Error("Navigator public summon returned unusable terminal"),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const candidates = outcome.decisiveFacts.candidates;
|
|
152
|
+
if (!Array.isArray(candidates)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Rejoin attendance prepare tool sink (same candidate shape as public advice).
|
|
156
|
+
await tool.execute(
|
|
157
|
+
"navigator-public-prepare",
|
|
158
|
+
{ candidates },
|
|
159
|
+
undefined,
|
|
160
|
+
undefined,
|
|
161
|
+
context as never,
|
|
162
|
+
);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (error instanceof NavigatorUnavailableError) throw error;
|
|
165
|
+
const fact = navigatorProviderFailureFromError(error);
|
|
166
|
+
providerFailure = fact ?? { source: "transport", cause: "transport" };
|
|
167
|
+
throw navigatorUnavailableError(providerFailure.source, error, providerFailure.cause);
|
|
168
|
+
}
|
|
169
|
+
})();
|
|
170
|
+
inFlightPrompt = run;
|
|
171
|
+
try {
|
|
172
|
+
await run;
|
|
173
|
+
} finally {
|
|
174
|
+
if (inFlightPrompt === run) inFlightPrompt = undefined;
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
providerFailure: () => providerFailure,
|
|
178
|
+
noReceipt: () => noReceipt,
|
|
179
|
+
appendEntry: (customType, data) => {
|
|
180
|
+
sessionManager.appendCustomEntry(customType, data);
|
|
181
|
+
try {
|
|
182
|
+
sitianReport({
|
|
183
|
+
level: "event",
|
|
184
|
+
kind: "attendance",
|
|
185
|
+
cwd: context.cwd,
|
|
186
|
+
sessionParent: sessionManager.getSessionFile(),
|
|
187
|
+
payload: { customType, data },
|
|
188
|
+
source: "navigator-public-session",
|
|
189
|
+
});
|
|
190
|
+
} catch {
|
|
191
|
+
// best-effort
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
entries: () => sessionManager.getEntries(),
|
|
195
|
+
setModel: async (next, nextThinking) => {
|
|
196
|
+
let nextParsed: ReturnType<typeof parseNavigatorModelSetting>;
|
|
197
|
+
try {
|
|
198
|
+
nextParsed = parseNavigatorModelSetting(next);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
throw navigatorUnavailableError("model", error);
|
|
201
|
+
}
|
|
202
|
+
// No live provider session is pinned here: every prompt is an independent
|
|
203
|
+
// public summon whose nested CLI reads the current seat table (#675 验收②
|
|
204
|
+
// / #617 DK-3). A seat edit between prepares therefore applies on the next
|
|
205
|
+
// summon — it is never an unavailable session.
|
|
206
|
+
thinkingLevel = nextThinking ?? nextParsed.thinkingLevel;
|
|
207
|
+
},
|
|
208
|
+
getThinkingLevel: () => thinkingLevel,
|
|
209
|
+
recordPointer: () => sessionManager.getSessionDir(),
|
|
210
|
+
dispose: async () => {
|
|
211
|
+
disposed = true;
|
|
212
|
+
const pending = inFlightPrompt;
|
|
213
|
+
if (pending !== undefined) await pending.catch(() => undefined);
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
}
|