@akagilnc/pi-workflow-roles 0.1.5575 → 0.1.5583

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.
@@ -15,7 +15,6 @@ import { bindAdmittedTicketNumber, buildInstructionTransportPrompt, freezeAttach
15
15
  import { readRecordedSubmissionRows } from "../submission-ledger.js";
16
16
  import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.js";
17
17
  import { pathContainedIn } from "../activation-ledger-topology.js";
18
- import { pickEngineAxis } from "../package-resources/engine-material.js";
19
18
  import { readStoredHostSessionId } from "../session-identity.js";
20
19
  import { reportRunRecord } from "../sitian-facade.js";
21
20
  import { createReceiptDeliveryPolicy, deliveryLimitFromConfig, } from "../receipt-delivery-policy.js";
@@ -1345,13 +1344,7 @@ export async function runPostAdmissionOneShot(input) {
1345
1344
  ...input.request,
1346
1345
  continuation: {
1347
1346
  kind: "resume",
1348
- prompt: buildAutoResumeContinuationPrompt({
1349
- packageRoot: input.env.packageRoot,
1350
- ...pickEngineAxis({
1351
- engine: input.effectiveEngine ?? input.env.engine,
1352
- engineModel: input.env.engineModel,
1353
- }),
1354
- }),
1347
+ prompt: buildAutoResumeContinuationPrompt(),
1355
1348
  },
1356
1349
  }),
1357
1350
  adapters: input.adapters,
@@ -13,7 +13,6 @@ import { isSafePositiveTicketNumber } from "../run-ticket-number.js";
13
13
  import { CliUsageError } from "./cli-errors.js";
14
14
  import { parseCollectorRepository } from "../collector-config.js";
15
15
  import { retainedRunPathsMatch, rewriteAdmittedRoleRunPage, rewriteRunDirectoryPathValue, } from "../role-run-relocation.js";
16
- import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, pickEngineAxis, } from "../package-resources/engine-material.js";
17
16
  import { packagedResumeSourcePath, packagedRoleMetadata, } from "../packaged-role-registry.js";
18
17
  import { admittedReviewerBaseFault, recordEffectiveInvocationModel, requireAuthorityRef, isReviewerLens, } from "./invocation.js";
19
18
  import { readPageSync, writeSectionSync } from "../run-dossier.js";
@@ -33,14 +32,12 @@ export const AUTO_RESUME_LIMIT = 2;
33
32
  export const RESUME_TRANSPORT_ENVELOPE = "继续。";
34
33
  /**
35
34
  * Auto-resume continuation only (#959 / ADR 0080).
36
- * Always non-empty: Chinese neutral envelope plus optional engine pointers.
35
+ * Always non-empty Chinese neutral envelope. Engine / outsourcing material
36
+ * rides startup readingMaterial (#1167), never this continuation.
37
37
  * Never call this from manual `ak-role resume`.
38
38
  */
