@akagilnc/pi-workflow-roles 0.1.3837 → 0.1.3884

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 (59) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +3 -3
  3. package/dist/acp-host/production-host.js +22736 -22720
  4. package/dist/engine-detour-tool.js +2 -2
  5. package/dist/engine-detour.js +23 -7
  6. package/dist/headless-host/production-host.js +4926 -4926
  7. package/dist/host-descriptions.js +5 -2
  8. package/dist/merger-contracts.js +23 -42
  9. package/dist/merger-git-state.js +69 -42
  10. package/dist/merger-role.js +30 -41
  11. package/dist/pi/role-turn-host.js +20 -92
  12. package/dist/public-cli/invocation.js +12 -39
  13. package/dist/public-cli/load-production-acp-host.js +1 -1
  14. package/dist/public-cli/load-production-external-host.js +10 -0
  15. package/dist/public-cli/load-production-headless-host.js +1 -1
  16. package/dist/public-cli/main.js +314 -433
  17. package/dist/public-cli/option-definitions.js +5 -5
  18. package/dist/public-cli/run-lifecycle.js +0 -2
  19. package/dist/public-role-summons.js +7 -49
  20. package/dist/reviewer-agent.js +1 -0
  21. package/dist/reviewer-child-executor.js +3 -2
  22. package/dist/role-activation-flags.js +50 -0
  23. package/dist/role-runtime.js +9 -16
  24. package/extensions/role-runtime.ts +0 -3
  25. package/package.json +1 -1
  26. package/scripts/build-package.mjs +2 -2
  27. package/souls/merger.md +1 -1
  28. package/src/acp-host/production-host.ts +31 -94
  29. package/src/acp-host/role-turn-host.ts +178 -263
  30. package/src/diagnostic-tail.ts +18 -0
  31. package/src/engine-detour-tool.ts +5 -4
  32. package/src/engine-detour.ts +44 -9
  33. package/src/external-host-turn-loop.ts +172 -0
  34. package/src/headless-host/production-host.ts +10 -7
  35. package/src/headless-host/role-turn-host.ts +271 -263
  36. package/src/host-descriptions.ts +5 -2
  37. package/src/host-session-record.ts +41 -0
  38. package/src/merger-contracts.ts +24 -38
  39. package/src/merger-git-state.ts +67 -37
  40. package/src/merger-role.ts +52 -48
  41. package/src/pi/role-turn-host.ts +18 -93
  42. package/src/prepared-role-turn.ts +68 -0
  43. package/src/public-cli/cli.ts +11 -20
  44. package/src/public-cli/invocation.ts +13 -44
  45. package/src/public-cli/load-production-acp-host.ts +2 -1
  46. package/src/public-cli/load-production-external-host.ts +17 -0
  47. package/src/public-cli/load-production-headless-host.ts +2 -1
  48. package/src/public-cli/merger-run.ts +2 -113
  49. package/src/public-cli/option-definitions.ts +5 -5
  50. package/src/public-cli/run-lifecycle.ts +0 -2
  51. package/src/public-role-summons.ts +12 -69
  52. package/src/reviewer-agent.ts +12 -1
  53. package/src/reviewer-child-executor.ts +10 -2
  54. package/src/role-activation-flags.ts +53 -0
  55. package/src/{acp-host/role-envelope.ts → role-envelope.ts} +28 -84
  56. package/src/role-runtime-dependencies.ts +89 -0
  57. package/src/role-runtime.ts +16 -17
  58. package/src/{acp-host/session-identity.ts → session-identity.ts} +6 -6
  59. /package/src/{acp-host/mcp-relay.mjs → mcp-relay.mjs} +0 -0
@@ -552,7 +552,7 @@ const NOTARY_OPTIONS = [
552
552
  },
553
553
  ];
