@akagilnc/pi-workflow-roles 0.1.3644 → 0.1.3652

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 (92) hide show
  1. package/dist/acp-host/production-host.js +3089 -2187
  2. package/dist/activation-ledger-session.js +105 -0
  3. package/dist/activation-ledger.js +139 -0
  4. package/dist/activation-trace.js +33 -0
  5. package/dist/agent-start-materials.js +11 -0
  6. package/dist/audit-escalation.js +5 -5
  7. package/dist/auditor-role.js +12 -0
  8. package/dist/canonical-skill-binding.js +64 -0
  9. package/dist/collector-identity.js +151 -0
  10. package/dist/collector-receipt.js +95 -0
  11. package/dist/collector-role.js +441 -0
  12. package/dist/compliance-transport.js +11 -57
  13. package/dist/countersign-role.js +28 -0
  14. package/dist/diarist-contracts.js +0 -12
  15. package/dist/diarist-role.js +29 -0
  16. package/dist/doctor-contracts.js +0 -2
  17. package/dist/doctor-role.js +74 -0
  18. package/dist/fixer-bash-seatbelt.js +13 -0
  19. package/dist/gatekeeper-pass-envelope.js +72 -0
  20. package/dist/gleaner-left-contracts.js +0 -7
  21. package/dist/gleaner-left-role.js +26 -0
  22. package/dist/inspector-contracts.js +0 -4
  23. package/dist/inspector-role.js +29 -0
  24. package/dist/judge-role.js +106 -0
  25. package/dist/merger-role.js +66 -0
  26. package/dist/navigator-public-session.js +1 -5
  27. package/dist/navigator-role.js +12 -0
  28. package/dist/notary-contracts.js +0 -18
  29. package/dist/notary-role.js +137 -0
  30. package/dist/oauth-keepalive.js +158 -0
  31. package/dist/package-contracts/auditor-output.js +0 -13
  32. package/dist/package-contracts/gatekeeper-output.js +0 -10
  33. package/dist/package-contracts/judge-output.js +0 -2
  34. package/dist/package-contracts/navigator-output.js +0 -4
  35. package/dist/package-contracts/terminating-tools.js +2 -16
  36. package/dist/packaged-role-registry.js +1 -18
  37. package/dist/pi/adapter.js +211 -0
  38. package/dist/pi/role-turn-host.js +0 -2
  39. package/dist/public-cli/instruction-seat-run.js +2 -6
  40. package/dist/public-cli/invocation.js +0 -7
  41. package/dist/public-cli/main.js +138 -735
  42. package/dist/public-cli/option-definitions.js +0 -14
  43. package/dist/public-cli/registry.js +1 -2
  44. package/dist/public-cli/run-lifecycle.js +1 -3
  45. package/dist/public-cli/settlement.js +66 -537
  46. package/dist/public-role-summons.js +0 -14
  47. package/dist/reviewer-child-executor.js +163 -70
  48. package/dist/reviewer-construction.js +2 -2
  49. package/dist/reviewer-role.js +159 -0
  50. package/dist/reviewer-settlement.js +66 -0
  51. package/dist/role-runtime.js +1394 -0
  52. package/dist/stderr-jsonl.js +26 -0
  53. package/dist/tool-execution-observation.js +141 -0
  54. package/dist/worker-role.js +343 -0
  55. package/dist/worker-submission-gates.js +305 -0
  56. package/extensions/role-runtime.ts +1 -2
  57. package/package.json +1 -1
  58. package/src/acp-host/production-host.ts +0 -1
  59. package/src/audit-escalation.ts +9 -9
  60. package/src/compliance-transport.ts +28 -94
  61. package/src/diarist-contracts.ts +0 -12
  62. package/src/doctor-contracts.ts +0 -1
  63. package/src/doctor-role.ts +2 -2
  64. package/src/gleaner-left-contracts.ts +0 -9
  65. package/src/host-contracts.ts +0 -2
  66. package/src/inspector-contracts.ts +0 -8
  67. package/src/judge-role.ts +4 -5
  68. package/src/navigator-public-session.ts +4 -5
  69. package/src/notary-contracts.ts +0 -18
  70. package/src/package-contracts/auditor-output.ts +0 -11
  71. package/src/package-contracts/gatekeeper-output.ts +0 -11
  72. package/src/package-contracts/judge-output.ts +0 -1
  73. package/src/package-contracts/navigator-output.ts +0 -3
  74. package/src/package-contracts/terminating-tools.ts +1 -19
  75. package/src/packaged-role-registry.ts +1 -19
  76. package/src/pi/role-turn-host.ts +0 -2
  77. package/src/public-cli/cli.ts +1 -5
  78. package/src/public-cli/instruction-seat-run.ts +5 -14
  79. package/src/public-cli/invocation.ts +3 -21
  80. package/src/public-cli/option-definitions.ts +0 -16
  81. package/src/public-cli/registry.ts +1 -2
  82. package/src/public-cli/run-lifecycle.ts +0 -6
  83. package/src/public-cli/settlement.ts +58 -572
  84. package/src/public-cli/terminal.ts +0 -1
  85. package/src/public-role-summons.ts +0 -15
  86. package/src/reviewer-child-executor.ts +199 -98
  87. package/src/reviewer-construction.ts +3 -3
  88. package/src/role-runtime.ts +1 -33
  89. package/dist/package-contracts/evidence-child-output.js +0 -34
  90. package/souls/evidence-child.md +0 -1
  91. package/src/evidence-child-role.ts +0 -22
  92. package/src/package-contracts/evidence-child-output.ts +0 -51
