@akagilnc/pi-workflow-roles 0.1.3932 → 0.1.3975

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 (162) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/acp-host/production-host.js +2389 -6018
  3. package/dist/analyst-gate-cycles-read.js +15 -19
  4. package/dist/audit-escalation.js +16 -46
  5. package/dist/auditor-dossier-tool.js +7 -62
  6. package/dist/collector-identity.js +0 -157
  7. package/dist/collector-ledger.js +2 -42
  8. package/dist/compliance-transport.js +40 -14
  9. package/dist/diarist-contracts.js +2 -40
  10. package/dist/doctor-contracts.js +39 -108
  11. package/dist/doctor-evidence.js +1 -5
  12. package/dist/engine-detour-tool.js +1 -1
  13. package/dist/gatekeeper-role.js +49 -38
  14. package/dist/headless-host/production-host.js +4814 -8450
  15. package/dist/host-contracts.js +0 -5
  16. package/dist/merger-contracts.js +4 -6
  17. package/dist/navigator-attendance.js +30 -52
  18. package/dist/navigator-invocation-identity.js +0 -1
  19. package/dist/navigator-public-session.js +2 -1
  20. package/dist/notary-contracts.js +4 -10
  21. package/dist/package-contracts/auditor-output.js +5 -23
  22. package/dist/package-contracts/fixer-output.js +25 -49
  23. package/dist/package-contracts/gatekeeper-output.js +3 -13
  24. package/dist/package-contracts/navigator-output.js +4 -7
  25. package/dist/package-contracts/reviewer-output.js +1 -95
  26. package/dist/package-contracts/terminating-tools.js +33 -144
  27. package/dist/package-contracts/worker-output.js +1 -3
  28. package/dist/public-cli/auto-resume.js +30 -68
  29. package/dist/public-cli/case-dossier-delivery.js +5 -17
  30. package/dist/public-cli/diarist-run.js +9 -4
  31. package/dist/public-cli/inspector-run.js +2 -8
  32. package/dist/public-cli/instruction-seat-run.js +3 -7
  33. package/dist/public-cli/invocation.js +5 -2
  34. package/dist/public-cli/main.js +6068 -6964
  35. package/dist/public-cli/notary-run.js +1 -8
  36. package/dist/public-cli/post-admission.js +314 -113
  37. package/dist/public-cli/run-lifecycle.js +14 -38
  38. package/dist/public-cli/settlement.js +209 -390
  39. package/dist/public-cli/terminal.js +51 -45
  40. package/dist/public-role-summons.js +1 -12
  41. package/dist/reviewer-dispatch.js +12 -258
  42. package/dist/submission-correctable-error.js +0 -12
  43. package/dist/submission-errors.js +6 -1
  44. package/dist/submission-ledger.js +125 -168
  45. package/dist/ticket-provenance-contracts.js +2 -49
  46. package/dist/ticket-provenance.js +82 -31
  47. package/extensions/role-runtime.ts +1 -6
  48. package/package.json +1 -1
  49. package/resources/836-deleted-machine-instruction-inventory.md +18 -0
  50. package/src/acp-host/role-turn-host.ts +8 -3
  51. package/src/analyst-gate-cycles-read.ts +17 -29
  52. package/src/audit-escalation.ts +20 -50
  53. package/src/auditor-dossier-tool.ts +5 -103
  54. package/src/canonical-skill-binding.ts +3 -2
  55. package/src/collector-identity.ts +0 -179
  56. package/src/collector-ledger.ts +3 -46
  57. package/src/collector-role.ts +32 -61
  58. package/src/collector-tool-schemas.ts +20 -14
  59. package/src/compliance-transport.ts +55 -17
  60. package/src/countersign-role.ts +17 -8
  61. package/src/diarist-contracts.ts +3 -36
  62. package/src/diarist-role.ts +13 -7
  63. package/src/diarist.ts +4 -16
  64. package/src/doctor-contracts.ts +73 -94
  65. package/src/doctor-evidence.ts +1 -1
  66. package/src/doctor-role.ts +3 -3
  67. package/src/engine-detour-tool.ts +4 -1
  68. package/src/external-host-turn-loop.ts +9 -3
  69. package/src/gatekeeper-pass-envelope.ts +7 -12
  70. package/src/gatekeeper-role.ts +56 -50
  71. package/src/gleaner-left-role.ts +4 -2
  72. package/src/headless-host/role-turn-host.ts +4 -5
  73. package/src/host-contracts.ts +3 -8
  74. package/src/judge-role.ts +19 -13
  75. package/src/merger-contracts.ts +10 -6
  76. package/src/merger-role.ts +1 -5
  77. package/src/navigator-attendance.ts +37 -74
  78. package/src/navigator-invocation-identity.ts +0 -6
  79. package/src/navigator-public-session.ts +2 -1
  80. package/src/notary-contracts.ts +7 -10
  81. package/src/package-contracts/auditor-output.ts +4 -21
  82. package/src/package-contracts/fixer-output.ts +34 -52
  83. package/src/package-contracts/gatekeeper-output.ts +5 -22
  84. package/src/package-contracts/navigator-output.ts +7 -7
  85. package/src/package-contracts/reviewer-output.ts +2 -150
  86. package/src/package-contracts/terminating-tools.ts +32 -147
  87. package/src/package-contracts/worker-output.ts +0 -1
  88. package/src/pi/adapter.ts +3 -8
  89. package/src/public-cli/auto-resume.ts +41 -94
  90. package/src/public-cli/case-dossier-delivery.ts +5 -15
  91. package/src/public-cli/countersign-run.ts +14 -6
  92. package/src/public-cli/diarist-run.ts +11 -9
  93. package/src/public-cli/inspector-run.ts +2 -18
  94. package/src/public-cli/instruction-seat-run.ts +3 -17
  95. package/src/public-cli/invocation.ts +5 -2
  96. package/src/public-cli/merger-run.ts +1 -1
  97. package/src/public-cli/notary-run.ts +1 -18
  98. package/src/public-cli/post-admission.ts +385 -115
  99. package/src/public-cli/run-lifecycle.ts +13 -44
  100. package/src/public-cli/settlement.ts +314 -562
  101. package/src/public-cli/terminal.ts +66 -72
  102. package/src/public-role-summons.ts +5 -30
  103. package/src/reviewer-dispatch.ts +29 -331
  104. package/src/reviewer-role.ts +16 -83
  105. package/src/role-envelope.ts +16 -24
  106. package/src/role-runtime-dependencies.ts +1 -6
  107. package/src/role-runtime.ts +65 -201
  108. package/src/submission-correctable-error.ts +0 -14
  109. package/src/submission-errors.ts +6 -1
  110. package/src/submission-ledger.ts +187 -187
  111. package/src/ticket-provenance-contracts.ts +4 -60
  112. package/src/ticket-provenance.ts +98 -32
  113. package/src/worker-role.ts +35 -36
  114. package/dist/activation-ledger-session.js +0 -105
  115. package/dist/activation-ledger.js +0 -139
  116. package/dist/activation-trace.js +0 -33
  117. package/dist/atomic-write.js +0 -23
  118. package/dist/auditor-role.js +0 -12
  119. package/dist/canonical-skill-binding.js +0 -61
  120. package/dist/collector-handbook.js +0 -142
  121. package/dist/collector-receipt.js +0 -107
  122. package/dist/collector-role.js +0 -566
  123. package/dist/collector-tool-schemas.js +0 -107
  124. package/dist/countersign-role.js +0 -28
  125. package/dist/diarist-role.js +0 -44
  126. package/dist/diarist.js +0 -66
  127. package/dist/doctor-role.js +0 -75
  128. package/dist/fixer-bash-seatbelt.js +0 -13
  129. package/dist/gatekeeper-pass-envelope.js +0 -77
  130. package/dist/gleaner-left-role.js +0 -26
  131. package/dist/inspector-role.js +0 -29
  132. package/dist/judge-role.js +0 -104
  133. package/dist/merger-role.js +0 -55
  134. package/dist/navigator-role.js +0 -12
  135. package/dist/notary-role.js +0 -134
  136. package/dist/oauth-keepalive.js +0 -158
  137. package/dist/pi/adapter.js +0 -223
  138. package/dist/reviewer-agent.js +0 -122
  139. package/dist/reviewer-child-executor.js +0 -194
  140. package/dist/reviewer-construction.js +0 -103
  141. package/dist/reviewer-execution-ledger.js +0 -166
  142. package/dist/reviewer-failure-diagnostic.js +0 -17
  143. package/dist/reviewer-role.js +0 -159
  144. package/dist/reviewer-scope-prompt.js +0 -21
  145. package/dist/reviewer-settlement.js +0 -66
  146. package/dist/reviewer-workspace.js +0 -132
  147. package/dist/role-runtime.js +0 -1604
  148. package/dist/stderr-jsonl.js +0 -26
  149. package/dist/tool-execution-observation.js +0 -141
  150. package/dist/worker-role.js +0 -345
  151. package/dist/worker-submission-gates.js +0 -305
  152. package/src/collector-receipt.ts +0 -140
  153. package/src/diagnostic-tail.ts +0 -18
  154. package/src/reviewer-agent.ts +0 -135
  155. package/src/reviewer-child-executor.ts +0 -252
  156. package/src/reviewer-construction.ts +0 -195
  157. package/src/reviewer-execution-ledger.ts +0 -212
  158. package/src/reviewer-failure-diagnostic.ts +0 -18
  159. package/src/reviewer-scope-prompt.ts +0 -24
  160. package/src/reviewer-settlement.ts +0 -74
  161. package/src/reviewer-workspace.ts +0 -105
  162. package/src/shape-unreadable-failure.ts +0 -49
