@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.
Files changed (142) hide show
  1. package/README.md +4 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +18477 -8099
  4. package/dist/analyst-gate-cycles-read.js +387 -0
  5. package/dist/archivist-record-entry.js +22 -1
  6. package/dist/audit-escalation.js +6 -0
  7. package/dist/auditor-soul.js +74 -0
  8. package/dist/collector-config.js +86 -0
  9. package/dist/collector-evidence.js +316 -0
  10. package/dist/collector-github.js +527 -0
  11. package/dist/collector-ledger.js +853 -0
  12. package/dist/collector-tool-schemas.js +33 -0
  13. package/dist/compliance-transport.js +104 -52
  14. package/dist/diarist-contracts.js +72 -0
  15. package/dist/diarist-mechanical.js +411 -0
  16. package/dist/diarist-ticket-resolution.js +177 -0
  17. package/dist/diarist.js +311 -0
  18. package/dist/doctor-auditor.js +25 -0
  19. package/dist/doctor-contracts.js +2 -0
  20. package/dist/doctor-evidence.js +142 -0
  21. package/dist/gatekeeper-role.js +104 -81
  22. package/dist/host-transition-prior-native.js +78 -0
  23. package/dist/institutional-resolution.js +1 -95
  24. package/dist/judge-auditor.js +26 -0
  25. package/dist/ledger-session-read.js +227 -0
  26. package/dist/merger-git-state.js +78 -0
  27. package/dist/navigator-attendance.js +15 -7
  28. package/dist/navigator-public-session.js +170 -0
  29. package/dist/navigator-session-contracts.js +36 -40
  30. package/dist/notary-source-run.js +122 -0
  31. package/dist/package-contracts/auditor-output.js +66 -0
  32. package/dist/package-contracts/evidence-child-output.js +34 -0
  33. package/dist/package-contracts/judge-output.js +2 -0
  34. package/dist/package-contracts/terminating-tools.js +44 -3
  35. package/dist/package-resources/method-skill.js +254 -0
  36. package/dist/packaged-role-registry.js +56 -0
  37. package/dist/pi/durable-principal.js +61 -0
  38. package/dist/pi/in-process-session.js +25 -6
  39. package/dist/pi/known-failure.js +52 -0
  40. package/dist/pi/role-turn-host.js +430 -0
  41. package/dist/public-cli/auto-resume.js +414 -0
  42. package/dist/public-cli/cli-errors.js +8 -0
  43. package/dist/public-cli/cli-io.js +1 -0
  44. package/dist/public-cli/command-renderer.js +5 -0
  45. package/dist/public-cli/doctor-run.js +84 -0
  46. package/dist/public-cli/inspector-run.js +136 -0
  47. package/dist/public-cli/instruction-seat-run.js +256 -0
  48. package/dist/public-cli/invocation.js +2225 -0
  49. package/dist/public-cli/judge-run.js +118 -0
  50. package/dist/public-cli/load-production-acp-host.js +40 -0
  51. package/dist/public-cli/main.js +1511 -1124
  52. package/dist/public-cli/notary-run.js +161 -0
  53. package/dist/public-cli/option-definitions.js +1192 -0
  54. package/dist/public-cli/post-admission.js +667 -0
  55. package/dist/public-cli/public-run-credentials.js +49 -0
  56. package/dist/public-cli/registry.js +8 -2
  57. package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
  58. package/dist/public-cli/run-lifecycle.js +1344 -0
  59. package/dist/public-cli/seat-ticket-binding.js +113 -0
  60. package/dist/public-cli/settlement.js +3441 -0
  61. package/dist/public-cli/terminal.js +135 -0
  62. package/dist/public-cli/turn-request.js +25 -0
  63. package/dist/public-role-summons.js +301 -0
  64. package/dist/receipt-delivery-policy.js +10 -0
  65. package/dist/reviewer-child-executor.js +80 -8
  66. package/dist/reviewer-execution-ledger.js +2 -1
  67. package/dist/run-terminal-artifacts.js +195 -0
  68. package/dist/run-ticket-number.js +40 -0
  69. package/dist/session-assistant-usage.js +107 -0
  70. package/dist/shape-unreadable-failure.js +34 -0
  71. package/dist/submission-errors.js +3 -0
  72. package/dist/submission-ledger.js +388 -0
  73. package/dist/ticket-provenance-contracts.js +115 -0
  74. package/dist/ticket-provenance.js +340 -0
  75. package/extensions/role-runtime.ts +8 -0
  76. package/package.json +1 -1
  77. package/resources/diarist-collect.md +6 -10
  78. package/scripts/build-package.mjs +2 -1
  79. package/souls/diarist.md +9 -0
  80. package/souls/doctor-auditor.md +1 -0
  81. package/souls/evidence-child.md +1 -0
  82. package/src/acp-host/production-host.ts +4 -0
  83. package/src/acp-host/role-envelope.ts +4 -3
  84. package/src/acp-host/role-turn-host.ts +9 -1
  85. package/src/analyst-gate-cycles-read.ts +37 -2
  86. package/src/archivist-record-entry.ts +45 -1
  87. package/src/audit-escalation.ts +17 -0
  88. package/src/auditor-role.ts +22 -0
  89. package/src/auditor-soul.ts +42 -0
  90. package/src/compliance-transport.ts +177 -66
  91. package/src/diarist-contracts.ts +88 -0
  92. package/src/diarist-role.ts +60 -0
  93. package/src/diarist.ts +253 -179
  94. package/src/doctor-auditor.ts +10 -14
  95. package/src/doctor-contracts.ts +1 -0
  96. package/src/doctor-role.ts +2 -2
  97. package/src/evidence-child-role.ts +22 -0
  98. package/src/gatekeeper-pass-envelope.ts +109 -0
  99. package/src/gatekeeper-role.ts +145 -104
  100. package/src/host-contracts.ts +15 -2
  101. package/src/institutional-resolution.ts +8 -163
  102. package/src/judge-auditor.ts +10 -15
  103. package/src/judge-role.ts +8 -0
  104. package/src/navigator-attendance.ts +25 -9
  105. package/src/navigator-public-session.ts +217 -0
  106. package/src/navigator-session-contracts.ts +61 -54
  107. package/src/notary-source-run.ts +3 -1
  108. package/src/package-contracts/auditor-output.ts +82 -0
  109. package/src/package-contracts/evidence-child-output.ts +51 -0
  110. package/src/package-contracts/judge-output.ts +1 -0
  111. package/src/package-contracts/reviewer-output.ts +2 -2
  112. package/src/package-contracts/terminating-tools.ts +53 -3
  113. package/src/packaged-role-registry.ts +63 -0
  114. package/src/pi/adapter.ts +2 -1
  115. package/src/pi/in-process-session.ts +38 -12
  116. package/src/pi/role-turn-host.ts +42 -0
  117. package/src/public-cli/cli.ts +43 -6
  118. package/src/public-cli/countersign-run.ts +9 -129
  119. package/src/public-cli/diarist-run.ts +312 -0
  120. package/src/public-cli/instruction-seat-run.ts +289 -50
  121. package/src/public-cli/invocation.ts +87 -25
  122. package/src/public-cli/option-definitions.ts +79 -0
  123. package/src/public-cli/post-admission.ts +9 -3
  124. package/src/public-cli/registry.ts +7 -1
  125. package/src/public-cli/run-lifecycle.ts +53 -6
  126. package/src/public-cli/settlement.ts +170 -7
  127. package/src/public-cli/terminal.ts +4 -1
  128. package/src/public-role-summons.ts +431 -0
  129. package/src/receipt-delivery-policy.ts +10 -0
  130. package/src/reviewer-agent.ts +1 -1
  131. package/src/reviewer-child-executor.ts +114 -12
  132. package/src/reviewer-execution-ledger.ts +3 -2
  133. package/src/role-runtime.ts +275 -23
  134. package/src/session-assistant-usage.ts +119 -0
  135. package/src/session-opening-materials.ts +1 -1
  136. package/src/shape-unreadable-failure.ts +49 -0
  137. package/src/submission-errors.ts +3 -0
  138. package/src/ticket-provenance-contracts.ts +1 -0
  139. package/src/ticket-provenance.ts +0 -23
  140. package/dist/evidence-child-executor.js +0 -829
  141. package/src/diarist-llm-collector.ts +0 -324
  142. package/src/evidence-child-executor.ts +0 -1140