39
- export function buildAutoResumeContinuationPrompt(options) {
40
- return appendEngineSessionMaterial([RESUME_TRANSPORT_ENVELOPE], engineSessionMaterialFromOptions({
41
- packageRoot: options.packageRoot,
42
- ...pickEngineAxis(options),
43
- })).join("\n");
39
+ export function buildAutoResumeContinuationPrompt() {
40
+ return RESUME_TRANSPORT_ENVELOPE;
44
41
  }
45
42
  const WRITER_LOCK_FILE = "writer.lock";
46
43
  export async function writeRoleRunState(runDirectory, record) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5575",
3
+ "version": "0.1.5583",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -1,23 +1,27 @@
1
1
  /**
2
- * Packaged engine method-material seam (#356 T1 / ADR 0069 / #376).
2
+ * Packaged engine method-material seam (#356 T1 / ADR 0069 / #376 / #1167).
3
3
  * Engine names are owner pool-directive labels — not a closed material catalog.
4
4
  * Material body is optional data for the LLM, not a code contract.
5
5
  * Only path-safety syntax is checked at real I/O seams.
6
+ * Delivery is startup readingMaterial (handbook bodies), never the transport prompt.
6
7
  */
7
- import { existsSync, readdirSync } from "node:fs";
8
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
8
9
  import { join } from "node:path";
9
10
 
10
11
  const ENGINE_MATERIAL_RELATIVE_ROOT = "resources/engines" as const;
12
+ const ENGINE_DISPATCH_RELATIVE_PATH = "resources/engine-dispatch.md" as const;
11
13
 
12
14
  export type EngineSessionMaterial = Readonly<{
13
15
  name: string;
14
- /** Present only when a packaged notes file exists for this name. */
15
- materialPath?: string;
16
16
  /**
17
17
  * Optional owner pool-directive model id for multi-model engines (#883).
18
18
  * Opaque pass-through coordinate for the seat — not validated against a catalog.
19
19
  */
20
20
  model?: string;
21
+ /** Packaged per-engine handbook body when notes exist. */
22
+ handbook?: string;
23
+ /** Shared engine-dispatch.md body when the per-engine handbook exists. */
24
+ dispatchHandbook?: string;
21
25
  }>;
22
26
 
23
27
  /** Project engine name + optional model for env / request / material spreads. */
@@ -57,6 +61,10 @@ export function resolveEngineMaterialDirectory(packageRoot: string): string {
57
61
  return join(packageRoot, ENGINE_MATERIAL_RELATIVE_ROOT);
58
62
  }
59
63
 
64
+ export function resolveEngineDispatchMaterialPath(packageRoot: string): string {
65
+ return join(packageRoot, ENGINE_DISPATCH_RELATIVE_PATH);
66
+ }
67
+
60
68
  /**
61
69
  * Enumerate packaged engine notes stems (discovery only — not a legal-name gate).
62
70
  * Only `*.md` stems that pass name syntax are listed.
@@ -99,7 +107,7 @@ export function assertLegalEngineName(name: string): string {
99
107
  /**
100
108
  * Resolve optional engine options into session material coordinates.
101
109
  * No engine → undefined (caller keeps default prompt bytes).
102
- * Engine with packaged notes → name + absolute material path.
110
+ * Engine with packaged notes → name + handbook body + dispatch handbook body.
103
111
  * Engine without notes → name only (pass-through; no warning).
104
112
  */
105
113
  export function engineSessionMaterialFromOptions(options: {
@@ -119,41 +127,40 @@ export function engineSessionMaterialFromOptions(options: {
119
127
  : assertLegalEngineModel(options.engineModel);
120
128
  const materialPath = resolveEngineMaterialPath(options.packageRoot, name);
121
129
  const modelField = model === undefined ? {} : { model };
122
- if (existsSync(materialPath)) {
123
- return Object.freeze({ name, materialPath, ...modelField });
130
+ if (!existsSync(materialPath)) {
131
+ return Object.freeze({ name, ...modelField });
124
132
  }
125
- return Object.freeze({ name, ...modelField });
133
+ const handbook = readFileSync(materialPath, "utf8");
134
+ const dispatchPath = resolveEngineDispatchMaterialPath(options.packageRoot);
135
+ const dispatchField = existsSync(dispatchPath)
136
+ ? { dispatchHandbook: readFileSync(dispatchPath, "utf8") }
137
+ : {};
138
+ return Object.freeze({ name, handbook, ...dispatchField, ...modelField });
126
139
  }
127
140
 
128
141
  /**
129
- * Append engine method-material delivery to session initial material lines.
130
- * No engine → identity copy (byte-stable when joined the same way).
131
- * With notes → Chinese neutral handbook header + engine name + absolute material path.
132
- * Name only → engine name coordinate only (no handbook header, no path, no warning).
133
- * Never delivers material body.
142
+ * Typed startup readingMaterial for a resolved engine session material.
143
+ * Undefined engine → undefined (no outsourcing segment).
134
144
  */
135
- export function appendEngineSessionMaterial(
136
- lines: readonly string[],
145
+ export function engineSessionReadingMaterial(
137
146
  engineMaterial?: EngineSessionMaterial,
138
- ): string[] {
139
- if (engineMaterial === undefined) {
140
- return [...lines];
141
- }
142
- const out = [...lines];
143
- out.push("");
144
- if (engineMaterial.materialPath !== undefined) {
145
- out.push("本次配置的劳务引擎及其手册:");
146
- out.push(`- engine: ${engineMaterial.name}`);
147
- if (engineMaterial.model !== undefined) {
148
- out.push(`- engineModel: ${engineMaterial.model}`);
149
- }
150
- out.push(`- ${engineMaterial.materialPath}`);
151
- } else {
152
- // Name-only pass-through: no packaged bytes to claim as handbook.
153
- out.push(`- engine: ${engineMaterial.name}`);
154
- if (engineMaterial.model !== undefined) {
155
- out.push(`- engineModel: ${engineMaterial.model}`);
147
+ ):
148
+ | {
149
+ readonly kind: "engine-session-material";
150
+ readonly name: string;
151
+ readonly model?: string;
152
+ readonly handbook?: string;
153
+ readonly dispatchHandbook?: string;
156
154
  }
157
- }
158
- return out;
155
+ | undefined {
156
+ if (engineMaterial === undefined) return undefined;
157
+ return {
158
+ kind: "engine-session-material" as const,
159
+ name: engineMaterial.name,
160
+ ...(engineMaterial.model === undefined ? {} : { model: engineMaterial.model }),
161
+ ...(engineMaterial.handbook === undefined ? {} : { handbook: engineMaterial.handbook }),
162
+ ...(engineMaterial.dispatchHandbook === undefined
163
+ ? {}
164
+ : { dispatchHandbook: engineMaterial.dispatchHandbook }),
165
+ };
159
166
  }
@@ -30,7 +30,6 @@ import {
30
30
  import { isSafePositiveTicketNumber, readBoardTicketNumber } from "../run-ticket-number.ts";
31
31
  import type { NotarySourceRunLocator } from "../notary-contracts.ts";
32
32
  import { NotarySourceRunError, resolveNotarySourceRunLocator } from "../notary-source-run.ts";
33
- import { engineSessionMaterialFromOptions, pickEngineAxis } from "../package-resources/engine-material.ts";
34
33
  import type { PackagedRole } from "../packaged-role-registry.ts";
35
34
  import {
36
35
  packagedAdmitsCountersign,
@@ -302,13 +301,7 @@ function initialPrompt(
302
301
  if (reask !== undefined && "reaskPrompt" in record && record.reaskPrompt === true) {
303
302
  return reask;
304
303
  }
305
- return buildInstructionTransportPrompt(
306
- admitted,
307
- engineSessionMaterialFromOptions({
308
- ...pickEngineAxis(env),
309
- packageRoot: env.packageRoot,
310
- }),
311
- );
304
+ return buildInstructionTransportPrompt(admitted);
312
305
  }
313
306
 
314
307
  function isBoardTicketSeat(
@@ -439,10 +432,7 @@ async function dispatchAdmitted(
439
432
  admitted,
440
433
  roleTurnOptions(activeEnv, admitted, {
441
434
  kind: "resume",
442
- prompt: buildAutoResumeContinuationPrompt({
443
- packageRoot: activeEnv.packageRoot,
444
- ...pickEngineAxis(activeEnv),
445
- }),
435
+ prompt: buildAutoResumeContinuationPrompt(),
446
436
  }),
447
437
  ),
448
438
  adapters,
@@ -666,13 +656,7 @@ async function runCountersignBody(
666
656
  const turnProjection = roleTurnOptions(env, admitted, {
667
657
  kind: "initial",
668
658
  prompt: (env.reviewReask ?? env.gateReviewInstruction)
669
- ?? buildInstructionTransportPrompt(
670
- admitted,
671
- engineSessionMaterialFromOptions({
672
- ...pickEngineAxis(env),
673
- packageRoot: env.packageRoot,
674
- }),
675
- ),
659
+ ?? buildInstructionTransportPrompt(admitted),
676
660
  });
677
661
  const turnRequest = buildInstructionSeatTurnRequest(admitted, turnProjection);
678
662
  const result = await runPostAdmissionOneShot({
@@ -94,10 +94,6 @@ import {
94
94
  NotarySourceRunError,
95
95
  resolveNotarySourceRunLocator,
96
96
  } from "../notary-source-run.ts";
97
- import {
98
- appendEngineSessionMaterial,
99
- type EngineSessionMaterial,
100
- } from "../package-resources/engine-material.ts";
101
97
  import { CliUsageError } from "./cli-errors.ts";
102
98
  import {
103
99
  REJECTED_PUBLIC_SPELLINGS,
@@ -2074,17 +2070,17 @@ function admittedTransportPromptKind(
2074
2070
  * One initial prompt transport. The registry `transportPrompt` leaf selects
2075
2071
  * a fixed kickoff, a bound baseline, or frozen skill args. Absent means the
2076
2072
  * caller instruction plus frozen attachment paths.
2073
+ * Engine / outsourcing material rides startup readingMaterial (#1167), not here.
2077
2074
  */
2078
2075
  export function buildInstructionTransportPrompt(
2079
2076
  admitted: InstructionTransportSource,
2080
- engineMaterial?: EngineSessionMaterial,
2081
2077
  ): string {
2082
2078
  const kind = admittedTransportPromptKind(admitted);
2083
2079
  if (kind === "fixed-kickoff") {
2084
2080
  if (admitted.sourceRunPath === undefined || admitted.sourceRunPath.trim() === "") {
2085
2081
  throw new Error("fixed-kickoff transport prompt is missing the source run pointer");
2086
2082
  }
2087
- return appendEngineSessionMaterial([admitted.sourceRunPath], engineMaterial).join("\n");
2083
+ return admitted.sourceRunPath;
2088
2084
  }
2089
2085
  if (kind === "baseline") {
2090
2086
  if (admitted.baseRevision === undefined) {
@@ -2094,7 +2090,7 @@ export function buildInstructionTransportPrompt(
2094
2090
  if (!admitted.instructionEmpty) {
2095
2091
  lines.push("", admitted.instruction);
2096
2092
  }
2097
- return appendEngineSessionMaterial(lines, engineMaterial).join("\n");
2093
+ return lines.join("\n");
2098
2094
  }
2099
2095
  if (kind === "skill-args") {
2100
2096
  if (
@@ -2112,7 +2108,7 @@ export function buildInstructionTransportPrompt(
2112
2108
  if (!admitted.instructionEmpty && admitted.instruction.trim() !== "") {
2113
2109
  lines.push("", admitted.instruction);
2114
2110
  }
2115
- return appendEngineSessionMaterial(lines, engineMaterial).join("\n");
2111
+ return lines.join("\n");
2116
2112
  }
2117
2113
  const lines: string[] = [admitted.instructionEmpty ? "" : admitted.instruction];
2118
2114
  if (admitted.attachments.length > 0) {
@@ -2122,7 +2118,7 @@ export function buildInstructionTransportPrompt(
2122
2118
  lines.push(`- ${attachment.frozenPath}`);
2123
2119
  }
2124
2120
  }
2125
- return appendEngineSessionMaterial(lines, engineMaterial).join("\n");
2121
+ return lines.join("\n");
2126
2122
  }
2127
2123
 
2128
2124
  export type AdmitCountersignInvocationOptions = {
@@ -28,7 +28,6 @@ import {
28
28
  import { readRecordedSubmissionRows } from "../submission-ledger.ts";
29
29
  import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.ts";
30
30
  import { pathContainedIn } from "../activation-ledger-topology.ts";
31
- import { pickEngineAxis } from "../package-resources/engine-material.ts";
32
31
  import { readStoredHostSessionId } from "../session-identity.ts";
33
32
  import { reportRunRecord } from "../sitian-facade.ts";
34
33
  import {
@@ -2058,13 +2057,7 @@ export async function runPostAdmissionOneShot<
2058
2057
  ...input.request,
2059
2058
  continuation: {
2060
2059
  kind: "resume",
2061
- prompt: buildAutoResumeContinuationPrompt({
2062
- packageRoot: input.env.packageRoot,
2063
- ...pickEngineAxis({
2064
- engine: input.effectiveEngine ?? input.env.engine,
2065
- engineModel: input.env.engineModel,
2066
- }),
2067
- }),
2060
+ prompt: buildAutoResumeContinuationPrompt(),
2068
2061
  },
2069
2062
  }),
2070
2063
  adapters: input.adapters,
@@ -29,11 +29,6 @@ import {
29
29
  rewriteAdmittedRoleRunPage,
30
30
  rewriteRunDirectoryPathValue,
31
31
  } from "../role-run-relocation.ts";
32
- import {
33
- appendEngineSessionMaterial,
34
- engineSessionMaterialFromOptions,
35
- pickEngineAxis,
36
- } from "../package-resources/engine-material.ts";
37
32
  import type { PublicThinkingLevel } from "./registry.ts";
38
33
  import {
39
34
  packagedResumeSourcePath,
@@ -137,21 +132,12 @@ export type SameTicketSummonsMaterials = {
137
132
 
138
133
  /**
139
134
  * Auto-resume continuation only (#959 / ADR 0080).
140
- * Always non-empty: Chinese neutral envelope plus optional engine pointers.
135
+ * Always non-empty Chinese neutral envelope. Engine / outsourcing material
136
+ * rides startup readingMaterial (#1167), never this continuation.
141
137
  * Never call this from manual `ak-role resume`.
142
138
  */
143
- export function buildAutoResumeContinuationPrompt(options: {
144
- packageRoot: string;
145
- engine?: string;
146
- engineModel?: string;
147
- }): string {
148
- return appendEngineSessionMaterial(
149
- [RESUME_TRANSPORT_ENVELOPE],
150
- engineSessionMaterialFromOptions({
151
- packageRoot: options.packageRoot,
152
- ...pickEngineAxis(options),
153
- }),
154
- ).join("\n");
139
+ export function buildAutoResumeContinuationPrompt(): string {
140
+ return RESUME_TRANSPORT_ENVELOPE;
155
141
  }
156
142
 
157
143
  const WRITER_LOCK_FILE = "writer.lock";
@@ -33,7 +33,10 @@ import {
33
33
  resolveEngineModel,
34
34
  resolveEngineName,
35
35
  } from "./engine-detour.ts";
36
- import { engineSessionMaterialFromOptions } from "./package-resources/engine-material.ts";
36
+ import {
37
+ engineSessionMaterialFromOptions,
38
+ engineSessionReadingMaterial,
39
+ } from "./package-resources/engine-material.ts";
37
40
  import { registerEngineDetourTool } from "./engine-detour-tool.ts";
38
41
  import {
39
42
  createReceiptDeliveryPolicy,
@@ -114,7 +117,15 @@ import {
114
117
  resolveLifecycleInvocationPrincipal,
115
118
  } from "./navigator-invocation-identity.ts";
116
119
  import { NAVIGATOR_POST_ROLE_GRACE_MS, raceNavigatorGrace } from "./public-cli/settlement.ts";
117
- import { PACKAGED_ROLE_REGISTRY, isOfficerReviewSeat, packagedRoleActivationFlags, packagedRoleInputFlag, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
120
+ import {
121
+ PACKAGED_ROLE_REGISTRY,
122
+ packagedRoleActivationFlags,
123
+ packagedRoleInputFlag,
124
+ packagedRoleMetadata,
125
+ packagedRoleOutputTool,
126
+ packagedRolePhaseFlag,
127
+ type PackagedRole,
128
+ } from "./packaged-role-registry.ts";
118
129
  import {
119
130
  createJudgeRoleRuntime,
120
131
  } from "./judge-role.ts";
@@ -1198,12 +1209,9 @@ export function createRoleRuntimeExtension(
1198
1209
  };
1199
1210
  }
1200
1211
  });
1201
- // #879: engine coordinates ride readingMaterial only when station-child
1202
- // officer dialogue replaced the ordinary transport prompt (no double fold).
1212
+ // #1167: engine / outsourcing coordinates ride startup readingMaterial for
1213
+ // every seat (handbook bodies). Transport prompt stays dispatch-only.
1203
1214
  roleHost.on("before_agent_start", () => {
1204
- const role = selectedRole ?? roleHost.getFlag(ROLE_FLAG.name);
1205
- if (typeof role !== "string" || !isOfficerReviewSeat(role)) return;
1206
- if (roleHost.getFlag(STATION_CHILD_FLAG.name) !== true) return;
1207
1215
  const engine = resolveEngineName((name) => roleHost.getFlag(name));
1208
1216
  if (engine === undefined || dependencies.packageRoot === undefined) return;
1209
1217
  const engineModel = resolveEngineModel((name) => roleHost.getFlag(name));
@@ -1212,15 +1220,9 @@ export function createRoleRuntimeExtension(
1212
1220
  ...(engineModel === undefined ? {} : { engineModel }),
1213
1221
  packageRoot: dependencies.packageRoot,
1214
1222
  });
1215
- if (material === undefined) return;
1216
- return {
1217
- readingMaterial: {
1218
- kind: "engine-session-material" as const,
1219
- name: material.name,
1220
- ...(material.model === undefined ? {} : { model: material.model }),
1221
- ...(material.materialPath === undefined ? {} : { materialPath: material.materialPath }),
1222
- },
1223
- };
1223
+ const reading = engineSessionReadingMaterial(material);
1224
+ if (reading === undefined) return;
1225
+ return { readingMaterial: reading };
1224
1226
  });
1225
1227
  roleHost.on("before_agent_start", () => {
1226
1228
  const path = roleHost.getFlag(INSPECTOR_SOURCE_RUN_FLAG.name);