@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
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Extension-side OAuth credential keepalive (#351).
3
+ *
4
+ * Periodically calls the public `ctx.modelRegistry.refresh` facade so expired
5
+ * OAuth tokens (e.g. kimi-coding 900s TTL) are renewed during long sessions.
6
+ * Does not read auth.json, touch pi-ai private APIs, or reuse the 183s idle gate.
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ export const DEFAULT_OAUTH_KEEPALIVE_PROVIDERS = ["kimi-coding"];
12
+ export const OAUTH_KEEPALIVE_INTERVAL_MS = 60_000;
13
+ /** Persistent extension setting filename under PI_CODING_AGENT_DIR / ~/.pi/agent. */
14
+ export const OAUTH_KEEPALIVE_SETTING_FILENAME = "oauth-keepalive.json";
15
+ /** Path to the static provider-list extension setting (default ["kimi-coding"]). */
16
+ export function oauthKeepaliveSettingPath() {
17
+ return join(process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent"), OAUTH_KEEPALIVE_SETTING_FILENAME);
18
+ }
19
+ function isExactRecord(value) {
20
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21
+ }
22
+ /**
23
+ * Read the static keepalive provider list from the extension setting file.
24
+ * Missing file → default ["kimi-coding"]. Malformed content fails closed.
25
+ * Production extension root is the sole caller that feeds this into keepalive.
26
+ */
27
+ export function readOAuthKeepaliveProviders(path = oauthKeepaliveSettingPath()) {
28
+ try {
29
+ const raw = JSON.parse(readFileSync(path, "utf8"));
30
+ if (!isExactRecord(raw) || !Array.isArray(raw.providers)) {
31
+ throw new Error("OAuth keepalive setting is malformed: expected { providers: string[] }");
32
+ }
33
+ const providers = raw.providers.map((id, index) => {
34
+ if (typeof id !== "string" || id.trim() === "") {
35
+ throw new Error(`OAuth keepalive setting providers[${index}] must be a non-empty string`);
36
+ }
37
+ // Accept via trim-non-empty; return the normalized id so refresh can hit real providers.
38
+ return id.trim();
39
+ });
40
+ return Object.freeze(providers);
41
+ }
42
+ catch (error) {
43
+ // Absent optional setting uses the documented default; all other causes propagate.
44
+ if (error instanceof Error &&
45
+ "code" in error &&
46
+ error.code === "ENOENT") {
47
+ return DEFAULT_OAUTH_KEEPALIVE_PROVIDERS;
48
+ }
49
+ throw error;
50
+ }
51
+ }
52
+ const defaultScheduler = {
53
+ every(ms, tick) {
54
+ const timer = setInterval(tick, ms);
55
+ // Allow the process to exit if a caller forgets stop (same discipline as trajectory).
56
+ timer.unref?.();
57
+ return () => clearInterval(timer);
58
+ },
59
+ };
60
+ function errorClassName(error) {
61
+ if (error instanceof Error && error.name.trim() !== "")
62
+ return error.name;
63
+ if (error !== null && typeof error === "object" && "name" in error) {
64
+ const name = error.name;
65
+ if (typeof name === "string" && name.trim() !== "")
66
+ return name;
67
+ }
68
+ return "Error";
69
+ }
70
+ function formatWarning(providerId, error) {
71
+ const cls = errorClassName(error);
72
+ const detail = error instanceof Error
73
+ ? error.message
74
+ : typeof error === "string"
75
+ ? error
76
+ : String(error);
77
+ return `oauth-keepalive: refresh failed for provider ${providerId} (${cls}): ${detail}`;
78
+ }
79
+ function emitWarning(ctx, providerId, error) {
80
+ const text = formatWarning(providerId, error);
81
+ // Exactly one visible surface per failure: prefer ui.notify when present;
82
+ // console.warn only when notify is unavailable (print/json / headless)
83
+ // or when notify itself throws (still one surface; carry the notify cause).
84
+ const notify = ctx.ui?.notify;
85
+ if (typeof notify === "function") {
86
+ try {
87
+ notify(text, "warning");
88
+ }
89
+ catch (notifyError) {
90
+ // Notify threw: fall back once so refresh failure stays visible.
91
+ console.warn(text, notifyError);
92
+ }
93
+ return;
94
+ }
95
+ console.warn(text);
96
+ }
97
+ /**
98
+ * Session-lifecycle owner for periodic OAuth refresh via the public modelRegistry facade.
99
+ * Single-flight: overlapping ticks are skipped. stop() cancels the interval and aborts in-flight work.
100
+ */
101
+ export function createOAuthKeepalive(options = {}) {
102
+ const providers = Object.freeze((options.providers ?? DEFAULT_OAUTH_KEEPALIVE_PROVIDERS).map((id) => id));
103
+ const intervalMs = options.intervalMs ?? OAUTH_KEEPALIVE_INTERVAL_MS;
104
+ const scheduler = options.scheduler ?? defaultScheduler;
105
+ let cancel;
106
+ let abortController;
107
+ /** Bare single-flight gate; survives stop/start until the in-flight call settles. */
108
+ let inFlight = false;
109
+ const stop = () => {
110
+ cancel?.();
111
+ cancel = undefined;
112
+ abortController?.abort();
113
+ abortController = undefined;
114
+ // Do not clear unsettled inFlight — single-flight is continuous across stop/start.
115
+ };
116
+ const runTick = async (ctx) => {
117
+ if (inFlight)
118
+ return;
119
+ const ac = abortController;
120
+ if (ac === undefined || ac.signal.aborted)
121
+ return;
122
+ inFlight = true;
123
+ try {
124
+ const result = await ctx.modelRegistry.refresh({
125
+ providers: [...providers],
126
+ allowNetwork: true,
127
+ signal: ac.signal,
128
+ });
129
+ if (ac.signal.aborted || result.aborted)
130
+ return;
131
+ for (const [providerId, error] of result.errors) {
132
+ emitWarning(ctx, providerId, error);
133
+ }
134
+ }
135
+ catch (error) {
136
+ if (ac.signal.aborted)
137
+ return;
138
+ // Whole-call failure: one warning per configured provider (ticket error surface).
139
+ for (const providerId of providers) {
140
+ emitWarning(ctx, providerId, error);
141
+ }
142
+ }
143
+ finally {
144
+ inFlight = false;
145
+ }
146
+ };
147
+ return {
148
+ start(ctx) {
149
+ // Reload / re-start: tear down any prior interval before hanging a new one.
150
+ stop();
151
+ abortController = new AbortController();
152
+ cancel = scheduler.every(intervalMs, () => {
153
+ void runTick(ctx);
154
+ });
155
+ },
156
+ stop,
157
+ };
158
+ }
@@ -51,16 +51,3 @@ export function validateRecordedAuditorOutput(value) {
51
51
  }
52
52
  return projected;
53
53
  }
54
- export function auditorDecisiveFacts(output) {
55
- const facts = { status: output.status };
56
- if (output.status === "bounce" && output.violations !== undefined) {
57
- facts.violations = output.violations;
58
- }
59
- if (output.status === "escalate") {
60
- if (output.conflicts !== undefined)
61
- facts.conflicts = output.conflicts;
62
- if (output.decisionGate !== undefined)
63
- facts.decisionGate = output.decisionGate;
64
- }
65
- return facts;
66
- }
@@ -43,20 +43,10 @@ function validateRecordedGatekeeperOutput(value) {
43
43
  }
44
44
  return projected;
45
45
  }