554
554
  const MERGER_OPTIONS = [
555
- // Merger project face differs: requires an in-progress ordinary merge root.
555
+ // Merger project face: worktree whose Git materials are handed to the role.
556
556
  {
557
557
  id: "project",
558
558
  owner: "merger",
@@ -563,8 +563,8 @@ const MERGER_OPTIONS = [
563
563
  repeatable: false,
564
564
  form: "option",
565
565
  description: {
566
- en: "Project root with one ordinary in-progress merge (defaults to cwd).",
567
- zh: "已有进行中 ordinary merge 的项目根(默认 cwd)。",
566
+ en: "Project root whose Git merge materials are read (defaults to cwd).",
567
+ zh: "读取 Git merge 材料的项目根(默认 cwd)。",
568
568
  },
569
569
  },
570
570
  bindOwner("merger", SHARED_ATTACH_SEMANTICS),
@@ -1003,10 +1003,10 @@ const ROLE_COMMAND_HELP = {
1003
1003
  },
1004
1004
  merger: {
1005
1005
  command: "merger",
1006
- summary: "Resolve one ordinary merge already in conflict.",
1006
+ summary: "Reconcile merge materials in a worktree (escalate when nothing to merge).",
1007
1007
  usage: ["ak-role merger [options] <instruction>"],
1008
1008
  examples: [
1009
- 'ak-role merger --project /path/to/worktree "Reconcile the active merge."',
1009
+ 'ak-role merger --project /path/to/worktree "Reconcile the merge."',
1010
1010
  ],
1011
1011
  },
1012
1012
  inspector: {
@@ -997,7 +997,6 @@ async function loadResumableRunRecord(home, runId, authority) {
997
997
  const d = record.derived;
998
998
  if (typeof d.targetObjectId === "string" &&
999
999
  typeof d.sourceObjectId === "string" &&
1000
- typeof d.automaticMergeTreeId === "string" &&
1001
1000
  Array.isArray(d.expectedConflictPaths) &&
1002
1001
  Array.isArray(d.resolutionScope) &&
1003
1002
  d.expectedConflictPaths.every((p) => typeof p === "string") &&
@@ -1005,7 +1004,6 @@ async function loadResumableRunRecord(home, runId, authority) {
1005
1004
  derived = {
1006
1005
  targetObjectId: d.targetObjectId,
1007
1006
  sourceObjectId: d.sourceObjectId,
1008
- automaticMergeTreeId: d.automaticMergeTreeId,
1009
1007
  expectedConflictPaths: d.expectedConflictPaths,
1010
1008
  resolutionScope: d.resolutionScope,
1011
1009
  };
@@ -100,24 +100,17 @@ async function createSummonEnv(options) {
100
100
  let roleTurnHost = piHost;
101
101
  if (hostName !== "pi") {
102
102
  const { lookupHostFamily } = await import("./host-descriptions.js");
103
- const { loadProductionExternalHostFactory } = await import("./public-cli/load-production-external-host.js");
103
+ const { createLazyProductionExternalHost } = await import("./public-cli/load-production-external-host.js");
104
104
  if (lookupHostFamily(hostName) === void 0) {
105
105
  throw new Error(
106
106
  `public role summons host unregistered: host=${hostName} seat=${options.role}`
107
107
  );
108
108
  }
109
- let hostPromise;
110
- roleTurnHost = {
111
- executeTurn: async (request) => {
112
- hostPromise ??= loadProductionExternalHostFactory(options.packageRoot, hostName).then(
113
- (create) => create({
114
- packageRoot: options.packageRoot,
115
- principalAuthority
116
- })
117
- );
118
- return (await hostPromise).executeTurn(request);
119
- }
120
- };
109
+ roleTurnHost = createLazyProductionExternalHost({
110
+ packageRoot: options.packageRoot,
111
+ hostName,
112
+ principalAuthority
113
+ });
121
114
  }
122
115
  const { loadHostProvidersTable, projectHostFacingProvider } = await import("./public-cli/host-providers.js");
123
116
  const hostFacingSelection = options.seat.selection === void 0 ? void 0 : projectHostFacingProvider(
@@ -151,8 +144,7 @@ async function summonPublicRole(options) {
151
144
  } = await import("./public-cli/config.js");
152
145
  const credentials = options.credentials ?? await loadCredentialProviders(agentDir);
153
146
  const config = await loadPublicCliConfig(home);
154
- const invocation = options.host === void 0 ? void 0 : { host: options.host };
155
- const seat = resolveEffectiveSeat(config, options.role, credentials, invocation);
147
+ const seat = resolveEffectiveSeat(config, options.role, credentials);
156
148
  const env = {
157
149
  ...await createSummonEnv({
158
150
  role: options.role,
@@ -264,38 +256,6 @@ async function summonPublicRole(options) {
264
256
  ...stderr === void 0 || stderr === "" ? {} : { stderr }
265
257
  };
266
258
  }
267
- async function parentInvocationHost(sourceRunDirectory) {
268
- const { readFile } = await import("node:fs/promises");
269
- const path = join(sourceRunDirectory, "invocation.json");
270
- let text;
271
- try {
272
- text = await readFile(path, "utf8");
273
- } catch (error) {
274
- const code = error instanceof Error && "code" in error ? error.code : void 0;
275
- throw new Error(
276
- `parent invocation.json required for gate source run at ${path}: ${code ?? (error instanceof Error ? error.message : String(error))}`,
277
- { cause: error }
278
- );
279
- }
280
- let raw;
281
- try {
282
- raw = JSON.parse(text);
283
- } catch (error) {
284
- throw new Error(
285
- `parent invocation.json unreadable at ${path}: ${error instanceof Error ? error.message : String(error)}`,
286
- { cause: error }
287
- );
288
- }
289
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
290
- throw new Error(`parent invocation.json has non-object shape at ${path}`);
291
- }
292
- const host = raw.host;
293
- if (host === void 0) return void 0;
294
- if (typeof host !== "string" || host.trim() === "") {
295
- throw new Error(`parent invocation.json host must be a non-empty string at ${path}`);
296
- }
297
- return host;
298
- }
299
259
  async function summonGateOfficer(options) {
300
260
  let home = options.home;
301
261
  if (home === void 0) {
@@ -309,7 +269,6 @@ async function summonGateOfficer(options) {
309
269
  submission: options.submission
310
270
  });
311
271
  }
312
- const parentHost = await parentInvocationHost(options.sourceRunDirectory);
313
272
  const common = {
314
273
  cwd: options.cwd,
315
274
  ...home === void 0 ? {} : { home },
@@ -318,7 +277,6 @@ async function summonGateOfficer(options) {
318
277
  ...options.signal === void 0 ? {} : { signal: options.signal },
319
278
  ...options.reask === void 0 ? {} : { reviewReask: options.reask },
320
279
  ...gateReviewInstruction === void 0 ? {} : { gateReviewInstruction },
321
- ...parentHost === void 0 ? {} : { host: parentHost },
322
280
  ...options.roleTurnHost === void 0 ? {} : { roleTurnHost: options.roleTurnHost },
323
281
  ...options.createRunId === void 0 ? {} : { createRunId: options.createRunId }
324
282
  };
@@ -76,6 +76,7 @@ export function createReviewerAgentRunner(dependencies = {}) {
76
76
  ...(dependencies.packageRoot === undefined
77
77
  ? {}
78
78
  : { packageRoot: dependencies.packageRoot }),
79
+ ...(options.getFlag === undefined ? {} : { getFlag: options.getFlag }),
79
80
  });
80
81
  const disposition = await workspaceOwner.dispose(workspace);
81
82
  return [leg.axis, Object.freeze({ status: "successful", report: child.report, usage: child.usage, target: batch.target, prompt: child.prompt, workspaceDisposition: disposition })];
@@ -1,5 +1,5 @@
1
1
  import { createEngineDetourToolDefinition } from "./engine-detour-tool.js";
2
- import { engineNameFromEnv } from "./engine-detour.js";
2
+ import { resolveEngineName } from "./engine-detour.js";
3
3
  import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, } from "./package-resources/engine-material.js";
4
4
  import { readPackageMaterial } from "./session-opening-materials.js";
5
5
  import { hasUpstreamErrorTestimony, isNonSuccessHttpStatus } from "./upstream-error-testimony.js";
@@ -98,7 +98,8 @@ export async function executeReviewerChild(workspace, leg, context, options = {}
98
98
  const selection = await parentSelection(context);
99
99
  const { openPiInProcessSession } = await import("./pi/in-process-session.js");
100
100
  const { createRecordSession } = await import("./archivist-record-entry.js");
101
- const engineName = engineNameFromEnv();
101
+ // One gate with parent registration (#818): request flag, else pi child env.
102
+ const engineName = resolveEngineName(options.getFlag);
102
103
  const engineMaterial = engineName === undefined
103
104
  ? undefined
104
105
  : options.packageRoot === undefined || options.packageRoot.trim() === ""
@@ -0,0 +1,50 @@
1
+ import { packagedRoleInputFlag, packagedRolePhaseFlag } from "./packaged-role-registry.js";
2
+ /** Project closed RoleTurnActivation onto the shared host flag map. */
3
+ export function projectActivationFlags(request) {
4
+ const activation = request.activation;
5
+ const flags = new Map([["ak-role", activation.role]]);
6
+ const inputFlag = packagedRoleInputFlag(activation.role);
7
+ const phaseFlag = packagedRolePhaseFlag(activation.role);
8
+ if ("phase" in activation && phaseFlag !== undefined)
9
+ flags.set(phaseFlag, activation.phase);
10
+ if (inputFlag !== undefined) {
11
+ const path = "taskPath" in activation ? activation.taskPath
12
+ : "packetPath" in activation ? activation.packetPath
13
+ : "casePath" in activation ? activation.casePath
14
+ : "inputPath" in activation ? activation.inputPath
15
+ : "sourceRun" in activation ? activation.sourceRun
16
+ : undefined;
17
+ if (path !== undefined)
18
+ flags.set(inputFlag, path);
19
+ }
20
+ if (activation.role === "fixer" && activation.prerequisitesPath !== undefined) {
21
+ flags.set("ak-fixer-prerequisites", activation.prerequisitesPath);
22
+ }
23
+ if (activation.role === "reviewer") {
24
+ flags.set("ak-review-base", activation.baseRevision);
25
+ flags.set("ak-review-authority-refs", JSON.stringify(activation.authorityRefs));
26
+ if (activation.ticketNumber !== undefined) {
27
+ flags.set("ak-review-ticket-number", String(activation.ticketNumber));
28
+ }
29
+ }
30
+ // countersign ticketNumber stays on activation/admission/invocation only —
31
+ // no private transport flag (inner-gate material path deleted in #632).
32
+ if (activation.role === "notary" && activation.ticketNumber !== undefined) {
33
+ flags.set("ak-notary-ticket-number", String(activation.ticketNumber));
34
+ }
35
+ if (activation.role === "gleaner-left") {
36
+ flags.set("ak-gleaner-left-base", activation.baseRevision);
37
+ }
38
+ if (activation.role === "collector") {
39
+ flags.set("ak-collector-repo", activation.repo);
40
+ // #676 D1: pr optional at admission; omit flag when role binds from materials.
41
+ if (activation.pr !== undefined)
42
+ flags.set("ak-collector-pr", activation.pr);
43
+ if (activation.requestManifestPath !== undefined) {
44
+ flags.set("ak-collector-request-manifest", activation.requestManifestPath);
45
+ }
46
+ if (activation.waitMs !== undefined)
47
+ flags.set("ak-collector-wait-ms", activation.waitMs);
48
+ }
49
+ return flags;
50
+ }
@@ -1090,7 +1090,12 @@ export function createRoleRuntimeExtension(dependencies) {
1090
1090
  async runDispatch(dispatch, options) {
1091
1091
  if (dependencies.runReviewerDispatch === undefined)
1092
1092
  throw new Error("Reviewer runtime dependencies are not configured");
1093
- return dependencies.runReviewerDispatch(dispatch, options);
1093
+ // #818: request-scoped engine flag reaches axis sub-session via same RoleHost
1094
+ // that parent registerEngineDetourTool reads — never ambient process.env.
1095
+ return dependencies.runReviewerDispatch(dispatch, {
1096
+ ...options,
1097
+ getFlag: (name) => roleHost.getFlag(name),
1098
+ });
1094
1099
  },
1095
1100
  ...(dependencies.shutdownReviewerAgent === undefined
1096
1101
  ? {}
@@ -1171,19 +1176,12 @@ export function createRoleRuntimeExtension(dependencies) {
1171
1176
  return dependencies.loadDiaristSoul();
1172
1177
  },
1173
1178
  });
1174
- let sessionMergerGitState = dependencies.mergerGitState;
1175
1179
  const merger = createMergerRoleRuntime(roleHost, {
1176
1180
  async loadSoul() { if (!dependencies.loadMergerSoul)
1177
1181
  throw new Error("Merger runtime dependencies are not configured"); return dependencies.loadMergerSoul(); },
1178
1182
  async loadInput(path) { if (!dependencies.loadMergerInput)
1179
1183
  throw new Error("Merger runtime dependencies are not configured"); return dependencies.loadMergerInput(path); },
1180
- gitState: {
1181
- activeMerge() { if (!sessionMergerGitState)
1182
- throw new Error("Merger runtime dependencies are not configured"); return sessionMergerGitState.activeMerge(); },
1183
- completedMerge(mergeCommitId, automaticMergeTreeId) { if (!sessionMergerGitState)
1184
- throw new Error("Merger runtime dependencies are not configured"); return sessionMergerGitState.completedMerge(mergeCommitId, automaticMergeTreeId); },
1185
- },
1186
- }, hostActions);
1184
+ });
1187
1185
  // #676 E: shared envelope owns collector lifecycle (mode/fork, tool surface,
1188
1186
  // event gates). Role module supplies business activate/tools/materials only.
1189
1187
  let activeCollector;
@@ -1463,11 +1461,6 @@ export function createRoleRuntimeExtension(dependencies) {
1463
1461
  auditor,
1464
1462
  diarist,
1465
1463
  merger: async () => {
1466
- if (dependencies.mergerGitState === undefined) {
1467
- sessionMergerGitState = dependencies.createMergerGitState?.(ctx.cwd);
1468
- }
1469
- if (sessionMergerGitState === undefined)
1470
- throw new Error("Merger runtime dependencies are not configured");
1471
1464
  await merger.activate();
1472
1465
  },
1473
1466
  };
@@ -1567,8 +1560,8 @@ export function createRoleRuntimeExtension(dependencies) {
1567
1560
  }
1568
1561
  }
1569
1562
  await executeActivationStage(entry.role, activationStage(entry.role, runtime), { clock, writeTrace });
1570
- // #357 T2 / #378 / #380 / #391: any role+engine activation registers the package detour tool once.
1571
- // Gate is env presence only — no per-engine execute branch; no role-module spawn.
1563
+ // #357 T2 / #378 / #380 / #391 / #818: any role+engine activation registers the package detour tool once.
1564
+ // Gate is resolveEngineName (RoleHost flag → env fallback) — no per-engine execute branch; no role-module spawn.
1572
1565
  if (!engineDetourRegistered) {
1573
1566
  engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
1574
1567
  }
@@ -32,7 +32,6 @@ import { loadPackagedCanonicalSkillBinding } from "../src/package-resources/meth
32
32
  import { JUDGE_OUTPUT_TOOL_NAME } from "../src/package-contracts/judge-output.ts";
33
33
  import { readOAuthKeepaliveProviders } from "../src/oauth-keepalive.ts";
34
34
  import {
35
- createProductionMergerGitState,
36
35
  formatNavigatorRoleHelp,
37
36
  } from "../src/role-runtime.ts";
38
37
  import { loadAuditorSoulFromSubjectInput } from "../src/auditor-soul.ts";
@@ -160,8 +159,6 @@ export default function roleRuntime(pi: ExtensionAPI): void {
160
159
  },
161
160
  loadMergerSoul: () => loadMainRoleSessionMaterials("merger"),
162
161
  loadMergerInput: async (path) => JSON.parse(await readFile(path, "utf8")),
163
- createMergerGitState: (repositoryRoot) =>
164
- createProductionMergerGitState(repositoryRoot),
165
162
  async loadCanonicalSkillBinding(name) {
166
163
  // Coder TDD (#109) and Reviewer code-review (#111) are package-owned.
167
164
  // Optional Fixer diagnosing-bugs is available via --skill without this binding.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3837",
3
+ "version": "0.1.3884",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -109,7 +109,7 @@ export async function buildAcpProductionHost(
109
109
  });
110
110
  // role-envelope resolves ./mcp-relay.mjs from import.meta.url — keep it beside the bundle.
111
111
  await copyFile(
112
- resolve("src/acp-host/mcp-relay.mjs"),
112
+ resolve("src/mcp-relay.mjs"),
113
113
  join(dirname(outfile), "mcp-relay.mjs"),
114
114
  );
115
115
  }
@@ -136,7 +136,7 @@ export async function buildHeadlessProductionHost(
136
136
  // Shared envelope resolves ./mcp-relay.mjs from import.meta.url of the bundle
137
137
  // (#645 headless reuses the ACP relay for AK tools under --mcp-config).
138
138
  await copyFile(
139
- resolve("src/acp-host/mcp-relay.mjs"),
139
+ resolve("src/mcp-relay.mjs"),
140
140
  join(dirname(outfile), "mcp-relay.mjs"),
141
141
  );
142
142
  }
package/souls/merger.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  你是校书郎。
4
4
 
5
- * 只解决调用方指定、已开始的一次 Git merge 的完整冲突集,保全双方已授权意图并创建该次普通双亲 merge commit;若需新的产品或权力决定则升级,不猜测。
5
+ * 保全双方已授权意图并完成一次普通双亲 merge commit;无进行中合并、无活可干、或需新的产品/权力决定则升级,不猜测。
@@ -6,108 +6,44 @@
6
6
  * factory does not create a run-scoped agent home, does not rewrite HOME, and
7
7
  * does not copy or scrub credentials. Sitian records on the run are the dossier.
8
8
  */
9
- import { readFile } from "node:fs/promises";
10
- import { fileURLToPath } from "node:url";
9
+ import { randomUUID } from "node:crypto";
11
10
 
12
- import { loadCanonicalSkillBinding as loadHomeCanonicalSkillBinding } from "../canonical-skill-binding.ts";
13
- import { createGhCollectorGitHubTransport, createGhIssueSoftFetcher } from "../collector-github.ts";
14
- import { createPiDoctorAuditor } from "../doctor-auditor.ts";
15
- import { loadDoctorCase } from "../doctor-evidence.ts";
16
- import type { DurablePrincipalAuthority, RoleTurnHost } from "../host-contracts.ts";
17
- import { createProductionMergerGitState } from "../merger-git-state.ts";
18
- import { createNativeNavigatorSessionFactory, createNavigatorAttendance } from "../navigator-attendance.ts";
19
- import { loadNavigatorWorkContext } from "../navigator-work-context.ts";
20
- import { loadNotarySourceRunLocator } from "../notary-source-run.ts";
21
- import { loadPackagedCanonicalSkillBinding } from "../package-resources/method-skill-binding.ts";
22
- import { createPerDispatchReviewerAgent } from "../reviewer-agent.ts";
23
- import { formatNavigatorRoleHelp, type RoleRuntimeDependencies } from "../role-runtime.ts";
24
- import { createReviewerPinnedGitReader } from "../reviewer-pinned-git.ts";
25
- import { loadAuditorSoulFromSubjectInput } from "../auditor-soul.ts";
26
- import { loadGatekeeperSessionMaterials, loadMainRoleSessionMaterials } from "../session-opening-materials.ts";
11
+ import type { DurablePrincipalAuthority, RoleTurnHost, RoleTurnRequest } from "../host-contracts.ts";
12
+ import { prepareRoleEnvelope } from "../role-envelope.ts";
13
+ import type { RoleRuntimeDependencies } from "../role-runtime.ts";
14
+ import { createRoleRuntimeDependencies } from "../role-runtime-dependencies.ts";
15
+ import { createSessionIdentityAuthority } from "../session-identity.ts";
27
16
  import { acpStdioArgs, resolveAcpBinary, type AcpHostDescription } from "./description.ts";
28
- import { createComposedAcpRoleTurnHost } from "./role-envelope.ts";
29
- import { connectAcpStdio } from "./role-turn-host.ts";
17
+ import {
18
+ connectAcpStdio,
19
+ createAcpRoleTurnHost,
20
+ type AcpRoleTurnHostConfig,
21
+ } from "./role-turn-host.ts";
30
22
  import { ensureSeatProfileSoul } from "./seat-profile-soul.ts";
31
- import { createAcpSessionIdentityAuthority } from "./session-identity.ts";
32
23
 
33
24
  export type ProductionAcpHostOptions = Readonly<{
34
25
  packageRoot: string;
35
26
  principalAuthority: DurablePrincipalAuthority;
36
27
  description: AcpHostDescription;
28
+ /** Seat-table host key (e.g. grok-build). */
29
+ hostName: string;
37
30
  }>;
38
31
 
39
- const navigatorRoutePlaybookPath = fileURLToPath(
40
- new URL("../../resources/navigator-route-playbook.md", import.meta.url),
41
- );
42
- const collectorHandbookSeedPath = fileURLToPath(
43
- new URL("../../resources/collector-bot-handbook.md", import.meta.url),
44
- );
45
-
46
- /** Host-neutral packaged role runtime deps for the ACP parent-process envelope. */
47
- export function createAcpRoleRuntimeDependencies(packageRoot: string): RoleRuntimeDependencies {
48
- const doctorAuditor = createPiDoctorAuditor();
49
- const reviewerAgent = createPerDispatchReviewerAgent({ packageRoot });
50
- const navigatorSessionFactory = createNativeNavigatorSessionFactory();
51
- return {
52
- loadJudgeSoul: () => loadMainRoleSessionMaterials("judge"),
53
- loadFixerSoul: () => loadMainRoleSessionMaterials("fixer"),
54
- loadFixPacket: (path) => readFile(path, "utf8"),
55
- loadCoderSoul: () => loadMainRoleSessionMaterials("coder"),
56
- loadCoderTask: (path) => readFile(path, "utf8"),
57
- loadReviewerSoul: () => loadMainRoleSessionMaterials("reviewer"),
58
- createReviewerPinnedGitReader: () => createReviewerPinnedGitReader(),
59
- createReviewerIssueFetcher: () => createGhIssueSoftFetcher(),
60
- loadCollectorSoul: () => loadMainRoleSessionMaterials("collector"),
61
- loadCollectorHandbookSeed: () => readFile(collectorHandbookSeedPath, "utf8"),
62
- createCollectorTransport: () => createGhCollectorGitHubTransport(),
63
- loadDoctorSoul: () => loadMainRoleSessionMaterials("doctor"),
64
- loadDoctorCase,
65
- loadInspectorSoul: () => loadMainRoleSessionMaterials("inspector"),
66
- loadGatekeeperSoul: () => loadGatekeeperSessionMaterials("gatekeeper"),
67
- loadNavigatorSoul: () => loadMainRoleSessionMaterials("navigator"),
68
- loadAuditorSoul: () => loadAuditorSoulFromSubjectInput(),
69
- loadNotarySoul: () => loadMainRoleSessionMaterials("notary"),
70
- loadCountersignSoul: () => loadMainRoleSessionMaterials("countersign"),
71
- loadGleanerLeftSoul: () => loadMainRoleSessionMaterials("gleaner-left"),
72
- loadDiaristSoul: () => loadMainRoleSessionMaterials("diarist"),
73
- loadNotarySourceRun: loadNotarySourceRunLocator,
74
- loadMergerSoul: () => loadMainRoleSessionMaterials("merger"),
75
- loadMergerInput: async (path) => JSON.parse(await readFile(path, "utf8")),
76
- createMergerGitState: (repositoryRoot) => createProductionMergerGitState(repositoryRoot),
77
- async loadCanonicalSkillBinding(name) {
78
- if (name === "tdd") {
79
- return loadPackagedCanonicalSkillBinding(packageRoot, "tdd");
80
- }
81
- if (name === "code-review") {
82
- return loadPackagedCanonicalSkillBinding(packageRoot, "code-review");
83
- }
84
- return loadHomeCanonicalSkillBinding(name);
85
- },
86
- // #590: doctor compliance still on disposeCompliance path; judge→auditor is gate queue (#756).
87
- auditDoctorCompliance: (options) => doctorAuditor(options),
88
- runReviewerDispatch: (dispatch, options) => reviewerAgent.run(dispatch, options),
89
- shutdownReviewerAgent: () => reviewerAgent.shutdown(),
90
- loadNavigatorWorkContext: (options) => loadNavigatorWorkContext({
91
- context: options.context,
92
- role: options.role,
93
- ...(options.getFlag === undefined ? {} : { getFlag: options.getFlag }),
94
- }),
95
- createNavigatorAttendance: (options) => createNavigatorAttendance({
96
- context: options.context,
97
- role: options.role,
98
- phase: options.phase,
99
- subjectKey: options.subjectKey,
100
- subject: options.subject,
101
- authority: options.authority,
102
- invocationId: options.invocationId,
103
- loadSoul: () => loadMainRoleSessionMaterials("navigator"),
104
- loadRoutePlaybook: () => readFile(navigatorRoutePlaybookPath, "utf8"),
105
- loadRoleHelp: async (role) => formatNavigatorRoleHelp(role),
106
- createSession: navigatorSessionFactory,
107
- ...(options.contextError === undefined ? {} : { contextError: options.contextError }),
108
- onEvent: options.onEvent,
32
+ function createComposedAcpRoleTurnHost(
33
+ config: Omit<AcpRoleTurnHostConfig, "prepare"> & {
34
+ readonly roleRuntimeDependencies: RoleRuntimeDependencies;
35
+ readonly socketPath?: (request: RoleTurnRequest) => string;
36
+ },
37
+ ) {
38
+ return createAcpRoleTurnHost({
39
+ ...config,
40
+ prepare: (request) => prepareRoleEnvelope({
41
+ request,
42
+ dependencies: config.roleRuntimeDependencies,
43
+ sessionFile: config.sessionIdentity.resolveSessionFile(request.principal),
44
+ socketPath: config.socketPath?.(request) ?? `/tmp/ak-acp-mcp-${randomUUID()}.sock`,
109
45
  }),
110
- };
46
+ });
111
47
  }
112
48
 
113
49
  /**
@@ -116,7 +52,7 @@ export function createAcpRoleRuntimeDependencies(packageRoot: string): RoleRunti
116
52
  * is sitian-only.
117
53
  */
118
54
  export function createProductionAcpRoleTurnHost(options: ProductionAcpHostOptions): RoleTurnHost {
119
- const { packageRoot, principalAuthority, description } = options;
55
+ const { packageRoot, principalAuthority, description, hostName } = options;
120
56
  const env: NodeJS.ProcessEnv = {
121
57
  ...process.env,
122
58
  ...description.childEnv,
@@ -124,10 +60,11 @@ export function createProductionAcpRoleTurnHost(options: ProductionAcpHostOption
124
60
  };
125
61
 
126
62
  return createComposedAcpRoleTurnHost({
127
- sessionIdentity: createAcpSessionIdentityAuthority(principalAuthority, description.sessionBindingFile),
63
+ hostName,
64
+ sessionIdentity: createSessionIdentityAuthority(principalAuthority, description.sessionBindingFile),
128
65
  boundResume: description.boundResume,
129
66
  modelPassing: description.modelPassing,
130
- roleRuntimeDependencies: createAcpRoleRuntimeDependencies(packageRoot),
67
+ roleRuntimeDependencies: createRoleRuntimeDependencies(packageRoot),
131
68
  async connect(request) {
132
69
  const seatProfile = description.seatProfileSoul;
133
70
  const profileName = seatProfile === undefined