@akagilnc/pi-workflow-roles 0.1.3826 → 0.1.3873

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 (52) hide show
  1. package/dist/acp-host/production-host.js +22757 -22676
  2. package/dist/canonical-skill-binding.js +0 -3
  3. package/dist/engine-detour-tool.js +2 -2
  4. package/dist/engine-detour.js +23 -7
  5. package/dist/headless-host/production-host.js +4658 -4593
  6. package/dist/host-descriptions.js +5 -2
  7. package/dist/pi/adapter.js +11 -0
  8. package/dist/pi/role-turn-host.js +44 -93
  9. package/dist/public-cli/invocation.js +4 -5
  10. package/dist/public-cli/load-production-acp-host.js +1 -1
  11. package/dist/public-cli/load-production-external-host.js +10 -0
  12. package/dist/public-cli/load-production-headless-host.js +1 -1
  13. package/dist/public-cli/main.js +145 -140
  14. package/dist/public-role-summons.js +7 -49
  15. package/dist/reviewer-agent.js +1 -0
  16. package/dist/reviewer-child-executor.js +3 -2
  17. package/dist/role-activation-flags.js +50 -0
  18. package/dist/role-runtime.js +12 -10
  19. package/dist/worker-role.js +14 -12
  20. package/package.json +1 -1
  21. package/scripts/build-package.mjs +2 -2
  22. package/src/acp-host/production-host.ts +31 -94
  23. package/src/acp-host/role-turn-host.ts +178 -263
  24. package/src/canonical-skill-binding.ts +4 -4
  25. package/src/diagnostic-tail.ts +18 -0
  26. package/src/engine-detour-tool.ts +5 -4
  27. package/src/engine-detour.ts +44 -9
  28. package/src/external-host-turn-loop.ts +172 -0
  29. package/src/headless-host/production-host.ts +10 -7
  30. package/src/headless-host/role-turn-host.ts +271 -263
  31. package/src/host-contracts.ts +6 -0
  32. package/src/host-descriptions.ts +5 -2
  33. package/src/host-session-record.ts +41 -0
  34. package/src/package-resources/method-skill-binding.ts +0 -3
  35. package/src/pi/adapter.ts +13 -0
  36. package/src/pi/role-turn-host.ts +46 -94
  37. package/src/prepared-role-turn.ts +68 -0
  38. package/src/public-cli/cli.ts +11 -20
  39. package/src/public-cli/invocation.ts +4 -5
  40. package/src/public-cli/load-production-acp-host.ts +2 -1
  41. package/src/public-cli/load-production-external-host.ts +17 -0
  42. package/src/public-cli/load-production-headless-host.ts +2 -1
  43. package/src/public-role-summons.ts +12 -69
  44. package/src/reviewer-agent.ts +12 -1
  45. package/src/reviewer-child-executor.ts +10 -2
  46. package/src/role-activation-flags.ts +53 -0
  47. package/src/{acp-host/role-envelope.ts → role-envelope.ts} +41 -98
  48. package/src/role-runtime-dependencies.ts +91 -0
  49. package/src/role-runtime.ts +21 -11
  50. package/src/{acp-host/session-identity.ts → session-identity.ts} +6 -6
  51. package/src/worker-role.ts +14 -13
  52. /package/src/{acp-host/mcp-relay.mjs → mcp-relay.mjs} +0 -0