46
- function gatekeeperDecisiveFacts(output) {
47
- const facts = { status: output.status };
48
- if (output.status === "dispatch") {
49
- facts.officer = output.officer;
50
- } else if (Array.isArray(output.findings)) {
51
- facts.findingsCount = output.findings.length;
52
- }
53
- return facts;
54
- }
55
46
  export {
56
47
  GATEKEEPER_ACCEPTED_TEXT,
57
48
  GATEKEEPER_OUTPUT_TOOL_NAME,
58
49
  gatekeeperDecisionSchema,
59
- gatekeeperDecisiveFacts,
60
50
  gatekeeperOutputSchema,
61
51
  projectLawfulGatekeeperOutput,
62
52
  validateRecordedGatekeeperOutput
@@ -1,7 +1,6 @@
1
1
  const JUDGE_OUTPUT_TOOL_NAME = "ak_judge_output";
2
2
  const JUDGE_ACCEPTED_TEXT = "\u5927\u7406\u5BFA\u56DE\u6267\u5DF2\u63A5\u53D7";
3
3
  const JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT = "\u5927\u7406\u5BFA\u56DE\u6267\u5DF2\u63A5\u53D7\uFF1B\u5BA1\u8BA1\u65E0\u56DE\u6267";
4
- const JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT = "\u5927\u7406\u5BFA\u56DE\u6267\u5DF2\u63A5\u53D7\uFF1B\u5BA1\u8BA1\u5F62\u72B6\u4E0D\u53EF\u8BFB";
5
4
  function validateAcceptedJudgeDetails(verdict) {
6
5
  if (verdict === null || typeof verdict !== "object" || Array.isArray(verdict)) throw new Error("Judge verdict has no execution discriminator");
7
6
  let judgeStatus;
@@ -20,7 +19,6 @@ function validateAcceptedJudgeDetails(verdict) {
20
19
  }
21
20
  export {
22
21
  JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT,
23
- JUDGE_ACCEPTED_AUDIT_UNREADABLE_TEXT,
24
22
  JUDGE_ACCEPTED_TEXT,
25
23
  JUDGE_OUTPUT_TOOL_NAME,
26
24
  validateAcceptedJudgeDetails
@@ -30,13 +30,9 @@ function validateRecordedNavigatorOutput(value) {
30
30
  }
31
31
  return projected;
32
32
  }
33
- function navigatorDecisiveFacts(output) {
34
- return { status: "advice", candidates: output.candidates };
35
- }
36
33
  export {
37
34
  NAVIGATOR_ACCEPTED_TEXT,
38
35
  NAVIGATOR_OUTPUT_TOOL_NAME,
39
- navigatorDecisiveFacts,
40
36
  navigatorOutputSchema,
41
37
  projectLawfulNavigatorOutput,
42
38
  validateRecordedNavigatorOutput
@@ -11,7 +11,6 @@ import { DOCTOR_ACCEPTED_TEXT, DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmission
11
11
  import { GATEKEEPER_ACCEPTED_TEXT, GATEKEEPER_OUTPUT_TOOL_NAME, validateRecordedGatekeeperOutput } from "./gatekeeper-output.js";
12
12
  import { NAVIGATOR_ACCEPTED_TEXT, NAVIGATOR_OUTPUT_TOOL_NAME, validateRecordedNavigatorOutput } from "./navigator-output.js";
13
13
  import { AUDITOR_ACCEPTED_TEXT, AUDITOR_OUTPUT_TOOL_NAME, validateRecordedAuditorOutput } from "./auditor-output.js";
14
- import { EVIDENCE_CHILD_ACCEPTED_TEXT, EVIDENCE_CHILD_OUTPUT_TOOL_NAME, validateRecordedEvidenceChildOutput } from "./evidence-child-output.js";
15
14
  import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateMergerOutput } from "../merger-contracts.js";
16
15
  import { NOTARY_ACCEPTED_TEXT, NOTARY_OUTPUT_TOOL_NAME, validateRecordedNotaryOutput } from "../notary-contracts.js";
17
16
  import { COUNTERSIGN_ACCEPTED_TEXT, COUNTERSIGN_OUTPUT_TOOL_NAME, validateRecordedCountersignOutput } from "../countersign-contracts.js";
@@ -19,7 +18,7 @@ import { GLEANER_LEFT_ACCEPTED_TEXT, GLEANER_LEFT_OUTPUT_TOOL_NAME, validateReco
19
18
  import { INSPECTOR_ACCEPTED_TEXT, INSPECTOR_OUTPUT_TOOL_NAME, validateRecordedInspectorOutput } from "../inspector-contracts.js";
20
19
  import { DIARIST_ACCEPTED_TEXT, DIARIST_OUTPUT_TOOL_NAME, validateRecordedDiaristOutput } from "../diarist-contracts.js";
21
20
  import { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, validateAcceptedWorkerDetails, } from "./worker-output.js";
22
- export { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateAcceptedCollectorReceipt, validateAcceptedJudgeDetails, projectReviewerIntentToReceipt, validateReviewerIntent, validateRuntimeReviewerReceipt, validateAcceptedWorkerDetails, validateDoctorSubmissionShape, validateRecordedDoctorOutput, validateMergerOutput, validateRecordedNotaryOutput, validateRecordedCountersignOutput, validateRecordedGleanerLeftOutput, validateRecordedInspectorOutput, validateRecordedGatekeeperOutput, validateRecordedNavigatorOutput, validateRecordedAuditorOutput, validateRecordedEvidenceChildOutput, validateRecordedDiaristOutput, };
21
+ export { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateAcceptedCollectorReceipt, validateAcceptedJudgeDetails, projectReviewerIntentToReceipt, validateReviewerIntent, validateRuntimeReviewerReceipt, validateAcceptedWorkerDetails, validateDoctorSubmissionShape, validateRecordedDoctorOutput, validateMergerOutput, validateRecordedNotaryOutput, validateRecordedCountersignOutput, validateRecordedGleanerLeftOutput, validateRecordedInspectorOutput, validateRecordedGatekeeperOutput, validateRecordedNavigatorOutput, validateRecordedAuditorOutput, validateRecordedDiaristOutput, };
23
22
  export const TERMINATING_TOOL_NAMES = [
24
23
  CODER_OUTPUT_TOOL_NAME,
25
24
  FIXER_OUTPUT_TOOL_NAME,
@@ -35,7 +34,6 @@ export const TERMINATING_TOOL_NAMES = [
35
34
  GATEKEEPER_OUTPUT_TOOL_NAME,
36
35
  NAVIGATOR_OUTPUT_TOOL_NAME,
37
36
  AUDITOR_OUTPUT_TOOL_NAME,
38
- EVIDENCE_CHILD_OUTPUT_TOOL_NAME,
39
37
  DIARIST_OUTPUT_TOOL_NAME,
40
38
  ];
41
39
  export function isTerminatingToolName(name) {
@@ -71,8 +69,6 @@ export function acceptedTextFor(toolName) {
71
69
  return NAVIGATOR_ACCEPTED_TEXT;
72
70
  case AUDITOR_OUTPUT_TOOL_NAME:
73
71
  return AUDITOR_ACCEPTED_TEXT;
74
- case EVIDENCE_CHILD_OUTPUT_TOOL_NAME:
75
- return EVIDENCE_CHILD_ACCEPTED_TEXT;
76
72
  case DIARIST_OUTPUT_TOOL_NAME:
77
73
  return DIARIST_ACCEPTED_TEXT;
78
74
  }
@@ -127,20 +123,14 @@ export function validateAcceptedDetails(toolName, details) {
127
123
  [GATEKEEPER_OUTPUT_TOOL_NAME]: ["dispatch", "pass"],
128
124
  [NAVIGATOR_OUTPUT_TOOL_NAME]: ["advice"],
129
125
  [AUDITOR_OUTPUT_TOOL_NAME]: ["pass", "bounce", "escalate"],
130
- [EVIDENCE_CHILD_OUTPUT_TOOL_NAME]: [],
131
126
  [DIARIST_OUTPUT_TOOL_NAME]: ["completed"],
132
127
  };
133
128
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
134
- // Evidence-child: any object is a candidate body — no report-field presence gate (#675 / ADR 0055).
135
- const evidenceChildDiscriminator = toolName === EVIDENCE_CHILD_OUTPUT_TOOL_NAME
136
- && candidate !== null
137
- && typeof candidate === "object"
138
- && !Array.isArray(candidate);
139
129
  const baseDiscriminator = discriminator;
140
130
  const runtimeBindingMissing = (toolName === DOCTOR_OUTPUT_TOOL_NAME && baseDiscriminator === "completed" && !(candidate?.cost !== null && typeof candidate?.cost === "object")) ||
141
131
  (toolName === REVIEWER_OUTPUT_TOOL_NAME && candidate?.version !== 2);
142
132
  if (runtimeBindingMissing ||
143
- (!collectorDiscriminator && !evidenceChildDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator)))) {
133
+ (!collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator)))) {
144
134
  throw new AcceptedDetailsContractError("terminating receipt has no recognized execution discriminator");
145
135
  }
146
136
  try {
@@ -173,8 +163,6 @@ export function validateAcceptedDetails(toolName, details) {
173
163
  return validateRecordedNavigatorOutput(details);
174
164
  case AUDITOR_OUTPUT_TOOL_NAME:
175
165
  return validateRecordedAuditorOutput(details);
176
- case EVIDENCE_CHILD_OUTPUT_TOOL_NAME:
177
- return validateRecordedEvidenceChildOutput(details);
178
166
  case DIARIST_OUTPUT_TOOL_NAME:
179
167
  return validateRecordedDiaristOutput(details);
180
168
  }
@@ -231,8 +219,6 @@ export function acceptedFacts(toolName, details) {
231
219
  case NAVIGATOR_OUTPUT_TOOL_NAME:
232
220
  case AUDITOR_OUTPUT_TOOL_NAME:
233
221
  return { status: details.status };
234
- case EVIDENCE_CHILD_OUTPUT_TOOL_NAME:
235
- return { status: "report" };
236
222
  case DIARIST_OUTPUT_TOOL_NAME: return { status: details.status };
237
223
  case JUDGE_OUTPUT_TOOL_NAME: return { status: details.judgeStatus };
238
224
  case COUNTERSIGN_OUTPUT_TOOL_NAME: return { status: details.countersignStatus };
@@ -11,7 +11,6 @@ import { COUNTERSIGN_OUTPUT_TOOL_NAME } from "./countersign-contracts.js";
11
11
  import { GLEANER_LEFT_OUTPUT_TOOL_NAME } from "./gleaner-left-contracts.js";
12
12
  import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.js";
13
13
  import { AUDITOR_OUTPUT_TOOL_NAME } from "./package-contracts/auditor-output.js";
14
- import { EVIDENCE_CHILD_OUTPUT_TOOL_NAME } from "./package-contracts/evidence-child-output.js";
15
14
  import {
16
15
  DIARIST_OUTPUT_TOOL_NAME,
17
16
  DIARIST_SOURCES_FLAG
@@ -36,12 +35,6 @@ const AUDITOR_PUBLIC_SESSION_MATERIALS = [
36
35
  "souls/audit-law.md",
37
36
  "souls/quality-law.md"
38
37
  ];
39
- const EVIDENCE_CHILD_SESSION_MATERIALS = [
40
- "CLAUDE.md",
41
- "souls/evidence-child.md",
42
- "souls/audit-law.md",
43
- "souls/quality-law.md"
44
- ];
45
38
  const PUBLIC_ROLE_RECORDS = [
46
39
  {
47
40
  role: "judge",
@@ -195,7 +188,7 @@ const PUBLIC_ROLE_RECORDS = [
195
188
  activationStage: "load-and-install",
196
189
  sessionMaterials: ["CLAUDE.md", "souls/navigator.md"]
197
190
  },
198
- // #675: 审刑院 / evidence-child are roles like any other — public ak-role entries.
191
+ // #675: 审刑院 is a role like any other — public ak-role entry. (#744: evidence-child deleted)
199
192
  {
200
193
  role: "auditor",
201
194
  phases: [null],
@@ -205,15 +198,6 @@ const PUBLIC_ROLE_RECORDS = [
205
198
  activationStage: "load-and-install",
206
199
  sessionMaterials: AUDITOR_PUBLIC_SESSION_MATERIALS
207
200
  },
208
- {
209
- role: "evidence-child",
210
- phases: [null],
211
- outputTool: EVIDENCE_CHILD_OUTPUT_TOOL_NAME,
212
- inputFlag: void 0,
213
- phaseFlag: void 0,
214
- activationStage: "load-and-install",
215
- sessionMaterials: EVIDENCE_CHILD_SESSION_MATERIALS
216
- },
217
201
  // #708 / ADR 0075 `diarist-is-role`: 起居郎 is a seat like any other. The
218
202
  // frozen source catalog rides the shared input-flag seam; it is absent for a
219
203
  // true-unbound summons (no ticket → no diary).
@@ -246,7 +230,6 @@ function packagedRoleOutputTool(role) {
246
230
  }
247
231
  export {
248
232
  AUDITOR_PUBLIC_SESSION_MATERIALS,
249
- EVIDENCE_CHILD_SESSION_MATERIALS,
250
233
  INSPECTOR_SESSION_MATERIALS,
251
234
  NOTARY_SESSION_MATERIALS,
252
235
  PACKAGED_ROLE_REGISTRY,
@@ -0,0 +1,211 @@
1
+ import { parseSkillBlock, } from "@earendil-works/pi-coding-agent";
2
+ import { requireGatekeeperPass } from "../gatekeeper-pass-envelope.js";
3
+ import { createOAuthKeepalive } from "../oauth-keepalive.js";
4
+ import { createRoleRuntimeExtension } from "../role-runtime.js";
5
+ import { renderAgentStartMaterials } from "../agent-start-materials.js";
6
+ const piContexts = new WeakMap();
7
+ /** Pi-only entrypoint adapter around the host-neutral shared composition. */
8
+ export function createPiRoleRuntimeExtension(dependencies, options = {}) {
9
+ return (pi) => createRoleRuntimeExtension(dependencies)(createPiRoleHostAdapter(pi, options));
10
+ }
11
+ /** Project Pi's activation context onto the package-owned host contract. */
12
+ function projectPiContext(context, transcriptFromContext) {
13
+ const sessionManager = context.sessionManager;
14
+ const host = {
15
+ cwd: context.cwd,
16
+ mode: context.mode,
17
+ model: context.model === undefined ? undefined : { provider: context.model.provider },
18
+ sessionManager: {
19
+ getLeafEntry: () => context.sessionManager.getLeafEntry(),
20
+ getLeafId: () => context.sessionManager.getLeafId(),
21
+ getEntries: () => context.sessionManager.getEntries(),
22
+ getSessionDir: () => context.sessionManager.getSessionDir(),
23
+ getSessionFile: () => context.sessionManager.getSessionFile(),
24
+ getHeader: () => context.sessionManager.getHeader(),
25
+ setSessionFile: (path) => sessionManager.setSessionFile(path),
26
+ appendCustomEntry: (customType, data) => sessionManager.appendCustomEntry(customType, data),
27
+ },
28
+ ...(context.signal === undefined ? {} : { signal: context.signal }),
29
+ ...(context.ui === undefined ? {} : { ui: { notify: (message, type) => context.ui.notify(message, type) } }),
30
+ ...(transcriptFromContext === undefined ? {} : { transcript: () => transcriptFromContext(context) }),
31
+ abort: () => context.abort(),
32
+ };
33
+ piContexts.set(host, context);
34
+ return host;
35
+ }
36
+ /** Recover the Pi context only at the Pi composition boundary. */
37
+ export function toPiContext(context) {
38
+ const piContext = piContexts.get(context);
39
+ if (piContext === undefined)
40
+ throw new Error("Host context is not backed by the Pi adapter");
41
+ return piContext;
42
+ }
43
+ /** Gatekeeper receives HostContext directly (#518 §1③) without recovering raw ExtensionContext via WeakMap. */
44
+ function requirePiGatekeeperPass(options) {
45
+ return requireGatekeeperPass({
46
+ context: options.context,
47
+ subject: options.subject,
48
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
49
+ hostActions: {
50
+ failInfrastructure(error, _context, toolCallId) {
51
+ options.hostActions.failInfrastructure(error, options.context, toolCallId);
52
+ },
53
+ bindSubmissionNonPass: options.hostActions.bindSubmissionNonPass,
54
+ },
55
+ toolCallId: options.toolCallId,
56
+ });
57
+ }
58
+ function toPiResult(result) {
59
+ return result;
60
+ }
61
+ /** Fold a host before_agent_start return: push readingMaterial into the provider-visible
62
+ * systemPrompt via the shared renderer and strip the typed field before Pi sees it. */
63
+ function foldBeforeAgentStartReturn(result) {
64
+ if (result === undefined || result === null || typeof result !== "object")
65
+ return undefined;
66
+ const record = result;
67
+ const { systemPrompt, readingMaterial } = record;
68
+ const folded = readingMaterial === undefined
69
+ ? systemPrompt
70
+ : renderAgentStartMaterials(systemPrompt ?? "", [readingMaterial]);
71
+ return folded === undefined ? {} : { systemPrompt: folded };
72
+ }
73
+ function toPiToolDefinition(tool, projectContext) {
74
+ return {
75
+ name: tool.name,
76
+ label: tool.label,
77
+ description: tool.description,
78
+ ...(tool.promptSnippet === undefined ? {} : { promptSnippet: tool.promptSnippet }),
79
+ parameters: tool.parameters,
80
+ execute: async (toolCallId, params, signal, update, context) => toPiResult(await tool.execute(toolCallId, params, signal, update === undefined ? undefined : (result) => update(toPiResult(result)), projectContext(context))),
81
+ };
82
+ }
83
+ /** Pi composition boundary. Each consumed capability is adapted explicitly. */
84
+ export function createPiRoleHostAdapter(pi, options = {}) {
85
+ const keepalive = createOAuthKeepalive(options.oauthKeepalive);
86
+ const host = {
87
+ deliverSubmissionRejection(rejection) {
88
+ pi.sendMessage({
89
+ customType: "ak-role-submission-rejection",
90
+ content: "The terminal submission was rejected because it was not the sole tool call in its turn. Correct the call pattern and resubmit.",
91
+ display: true,
92
+ details: rejection,
93
+ }, { triggerTurn: true, deliverAs: "followUp" });
94
+ },
95
+ capabilities: {
96
+ skillExpansion(prompt) {
97
+ const parsed = parseSkillBlock(prompt);
98
+ if (parsed == null)
99
+ return undefined;
100
+ const userMessage = parsed.userMessage ?? "";
101
+ return Object.freeze({
102
+ name: parsed.name,
103
+ location: parsed.location,
104
+ content: parsed.content,
105
+ userMessage,
106
+ });
107
+ },
108
+ },
109
+ registerFlag: (name, definition) => pi.registerFlag(name, definition),
110
+ getFlag: (name) => pi.getFlag(name),
111
+ registerTool: (tool) => pi.registerTool(toPiToolDefinition(tool, (context) => projectPiContext(context, options.transcriptFromContext))),
112
+ getAllTools: () => pi.getAllTools().map(({ name, sourceInfo }) => ({
113
+ name,
114
+ ...(sourceInfo?.path === undefined ? {} : { sourceInfo: { path: sourceInfo.path } }),
115
+ })),
116
+ setActiveTools: (names) => pi.setActiveTools(names),
117
+ getActiveTools: () => pi.getActiveTools(),
118
+ requireGatekeeperPass: requirePiGatekeeperPass,
119
+ on(...registration) {
120
+ const context = (value) => projectPiContext(value, options.transcriptFromContext);
121
+ if (registration[0] === "before_agent_start") {
122
+ const [, handler] = registration;
123
+ pi.on("before_agent_start", (value, ctx) => {
124
+ const result = handler({ prompt: value.prompt, systemPrompt: value.systemPrompt, systemPromptOptions: value.systemPromptOptions }, context(ctx));
125
+ if (result instanceof Promise)
126
+ return result.then(foldBeforeAgentStartReturn);
127
+ return foldBeforeAgentStartReturn(result);
128
+ });
129
+ }
130
+ else if (registration[0] === "input") {
131
+ const [, handler] = registration;
132
+ pi.on("input", (value, ctx) => handler({ text: value.text, ...(value.images === undefined ? {} : { images: value.images }), source: value.source }, context(ctx)));
133
+ }
134
+ else if (registration[0] === "tool_call") {
135
+ const [, handler] = registration;
136
+ pi.on("tool_call", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, input: value.input }, context(ctx)));
137
+ }
138
+ else if (registration[0] === "tool_result") {
139
+ const [, handler] = registration;
140
+ pi.on("tool_result", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, isError: value.isError, content: (value.content ?? []).map((part) => part.type === "text" ? { type: "text", text: part.text } : { type: "image", data: part.data, mimeType: part.mimeType }), details: value.details }, context(ctx)));
141
+ }
142
+ else if (registration[0] === "session_start") {
143
+ const [, handler] = registration;
144
+ pi.on("session_start", (value, ctx) => handler({ reason: value.reason }, context(ctx)));
145
+ }
146
+ else if (registration[0] === "session_shutdown") {
147
+ const [, handler] = registration;
148
+ pi.on("session_shutdown", (_value, ctx) => handler({}, context(ctx)));
149
+ }
150
+ else if (registration[0] === "after_provider_response") {
151
+ const [, handler] = registration;
152
+ pi.on("after_provider_response", (value, ctx) => handler({ status: value.status }, context(ctx)));
153
+ }
154
+ else if (registration[0] === "agent_end") {
155
+ const [, handler] = registration;
156
+ pi.on("agent_end", (value, ctx) => handler({
157
+ messages: value.messages.map((message) => ({
158
+ role: message.role,
159
+ content: "content" in message && Array.isArray(message.content) ? message.content.map((part) => part.type === "text"
160
+ ? { type: "text", text: part.text }
161
+ : part.type === "toolCall"
162
+ ? { type: "toolCall", id: part.id, name: part.name, arguments: part.arguments }
163
+ : { type: part.type }) : [],
164
+ ...("toolName" in message && typeof message.toolName === "string" ? { toolName: message.toolName } : {}),
165
+ ...("isError" in message && typeof message.isError === "boolean" ? { isError: message.isError } : {}),
166
+ ...("stopReason" in message && typeof message.stopReason === "string" ? { stopReason: message.stopReason } : {}),
167
+ })),
168
+ }, context(ctx)));
169
+ }
170
+ else if (registration[0] === "turn_end") {
171
+ const [, handler] = registration;
172
+ pi.on("turn_end", (value, ctx) => handler({
173
+ turnIndex: value.turnIndex,
174
+ calls: value.toolResults.map((result) => ({
175
+ toolCallId: result.toolCallId,
176
+ toolName: result.toolName,
177
+ })),
178
+ }, context(ctx)));
179
+ }
180
+ else if (registration[0] === "agent_settled") {
181
+ const [, handler] = registration;
182
+ pi.on("agent_settled", (_value, ctx) => handler({}, context(ctx)));
183
+ }
184
+ else if (registration[0] === "tool_execution_start") {
185
+ const [, handler] = registration;
186
+ pi.on("tool_execution_start", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId }, context(ctx)));
187
+ }
188
+ else if (registration[0] === "tool_execution_update") {
189
+ const [, handler] = registration;
190
+ pi.on("tool_execution_update", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, partialResult: value.partialResult }, context(ctx)));
191
+ }
192
+ else {
193
+ const [, handler] = registration;
194
+ pi.on("tool_execution_end", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, isError: value.isError }, context(ctx)));
195
+ }
196
+ },
197
+ ...(typeof pi.getCommands === "function" ? { getCommands: () => pi.getCommands().map(({ name }) => ({ name })) } : {}),
198
+ };
199
+ return {
200
+ host,
201
+ appendEntry: (customType, data) => pi.appendEntry(customType, data),
202
+ sendMessage: (message, sendOptions) => pi.sendMessage({
203
+ customType: message.customType,
204
+ content: message.content,
205
+ display: message.display ?? false,
206
+ details: message.details,
207
+ }, sendOptions),
208
+ startKeepalive: (context) => keepalive.start(toPiContext(context)),
209
+ stopKeepalive: () => keepalive.stop(),
210
+ };
211
+ }
@@ -127,8 +127,6 @@ function buildActivationFlagArgs(activation) {
127
127
  return ["--ak-role", "navigator"];
128
128
  case "auditor":
129
129
  return ["--ak-role", "auditor"];
130
- case "evidence-child":
131
- return ["--ak-role", "evidence-child"];
132
130
  case "diarist":
133
131
  return [
134
132
  "--ak-role",
@@ -1,11 +1,11 @@
1
1
  import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.js";
2
2
  import { readRunTicketNumber } from "../run-ticket-number.js";
3
3
  import { CliUsageError } from "./cli-errors.js";
4
- import { admitAuditorInvocation, admitEvidenceChildInvocation, admitGatekeeperInvocation, admitNavigatorInvocation, bindAdmittedTicketNumber, buildInstructionTransportPrompt, persistAdmittedSourceRunPath, } from "./invocation.js";
4
+ import { admitAuditorInvocation, admitGatekeeperInvocation, admitNavigatorInvocation, bindAdmittedTicketNumber, buildInstructionTransportPrompt, persistAdmittedSourceRunPath, } from "./invocation.js";
5
5
  import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissionSeatResume, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
6
6
  import { loadResumableInstructionSeatRun, markRunAdmitted, } from "./run-lifecycle.js";
7
7
  import { applyInstructionTicketProbe, probeInstructionTicket, ticketNumberFromProbe, tryResumeSameTicketSeatRun, } from "./seat-ticket-binding.js";
8
- import { presentStructuralRejection, readEngineDetourInfrastructureFailure, trySettleAuditorTerminalResult, trySettleEvidenceChildTerminalResult, trySettleGatekeeperTerminalResult, trySettleNavigatorTerminalResult, } from "./settlement.js";
8
+ import { presentStructuralRejection, readEngineDetourInfrastructureFailure, trySettleAuditorTerminalResult, trySettleGatekeeperTerminalResult, trySettleNavigatorTerminalResult, } from "./settlement.js";
9
9
  import { projectRoleTurnRequest, } from "./turn-request.js";
10
10
  /** Project an admitted instruction-seat invocation onto the host-neutral turn request. */
11
11
  export function buildInstructionSeatTurnRequest(admitted, options) {
@@ -25,8 +25,6 @@ function instructionSeatAdapters(options) {
25
25
  return trySettleNavigatorTerminalResult(admitted, authority, scope);
26
26
  case "auditor":
27
27
  return trySettleAuditorTerminalResult(admitted, authority, scope);
28
- case "evidence-child":
29
- return trySettleEvidenceChildTerminalResult(admitted, authority, scope);
30
28
  }
31
29
  },
32
30
  // Accepted receipts and failure terminals both present via shared path.
@@ -58,8 +56,6 @@ async function admitInstructionSeat(role, options) {
58
56
  return admitNavigatorInvocation(options);
59
57
  case "auditor":
60
58
  return admitAuditorInvocation(options);
61
- case "evidence-child":
62
- return admitEvidenceChildInvocation(options);
63
59
  }
64
60
  }
65
61
  export async function runPublicInstructionSeatResume(request, env, io) {
@@ -369,9 +369,6 @@ export function parseNavigatorArgv(args) {
369
369
  export function parseAuditorArgv(args) {
370
370
  return parseInstructionArgv(args, "auditor");
371
371
  }
372
- export function parseEvidenceChildArgv(args) {
373
- return parseInstructionArgv(args, "evidence-child");
374
- }
375
372
  export function parseDiaristArgv(args) {
376
373
  return parseInstructionArgv(args, "diarist");
377
374
  }
@@ -621,10 +618,6 @@ export async function admitNavigatorInvocation(options) {
621
618
  export async function admitAuditorInvocation(options) {
622
619
  return admitStandardMaterialInvocation("auditor", options);
623
620
  }
624
- /** Admit a public evidence-child run (#675). */
625
- export async function admitEvidenceChildInvocation(options) {
626
- return admitStandardMaterialInvocation("evidence-child", options);
627
- }
628
621
  /**
629
622
  * Admit a direct Diarist (起居郎) run (#708): freeze attachments, persist the
630
623
  * request, reserve session placement — same instruction-seat face.