@akagilnc/pi-workflow-roles 0.1.5583 → 0.1.5593

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.
@@ -7,7 +7,6 @@
7
7
  */
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { writeFile } from "node:fs/promises";
10
- import { isAbsolute, join, resolve } from "node:path";
11
10
 
12
11
  import {
13
12
  buildAutoResumeContinuationPrompt,
@@ -22,12 +21,10 @@ import { projectPublicTurnAxes, type RoleTurnRequestProjectionOptions } from "./
22
21
  import {
23
22
  bindAdmittedTicketNumber,
24
23
  buildInstructionTransportPrompt,
25
- freezeAttachmentsIntoRun,
26
24
  relocateAdmittedRunToTicket,
27
25
  } from "./invocation.ts";
28
26
  import { readRecordedSubmissionRows } from "../submission-ledger.ts";
29
27
  import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.ts";
30
- import { pathContainedIn } from "../activation-ledger-topology.ts";
31
28
  import { readStoredHostSessionId } from "../session-identity.ts";
32
29
  import { reportRunRecord } from "../sitian-facade.ts";
33
30
  import {
@@ -135,10 +132,11 @@ function projectRelocatedTurnIdentity(
135
132
  mutableRequest.principal = admitted.principal;
136
133
  const activation = mutableRequest.activation;
137
134
  if (isRecord(activation)) {
138
- for (const field of ["taskPath", "packetPath", "prerequisitesPath", "inputPath", "requestManifestPath"] as const) {
135
+ for (const field of ["taskPath", "packetPath", "prerequisitesPath", "inputPath"] as const) {
139
136
  const record = activation as Record<string, unknown>;
140
137
  if (field in record) record[field] = rewrite(record[field]);
141
138
  }
139
+ // #1165: activation.requestManifestPath is a caller path — do not rewrite.
142
140
  }
143
141
  if (result.terminal !== undefined) {
144
142
  for (const artifact of result.terminal.artifacts) {
@@ -1476,7 +1474,7 @@ export function resumeTurnRequestProjectionOptions(
1476
1474
  summonsPrepared?: {
1477
1475
  readonly instruction: string;
1478
1476
  readonly instructionEmpty: boolean;
1479
- readonly attachments: readonly { frozenPath: string }[];
1477
+ readonly attachments: readonly { path: string }[];
1480
1478
  },
1481
1479
  ): RoleTurnRequestProjectionOptions {
1482
1480
  // #879: officer dialogue content is caller/peer words only — no「请重读」,
@@ -1487,7 +1485,7 @@ export function resumeTurnRequestProjectionOptions(
1487
1485
  if (request.message !== undefined) {
1488
1486
  if (summonsPrepared !== undefined) {
1489
1487
  // #755: same-ticket review / open-court — caller words.
1490
- // #879 station-child officer: words only (attachments are independent freeze).
1488
+ // #879 station-child officer: words only (paths remain on summons materials).
1491
1489
  prompt = officerDialogue
1492
1490
  ? request.message
1493
1491
  : buildInstructionTransportPrompt({
@@ -1504,8 +1502,10 @@ export function resumeTurnRequestProjectionOptions(
1504
1502
  } else if (summonsPrepared !== undefined) {
1505
1503
  // #879 station-child officer: instruction bytes === peer body/reask (no wrap).
1506
1504
  // Other seats keep #755 instruction + optional attachment path listing.
1505
+ // #1165 J3: officer dialogue is raw instruction bytes; do not gate on
1506
+ // instructionEmpty metadata (resume of older admitted pages).
1507
1507
  prompt = officerDialogue
1508
- ? (summonsPrepared.instructionEmpty ? "" : summonsPrepared.instruction)
1508
+ ? summonsPrepared.instruction
1509
1509
  : buildInstructionTransportPrompt(summonsPrepared);
1510
1510
  } else if (request.summons !== undefined) {
1511
1511
  // #879: same-ticket summons with no instruction (e.g. notary source-run binding
@@ -1634,31 +1634,17 @@ async function runSettledAutoResumeLoop<
1634
1634
  });
1635
1635
  }
1636
1636
 
1637
- function isAlreadyFrozenSummonsAttachment(
1638
- runDirectory: string,
1639
- attachmentPath: string,
1640
- ): boolean {
1641
- const absolute = isAbsolute(attachmentPath)
1642
- ? attachmentPath
1643
- : resolve(attachmentPath);
1644
- return pathContainedIn(join(runDirectory, "attachments"), absolute);
1645
- }
1646
-
1647
1637
  /**
1648
- * Freeze same-ticket summons attachments into the retained run directory (#637).
1649
- * No-op materials (no paths / instruction-only) skip the freeze.
1650
- * Paths already under this run's attachments/ are the accepted freeze identity —
1651
- * reuse them for the same internal re-summons flow.
1652
- * Manual resume never calls this — old attachment semantics stay intact.
1638
+ * Same-ticket summons materials: caller paths as-is (#1165). No copy or freeze.
1653
1639
  */
1654
1640
  export async function prepareSummonsResumeMaterials(
1655
- runDirectory: string,
1641
+ _runDirectory: string,
1656
1642
  summons: SameTicketSummonsMaterials | undefined,
1657
1643
  ): Promise<
1658
1644
  | {
1659
1645
  readonly instruction: string;
1660
1646
  readonly instructionEmpty: boolean;
1661
- readonly attachments: readonly { frozenPath: string }[];
1647
+ readonly attachments: readonly { path: string }[];
1662
1648
  }
1663
1649
  | undefined
1664
1650
  > {
@@ -1668,16 +1654,8 @@ export async function prepareSummonsResumeMaterials(
1668
1654
  }
1669
1655
  const instruction = summons.instruction ?? "";
1670
1656
  const instructionEmpty =
1671
- summons.instructionEmpty ?? instruction.trim() === "";
1672
- let attachments: readonly { frozenPath: string }[] = [];
1673
- if (summons.attachmentPaths !== undefined && summons.attachmentPaths.length > 0) {
1674
- const alreadyFrozen = summons.attachmentPaths.every((path) =>
1675
- isAlreadyFrozenSummonsAttachment(runDirectory, path),
1676
- );
1677
- attachments = alreadyFrozen
1678
- ? summons.attachmentPaths.map((frozenPath) => ({ frozenPath }))
1679
- : await freezeAttachmentsIntoRun(summons.attachmentPaths, runDirectory);
1680
- }
1657
+ summons.instructionEmpty ?? instruction.length === 0;
1658
+ const attachments = (summons.attachmentPaths ?? []).map((path) => ({ path }));
1681
1659
  return { instruction, instructionEmpty, attachments };
1682
1660
  }
1683
1661
 
@@ -1778,28 +1756,6 @@ export async function runPostAdmissionSeatResume<
1778
1756
  }
1779
1757
  }
1780
1758
 
1781
- // Internal re-summons freezes external paths once and records that identity.
1782
- if (request.summons !== undefined) {
1783
- const prepared = await prepareSummonsResumeMaterials(
1784
- admittedForBuild.runDirectory,
1785
- request.summons,
1786
- );
1787
- if (
1788
- prepared !== undefined &&
1789
- (request.summons.attachmentPaths?.length ?? 0) > 0
1790
- ) {
1791
- request = {
1792
- ...request,
1793
- summons: {
1794
- ...request.summons,
1795
- attachmentPaths: prepared.attachments.map(
1796
- (attachment) => attachment.frozenPath,
1797
- ),
1798
- },
1799
- };
1800
- }
1801
- }
1802
-
1803
1759
  // Public explicit resume: attach the stored native id when the caller
1804
1760
  // left it unset (399c3c8c). In-call auto-resume / 催交 / gate retry omit
1805
1761
  // the field; those load inside the host adapter (host-contracts).
@@ -53,6 +53,7 @@ import {
53
53
  type AdmittedRoleInvocation,
54
54
  type CoderPhase,
55
55
  type DerivedMergerEnvelope,
56
+ normalizeAdmittedAttachment,
56
57
  type FrozenAttachment,
57
58
  type InvocationEffectiveModel,
58
59
  type ReviewerLens,
@@ -183,7 +184,7 @@ function parseSameTicketSummonsMaterials(
183
184
  const instructionEmpty =
184
185
  typeof record.instructionEmpty === "boolean" ? record.instructionEmpty : undefined;
185
186
  const attachmentPaths = Array.isArray(record.attachmentPaths)
186
- ? record.attachmentPaths.filter((p): p is string => typeof p === "string" && p.length > 0)
187
+ ? record.attachmentPaths.filter((p): p is string => typeof p === "string")
187
188
  : undefined;
188
189
  const sourceRunPath =
189
190
  typeof record.sourceRunPath === "string" && record.sourceRunPath.trim() !== ""
@@ -1007,7 +1008,6 @@ type LoadedAdmittedRequestFields = {
1007
1008
  readonly repository?: string;
1008
1009
  readonly repositoryDisplay?: string;
1009
1010
  readonly requestManifestPath?: string;
1010
- readonly manifestDigest?: string;
1011
1011
  /** Collector wait-window ms restored on resume (#678). */
1012
1012
  readonly waitWindowMs?: number;
1013
1013
  /** Doctor — admitted single-case identity restored on resume (#633). */
@@ -1096,7 +1096,6 @@ async function loadResumableRunRecord(
1096
1096
  let repository: string | undefined;
1097
1097
  let repositoryDisplay: string | undefined;
1098
1098
  let requestManifestPath: string | undefined;
1099
- let manifestDigest: string | undefined;
1100
1099
  let waitWindowMs: number | undefined;
1101
1100
  let issueNumber: number | undefined;
1102
1101
  let caseRunsPath: string | undefined;
@@ -1122,7 +1121,9 @@ async function loadResumableRunRecord(
1122
1121
  instructionEmpty = record.instructionEmpty;
1123
1122
  }
1124
1123
  if (Array.isArray(record.attachments)) {
1125
- attachments = record.attachments as FrozenAttachment[];
1124
+ attachments = record.attachments
1125
+ .map((item) => normalizeAdmittedAttachment(item))
1126
+ .filter((item): item is FrozenAttachment => item !== undefined);
1126
1127
  }
1127
1128
  if (record.phase === "plan" || record.phase === "apply") {
1128
1129
  phase = record.phase;
@@ -1161,12 +1162,10 @@ async function loadResumableRunRecord(
1161
1162
  if (typeof record.repositoryDisplay === "string" && record.repositoryDisplay.trim() !== "") {
1162
1163
  repositoryDisplay = record.repositoryDisplay;
1163
1164
  }
1164
- if (typeof record.requestManifestPath === "string" && record.requestManifestPath.trim() !== "") {
1165
+ // #1165: opaque caller path on resume — do not trim-filter provided values.
1166
+ if (typeof record.requestManifestPath === "string") {
1165
1167
  requestManifestPath = record.requestManifestPath;
1166
1168
  }
1167
- if (typeof record.manifestDigest === "string" && record.manifestDigest.trim() !== "") {
1168
- manifestDigest = record.manifestDigest;
1169
- }
1170
1169
  if (typeof record.waitWindowMs === "number" && Number.isSafeInteger(record.waitWindowMs) && record.waitWindowMs >= 1) {
1171
1170
  waitWindowMs = record.waitWindowMs;
1172
1171
  }
@@ -1335,7 +1334,6 @@ async function loadResumableRunRecord(
1335
1334
  ...(repository === undefined ? {} : { repository }),
1336
1335
  ...(repositoryDisplay === undefined ? {} : { repositoryDisplay }),
1337
1336
  ...(requestManifestPath === undefined ? {} : { requestManifestPath }),
1338
- ...(manifestDigest === undefined ? {} : { manifestDigest }),
1339
1337
  ...(waitWindowMs === undefined ? {} : { waitWindowMs }),
1340
1338
  ...(issueNumber === undefined ? {} : { issueNumber }),
1341
1339
  ...(caseRunsPath === undefined ? {} : { caseRunsPath }),
@@ -1575,12 +1573,8 @@ function admitResumedRole(loaded: {
1575
1573
  return admitted;
1576
1574
  }
1577
1575
  case "collect-target": {
1578
- const { prNumber, repository, repositoryDisplay, manifestDigest } = fields;
1579
- if (
1580
- repository === undefined ||
1581
- repositoryDisplay === undefined ||
1582
- manifestDigest === undefined
1583
- ) {
1576
+ const { prNumber, repository, repositoryDisplay } = fields;
1577
+ if (repository === undefined || repositoryDisplay === undefined) {
1584
1578
  throw new CliUsageError(
1585
1579
  `role run admitted collector repository identity is missing: ${runId}`,
1586
1580
  );
@@ -1606,7 +1600,6 @@ function admitResumedRole(loaded: {
1606
1600
  ? {}
1607
1601
  : { requestManifestPath: fields.requestManifestPath }),
1608
1602
  ...(fields.waitWindowMs === undefined ? {} : { waitWindowMs: fields.waitWindowMs }),
1609
- manifestDigest,
1610
1603
  };
1611
1604
  return admitted;
1612
1605
  }
@@ -496,7 +496,7 @@ export async function summonPublicRole(
496
496
  runId: resumeRunId,
497
497
  summons: {
498
498
  instruction,
499
- instructionEmpty: instruction.trim() === "",
499
+ instructionEmpty: instruction.length === 0,
500
500
  },
501
501
  },
502
502
  env as never,
@@ -170,6 +170,5 @@ export function ensureRoleRunPlacement(
170
170
  ledgerHome: string,
171
171
  placement: RoleRunPlacement,
172
172
  ): void {
173
- // attachments/ appears only when something is frozen into it.
174
173
  ensureRoleRunDirectory(ledgerHome, placement.sessionDirectory);
175
174
  }
@@ -26,7 +26,7 @@ const ADMITTED_PAGE_FIELDS = [
26
26
  "taskPath",
27
27
  "packetPath",
28
28
  "prerequisitesPath",
29
- "requestManifestPath",
29
+ // #1165: requestManifestPath is a caller path — never rewrite.
30
30
  "mergerInputPath",
31
31
  "sourceRunPath",
32
32
  ] as const;
@@ -202,7 +202,7 @@ function rewriteSourceRunLocator(
202
202
  );
203
203
  }
204
204
 
205
- /** Same-ticket summons materials: typed source-run + frozen attachment pointers. */
205
+ /** Same-ticket summons materials: typed source-run; caller attach paths stay as-is (#1165). */
206
206
  function rewriteSummonsMaterials(
207
207
  value: unknown,
208
208
  rewrites: readonly RunDirectoryPathRewrite[],
@@ -214,12 +214,7 @@ function rewriteSummonsMaterials(
214
214
  rewrites,
215
215
  );
216
216
  rewriteSourceRunLocator(value.sourceRun, rewrites);
217
- // Bare-resume frozen attachment pointers (string[]), not free text / bytes.
218
- if (Array.isArray(value.attachmentPaths)) {
219
- value.attachmentPaths = value.attachmentPaths.map((path) =>
220
- rewriteRunDirectoryPathValueAgainstRewrites(path, rewrites),
221
- );
222
- }
217
+ // #1165: summons.attachmentPaths are caller paths — do not rewrite.
223
218
  }
224
219
 
225
220
  /** Project one admitted page in memory after its containing run moved. */
@@ -229,13 +224,7 @@ export function rewriteAdmittedRoleRunPage(
229
224
  ): void {
230
225
  rewriteRunDirectoryPathFieldsAgainstRewrites(page, ADMITTED_PAGE_FIELDS, rewrites);
231
226
  rewriteSourceRunLocator(page.sourceRun, rewrites);
232
- if (Array.isArray(page.attachments)) {
233
- for (const attachment of page.attachments) {
234
- if (isRecord(attachment)) {
235
- rewriteRunDirectoryPathFieldsAgainstRewrites(attachment, ["frozenPath"], rewrites);
236
- }
237
- }
238
- }
227
+ // #1165: admitted attachments are caller paths outside the run directory — do not rewrite.
239
228
  if (isRecord(page.principal)) {
240
229
  rewriteRunDirectoryPathFieldsAgainstRewrites(
241
230
  page.principal,
@@ -432,12 +421,13 @@ async function rewriteNestedMachinePathPages(
432
421
  }
433
422
 
434
423
  /**
435
- * Rewrite the admitted / invocation / runState fact rows (path fields: attachment
436
- * frozenPath / summons.attachmentPaths pointers only), then nested package-owned
437
- * session seams. Callers may rewrite before or after the filesystem move/copy:
438
- * `pagesDirectory` is where the pages currently live on disk; path strings that
439
- * still name `oldRunDirectory` become `newRunDirectory`. `crossRunRewrites`
440
- * covers officer/parent pointers that landed under a different final placement.
424
+ * Rewrite the admitted / invocation / runState fact rows (typed run placement
425
+ * paths only; caller --attach / --request-manifest paths stay as-is, #1165),
426
+ * then nested package-owned session seams. Callers may rewrite before or after
427
+ * the filesystem move/copy: `pagesDirectory` is where the pages currently live
428
+ * on disk; path strings that still name `oldRunDirectory` become
429
+ * `newRunDirectory`. `crossRunRewrites` covers officer/parent pointers that
430
+ * landed under a different final placement.
441
431
  */
442
432
  export async function rewriteRoleRunDurablePages(input: {
443
433
  readonly pagesDirectory: string;
@@ -462,7 +452,7 @@ export async function rewriteRoleRunDurablePages(input: {
462
452
  rewrites,
463
453
  );
464
454
  }
465
- // Open court summons: source-run locators + frozen attachment path pointers.
455
+ // Open court summons: source-run locators only; caller attach paths stay (#1165).
466
456
  if (isRecord(page.currentCourt)) {
467
457
  rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
468
458
  }