@@ -10,12 +10,9 @@ import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
10
10
  import { Type } from "typebox";
11
11
  import { Value } from "typebox/value";
12
12
 
13
- import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
14
13
  import type { HostContext, HostInstitutionalModelSelection } from "./host-contracts.ts";
15
- import {
16
- InstitutionalResolutionError,
17
- readInstitutionalSeatSelection,
18
- } from "./institutional-resolution.ts";
14
+ import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
15
+
19
16
 
20
17
  export const NAVIGATOR_PREPARE_TOOL_NAME = "ak_navigator_prepare" as const;
21
18
  export const NAVIGATOR_DEFAULT_MODEL = "openai-codex/gpt-5.6-luna:max" as const;
@@ -162,6 +159,12 @@ export type NavigatorPreparationSession = {
162
159
  appendEntry(customType: string, data: unknown): void;
163
160
  entries(): readonly unknown[];
164
161
  providerFailure?(): NavigatorProviderFailureFact | undefined;
162
+ /**
163
+ * Facts of a nested session that already settled without an accepted receipt.
164
+ * Present means its own delivery budget is spent — another prompt would open an
165
+ * independent summon rather than press the session that owes the receipt.
166
+ */
167
+ noReceipt?(): NoReceiptLifecycleFacts | undefined;
165
168
  setModel?(model: string, thinkingLevel?: string): Promise<void>;
166
169
  getThinkingLevel?(): string | undefined;
167
170
  recordPointer(): string;
@@ -204,6 +207,7 @@ export async function writeNavigatorModelSetting(model: string, path = navigator
204
207
  export function parseNavigatorModelSetting(value: string): {
205
208
  provider: string;
206
209
  model: string;
210
+ /** Host thinking passthrough — omit when bare provider/model (#675 ⑥). */
207
211
  thinkingLevel?: string;
208
212
  } {
209
213
  const slash = value.indexOf("/");
@@ -216,68 +220,71 @@ export function parseNavigatorModelSetting(value: string): {
216
220
  const suffix = colon < 0 ? undefined : modelWithThinking.slice(colon + 1);
217
221
  const model = colon < 0 ? modelWithThinking : modelWithThinking.slice(0, colon);
218
222
  if (model === "") throw new Error("Navigator model setting must include a model");
219
- // Suffix is opaque pass-through; bare provider/model omits thinkingLevel (Pi default).
220
- return suffix === undefined
221
- ? { provider, model }
222
- : { provider, model, thinkingLevel: suffix };
223
+ if (suffix !== undefined && suffix.trim() === "") {
224
+ throw new Error("Navigator model setting thinking suffix must be non-blank");
225
+ }
226
+ return {
227
+ provider,
228
+ model,
229
+ ...(suffix === undefined ? {} : { thinkingLevel: suffix }),
230
+ };
223
231
  }
224
232
 
225
233
  /**
226
- * Prefer institutional-resolution navigator seat; fall back to model file only when
227
- * the page is missing or lacks the seat (typed reasons). Corrupted pages fail loud.
234
+ * Navigator model selection from the public seat table only (#675 / #617 DK-3).
235
+ * No legacy navigator-model.json fallback — missing seat is a real model failure.
236
+ * Host/engine axes ride the shared public summons path; attendance open consumes
237
+ * model/thinking here and defers host selection to the shared envelope when present.
228
238
  */
229
239
  export async function resolveNavigatorSeatSelection(
230
240
  context: HostContext,
231
- modelSettingPath: string | undefined,
232
- defaultModelSettingPath: string,
233
241
  ): Promise<{ selection: HostInstitutionalModelSelection; configuredLabel: string; thinkingLevel?: string }> {
234
- const runDirectory = auditorRunDirectory(context);
235
- if (runDirectory !== undefined) {
236
- try {
237
- const selection = await readInstitutionalSeatSelection(runDirectory, "navigator");
242
+ try {
243
+ const { loadPublicCliConfig } = await import("./public-cli/config.ts");
244
+ const { seatModelOnly } = await import("./public-cli/registry.ts");
245
+ const { packageMachineHome } = await import("./activation-ledger-topology.ts");
246
+ const homeCandidate = (context as unknown as { home?: unknown }).home;
247
+ const envHome = process.env.HOME;
248
+ // Prefer explicit context home, then process HOME (hermetic tests), never
249
+ // jump straight to the real machine home when a temp HOME is armed.
250
+ const home =
251
+ typeof homeCandidate === "string"
252
+ ? homeCandidate
253
+ : typeof envHome === "string" && envHome.trim() !== ""
254
+ ? envHome
255
+ : packageMachineHome();
256
+ const config = await loadPublicCliConfig(home);
257
+ const modelOnly = seatModelOnly(config.seats.navigator);
258
+ // Seat table is the only model authority — no legacy navigator-model.json.
259
+ // Bare missing seat uses the package default constant (not a file path).
260
+ if (modelOnly === undefined) {
261
+ const parsed = parseNavigatorModelSetting(NAVIGATOR_DEFAULT_MODEL);
238
262
  return {
239
263
  selection: {
240
- provider: selection.provider,
241
- model: selection.model,
242
- ...(selection.thinking === undefined ? {} : { thinking: selection.thinking }),
264
+ provider: parsed.provider,
265
+ model: parsed.model,
266
+ ...(parsed.thinkingLevel === undefined ? {} : { thinking: parsed.thinkingLevel }),
243
267
  },
244
- configuredLabel: selection.thinking === undefined
245
- ? `${selection.provider}/${selection.model}`
246
- : `${selection.provider}/${selection.model}:${selection.thinking}`,
247
- ...(selection.thinking === undefined ? {} : { thinkingLevel: selection.thinking }),
268
+ configuredLabel: NAVIGATOR_DEFAULT_MODEL,
269
+ ...(parsed.thinkingLevel === undefined ? {} : { thinkingLevel: parsed.thinkingLevel }),
248
270
  };
249
- } catch (error) {
250
- if (
251
- error instanceof InstitutionalResolutionError
252
- && (error.reason === "missing-page" || error.reason === "missing-seat")
253
- ) {
254
- // documented bare-seam fallback
255
- } else {
256
- throw error instanceof NavigatorUnavailableError
257
- ? error
258
- : navigatorUnavailableError("model", error);
259
- }
260
271
  }
261
- }
262
- let configured: string;
263
- try {
264
- configured = await readNavigatorModelSetting(modelSettingPath ?? defaultModelSettingPath);
272
+ // Host params passthrough only — no map/validate/default (#675 ⑥).
273
+ const thinkingRaw = modelOnly.thinking;
274
+ const configuredLabel =
275
+ thinkingRaw === undefined
276
+ ? `${modelOnly.provider}/${modelOnly.model}`
277
+ : `${modelOnly.provider}/${modelOnly.model}:${thinkingRaw}`;
278
+ return {
279
+ selection: {
280
+ provider: modelOnly.provider,
281
+ model: modelOnly.model,
282
+ ...(thinkingRaw === undefined ? {} : { thinking: thinkingRaw }),
283
+ },
284
+ configuredLabel,
285
+ ...(thinkingRaw === undefined ? {} : { thinkingLevel: thinkingRaw }),
286
+ };
265
287
  } catch (error) {
266
288
  throw navigatorUnavailableError("model", error);
267
289
  }
268
- let parsed: ReturnType<typeof parseNavigatorModelSetting>;
269
- try {
270
- parsed = parseNavigatorModelSetting(configured);
271
- } catch (error) {
272
- throw navigatorUnavailableError("model", error);
273
- }
274
- return {
275
- selection: {
276
- provider: parsed.provider,
277
- model: parsed.model,
278
- ...(parsed.thinkingLevel === undefined ? {} : { thinking: parsed.thinkingLevel }),
279
- },
280
- configuredLabel: configured,
281
- ...(parsed.thinkingLevel === undefined ? {} : { thinkingLevel: parsed.thinkingLevel }),
282
- };
283
290
  }
@@ -15,8 +15,10 @@ import {
15
15
  import type { NotarySourceRunLocator } from "./notary-contracts.ts";
16
16
  import { readRoleRunIdentity } from "./public-cli/run-lifecycle.ts";
17
17
 
18
+ // Run directory leaf: <runId>@<role>. Production uses uuidv7 runIds; offline tracers
19
+ // may use deterministic non-uuid leaves. Role token stays identifier-shaped.
18
20
  const RUN_DIR_NAME =
19
- /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})@([A-Za-z][A-Za-z0-9_-]*)$/i;
21
+ /^([A-Za-z0-9][A-Za-z0-9._-]*)@([A-Za-z][A-Za-z0-9_-]*)$/;
20
22
 
21
23
  export class NotarySourceRunError extends Error {
22
24
  constructor(message: string, options?: ErrorOptions) {
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Public Auditor (审刑院) terminating receipt contracts (#675).
3
+ * Lawful explicit releases: pass | revise | escalate.
4
+ */
5
+ import { Type } from "typebox";
6
+
7
+ import { openToolObject } from "../open-tool-schema.ts";
8
+ import { withInfrastructureFailureDeclaration } from "./terminating-infrastructure.ts";
9
+
10
+ export const AUDITOR_OUTPUT_TOOL_NAME = "ak_auditor_output" as const;
11
+ export const AUDITOR_ACCEPTED_TEXT = "审刑院回执已接受";
12
+
13
+ export const auditorOutputSchema = withInfrastructureFailureDeclaration(
14
+ openToolObject(
15
+ Type.Object({
16
+ status: Type.Unknown({
17
+ description: "pass | revise | escalate — 形状指引,非 schema 闸",
18
+ }),
19
+ violations: Type.Optional(Type.Unknown({
20
+ description: "status 为 revise 时的违规条目",
21
+ })),
22
+ conflicts: Type.Optional(Type.Unknown({
23
+ description: "status 为 escalate 时的冲突",
24
+ })),
25
+ decisionGate: Type.Optional(Type.Unknown({
26
+ description: "status 为 escalate 时的决策闸",
27
+ })),
28
+ }),
29
+ ),
30
+ );
31
+
32
+ export type AuditorOutput =
33
+ | { readonly status: "pass" }
34
+ | { readonly status: "revise"; readonly violations?: unknown }
35
+ | {
36
+ readonly status: "escalate";
37
+ readonly conflicts?: unknown;
38
+ readonly decisionGate?: unknown;
39
+ };
40
+
41
+ function isRecord(value: unknown): value is Record<string, unknown> {
42
+ return typeof value === "object" && value !== null && !Array.isArray(value);
43
+ }
44
+
45
+ export function projectLawfulAuditorOutput(value: unknown): AuditorOutput | undefined {
46
+ if (!isRecord(value)) return undefined;
47
+ if (value.status === "pass") return { status: "pass" };
48
+ if (value.status === "revise") {
49
+ return {
50
+ status: "revise",
51
+ ...(Object.hasOwn(value, "violations") ? { violations: value.violations } : {}),
52
+ };
53
+ }
54
+ if (value.status === "escalate") {
55
+ return {
56
+ status: "escalate",
57
+ ...(Object.hasOwn(value, "conflicts") ? { conflicts: value.conflicts } : {}),
58
+ ...(Object.hasOwn(value, "decisionGate") ? { decisionGate: value.decisionGate } : {}),
59
+ };
60
+ }
61
+ return undefined;
62
+ }
63
+
64
+ export function validateRecordedAuditorOutput(value: unknown): AuditorOutput {
65
+ const projected = projectLawfulAuditorOutput(value);
66
+ if (projected === undefined) {
67
+ throw new Error("Auditor output has no recognized execution discriminator");
68
+ }
69
+ return projected;
70
+ }
71
+
72
+ export function auditorDecisiveFacts(output: AuditorOutput): Record<string, unknown> {
73
+ const facts: Record<string, unknown> = { status: output.status };
74
+ if (output.status === "revise" && output.violations !== undefined) {
75
+ facts.violations = output.violations;
76
+ }
77
+ if (output.status === "escalate") {
78
+ if (output.conflicts !== undefined) facts.conflicts = output.conflicts;
79
+ if (output.decisionGate !== undefined) facts.decisionGate = output.decisionGate;
80
+ }
81
+ return facts;
82
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Public Evidence-Child terminating receipt contracts (#675).
3
+ * One report body; shape is not an admission gate (ADR 0055).
4
+ */
5
+ import { Type } from "typebox";
6
+
7
+ import { openToolObject } from "../open-tool-schema.ts";
8
+ import { withInfrastructureFailureDeclaration } from "./terminating-infrastructure.ts";
9
+
10
+ export const EVIDENCE_CHILD_OUTPUT_TOOL_NAME = "ak_evidence_child_output" as const;
11
+ export const EVIDENCE_CHILD_ACCEPTED_TEXT = "取证回执已接受";
12
+
13
+ export const evidenceChildOutputSchema = withInfrastructureFailureDeclaration(
14
+ openToolObject(
15
+ Type.Object({
16
+ report: Type.Unknown({
17
+ description: "取证报告正文 — 形状指引,非 schema 闸",
18
+ }),
19
+ }),
20
+ ),
21
+ );
22
+
23
+ /** Report body retained as delivered — no type/blank/field-presence reshape (ADR 0055 / §0). */
24
+ export type EvidenceChildOutput = { readonly report: unknown };
25
+
26
+ function isRecord(value: unknown): value is Record<string, unknown> {
27
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28
+ }
29
+
30
+ export function projectLawfulEvidenceChildOutput(
31
+ value: unknown,
32
+ ): EvidenceChildOutput | undefined {
33
+ // No field-presence gate (ADR 0055 / CLAUDE.md §0 / #675). Retain delivered bytes:
34
+ // report key when present, otherwise the whole object as the report body.
35
+ if (!isRecord(value)) return undefined;
36
+ return { report: Object.hasOwn(value, "report") ? value.report : value };
37
+ }
38
+
39
+ export function validateRecordedEvidenceChildOutput(value: unknown): EvidenceChildOutput {
40
+ const projected = projectLawfulEvidenceChildOutput(value);
41
+ if (projected === undefined) {
42
+ throw new Error("Evidence-child output is not an object");
43
+ }
44
+ return projected;
45
+ }
46
+
47
+ export function evidenceChildDecisiveFacts(
48
+ output: EvidenceChildOutput,
49
+ ): Record<string, unknown> {
50
+ return { report: output.report };
51
+ }
@@ -4,6 +4,7 @@
4
4
  export const JUDGE_OUTPUT_TOOL_NAME = "ak_judge_output";
5
5
  export const JUDGE_ACCEPTED_TEXT = "大理寺回执已接受";
6
6
  export const JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT = "大理寺回执已接受;审计无回执";
7
+ export const JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT = "大理寺回执已接受;审计形状不可读";
7
8
 
8
9
  export type JudgeClass = {
9
10
  name: string;
@@ -10,8 +10,8 @@ export type ReviewerAmendments = Readonly<Partial<Record<"standards" | "spec", s
10
10
  export type ReviewerIntent =
11
11
  | Readonly<{ status: "completed"; amendments?: ReviewerAmendments }>
12
12
  | Readonly<{ status: "refused"; diagnostic: string; amendments?: ReviewerAmendments }>;
13
- /** Child report is plain text — no length/digest identity shell (ADR 0031). */
14
- export type VerbatimChildReport = Readonly<{ text: string }>;
13
+ /** Child report body retained as delivered — no type/blank reshape (ADR 0031 / 0055 / #675). */
14
+ export type VerbatimChildReport = Readonly<{ text: unknown }>;
15
15
  /** Prompt projection on the receipt face is plain text (ADR 0031). */
16
16
  export type ReviewerReceiptPrompt = Readonly<{ text: string }>;
17
17
  /** Canonical Skill content on the receipt face is plain text (ADR 0031/0032). */
@@ -29,11 +29,14 @@ import { CorrectableSubmissionError } from "../submission-correctable-error.ts";
29
29
  import { DOCTOR_ACCEPTED_TEXT, DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmissionShape, validateRecordedDoctorOutput, type DoctorOutput, type DoctorSubmission } from "../doctor-contracts.ts";
30
30
  import { GATEKEEPER_ACCEPTED_TEXT, GATEKEEPER_OUTPUT_TOOL_NAME, validateRecordedGatekeeperOutput, type GatekeeperDirectOutput } from "./gatekeeper-output.ts";
31
31
  import { NAVIGATOR_ACCEPTED_TEXT, NAVIGATOR_OUTPUT_TOOL_NAME, validateRecordedNavigatorOutput, type NavigatorAdvice } from "./navigator-output.ts";
32
+ import { AUDITOR_ACCEPTED_TEXT, AUDITOR_OUTPUT_TOOL_NAME, validateRecordedAuditorOutput, type AuditorOutput } from "./auditor-output.ts";
33
+ import { EVIDENCE_CHILD_ACCEPTED_TEXT, EVIDENCE_CHILD_OUTPUT_TOOL_NAME, validateRecordedEvidenceChildOutput, type EvidenceChildOutput } from "./evidence-child-output.ts";
32
34
  import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateMergerOutput, type MergerOutput } from "../merger-contracts.ts";
33
35
  import { NOTARY_ACCEPTED_TEXT, NOTARY_OUTPUT_TOOL_NAME, validateRecordedNotaryOutput, type NotaryOutput } from "../notary-contracts.ts";
34
36
  import { COUNTERSIGN_ACCEPTED_TEXT, COUNTERSIGN_OUTPUT_TOOL_NAME, validateRecordedCountersignOutput, type CountersignVerdict } from "../countersign-contracts.ts";
35
37
  import { GLEANER_LEFT_ACCEPTED_TEXT, GLEANER_LEFT_OUTPUT_TOOL_NAME, validateRecordedGleanerLeftOutput, type GleanerLeftOutput } from "../gleaner-left-contracts.ts";
36
38
  import { INSPECTOR_ACCEPTED_TEXT, INSPECTOR_OUTPUT_TOOL_NAME, validateRecordedInspectorOutput, type InspectorOutput } from "../inspector-contracts.ts";
39
+ import { DIARIST_ACCEPTED_TEXT, DIARIST_OUTPUT_TOOL_NAME, validateRecordedDiaristOutput, type DiaristOutput } from "../diarist-contracts.ts";
37
40
  import {
38
41
  CODER_ACCEPTED_TEXT,
39
42
  CODER_OUTPUT_TOOL_NAME,
@@ -71,6 +74,9 @@ export {
71
74
  validateRecordedInspectorOutput,
72
75
  validateRecordedGatekeeperOutput,
73
76
  validateRecordedNavigatorOutput,
77
+ validateRecordedAuditorOutput,
78
+ validateRecordedEvidenceChildOutput,
79
+ validateRecordedDiaristOutput,
74
80
  };
75
81
  export type {
76
82
  CollectorReceipt,
@@ -87,6 +93,9 @@ export type {
87
93
  InspectorOutput,
88
94
  GatekeeperDirectOutput,
89
95
  NavigatorAdvice,
96
+ AuditorOutput,
97
+ EvidenceChildOutput,
98
+ DiaristOutput,
90
99
  };
91
100
 
92
101
  export const TERMINATING_TOOL_NAMES = [
@@ -103,6 +112,9 @@ export const TERMINATING_TOOL_NAMES = [
103
112
  INSPECTOR_OUTPUT_TOOL_NAME,
104
113
  GATEKEEPER_OUTPUT_TOOL_NAME,
105
114
  NAVIGATOR_OUTPUT_TOOL_NAME,
115
+ AUDITOR_OUTPUT_TOOL_NAME,
116
+ EVIDENCE_CHILD_OUTPUT_TOOL_NAME,
117
+ DIARIST_OUTPUT_TOOL_NAME,
106
118
  ] as const;
107
119
 
108
120
  export type TerminatingToolName = (typeof TERMINATING_TOOL_NAMES)[number];
@@ -119,7 +131,10 @@ export type AcceptedDetails =
119
131
  | GleanerLeftOutput
120
132
  | InspectorOutput
121
133
  | GatekeeperDirectOutput
122
- | NavigatorAdvice;
134
+ | NavigatorAdvice
135
+ | AuditorOutput
136
+ | EvidenceChildOutput
137
+ | DiaristOutput;
123
138
 
124
139
  export function isTerminatingToolName(
125
140
  name: string,
@@ -155,6 +170,12 @@ export function acceptedTextFor(toolName: TerminatingToolName): string {
155
170
  return GATEKEEPER_ACCEPTED_TEXT;
156
171
  case NAVIGATOR_OUTPUT_TOOL_NAME:
157
172
  return NAVIGATOR_ACCEPTED_TEXT;
173
+ case AUDITOR_OUTPUT_TOOL_NAME:
174
+ return AUDITOR_ACCEPTED_TEXT;
175
+ case EVIDENCE_CHILD_OUTPUT_TOOL_NAME:
176
+ return EVIDENCE_CHILD_ACCEPTED_TEXT;
177
+ case DIARIST_OUTPUT_TOOL_NAME:
178
+ return DIARIST_ACCEPTED_TEXT;
158
179
  }
159
180
  }
160
181
 
@@ -213,15 +234,24 @@ export function validateAcceptedDetails(
213
234
  [INSPECTOR_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
214
235
  [GATEKEEPER_OUTPUT_TOOL_NAME]: ["dispatch", "pass"],
215
236
  [NAVIGATOR_OUTPUT_TOOL_NAME]: ["advice"],
237
+ [AUDITOR_OUTPUT_TOOL_NAME]: ["pass", "revise", "escalate"],
238
+ [EVIDENCE_CHILD_OUTPUT_TOOL_NAME]: [],
239
+ [DIARIST_OUTPUT_TOOL_NAME]: ["completed"],
216
240
  };
217
241
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
242
+ // Evidence-child: any object is a candidate body — no report-field presence gate (#675 / ADR 0055).
243
+ const evidenceChildDiscriminator =
244
+ toolName === EVIDENCE_CHILD_OUTPUT_TOOL_NAME
245
+ && candidate !== null
246
+ && typeof candidate === "object"
247
+ && !Array.isArray(candidate);
218
248
  const baseDiscriminator = discriminator;
219
249
  const runtimeBindingMissing =
220
250
  (toolName === DOCTOR_OUTPUT_TOOL_NAME && baseDiscriminator === "completed" && !(candidate?.cost !== null && typeof candidate?.cost === "object")) ||
221
251
  (toolName === REVIEWER_OUTPUT_TOOL_NAME && candidate?.version !== 2);
222
252
  if (
223
253
  runtimeBindingMissing ||
224
- (!collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator as string)))
254
+ (!collectorDiscriminator && !evidenceChildDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator as string)))
225
255
  ) {
226
256
  throw new AcceptedDetailsContractError("terminating receipt has no recognized execution discriminator");
227
257
  }
@@ -253,6 +283,12 @@ export function validateAcceptedDetails(
253
283
  return validateRecordedGatekeeperOutput(details);
254
284
  case NAVIGATOR_OUTPUT_TOOL_NAME:
255
285
  return validateRecordedNavigatorOutput(details);
286
+ case AUDITOR_OUTPUT_TOOL_NAME:
287
+ return validateRecordedAuditorOutput(details);
288
+ case EVIDENCE_CHILD_OUTPUT_TOOL_NAME:
289
+ return validateRecordedEvidenceChildOutput(details);
290
+ case DIARIST_OUTPUT_TOOL_NAME:
291
+ return validateRecordedDiaristOutput(details);
256
292
  }
257
293
  } catch (error) {
258
294
  if (error instanceof Error && error.constructor === Error) throw new AcceptedDetailsContractError(error.message, { cause: error });
@@ -280,6 +316,15 @@ export function validateAcceptedLifecycle(
280
316
  if (!deepEqual(testimony, projected)) throw new Error("accepted tool lifecycle details mismatch");
281
317
  return details;
282
318
  }
319
+ if (toolName === DIARIST_OUTPUT_TOOL_NAME) {
320
+ // Envelope-owned mechanical sitian facts are runtime-bound on details only
321
+ // (same shape as the Doctor runtime cost); the submitted arguments carry the
322
+ // role's own selections. Machine facts never come from model self-report.
323
+ const { sitian: _mechanical, ...submitted } = details as DiaristOutput & { sitian?: unknown };
324
+ const testimony = validateAcceptedDetails(toolName, argumentsValue);
325
+ if (!deepEqual(testimony, submitted)) throw new Error("accepted tool lifecycle details mismatch");
326
+ return details;
327
+ }
283
328
  const argumentsDetails = validateAcceptedDetails(toolName, argumentsValue);
284
329
  if (!deepEqual(argumentsDetails, details)) throw new Error("accepted tool lifecycle details mismatch");
285
330
  return details;
@@ -301,7 +346,12 @@ export function acceptedFacts(toolName: TerminatingToolName, details: AcceptedDe
301
346
  case GLEANER_LEFT_OUTPUT_TOOL_NAME:
302
347
  case INSPECTOR_OUTPUT_TOOL_NAME:
303
348
  case GATEKEEPER_OUTPUT_TOOL_NAME:
304
- case NAVIGATOR_OUTPUT_TOOL_NAME: return { status: (details as { status: string }).status };
349
+ case NAVIGATOR_OUTPUT_TOOL_NAME:
350
+ case AUDITOR_OUTPUT_TOOL_NAME:
351
+ return { status: (details as { status: string }).status };
352
+ case EVIDENCE_CHILD_OUTPUT_TOOL_NAME:
353
+ return { status: "report" };
354
+ case DIARIST_OUTPUT_TOOL_NAME: return { status: (details as { status: string }).status };
305
355
  case JUDGE_OUTPUT_TOOL_NAME: return { status: (details as { judgeStatus: string }).judgeStatus };
306
356
  case COUNTERSIGN_OUTPUT_TOOL_NAME: return { status: (details as { countersignStatus: string }).countersignStatus };
307
357
  case MERGER_OUTPUT_TOOL_NAME: {
@@ -11,6 +11,12 @@ import { NOTARY_OUTPUT_TOOL_NAME } from "./notary-contracts.ts";
11
11
  import { COUNTERSIGN_OUTPUT_TOOL_NAME } from "./countersign-contracts.ts";
12
12
  import { GLEANER_LEFT_OUTPUT_TOOL_NAME } from "./gleaner-left-contracts.ts";
13
13
  import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
14
+ import { AUDITOR_OUTPUT_TOOL_NAME } from "./package-contracts/auditor-output.ts";
15
+ import { EVIDENCE_CHILD_OUTPUT_TOOL_NAME } from "./package-contracts/evidence-child-output.ts";
16
+ import {
17
+ DIARIST_OUTPUT_TOOL_NAME,
18
+ DIARIST_SOURCES_FLAG,
19
+ } from "./diarist-contracts.ts";
14
20
 
15
21
  /** Shared by public notary and gatekeeper-province notary. */
16
22
  export const NOTARY_SESSION_MATERIALS = [
@@ -29,6 +35,28 @@ export const INSPECTOR_SESSION_MATERIALS = [
29
35
  "souls/gate-output-guide.md",
30
36
  ] as const;
31
37
 
38
+ /**
39
+ * Public 审刑院 shipping roster (#675 owner).
40
+ * Runtime assembly is subject-selected via AUDITOR_SESSION_MATERIALS
41
+ * (judge-auditor.md / doctor-auditor.md) — never a generic auditor.md.
42
+ * This list is the union of files that must ship; load path is loadAuditorSoul(subject).
43
+ */
44
+ export const AUDITOR_PUBLIC_SESSION_MATERIALS = [
45
+ "CLAUDE.md",
46
+ "souls/judge-auditor.md",
47
+ "souls/doctor-auditor.md",
48
+ "souls/audit-law.md",
49
+ "souls/quality-law.md",
50
+ ] as const;
51
+
52
+ /** Public evidence-child materials (#675). audit-law supplies lawful 取证授权; seat identity only in evidence-child.md. */
53
+ export const EVIDENCE_CHILD_SESSION_MATERIALS = [
54
+ "CLAUDE.md",
55
+ "souls/evidence-child.md",
56
+ "souls/audit-law.md",
57
+ "souls/quality-law.md",
58
+ ] as const;
59
+
32
60
  /**
33
61
  * One record per public callable role (#639: includes gatekeeper and navigator).
34
62
  */
@@ -185,6 +213,41 @@ export const PUBLIC_ROLE_RECORDS = [
185
213
  activationStage: "load-and-install",
186
214
  sessionMaterials: ["CLAUDE.md", "souls/navigator.md"],
187
215
  },
216
+ // #675: 审刑院 / evidence-child are roles like any other — public ak-role entries.
217
+ {
218
+ role: "auditor",
219
+ phases: [null],
220
+ outputTool: AUDITOR_OUTPUT_TOOL_NAME,
221
+ inputFlag: undefined,
222
+ phaseFlag: undefined,
223
+ activationStage: "load-and-install",
224
+ sessionMaterials: AUDITOR_PUBLIC_SESSION_MATERIALS,
225
+ },
226
+ {
227
+ role: "evidence-child",
228
+ phases: [null],
229
+ outputTool: EVIDENCE_CHILD_OUTPUT_TOOL_NAME,
230
+ inputFlag: undefined,
231
+ phaseFlag: undefined,
232
+ activationStage: "load-and-install",
233
+ sessionMaterials: EVIDENCE_CHILD_SESSION_MATERIALS,
234
+ },
235
+ // #708 / ADR 0075 `diarist-is-role`: 起居郎 is a seat like any other. The
236
+ // frozen source catalog rides the shared input-flag seam; it is absent for a
237
+ // true-unbound summons (no ticket → no diary).
238
+ {
239
+ role: "diarist",
240
+ phases: [null],
241
+ outputTool: DIARIST_OUTPUT_TOOL_NAME,
242
+ inputFlag: DIARIST_SOURCES_FLAG.name,
243
+ phaseFlag: undefined,
244
+ activationStage: "load-and-install",
245
+ sessionMaterials: [
246
+ "CLAUDE.md",
247
+ "souls/diarist.md",
248
+ "resources/diarist-collect.md",
249
+ ],
250
+ },
188
251
  ] as const;
189
252
 
190
253
  export type PublicRoleRecord = (typeof PUBLIC_ROLE_RECORDS)[number];
package/src/pi/adapter.ts CHANGED
@@ -7,7 +7,8 @@ import {
7
7
  type ToolDefinition,
8
8
  } from "@earendil-works/pi-coding-agent";
9
9
  import type { Static, TSchema } from "typebox";
10
- import { requireGatekeeperPass, type GatekeeperNonPassResult, type GatekeeperSubject } from "../gatekeeper-role.ts";
10
+ import type { GatekeeperNonPassResult, GatekeeperSubject } from "../gatekeeper-role.ts";
11
+ import { requireGatekeeperPass } from "../gatekeeper-pass-envelope.ts";
11
12
  import type {
12
13
  HostContext,
13
14
  HostEventRegistration,
@@ -207,7 +207,7 @@ export type OpenPiInstitutionalSessionResult = {
207
207
  readonly streamFailure: unknown;
208
208
  };
209
209
 
210
- export async function openPiInstitutionalSession(
210
+ export async function openPiInProcessSession(
211
211
  options: OpenPiInstitutionalSessionOptions,
212
212
  ): Promise<OpenPiInstitutionalSessionResult> {
213
213
  const label = options.label ?? "Institutional sub-session";
@@ -237,7 +237,7 @@ export async function openPiInstitutionalSession(
237
237
  ? childRegistry.find(selection.provider, selection.model)
238
238
  : undefined;
239
239
 
240
- const withTypedReason = (error: Error, reason: "auth" | "model"): Error =>
240
+ const withTypedReason = (error: Error, reason: "auth" | "model" | "thinking"): Error =>
241
241
  Object.assign(error, { reason });
242
242
 
243
243
  // Existing Navigator/model contract (pre-#590): unknown provider or model is
@@ -633,15 +633,26 @@ export async function openPiInstitutionalSession(
633
633
  }
634
634
  }
635
635
 
636
- // 7. Create AgentSession — thinking is opaque pass-through. Absent selection
637
- // omits thinkingLevel (Pi owns default). Pi clamps unsupported levels itself;
638
- // we do not re-check or invent package defaults.
636
+ // 7. Create AgentSession
637
+ // Thinking is opaque pass-through (#683 / #675 ⑥). Absent selection omits
638
+ // thinkingLevel (Pi owns default). Pi clamps unsupported levels itself;
639
+ // we do not re-check or invent package defaults. When reusing a session file,
640
+ // re-apply seat model after open so Pi cannot restore a stale model over
641
+ // selection (#675 ⑤ / #697).
642
+ const requestedThinking = selection.thinking;
643
+ const priorEntries =
644
+ typeof (sessionManager as { getEntries?: () => readonly unknown[] }).getEntries === "function"
645
+ ? (sessionManager as { getEntries: () => readonly unknown[] }).getEntries()
646
+ : [];
647
+ const reusedSession = priorEntries.some((entry) => {
648
+ if (typeof entry !== "object" || entry === null) return false;
649
+ const type = (entry as { type?: unknown }).type;
650
+ return type === "message" || type === "model_change" || type === "thinking_level_change";
651
+ });
639
652
  const { session } = await createAgentSession({
640
653
  cwd: options.cwd,
641
654
  model: effectiveModel,
642
- ...(options.selection.thinking === undefined
643
- ? {}
644
- : { thinkingLevel: options.selection.thinking as any }),
655
+ ...(requestedThinking === undefined ? {} : { thinkingLevel: requestedThinking as any }),
645
656
  modelRuntime: runtime,
646
657
  sessionManager,
647
658
  settingsManager: settings,
@@ -651,9 +662,23 @@ export async function openPiInstitutionalSession(
651
662
  ...(options.toolsAllowlist === undefined ? {} : { tools: options.toolsAllowlist as string[] }),
652
663
  ...(customTools.length === 0 ? {} : { customTools }),
653
664
  });
654
- await session.setModel(effectiveModel);
655
- if (options.selection.thinking !== undefined) {
656
- session.setThinkingLevel(options.selection.thinking as any);
665
+ if (reusedSession) {
666
+ try {
667
+ await session.setModel(effectiveModel);
668
+ if (requestedThinking !== undefined) {
669
+ session.setThinkingLevel(requestedThinking as any);
670
+ }
671
+ } catch (error) {
672
+ session.dispose();
673
+ throw withTypedReason(
674
+ error instanceof Error
675
+ ? error
676
+ : new Error(
677
+ `${label} failed to apply seat model/thinking for ${selection.provider}/${selection.model}`,
678
+ ),
679
+ requestedThinking !== undefined ? "thinking" : "model",
680
+ );
681
+ }
657
682
  }
658
683
 
659
684
  // 8. Event subscriptions
@@ -832,4 +857,5 @@ export async function openPiInstitutionalSession(
832
857
  }
833
858
  throw openError;
834
859
  }
835
- }
860
+ }
861
+