@@ -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
+ }
@@ -748,17 +748,14 @@ export function createRoleRuntimeExtension(dependencies) {
748
748
  const role = roleHost.getFlag(ROLE_FLAG.name);
749
749
  if (role !== undefined && !admitted)
750
750
  return { action: "handled" };
751
- // Envelope exclusively owns Reviewer Skill invocation transform + original-request capture.
751
+ // Reviewer: recover original request; Pi argv may already carry native form.
752
752
  if (role === "reviewer"
753
753
  && admitted
754
754
  && activeReviewerParent !== undefined
755
755
  && reviewerOriginalRequest === undefined) {
756
- reviewerOriginalRequest = event.text;
757
- return {
758
- action: "transform",
759
- text: activeReviewerParent.skillBinding.invocation(event.text),
760
- ...(event.images === undefined ? {} : { images: event.images }),
761
- };
756
+ reviewerOriginalRequest =
757
+ roleHost.capabilities?.skillOriginalRequest?.(activeReviewerParent.skillBinding.name, event.text) ?? event.text;
758
+ return { action: "continue" };
762
759
  }
763
760
  return { action: "continue" };
764
761
  });
@@ -1093,7 +1090,12 @@ export function createRoleRuntimeExtension(dependencies) {
1093
1090
  async runDispatch(dispatch, options) {
1094
1091
  if (dependencies.runReviewerDispatch === undefined)
1095
1092
  throw new Error("Reviewer runtime dependencies are not configured");
1096
- 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
+ });
1097
1099
  },
1098
1100
  ...(dependencies.shutdownReviewerAgent === undefined
1099
1101
  ? {}
@@ -1570,8 +1572,8 @@ export function createRoleRuntimeExtension(dependencies) {
1570
1572
  }
1571
1573
  }
1572
1574
  await executeActivationStage(entry.role, activationStage(entry.role, runtime), { clock, writeTrace });
1573
- // #357 T2 / #378 / #380 / #391: any role+engine activation registers the package detour tool once.
1574
- // Gate is env presence only — no per-engine execute branch; no role-module spawn.
1575
+ // #357 T2 / #378 / #380 / #391 / #818: any role+engine activation registers the package detour tool once.
1576
+ // Gate is resolveEngineName (RoleHost flag → env fallback) — no per-engine execute branch; no role-module spawn.
1575
1577
  if (!engineDetourRegistered) {
1576
1578
  engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
1577
1579
  }
@@ -227,6 +227,12 @@ export function createCoderRoleRuntime(pi, dependencies, hostActions) {
227
227
  });
228
228
  return {
229
229
  async activate(ctx) {
230
+ // Each activation owns its own Skill capture state; prior-session flags must
231
+ // not authorize a later apply completed (same RoleHost, sequential activate).
232
+ tddInvocationInjected = false;
233
+ originalRequest = undefined;
234
+ expansionPending = false;
235
+ expansionCaptured = false;
230
236
  soul = (await dependencies.loadSoul()).trim();
231
237
  if (soul.length === 0)
232
238
  throw new Error("Coder soul is empty");
@@ -303,18 +309,14 @@ export function createCoderRoleRuntime(pi, dependencies, hostActions) {
303
309
  }
304
310
  tddInvocationInjected = true;
305
311
  expansionPending = true;
306
- const isNativeTdd = event.text === "/skill:tdd" ||
307
- event.text.startsWith("/skill:tdd ");
308
- if (isNativeTdd) {
309
- originalRequest = event.text.slice("/skill:tdd".length).trim();
310
- return { action: "continue" };
311
- }
312
- originalRequest = event.text.trim();
313
- return {
314
- action: "transform",
315
- text: binding?.invocation(event.text) ?? `/skill:tdd ${event.text}`,
316
- ...(event.images === undefined ? {} : { images: event.images }),
317
- };
312
+ // Original request via host capability when Pi argv already carries native form;
313
+ // non-pi keeps plain original bytes (no consumer trim; #822 r3 / reviewer-aligned).
314
+ // Role never emits or parses `/skill:` (ADR 0082).
315
+ originalRequest = binding === undefined
316
+ ? event.text
317
+ : (pi.capabilities?.skillOriginalRequest?.(binding.name, event.text)
318
+ ?? event.text);
319
+ return { action: "continue" };
318
320
  });
319
321
  pi.on("before_agent_start", (event, ctx) => {
320
322
  if (soul === undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3826",
3
+ "version": "0.1.3873",
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
  }
@@ -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