@@ -65,11 +65,9 @@ function normalizeOfficerArg(raw) {
65
65
  return undefined;
66
66
  return OFFICER_ARG_ALIASES[raw.trim()];
67
67
  }
68
- /** Typed string findings only — non-strings dropped; missing/non-array → []. */
69
- function asStringFindings(value) {
70
- if (!Array.isArray(value))
71
- return [];
72
- return value.filter((item) => typeof item === "string");
68
+ /** Findings as recorded — missing/non-array → []. */
69
+ function asFindings(value) {
70
+ return Array.isArray(value) ? value : [];
73
71
  }
74
72
  /**
75
73
  * Optional non-empty dispatch reason. Trim only decides emptiness; a non-empty
@@ -102,11 +100,10 @@ function acceptedGateReceiptIds(rows) {
102
100
  }
103
101
  return accepted;
104
102
  }
105
- function requireAcceptedGateStatus(args, filePath) {
106
- if (args === undefined || typeof args.status !== "string" || args.status.trim() === "") {
107
- throw new Error(`accepted gate receipt missing usable status in ${filePath}`);
108
- }
109
- return args.status.trim();
103
+ function readAcceptedGateStatus(args) {
104
+ if (args === undefined)
105
+ return "";
106
+ return typeof args.status === "string" ? args.status : args.status === undefined ? "" : String(args.status);
110
107
  }
111
108
  function requireAcceptedGateSpan(rows, filePath) {
112
109
  const span = extractSessionTimestampSpan(rows);
@@ -182,25 +179,24 @@ function extractAllAcceptedGateToolCalls(rows) {
182
179
  function projectAcceptedGateCall(filePath, rows, call) {
183
180
  // Continuous memory volumes carry many summons; span only this binding's interval.
184
181
  const span = requireAcceptedGateSpan(intervalRowsForGateCall(rows, call.rowIndex), filePath);
185
- const status = requireAcceptedGateStatus(call.args, filePath);
186
- const findings = asStringFindings(call.args?.findings);
182
+ const status = readAcceptedGateStatus(call.args);
183
+ const findings = asFindings(call.args?.findings);
187
184
  const findingsCount = findings.length;
188
185
  const bindingFields = {
189
186
  ...(call.attemptEntryId === undefined ? {} : { attemptEntryId: call.attemptEntryId }),
190
187
  ...(call.parentSessionFile === undefined ? {} : { parentSessionFile: call.parentSessionFile }),
191
188
  };
192
189
  if (DISPATCH_TOOLS.has(call.toolName)) {
193
- // Lawful province non-dispatch release — opens no round, never unreadable (#597).
194
- if (status === "pass") {
195
- return undefined;
196
- }
197
- // Pairing terminal is dispatch; unknown/non-contract status stays loud (#475).
190
+ // Lawful province non-dispatch release — opens no round (#597).
191
+ // #836 / #622: bounce|escalate|unknown status must not throw and kill parent settlement.
192
+ // Only a real dispatch pairs; everything else is omitted from pairing (原 payload 已在账本).
198
193
  if (status !== "dispatch") {
199
- throw new Error(`accepted dispatch receipt has non-dispatch status ${JSON.stringify(status)} in ${filePath}`);
194
+ return undefined;
200
195
  }
201
196
  const officer = normalizeOfficerArg(call.args?.officer);
202
197
  if (officer === undefined) {
203
- throw new Error(`accepted dispatch receipt missing or unknown officer in ${filePath}`);
198
+ // Missing officer on a dispatch face — omit pairing; do not kill parent (#836).
199
+ return undefined;
204
200
  }
205
201
  const reason = optionalDispatchReason(call.args?.reason);
206
202
  return {
@@ -1,4 +1,5 @@
1
1
  import { readableGateItem } from "./readable-gate-item.js";
2
+ import { GatekeeperDecisionError } from "./submission-errors.js";
2
3
  export const AUDIT_ESCALATION_KIND = "audit_escalation";
3
4
  // Live Navigator settlement may consume only the projection produced by this
4
5
  // owner. Its private WeakSet cannot be authored by role output; persisted/
@@ -32,23 +33,10 @@ export function buildAuditEscalationResult(decision, deliveredOutput) {
32
33
  if (Object.hasOwn(decision, "findings") && decision.findings !== undefined) {
33
34
  auditOwned.findings = decision.findings;
34
35
  }
35
- const deliveredFields = deliveredOutput !== undefined &&
36
- deliveredOutput !== null &&
37
- typeof deliveredOutput === "object" &&
38
- !Array.isArray(deliveredOutput)
39
- ? { ...deliveredOutput }
40
- : {};
41
- // Role output cannot fill an absent audit/officer-owned field.
42
- delete deliveredFields.conflicts;
43
- delete deliveredFields.auditDecisionGate;
44
- if (Object.hasOwn(decision, "officer")) {
45
- delete deliveredFields.reason;
46
- delete deliveredFields.officer;
47
- delete deliveredFields.findings;
48
- }
49
36
  const result = {
50
- ...deliveredFields,
51
- ...auditOwned,
37
+ kind: AUDIT_ESCALATION_KIND,
38
+ ...(deliveredOutput !== undefined ? { receipt: deliveredOutput } : {}),
39
+ audit: auditOwned,
52
40
  };
53
41
  AUDIT_ESCALATION_LIVE_REGISTRY.add(result);
54
42
  return result;
@@ -59,39 +47,11 @@ export function isAuditEscalationProjection(value) {
59
47
  return false;
60
48
  return AUDIT_ESCALATION_LIVE_REGISTRY.has(value);
61
49
  }
62
- function humanDecisionText(decision, result) {
63
- const officer = Object.hasOwn(decision, "officer")
64
- ? decision.officer
65
- : undefined;
66
- const lines = [
67
- officer === undefined
68
- ? "Human decision required: compliance audit escalation."
69
- : `Human decision required: ${officer} escalation.`,
70
- ];
71
- if (officer !== undefined && typeof result.reason === "string") {
72
- lines.push(`Reason: ${result.reason}`);
73
- }
74
- if (Array.isArray(result.conflicts)) {
75
- lines.push("Conflicts:", ...result.conflicts.map((conflict) => `- ${readableGateItem(conflict)}`));
76
- }
77
- if (officer !== undefined && Array.isArray(result.findings) && result.findings.length > 0) {
78
- lines.push("Findings:", ...result.findings.map((finding) => `- ${readableGateItem(finding)}`));
79
- }
80
- const gate = result.auditDecisionGate;
81
- if (gate !== null && typeof gate === "object" && !Array.isArray(gate)) {
82
- const record = gate;
83
- if (typeof record.question === "string")
84
- lines.push(`Question: ${record.question}`);
85
- if (Array.isArray(record.options)) {
86
- lines.push("Options:", ...record.options.map((option) => `- ${readableGateItem(option)}`));
87
- }
88
- }
89
- return lines.join("\n");
90
- }
91
50
  export function projectAuditEscalation(decision, deliveredOutput) {
92
51
  const details = buildAuditEscalationResult(decision, deliveredOutput);
52
+ const original = deliveredOutput !== undefined ? deliveredOutput : decision;
93
53
  return {
94
- content: [{ type: "text", text: humanDecisionText(decision, details) }],
54
+ content: [{ type: "text", text: readableGateItem(original) }],
95
55
  details,
96
56
  terminate: true,
97
57
  ...(decision.usage === undefined ? {} : { usage: decision.usage }),
@@ -132,5 +92,15 @@ export async function disposeComplianceDecision(decision, handlers, deliveredOut
132
92
  return await handlers.bounce(decision.violations);
133
93
  case "escalate":
134
94
  return await handlers.escalate(projectAuditEscalation(decision, deliveredOutput));
95
+ case "transport_failure":
96
+ if (handlers.transportFailure !== undefined) {
97
+ return await handlers.transportFailure(decision);
98
+ }
99
+ throw new GatekeeperDecisionError({
100
+ status: "transport_failure",
101
+ stage: "auditor",
102
+ reason: decision.diagnostic,
103
+ submission: decision.submissions ?? decision.terminal,
104
+ });
135
105
  }
136
106
  }
@@ -1,64 +1,18 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
1
  import { dirname, join, resolve } from "node:path";
3
- import { readableGateItem } from "./readable-gate-item.js";
4
2
  import { Type } from "typebox";
5
3
  const AUDITOR_DOSSIER_TOOL_NAME = "ak_get_run_dossier";
6
- const GATE_SUBMISSION_CANDIDATE_FILE = "gate-submission-candidate.json";
7
- function isRecord(value) {
8
- return typeof value === "object" && value !== null && !Array.isArray(value);
9
- }
10
4
  function auditorRunDirectory(context) {
11
5
  const sessionFile = context.sessionManager?.getSessionFile?.();
12
6
  if (sessionFile === void 0) return void 0;
13
7
  return resolve(dirname(dirname(sessionFile)));
14
8
  }
15
- function readLatestToolCallLeaf(context) {
16
- const entries = [...context.sessionManager?.getEntries?.() ?? []];
17
- for (let i = entries.length - 1; i >= 0; i--) {
18
- const entry = entries[i];
19
- if (!isRecord(entry) || entry.type !== "message" || !isRecord(entry.message)) continue;
20
- const message = entry.message;
21
- if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
22
- for (const part of message.content) {
23
- if (isRecord(part) && part.type === "toolCall") {
24
- return entry;
25
- }
26
- }
27
- }
28
- return void 0;
29
- }
30
- function gateSubmissionCandidatePath(runDirectory) {
31
- return join(runDirectory, "artifacts", GATE_SUBMISSION_CANDIDATE_FILE);
32
- }
33
- function readLatestSubmissionArguments(context) {
34
- const leaf = readLatestToolCallLeaf(context);
35
- if (!isRecord(leaf) || !isRecord(leaf.message) || !Array.isArray(leaf.message.content)) {
36
- return void 0;
37
- }
38
- for (const part of leaf.message.content) {
39
- if (isRecord(part) && part.type === "toolCall" && "arguments" in part) {
40
- return part.arguments;
41
- }
42
- }
43
- return void 0;
44
- }
45
- function buildGateOfficerReviewInstruction(input) {
46
- return readableGateItem(input.submission);
47
- }
48
- function persistGateSubmissionCandidate(runDirectory, context) {
49
- const leaf = readLatestToolCallLeaf(context);
50
- if (leaf === void 0) return void 0;
51
- const path = gateSubmissionCandidatePath(runDirectory);
52
- mkdirSync(dirname(path), { recursive: true });
53
- writeFileSync(path, `${JSON.stringify(leaf)}
54
- `, "utf8");
55
- return path;
56
- }
57
- function createAuditorDossierTool(runDirectory, options) {
9
+ function createAuditorDossierTool(runDirectory) {
58
10
  return {
59
11
  name: AUDITOR_DOSSIER_TOOL_NAME,
60
12
  description: "\u5B9A\u4F4D\u672C\u5BA1\u8BA1\u5E2D\u7ED1\u5B9A\u7684 run \u5377\u5B97\u53CA\u5176\u8BC1\u636E\u5165\u53E3\u3002",
61
- parameters: Type.Object({}, { additionalProperties: false }),
13
+ // #836 r16 class 3: execute() ignores `_params` entirely — a closed root
14
+ // only rejects the role for saying more.
15
+ parameters: Type.Object({}, { additionalProperties: true }),
62
16
  async execute(_id, _params) {
63
17
  if (runDirectory === void 0) {
64
18
  return {
@@ -66,15 +20,12 @@ function createAuditorDossierTool(runDirectory, options) {
66
20
  details: void 0
67
21
  };
68
22
  }
69
- const sessionPath = join(runDirectory, "session", "session.jsonl");
70
- const submissionCandidate = options?.submissionCandidate;
71
23
  const details = {
72
24
  runDirectory,
73
25
  admittedRequest: join(runDirectory, "admitted-request.json"),
74
- parentSessionCandidate: submissionCandidate ?? sessionPath,
26
+ parentSessionCandidate: join(runDirectory, "session", "session.jsonl"),
75
27
  attachments: join(runDirectory, "attachments"),
76
- artifacts: join(runDirectory, "artifacts"),
77
- ...submissionCandidate === void 0 ? {} : { submissionCandidate }
28
+ artifacts: join(runDirectory, "artifacts")
78
29
  };
79
30
  return {
80
31
  content: [{ type: "text", text: JSON.stringify(details) }],
@@ -85,12 +36,6 @@ function createAuditorDossierTool(runDirectory, options) {
85
36
  }
86
37
  export {
87
38
  AUDITOR_DOSSIER_TOOL_NAME,
88
- GATE_SUBMISSION_CANDIDATE_FILE,
89
39
  auditorRunDirectory,
90
- buildGateOfficerReviewInstruction,
91
- createAuditorDossierTool,
92
- gateSubmissionCandidatePath,
93
- persistGateSubmissionCandidate,
94
- readLatestSubmissionArguments,
95
- readLatestToolCallLeaf
40
+ createAuditorDossierTool
96
41
  };
@@ -69,17 +69,6 @@ export class CollectorUnknownEvidenceError extends CorrectableSubmissionError {
69
69
  this.name = "CollectorUnknownEvidenceError";
70
70
  }
71
71
  }
72
- /**
73
- * Evidence-binding failure for a finding pointer that resolves to the wrong kind
74
- * of stored record, lacks a GitHub locator, or has no identity group — not a
75
- * free-shape gate on the submission envelope.
76
- */
77
- export class CollectorFindingsValidationError extends CorrectableSubmissionError {
78
- constructor(message) {
79
- super(message);
80
- this.name = "CollectorFindingsValidationError";
81
- }
82
- }
83
72
  /**
84
73
  * #676 D6: non-OPEN targets keep collected materials and must not fire new review
85
74
  * requests. Bounce the request as correctable so the seat can still seal output.
@@ -101,149 +90,3 @@ export class CollectorWaitWindowClosedError extends CorrectableSubmissionError {
101
90
  this.name = "CollectorWaitWindowClosedError";
102
91
  }
103
92
  }
104
- function candidateRecord(candidate) {
105
- if (candidate === undefined || candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
106
- return undefined;
107
- }
108
- return candidate;
109
- }
110
- /** Canonical top-level keys the collector output projection understands. */
111
- const COLLECTOR_OUTPUT_CANONICAL_KEYS = new Set([
112
- "findings",
113
- "unfinishedReasons",
114
- "infrastructureFailure",
115
- ]);
116
- /** Non-canonical own keys mean content was present but not under a projectable key. */
117
- function hasNonCanonicalOwnKeys(record) {
118
- for (const key of Object.keys(record)) {
119
- if (!COLLECTOR_OUTPUT_CANONICAL_KEYS.has(key))
120
- return true;
121
- }
122
- return false;
123
- }
124
- /**
125
- * #641 chain① / #676: turn model-submitted finding pointer refs into receipt findings.
126
- * Binds resolvable evidence references only — no pure shape rejection of the
127
- * candidate envelope. Unknown refs, wrong kinds, missing GitHub locator, and
128
- * missing identity group stay as binding failures. Unreadable findings content
129
- * is not washed into "zero findings": projection facts record the gap so
130
- * downstream can open the session 正本 (第 0 条 / #676 C).
131
- */
132
- export function enrichCollectorFindings(input) {
133
- // Candidate present but not a record — content existed, none projected (#676 C).
134
- if (input.candidate !== undefined && input.candidate !== null && candidateRecord(input.candidate) === undefined) {
135
- return { findingsSource: "unreadable", findingsProjectedCount: 0, findingsUnprojected: true };
136
- }
137
- const record = candidateRecord(input.candidate);
138
- if (record === undefined) {
139
- return { findingsSource: "absent", findingsProjectedCount: 0, findingsUnprojected: false };
140
- }
141
- if (!Object.hasOwn(record, "findings")) {
142
- // Non-canonical top-level content is not "absent" — record the projection gap (#676 C).
143
- if (hasNonCanonicalOwnKeys(record)) {
144
- return { findingsSource: "unreadable", findingsProjectedCount: 0, findingsUnprojected: true };
145
- }
146
- return { findingsSource: "absent", findingsProjectedCount: 0, findingsUnprojected: false };
147
- }
148
- const rawFindings = record["findings"];
149
- if (!Array.isArray(rawFindings)) {
150
- // Key present but not an array — content exists, none projected. No shape bounce.
151
- return { findingsSource: "unreadable", findingsProjectedCount: 0, findingsUnprojected: true };
152
- }
153
- const byEvidenceId = new Map(input.records.map((evidence) => [evidence.evidenceId, evidence]));
154
- let projected = 0;
155
- let unprojected = false;
156
- for (const raw of rawFindings) {
157
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
158
- unprojected = true;
159
- continue;
160
- }
161
- const item = raw;
162
- const evidenceId = item.evidenceId;
163
- if (typeof evidenceId !== "string" || evidenceId.length === 0) {
164
- // Present item without a bindable pointer — keep gap fact, do not fabricate.
165
- unprojected = true;
166
- continue;
167
- }
168
- const evidence = byEvidenceId.get(evidenceId);
169
- if (evidence === undefined) {
170
- throw new CollectorUnknownEvidenceError(evidenceId);
171
- }
172
- if (evidence.kind !== "review" && evidence.kind !== "issue_comment" && evidence.kind !== "review_comment") {
173
- throw new CollectorFindingsValidationError(`通进司 finding 指针指向不可承 finding 的证据种类 ${evidence.kind}`);
174
- }
175
- if (evidence.githubId === undefined) {
176
- throw new CollectorFindingsValidationError(`通进司 finding 指针证据 ${evidenceId} 缺少 GitHub id`);
177
- }
178
- const identity = evidence.machineIdentity ?? null;
179
- const group = input.groups.find((candidateGroup) => identityKey(candidateGroup.identity) === identityKey(identity));
180
- if (group === undefined) {
181
- throw new CollectorFindingsValidationError(`通进司 finding 指针证据 ${evidenceId} 无归属身份组`);
182
- }
183
- // Bound finding keeps pointer; non-string category/summary are unprojected field gaps
184
- // (pointer success ≠ full content projection) — #676 C / 3939511832.
185
- const category = item.category;
186
- const summary = item.summary;
187
- if (Object.hasOwn(item, "category") && typeof category !== "string")
188
- unprojected = true;
189
- if (Object.hasOwn(item, "summary") && typeof summary !== "string")
190
- unprojected = true;
191
- group.findings.push({
192
- identity,
193
- source: {
194
- kind: evidence.kind,
195
- id: evidence.githubId,
196
- evidenceId: evidence.evidenceId,
197
- headRelation: headRelationFor(evidence, input.targetHead),
198
- },
199
- ...(typeof category === "string" ? { category } : {}),
200
- ...(typeof summary === "string" ? { summary } : {}),
201
- pointer: {
202
- repository: input.repository,
203
- prNumber: input.prNumber,
204
- commentId: evidence.githubId,
205
- ...(evidence.htmlUrl === undefined ? {} : { htmlUrl: evidence.htmlUrl }),
206
- ...(evidence.authorLogin === undefined ? {} : { authorLogin: evidence.authorLogin }),
207
- kind: evidence.kind,
208
- authoritativeTime: evidence.authoritativeTime ?? null,
209
- ...(evidence.commitOid === undefined ? {} : { commitOid: evidence.commitOid }),
210
- },
211
- });
212
- projected += 1;
213
- }
214
- return {
215
- findingsSource: "array",
216
- findingsProjectedCount: projected,
217
- findingsUnprojected: unprojected,
218
- };
219
- }
220
- /**
221
- * #676 D6/C: optional unfinished reasons from the model submission.
222
- * Project readable strings; record when original content could not fully project
223
- * (do not wash unreadable unfinishedReasons into "none").
224
- */
225
- export function extractCollectorUnfinishedReasons(candidate) {
226
- if (candidate !== undefined && candidate !== null && candidateRecord(candidate) === undefined) {
227
- return { reasons: undefined, source: "unreadable", unprojected: true };
228
- }
229
- const record = candidateRecord(candidate);
230
- if (record === undefined) {
231
- return { reasons: undefined, source: "absent", unprojected: false };
232
- }
233
- if (!Object.hasOwn(record, "unfinishedReasons")) {
234
- // unfinishedReasons absent is fine when only findings (or nothing) present.
235
- // Non-canonical keys alone are recorded on the findings projection path.
236
- return { reasons: undefined, source: "absent", unprojected: false };
237
- }
238
- const raw = record["unfinishedReasons"];
239
- if (!Array.isArray(raw)) {
240
- return { reasons: undefined, source: "unreadable", unprojected: true };
241
- }
242
- const reasons = raw.filter((item) => typeof item === "string");
243
- const unprojected = reasons.length !== raw.length;
244
- return {
245
- reasons: reasons.length > 0 ? reasons : undefined,
246
- source: "array",
247
- unprojected,
248
- };
249
- }
@@ -22,36 +22,9 @@ export const COLLECTOR_OPERATIONAL_TOOLS = [
22
22
  COLLECTOR_WAIT_TOOL,
23
23
  COLLECTOR_HANDBOOK_WRITE_TOOL,
24
24
  ];