@@ -198,20 +198,6 @@ async function summonPublicRole(options) {
198
198
  );
199
199
  break;
200
200
  }
201
- case "evidence-child": {
202
- const [{ runPublicInstructionSeat }, { parseEvidenceChildArgv }] = await Promise.all([
203
- import("./public-cli/instruction-seat-run.js"),
204
- import("./public-cli/invocation.js")
205
- ]);
206
- result = await runPublicInstructionSeat(
207
- options.argv,
208
- env,
209
- io,
210
- "evidence-child",
211
- parseEvidenceChildArgv
212
- );
213
- break;
214
- }
215
201
  case "navigator": {
216
202
  const [{ runPublicInstructionSeat }, { parseNavigatorArgv }] = await Promise.all([
217
203
  import("./public-cli/instruction-seat-run.js"),
@@ -1,4 +1,68 @@
1
- import { auditorRunDirectory } from "./auditor-dossier-tool.js";
1
+ import { createEngineDetourToolDefinition } from "./engine-detour-tool.js";
2
+ import { engineNameFromEnv } from "./engine-detour.js";
3
+ import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, } from "./package-resources/engine-material.js";
4
+ import { readPackageMaterial } from "./session-opening-materials.js";
5
+ import { hasUpstreamErrorTestimony, isNonSuccessHttpStatus } from "./upstream-error-testimony.js";
6
+ /** Path roster only — cadence prose stays in owner material (ADR 0073). */
7
+ const AXIS_SUBSESSION_MATERIALS = ["souls/quality-law.md"];
8
+ async function buildAxisSystemPrompt(engineMaterial) {
9
+ const materials = [];
10
+ for (const relativePath of AXIS_SUBSESSION_MATERIALS) {
11
+ materials.push(await readPackageMaterial(relativePath));
12
+ }
13
+ return appendEngineSessionMaterial(materials, engineMaterial).join("\n");
14
+ }
15
+ function classifiedError(error, evidenceChildFailure) {
16
+ const diagnostic = typeof error === "object" && error !== null && typeof error.errorMessage === "string"
17
+ ? error.errorMessage
18
+ : error === undefined ? "" : String(error);
19
+ const wrapped = error instanceof Error
20
+ ? error
21
+ : Object.assign(new Error(diagnostic, { cause: error }), { evidenceChildOriginal: error });
22
+ const classification = "evidenceChildFailure" in wrapped
23
+ ? wrapped.evidenceChildFailure
24
+ : evidenceChildFailure;
25
+ return Object.assign(wrapped, { evidenceChildFailure: classification });
26
+ }
27
+ /** Read envelope-projected testimony on an assistant/stop node — no private walker copy. */
28
+ function nodeHasUpstreamTestimony(node) {
29
+ if (typeof node !== "object" || node === null)
30
+ return false;
31
+ const record = node;
32
+ const httpStatus = isNonSuccessHttpStatus(record.statusCode)
33
+ ? record.statusCode
34
+ : isNonSuccessHttpStatus(record.status)
35
+ ? record.status
36
+ : isNonSuccessHttpStatus(record.httpStatus)
37
+ ? record.httpStatus
38
+ : undefined;
39
+ const diagnostics = Array.isArray(record.diagnostics) && record.diagnostics.length > 0
40
+ ? record.diagnostics
41
+ : undefined;
42
+ return hasUpstreamErrorTestimony({
43
+ ...(httpStatus === undefined ? {} : { httpStatus }),
44
+ ...(diagnostics === undefined ? {} : { diagnostics }),
45
+ });
46
+ }
47
+ /** Parent seat model for the axis sub-session (pre-#675 parent-effective inheritance). Pi only. */
48
+ async function parentSelection(context) {
49
+ const { toPiContext } = await import("./pi/adapter.js");
50
+ let pi;
51
+ try {
52
+ pi = toPiContext(context);
53
+ }
54
+ catch (error) {
55
+ throw classifiedError(new Error("Reviewer Standards/Spec sub-session requires pi host context (other hosts: subagent not in this ticket)", { cause: error }), "child");
56
+ }
57
+ if (pi.model === undefined) {
58
+ throw classifiedError(new Error("Reviewer axis sub-session requires parent model"), "child");
59
+ }
60
+ return {
61
+ provider: pi.model.provider,
62
+ model: pi.model.id,
63
+ ...(pi.thinkingLevel === undefined ? {} : { thinking: String(pi.thinkingLevel) }),
64
+ };
65
+ }
2
66
  /**
3
67
  * Single conversion at the Reviewer adapter boundary: shared child classifications
4
68
  * become Reviewer failure classifications without a second error taxonomy.
@@ -12,39 +76,17 @@ export function projectSharedChildFailure(error) {
12
76
  }
13
77
  return error;
14
78
  }
15
- function failureClassification(cause) {
16
- if (cause === "provider")
17
- return "provider";
18
- if (cause === "output"
19
- || cause === "timeout"
20
- || cause === "activation"
21
- || cause === "session"
22
- || cause === "unrecognized") {
23
- return "child";
24
- }
25
- return "unknown";
79
+ function emptyUsage() {
80
+ return {
81
+ input: 0,
82
+ output: 0,
83
+ cacheRead: 0,
84
+ cacheWrite: 0,
85
+ totalTokens: 0,
86
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
87
+ };
26
88
  }
27
- function reportFromSummon(summoned) {
28
- const outcome = summoned.terminal?.roleOutcome;
29
- if (outcome === undefined) {
30
- throw Object.assign(new Error(`Evidence-child public summon produced no terminal (exit ${summoned.exitCode})`), { evidenceChildFailure: "child" });
31
- }
32
- if (outcome.kind === "failure") {
33
- throw Object.assign(new Error(outcome.diagnostic), {
34
- evidenceChildFailure: failureClassification(outcome.cause),
35
- });
36
- }
37
- if (outcome.kind === "accepted") {
38
- // No field-presence reject (ADR 0055 / #675). Keep original accepted bytes:
39
- // report key when present, otherwise the whole decisiveFacts body.
40
- const facts = outcome.decisiveFacts;
41
- if (Object.hasOwn(facts, "report"))
42
- return facts.report;
43
- return facts;
44
- }
45
- throw Object.assign(new Error("Evidence-child public summon returned no accepted report body"), { evidenceChildFailure: "child" });
46
- }
47
- /** Reviewer evidence leg via the public evidence-child activation path (#675). */
89
+ /** Reviewer Standards/Spec leg via pi in-process sub-session (#744). */
48
90
  export async function executeReviewerChild(workspace, leg, context, options = {}) {
49
91
  try {
50
92
  if (options.signal?.aborted) {
@@ -52,47 +94,98 @@ export async function executeReviewerChild(workspace, leg, context, options = {}
52
94
  evidenceChildFailure: "child",
53
95
  });
54
96
  }
55
- const runDirectory = options.runDirectory ?? auditorRunDirectory(context);
56
- const pointer = runDirectory === undefined
57
- ? ""
58
- : `\n卷宗指针:${runDirectory}`;
59
- const argv = [`${String(leg.prompt)}${pointer}`];
60
- const summon = options.summonEvidenceChild
61
- ?? (async (nextArgv, childSignal) => {
62
- const { summonPublicRole } = await import("./public-role-summons.js");
63
- // Parent run home owns nested public seats; leg workspace cwd is a bare worktree.
64
- let home;
65
- if (runDirectory !== undefined) {
66
- const { homeFromRunDirectory } = await import("./activation-ledger-topology.js");
67
- // Hard path resolve: fail loud — no packageMachineHome fallback (#604 / #675).
68
- home = homeFromRunDirectory(runDirectory);
69
- }
70
- return summonPublicRole({
71
- role: "evidence-child",
72
- argv: nextArgv,
73
- cwd: workspace,
74
- ...(home === undefined ? {} : { home }),
75
- ...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
76
- ...(childSignal === undefined ? {} : { signal: childSignal }),
97
+ const signal = options.signal;
98
+ const selection = await parentSelection(context);
99
+ const { openPiInProcessSession } = await import("./pi/in-process-session.js");
100
+ const { createRecordSession } = await import("./archivist-record-entry.js");
101
+ const engineName = engineNameFromEnv();
102
+ const engineMaterial = engineName === undefined
103
+ ? undefined
104
+ : options.packageRoot === undefined || options.packageRoot.trim() === ""
105
+ ? Object.freeze({ name: engineName })
106
+ : engineSessionMaterialFromOptions({
107
+ engine: engineName,
108
+ packageRoot: options.packageRoot,
77
109
  });
110
+ let engineDetourFailure;
111
+ const engineDetourTool = engineName === undefined
112
+ ? undefined
113
+ : createEngineDetourToolDefinition({
114
+ engineName,
115
+ fail(error) {
116
+ engineDetourFailure ??= error instanceof Error ? error : new Error(String(error));
117
+ throw engineDetourFailure;
118
+ },
78
119
  });
79
- const summoned = await summon(argv, options.signal);
80
- if (options.signal?.aborted) {
81
- throw Object.assign(new DOMException("The operation was aborted.", "AbortError"), {
82
- evidenceChildFailure: "child",
120
+ let opened;
121
+ try {
122
+ // No agentDir: envelope owns scratch via credentialScratchParent (ADR 0018).
123
+ opened = await openPiInProcessSession({
124
+ cwd: workspace,
125
+ selection,
126
+ systemPrompt: await buildAxisSystemPrompt(engineMaterial),
127
+ ...(options.credentialScratchParent === undefined
128
+ ? {}
129
+ : { credentialScratchParent: options.credentialScratchParent }),
130
+ ...(engineDetourTool === undefined
131
+ ? {}
132
+ : { customTools: [engineDetourTool] }),
133
+ sessionManager: createRecordSession({
134
+ cwd: workspace,
135
+ kind: "evidence-children",
136
+ parent: context.sessionManager,
137
+ }),
138
+ ...(signal === undefined ? {} : { signal }),
139
+ label: "Reviewer axis sub-session",
83
140
  });
84
141
  }
85
- const report = reportFromSummon(summoned);
86
- const { usageFromPublicSummon } = await import("./session-assistant-usage.js");
87
- // Real session usage when present; no session → token-zero without inventing cost.
88
- const usage = (await usageFromPublicSummon(summoned)) ?? {
89
- input: 0,
90
- output: 0,
91
- cacheRead: 0,
92
- cacheWrite: 0,
93
- totalTokens: 0,
94
- };
95
- return { report, usage, prompt: leg.prompt };
142
+ catch (error) {
143
+ throw classifiedError(error, "provider");
144
+ }
145
+ const { handle } = opened;
146
+ try {
147
+ const delivered = leg.prompt;
148
+ let turnResult;
149
+ try {
150
+ turnResult = await handle.prompt(String(delivered));
151
+ }
152
+ catch (error) {
153
+ if (engineDetourFailure !== undefined) {
154
+ throw classifiedError(engineDetourFailure, "child");
155
+ }
156
+ throw classifiedError(error, "provider");
157
+ }
158
+ if (engineDetourFailure !== undefined) {
159
+ throw classifiedError(engineDetourFailure, "child");
160
+ }
161
+ if (signal?.aborted)
162
+ throw new Error("Reviewer axis sub-session was cancelled");
163
+ const lastAssistant = turnResult.messages !== undefined
164
+ ? [...turnResult.messages].reverse().find((message) => typeof message === "object" && message !== null && message.role === "assistant")
165
+ : undefined;
166
+ if (turnResult.stopReason === "error" || turnResult.stopReason === "aborted"
167
+ || (typeof lastAssistant === "object" && lastAssistant !== null
168
+ && (lastAssistant.stopReason === "error"
169
+ || lastAssistant.stopReason === "aborted"))) {
170
+ const errMsg = turnResult.errorMessage
171
+ ?? (typeof lastAssistant === "object" && lastAssistant !== null
172
+ ? String(lastAssistant.errorMessage ?? "")
173
+ : "");
174
+ // Envelope already attaches observed HTTP status onto error/aborted assistants.
175
+ const hasTestimony = nodeHasUpstreamTestimony(lastAssistant)
176
+ || nodeHasUpstreamTestimony(opened.streamFailure);
177
+ throw classifiedError(new Error(errMsg, { cause: lastAssistant }), hasTestimony ? "provider" : "unknown");
178
+ }
179
+ const report = turnResult.text;
180
+ if (report.trim().length === 0) {
181
+ throw new Error("Reviewer axis sub-session returned a blank report");
182
+ }
183
+ const usage = turnResult.usage ?? emptyUsage();
184
+ return { report, usage, prompt: delivered };
185
+ }
186
+ finally {
187
+ await handle.close();
188
+ }
96
189
  }
97
190
  catch (error) {
98
191
  throw projectSharedChildFailure(error);
@@ -18,7 +18,7 @@ export function reviewerAxisMethodAdapter(axis) {
18
18
  ].join("\n");
19
19
  }
20
20
  /**
21
- * Spec-only evidence-child material carrier for durable authority references.
21
+ * Spec-only axis material carrier for durable authority references.
22
22
  * Exact values preserved; no prose extraction and no Standards/parent injection.
23
23
  */
24
24
  export function reviewerAuthorityRefsMaterial(authorityRefs) {
@@ -72,7 +72,7 @@ export function constructReviewerDispatch(input) {
72
72
  : [{ axis: "standards" }];
73
73
  const legs = axes.map((x) => {
74
74
  const parts = [common, reviewerAxisMethodAdapter(x.axis)];
75
- // Spec evidence-child only — never Standards or a parent replacement Spec leg.
75
+ // Spec axis only — never Standards or a parent replacement Spec leg.
76
76
  if (x.axis === "spec") {
77
77
  if (specFetchedMaterial !== undefined) {
78
78
  parts.push(reviewerFetchedSpecMaterial(specFetchedMaterial));
@@ -0,0 +1,159 @@
1
+ import { Type } from "typebox";
2
+ import { openToolObjectFromUnion } from "./open-tool-schema.js";
3
+ import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
4
+ import {} from "./reviewer-construction.js";
5
+ import { createReviewerDispatcher } from "./reviewer-dispatch.js";
6
+ import { ReviewerDispatchExecutionError } from "./reviewer-agent.js";
7
+ import { createReviewerExecutionLedger, projectAcceptedDispatch, projectReviewerDispatchOutcome } from "./reviewer-execution-ledger.js";
8
+ import { assembleRuntimeReviewerReceipt } from "./reviewer-settlement.js";
9
+ import { REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, validateReviewerIntent } from "./package-contracts/reviewer-output.js";
10
+ export { REVIEWER_OUTPUT_TOOL_NAME };
11
+ export const AGENT_TOOL_NAME = "Agent";
12
+ const reviewerAmendmentsSchema = Type.Object({
13
+ standards: Type.Optional(Type.String({ description: "相对 Standards 子报告的增量:增 finding、撤回或事实更正" })),
14
+ spec: Type.Optional(Type.String({ description: "相对 Spec 子报告的增量:增 finding、撤回或事实更正" })),
15
+ }, { additionalProperties: true, description: "相对子报告的可选轴增量;非替代报告。无增量的轴可省略。" });
16
+ const reviewerOutputVariants = Type.Union([
17
+ Type.Object({
18
+ status: Type.Literal("completed", { description: "completed — 形状指引,非 schema 闸" }),
19
+ amendments: Type.Optional(reviewerAmendmentsSchema),
20
+ }, { additionalProperties: false }),
21
+ Type.Object({
22
+ status: Type.Literal("refused", { description: "refused — 形状指引,非 schema 闸" }),
23
+ diagnostic: Type.String({ minLength: 1, description: "拒绝诊断说明" }),
24
+ amendments: Type.Optional(reviewerAmendmentsSchema),
25
+ }, { additionalProperties: false }),
26
+ ]);
27
+ export const reviewerOutputSchema = withInfrastructureFailureDeclaration(openToolObjectFromUnion(reviewerOutputVariants));
28
+ /**
29
+ * Reviewer behavior runtime: label, soul, evidence tools, decision tool, projection.
30
+ * No flag registration/decoding and no agent_start prompt lifecycle (ADR 0018 / envelope).
31
+ * Reviewer-side 审刑院 gate retired (#495 S6 / 风闻奏事); accept on typed validate only.
32
+ */
33
+ export function createReviewerRoleRuntime(pi, dependencies, hostActions) {
34
+ let soul;
35
+ let binding;
36
+ let reader;
37
+ let dispatcher;
38
+ let registered = false;
39
+ let fixedBaseRevision;
40
+ let acceptedDispatch;
41
+ const ledger = createReviewerExecutionLedger();
42
+ return {
43
+ async activate(_ctx, admitted) {
44
+ soul = (await dependencies.loadSoul()).trim();
45
+ if (!soul)
46
+ throw new Error("Reviewer soul is empty");
47
+ // Behavior layer receives frozen admitted inputs only — no pi.getFlag.
48
+ fixedBaseRevision = admitted.baseRevision;
49
+ const reviewScopeKeys = admitted.reviewScopeKeys;
50
+ const authorityRefs = admitted.authorityRefs;
51
+ const ticketNumber = admitted.ticketNumber;
52
+ const loaded = await dependencies.loadCanonicalSkillBinding("code-review");
53
+ if (loaded.name !== "code-review")
54
+ throw new Error("Canonical Skill binding loader returned tdd for code-review");
55
+ binding = loaded;
56
+ reader = await dependencies.createPinnedGitReader();
57
+ acceptedDispatch = undefined;
58
+ const executeAndProjectDispatch = async (execution, invocation) => {
59
+ const dispatch = acceptedDispatch;
60
+ if (dispatch === undefined || dispatch.identity !== execution.identity)
61
+ throw new Error("Reviewer execution lacks accepted construction evidence");
62
+ const { context, signal } = invocation;
63
+ ledger.append({ source: "reviewer-agent", type: "dispatch-started", dispatchIdentity: execution.identity, cardinality: execution.legs.length });
64
+ try {
65
+ const result = await dependencies.runDispatch(execution, { context, ...(signal === undefined ? {} : { signal }) });
66
+ projectReviewerDispatchOutcome(ledger, dispatch, result);
67
+ return result;
68
+ }
69
+ catch (error) {
70
+ if (error instanceof ReviewerDispatchExecutionError) {
71
+ try {
72
+ projectReviewerDispatchOutcome(ledger, dispatch, error.outcome);
73
+ }
74
+ catch (mismatch) {
75
+ throw ledger.recordInfrastructureFailure(mismatch);
76
+ }
77
+ throw error;
78
+ }
79
+ throw ledger.recordInfrastructureFailure(error);
80
+ }
81
+ };
82
+ dispatcher = createReviewerDispatcher({
83
+ canonicalSkill: binding.snapshot.raw,
84
+ reader,
85
+ ...(reviewScopeKeys === undefined ? {} : { reviewScopeKeys }),
86
+ ...(authorityRefs === undefined ? {} : { authorityRefs }),
87
+ ...(ticketNumber === undefined ? {} : { ticketNumber }),
88
+ ...(dependencies.fetchIssue === undefined ? {} : { fetchIssue: dependencies.fetchIssue }),
89
+ decisionEvidence(decision) {
90
+ try {
91
+ if (decision.disposition === "accepted") {
92
+ ledger.append(projectAcceptedDispatch(decision.dispatch));
93
+ acceptedDispatch = decision.dispatch;
94
+ }
95
+ else
96
+ ledger.append({ source: "reviewer-dispatch", type: "rejected", identity: decision.identity, violations: decision.violations, started: false });
97
+ }
98
+ catch (error) {
99
+ throw ledger.recordInfrastructureFailure(error);
100
+ }
101
+ },
102
+ run: executeAndProjectDispatch,
103
+ });
104
+ if (!registered) {
105
+ registered = true;
106
+ pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "御史台输出", description: "Standards/Spec 评审腿由 runtime 以取证子会话代跑,本席收腿报告后交薄回执。", promptSnippet: "提交御史台终局回执", parameters: reviewerOutputSchema,
107
+ async execute(id, parameters, _signal, _update, toolCtx) {
108
+ if (!soul || !binding)
109
+ throw new Error("御史台输入未装载");
110
+ const output = validateReviewerIntent(parameters);
111
+ let record;
112
+ try {
113
+ record = ledger.recordForAudit(output.status);
114
+ }
115
+ catch (error) {
116
+ if (error?.fatalReviewerInfrastructure)
117
+ hostActions.failInfrastructure(error, toolCtx, id);
118
+ throw error;
119
+ }
120
+ const candidate = assembleRuntimeReviewerReceipt({
121
+ intent: output,
122
+ record,
123
+ canonicalSkillText: binding.snapshot.raw,
124
+ });
125
+ try {
126
+ await dependencies.shutdownAgent?.();
127
+ }
128
+ catch (error) {
129
+ hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id);
130
+ }
131
+ return {
132
+ content: [{ type: "text", text: REVIEWER_ACCEPTED_TEXT }],
133
+ details: candidate,
134
+ terminate: true,
135
+ };
136
+ } });
137
+ // Skill invocation transform + agent_start expansion/prompt lifecycle: shared envelope (ADR 0018).
138
+ pi.on("session_shutdown", async () => { try {
139
+ await dependencies.shutdownAgent?.();
140
+ }
141
+ catch (error) {
142
+ throw ledger.recordInfrastructureFailure(error);
143
+ } });
144
+ }
145
+ const activatedSoul = soul;
146
+ const activatedBase = fixedBaseRevision;
147
+ const activatedBinding = binding;
148
+ return Object.freeze({
149
+ dispatcher,
150
+ fixedBaseRevision: activatedBase,
151
+ soul: activatedSoul,
152
+ skillBinding: activatedBinding,
153
+ getSpecDisposition() {
154
+ return acceptedDispatch?.specDisposition;
155
+ },
156
+ });
157
+ },
158
+ };
159
+ }
@@ -0,0 +1,66 @@
1
+ function freeze(value) {
2
+ if (Array.isArray(value))
3
+ return Object.freeze(value.map(freeze));
4
+ if (value !== null && typeof value === "object")
5
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, freeze(item)])));
6
+ return value;
7
+ }
8
+ function receiptPrompt(prompt) {
9
+ return Object.freeze({ text: prompt });
10
+ }
11
+ export function assembleRuntimeReviewerReceipt(input) {
12
+ const reports = {};
13
+ const outcomes = {};
14
+ for (const axis of ["standards", "spec"]) {
15
+ const result = input.record.results[axis];
16
+ if (result === undefined)
17
+ continue;
18
+ if (result.status === "successful") {
19
+ outcomes[axis] = {
20
+ status: "successful",
21
+ prompt: receiptPrompt(result.prompt),
22
+ workspaceDisposition: result.workspaceDisposition,
23
+ };
24
+ reports[axis] = { text: result.report };
25
+ }
26
+ else {
27
+ outcomes[axis] = {
28
+ status: "failed",
29
+ prompt: receiptPrompt(result.prompt),
30
+ workspaceDisposition: result.workspaceDisposition,
31
+ failure: result.failure,
32
+ diagnostic: result.diagnostic,
33
+ };
34
+ }
35
+ }
36
+ const accepted = input.record.accepted;
37
+ const skillText = accepted?.input.canonicalSkill ?? input.canonicalSkillText;
38
+ // Seat-owned amendments sit beside reports; never rewrite runtime-owned child bytes.
39
+ const amendments = input.intent.amendments;
40
+ return freeze({
41
+ version: 2,
42
+ status: input.intent.status,
43
+ ...(input.intent.status === "refused" ? { diagnostic: input.intent.diagnostic } : {}),
44
+ ...(accepted === undefined ? {} : {
45
+ acceptedBatch: {
46
+ identity: accepted.identity,
47
+ legs: accepted.legs.map(({ axis, prompt }) => ({ axis, prompt: receiptPrompt(prompt) })),
48
+ },
49
+ // Honest Spec skipped/missing notation when independent discovery confirmed absence.
50
+ ...(accepted.specDisposition === undefined
51
+ ? {}
52
+ : { specDisposition: accepted.specDisposition }),
53
+ // Self-fetch bytes + source annotation retained for audit (#343 fetch-then-store).
54
+ ...(accepted.specFetchedMaterial === undefined
55
+ ? {}
56
+ : { specFetchedMaterial: accepted.specFetchedMaterial }),
57
+ }),
58
+ reports,
59
+ ...(amendments === undefined ? {} : { amendments }),
60
+ outcomes,
61
+ identities: {
62
+ canonicalSkill: { text: skillText },
63
+ ...(accepted === undefined ? {} : { construction: { recipe: accepted.recipe }, target: accepted.target }),
64
+ },
65
+ });
66
+ }