25
- /**
26
- * #641 chain①: observe context projects at most this many bytes of each
27
- * material body; the full body stays reachable via the record pointer
28
- * (evidenceId + htmlUrl) and is preserved in the volume, never unconditionally
29
- * transcribed into model context or receipt.
30
- */
31
- const COLLECTOR_OBSERVE_BODY_HEAD_BYTES = 512;
32
- function utf8SafePrefix(text, maxBytes) {
33
- if (Buffer.byteLength(text, "utf8") <= maxBytes)
34
- return text;
35
- const bytes = Buffer.from(text, "utf8");
36
- let end = maxBytes;
37
- while (end > 0 && (bytes[end] & 0xc0) === 0x80)
38
- end -= 1;
39
- return `${bytes.subarray(0, end).toString("utf8")}…`;
40
- }
41
- /**
42
- * #641 chain① model-context projection: identical shape to the volume view,
43
- * with every material body bounded to a head preview. Full bodies remain
44
- * pointer-reachable (evidenceId/htmlUrl) and stay in the ledger volume
45
- * (collector-side; provider-visible observe details carry only this bounded
46
- * projection), never unconditionally transcribed into model context.
47
- */
25
+ /** #836: observe context is the full volume view — no body truncation. */
48
26
  function projectObserveContextView(modelView) {
49
- return {
50
- ...modelView,
51
- evidence: modelView.evidence.map((entry) => entry.body === undefined
52
- ? entry
53
- : { ...entry, body: utf8SafePrefix(entry.body, COLLECTOR_OBSERVE_BODY_HEAD_BYTES) }),
54
- };
27
+ return modelView;
55
28
  }
56
29
  export const COLLECTOR_ACTIVATION_ENTRY_TYPE = "ak-collector-activation";
57
30
  const COLLECTOR_SNAPSHOT_ENTRY_TYPE = "ak-collector-snapshot";
@@ -471,15 +444,6 @@ export function createCollectorLedger(input, options) {
471
444
  },
472
445
  bindTarget(prNumber) {
473
446
  assertNotFatal();
474
- if (outputCandidate || pendingOutputCallId !== undefined) {
475
- throw new Error("通进司已产出输出候选,本局不再受理目标绑定");
476
- }
477
- if (!Number.isSafeInteger(prNumber) || prNumber < 1) {
478
- throw new Error("通进司绑定目标要求正安全整数 PR 号");
479
- }
480
- if (config.prNumber !== undefined && config.prNumber !== prNumber) {
481
- throw new Error(`通进司目标已绑定 PR ${config.prNumber},不可改绑为 ${prNumber}`);
482
- }
483
447
  config.prNumber = prNumber;
484
448
  appendJournal("ak-collector-target-bound", {
485
449
  prNumber,
@@ -488,10 +452,6 @@ export function createCollectorLedger(input, options) {
488
452
  },
489
453
  beginOperational(toolName, toolCallId) {
490
454
  assertNotFatal();
491
- if (toolName !== COLLECTOR_OUTPUT_TOOL &&
492
- (outputCandidate || pendingOutputCallId !== undefined)) {
493
- throw new Error("通进司已产出输出候选,本局不再受理操作");
494
- }
495
455
  if (toolName === COLLECTOR_OUTPUT_TOOL) {
496
456
  pendingOutputCallId = toolCallId;
497
457
  return;
@@ -1,6 +1,7 @@
1
1
  import { Type } from "typebox";
2
2
  import { auditorRunDirectory } from "./auditor-dossier-tool.js";
3
- const AUDITOR_DOSSIER_PROMPT = "\u672C run \u5377\u5B97\u5DF2\u5C31\u7EEA\u3002";
3
+ import { OFFICER_CONCLUSION_REASK } from "./gatekeeper-role.js";
4
+ const AUDITOR_DOSSIER_PROMPT = "\u5377\u5B97\u6307\u9488\uFF1A";
4
5
  const nonblank = Type.String({ minLength: 1, pattern: "\\S" });
5
6
  const decisionGateSchema = Type.Object({ question: nonblank, options: Type.Array(nonblank, { minItems: 1 }) }, { additionalProperties: false });
6
7
  const complianceDecisionSchema = Type.Object({ status: Type.Unknown({ description: "pass | bounce | escalate \u2014 \u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8" }), violations: Type.Array(nonblank, { description: "\u89C2\u5BDF\u5230\u7684\u5408\u89C4\u8FDD\u89C4" }), conflicts: Type.Array(nonblank, { description: "\u672A\u51B3\u6743\u5A01\u6216\u6267\u884C\u51B2\u7A81" }), decisionGate: Type.Union([decisionGateSchema, Type.Null()], { description: "\u5347\u7EA7\u95EE\u9898\u4E0E\u53EF\u9009\u9009\u9879" }) }, { additionalProperties: true, required: [] });
@@ -22,11 +23,12 @@ function tryReadComplianceCandidate(arguments_, usage) {
22
23
  }
23
24
  const args = arguments_;
24
25
  const status = args.status;
25
- if (status === "pass") return { status, ...usage === void 0 ? {} : { usage } };
26
- if (status === "bounce") return { status, violations: readListField(args.violations), ...usage === void 0 ? {} : { usage } };
26
+ if (status === "pass") return { status, receipt: arguments_, ...usage === void 0 ? {} : { usage } };
27
+ if (status === "bounce") return { status, violations: readListField(args.violations), receipt: arguments_, ...usage === void 0 ? {} : { usage } };
27
28
  if (status === "escalate") {
28
29
  return {
29
30
  status,
31
+ receipt: arguments_,
30
32
  ...Object.hasOwn(args, "conflicts") ? { conflicts: args.conflicts } : {},
31
33
  ...Object.hasOwn(args, "decisionGate") ? { decisionGate: args.decisionGate } : {},
32
34
  ...usage === void 0 ? {} : { usage }
@@ -65,14 +67,29 @@ async function projectAuditorTerminal(summoned) {
65
67
  };
66
68
  }
67
69
  if (outcome.kind === "failure") {
68
- throw new Error(outcome.diagnostic);
70
+ const rows = outcome.payloads ?? summoned.terminal?.submissions ?? [];
71
+ return {
72
+ status: "transport_failure",
73
+ diagnostic: outcome.diagnostic,
74
+ ...rows.length > 0 ? { submissions: rows } : {},
75
+ ...summoned.terminal === void 0 ? {} : { terminal: summoned.terminal },
76
+ ...usage === void 0 ? {} : { usage }
77
+ };
69
78
  }
70
79
  if (outcome.kind === "accepted") {
71
- const candidate = {
72
- status: outcome.status,
73
- ...outcome.decisiveFacts
74
- };
75
- return readComplianceCandidate(candidate, usage);
80
+ const rows = outcome.payloads ?? summoned.terminal?.submissions ?? [];
81
+ if (rows.length === 0) {
82
+ return readComplianceCandidate({}, usage);
83
+ }
84
+ const decision = readComplianceCandidate(rows[rows.length - 1], usage);
85
+ if (rows.length === 1) return decision;
86
+ if (decision.status === "pass" || decision.status === "bounce" || decision.status === "escalate") {
87
+ return { ...decision, receipt: rows };
88
+ }
89
+ if (decision.status === "received") {
90
+ return { ...decision, reply: rows };
91
+ }
92
+ return decision;
76
93
  }
77
94
  return {
78
95
  status: "received",
@@ -86,7 +103,7 @@ async function runComplianceAudit(options) {
86
103
  throw new Error("Compliance audit requires a parent run directory pointer");
87
104
  }
88
105
  const subject = options.subject;
89
- const summon = options.summonAuditor ?? (async (auditSubject, sourceRunDirectory, auditSignal) => {
106
+ const summon = options.summonAuditor ?? (async (auditSubject, sourceRunDirectory, auditSignal, reask2) => {
90
107
  const { summonPublicRole } = await import("./public-role-summons.js");
91
108
  const { homeFromRunDirectory } = await import("./activation-ledger-topology.js");
92
109
  const home = homeFromRunDirectory(sourceRunDirectory);
@@ -97,15 +114,24 @@ async function runComplianceAudit(options) {
97
114
  auditSubject,
98
115
  "--source-run",
99
116
  sourceRunDirectory,
100
- AUDITOR_DOSSIER_PROMPT
117
+ `${AUDITOR_DOSSIER_PROMPT}${sourceRunDirectory}`
101
118
  ],
102
119
  cwd: options.context.cwd ?? process.cwd(),
103
120
  home,
104
- ...auditSignal === void 0 ? {} : { signal: auditSignal }
121
+ ...auditSignal === void 0 ? {} : { signal: auditSignal },
122
+ ...reask2 === void 0 ? {} : { reviewReask: reask2 }
105
123
  });
106
124
  });
107
- const summoned = await summon(subject, runDirectory, options.signal);
108
- return await projectAuditorTerminal(summoned);
125
+ let reask;
126
+ for (; ; ) {
127
+ const summoned = await summon(subject, runDirectory, options.signal, reask);
128
+ const decision = await projectAuditorTerminal(summoned);
129
+ if (decision.status === "received") {
130
+ reask = OFFICER_CONCLUSION_REASK;
131
+ continue;
132
+ }
133
+ return decision;
134
+ }
109
135
  }
110
136
  export {
111
137
  AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE,
@@ -25,46 +25,8 @@ export function validateRecordedDiaristOutput(value) {
25
25
  }
26
26
  throw new Error("Diarist output has no execution discriminator");
27
27
  }
28
- /**
29
- * Lenient projection of submitted entries (第 0 条: shape is not an admission
30
- * gate). Rows that cannot form a lawful volume entry are dropped from the
31
- * commit, never bounced back at the role. This is write-seam self-check only —
32
- * not quote/ticket/relevance judgment of LLM content (#779).
33
- */
28
+ /** Entries array as the diarist wrote it — no field rewrite (#836 B6.8/B6.9). */
34
29
  export function projectDiaristEntries(value) {
35
30
  const rows = value?.entries;
36
- if (!Array.isArray(rows))
37
- return [];
38
- const out = [];
39
- for (const row of rows) {
40
- if (row === null || typeof row !== "object" || Array.isArray(row))
41
- continue;
42
- const record = row;
43
- if (typeof record.sourceKind !== "string")
44
- continue;
45
- if (typeof record.transcript !== "string" || record.transcript.length === 0)
46
- continue;
47
- if (typeof record.timestamp !== "string" || record.timestamp.length === 0)
48
- continue;
49
- if (record.sourceRef === null || typeof record.sourceRef !== "object" || Array.isArray(record.sourceRef)) {
50
- continue;
51
- }
52
- const ref = record.sourceRef;
53
- const sourceRef = {
54
- ...(typeof ref.sessionFile === "string" ? { sessionFile: ref.sessionFile } : {}),
55
- ...(typeof ref.entryId === "string" || typeof ref.entryId === "number"
56
- ? { entryId: ref.entryId }
57
- : {}),
58
- ...(typeof ref.path === "string" ? { path: ref.path } : {}),
59
- ...(typeof ref.url === "string" ? { url: ref.url } : {}),
60
- };
61
- out.push({
62
- sourceKind: record.sourceKind,
63
- sourceRef,
64
- transcript: record.transcript,
65
- timestamp: record.timestamp,
66
- ...(typeof record.note === "string" ? { note: record.note } : {}),
67
- });
68
- }
69
- return out;
31
+ return Array.isArray(rows) ? [...rows] : [];
70
32
  }