@akagilnc/pi-workflow-roles 0.1.3652 → 0.1.3663

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.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 系统随案递送本票起居录的中立指针段(ADR 0081 `automatic-case-material`;
3
+ * 指针输入沿 ADR 0079 `summons-pointer-input`,不把卷宗正文塞进提示词)。
4
+ * 只读已有案卷:不刷新、不生成、不校验内容、不新增拒收或停工条件。
5
+ * 机器文本仅中立标识材料(ADR 0073),用途说明归角色材料所有。
6
+ */
7
+ import { stat } from "node:fs/promises";
8
+ import { resolveTicketProvenanceVolume } from "../ticket-provenance.js";
9
+ /** Section heading of the system-delivered dossier pointer. */
10
+ export const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)";
11
+ /** Honest one-line state of one dossier file: present, absent, or unreadable. */
12
+ async function describeDossierFile(path) {
13
+ try {
14
+ const stats = await stat(path);
15
+ if (!stats.isFile())
16
+ return `不可读(非普通文件):${path}`;
17
+ return path;
18
+ }
19
+ catch (error) {
20
+ const code = error.code;
21
+ if (code === "ENOENT")
22
+ return `尚未生成:${path}`;
23
+ return `不可读(${code ?? "未知错误"}):${path}`;
24
+ }
25
+ }
26
+ /**
27
+ * Pointer section for a bound ticket's existing 起居录, or undefined when the
28
+ * run carries no ticket identity (unbound calls stay legal and get no dossier).
29
+ * A bound ticket whose volume is missing or unreadable is stated as such —
30
+ * the section never claims a dossier that is not there.
31
+ */
32
+ export async function projectCaseDossierPointerSection(input) {
33
+ if (input.ticketNumber === undefined)
34
+ return undefined;
35
+ const volume = resolveTicketProvenanceVolume(input.ticketNumber, input.projectRoot, input.home);
36
+ return [
37
+ CASE_DOSSIER_SECTION_HEADING,
38
+ "",
39
+ `票号:#${input.ticketNumber}`,
40
+ `人读视图:${await describeDossierFile(volume.humanViewFile)}`,
41
+ `记录卷宗:${await describeDossierFile(volume.recordFile)}`,
42
+ ].join("\n");
43
+ }
@@ -1,6 +1,6 @@
1
1
  import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.js";
2
2
  import { CliUsageError } from "./cli-errors.js";
3
- import { applyInstructionTicketProbe, probeInstructionTicket, tryResumeSameTicketSeatRun, } from "./seat-ticket-binding.js";
3
+ import { bindReusedTicketNumber, resolveKnownTicketNumber, tryResumeSameTicketSeatRun, } from "./seat-ticket-binding.js";
4
4
  import { admitInspectorInvocation, buildInspectorTransportPrompt, } from "./invocation.js";
5
5
  import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissionSeatResume, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
6
6
  import { loadResumableInspectorRun, markRunAdmitted, parentRunPathFromGatePointerInstruction, } from "./run-lifecycle.js";
@@ -27,11 +27,14 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
27
27
  throw error;
28
28
  }
29
29
  // #747: same parent (卷宗指针) → resume prior inspector run with this summons' materials.
30
- // Probe captures DiaristTicketResolutionError so admit+beforeDispatch can settle
31
- // controlled failure (bare pre-admit throw skips terminal settlement).
30
+ // #709: ticket identity is reused from records this book already holds — no seat model call.
32
31
  // No bare catch→fresh: lookup/resume failures surface; only true absence mints new.
33
32
  const projectRoot = parsed.project ?? env.cwd;
34
- const ticketProbe = await probeInstructionTicket(parsed.instruction, projectRoot, env);
33
+ const reusedTicketNumber = await resolveKnownTicketNumber({
34
+ instruction: parsed.instruction,
35
+ projectRoot,
36
+ home: env.home,
37
+ });
35
38
  const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction);
36
39
  if (parentRunPath !== undefined) {
37
40
  const summons = {
@@ -99,8 +102,8 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
99
102
  request: turnRequest,
100
103
  adapters: inspectorAdapters({
101
104
  beforeDispatch: async (admittedSeat) => {
102
- // #635/#637: apply pre-admit probe inside controlled-failure boundary.
103
- await applyInstructionTicketProbe(admittedSeat, ticketProbe);
105
+ // #635/#709: bind the reused identity inside the controlled-failure boundary.
106
+ await bindReusedTicketNumber(admittedSeat, reusedTicketNumber);
104
107
  },
105
108
  }),
106
109
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
@@ -4,7 +4,7 @@ import { CliUsageError } from "./cli-errors.js";
4
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
- import { applyInstructionTicketProbe, probeInstructionTicket, ticketNumberFromProbe, tryResumeSameTicketSeatRun, } from "./seat-ticket-binding.js";
7
+ import { bindReusedTicketNumber, resolveKnownTicketNumber, tryResumeSameTicketSeatRun, } from "./seat-ticket-binding.js";
8
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. */
@@ -119,10 +119,10 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
119
119
  let auditorSubject;
120
120
  let auditorSourceRun;
121
121
  let auditorSourceTicket;
122
- let ticketProbe;
122
+ let reusedTicketNumber;
123
123
  // #637 / #747: resume prior seat run with this summons' materials.
124
124
  // Auditor (#747): parent --source-run path is the lookup key.
125
- // Other instruction seats: ticket probe (inspector face). No bare catch→fresh.
125
+ // Other instruction seats: reuse known ticket (#709). No bare catch→fresh.
126
126
  const projectRoot = parsed.project ?? env.cwd;
127
127
  if (role === "auditor") {
128
128
  if (parsed.subject !== "judge" && parsed.subject !== "doctor") {
@@ -151,8 +151,11 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
151
151
  }
152
152
  }
153
153
  else {
154
- // Inspector face: unconditional probe (same seam as inspector-run.ts).
155
- ticketProbe = await probeInstructionTicket(parsed.instruction, projectRoot, env);
154
+ reusedTicketNumber = await resolveKnownTicketNumber({
155
+ instruction: parsed.instruction,
156
+ projectRoot,
157
+ home: env.home,
158
+ });
156
159
  }
157
160
  const summons = {
158
161
  instruction: parsed.instruction,
@@ -176,21 +179,18 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
176
179
  if (resumed !== undefined)
177
180
  return resumed;
178
181
  }
179
- else {
180
- const probedTicketNumber = ticketProbe === undefined ? undefined : ticketNumberFromProbe(ticketProbe);
181
- if (probedTicketNumber !== undefined) {
182
- const resumed = await tryResumeSameTicketSeatRun({
183
- home: env.home,
184
- projectRoot,
185
- role,
186
- ticketNumber: probedTicketNumber,
187
- freshSummons: env.freshSummons,
188
- summons,
189
- resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
190
- });
191
- if (resumed !== undefined)
192
- return resumed;
193
- }
182
+ else if (reusedTicketNumber !== undefined) {
183
+ const resumed = await tryResumeSameTicketSeatRun({
184
+ home: env.home,
185
+ projectRoot,
186
+ role,
187
+ ticketNumber: reusedTicketNumber,
188
+ freshSummons: env.freshSummons,
189
+ summons,
190
+ resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
191
+ });
192
+ if (resumed !== undefined)
193
+ return resumed;
194
194
  }
195
195
  let admitted;
196
196
  try {
@@ -248,14 +248,14 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
248
248
  request: turnRequest,
249
249
  adapters: instructionSeatAdapters({
250
250
  beforeDispatch: async (admittedSeat) => {
251
- // #635/#637: ticket bind inside controlled-failure boundary.
251
+ // #635/#709: ticket bind inside controlled-failure boundary.
252
252
  // Auditor inherits source-run ticket (notary face).
253
- // Other seats: same applyInstructionTicketProbe as inspector-run.
253
+ // Other seats: reuse known ticket identity.
254
254
  if (auditorSourceTicket !== undefined) {
255
255
  await bindAdmittedTicketNumber(admittedSeat, auditorSourceTicket);
256
256
  }
257
- else if (ticketProbe !== undefined) {
258
- await applyInstructionTicketProbe(admittedSeat, ticketProbe);
257
+ else {
258
+ await bindReusedTicketNumber(admittedSeat, reusedTicketNumber);
259
259
  }
260
260
  },
261
261
  }),
@@ -146,16 +146,13 @@ export async function persistAdmittedSourceRunPath(admitted, sourceRunPath) {
146
146
  /**
147
147
  * Bind a post-admission resolved ticketNumber onto the in-memory admitted
148
148
  * object and both durable pages (invocation.json + admitted-request.json).
149
- * Used by seat LLM ticket binding when admission was unbound
150
- * (#635 / #582 diarist-resolves-ticket-llm-layer). Never clears an existing binding.
149
+ * Used by known-ticket reuse when admission was unbound (#635 / #709).
150
+ * Never clears an existing binding.
151
151
  */
152
152
  export async function bindAdmittedTicketNumber(admitted, ticketNumber) {
153
153
  if (!Number.isSafeInteger(ticketNumber) || ticketNumber < 1) {
154
154
  throw new Error(`bindAdmittedTicketNumber requires a safe positive integer, got ${String(ticketNumber)}`);
155
155
  }
156
- if (admitted.ticketResolution === "true-unbound") {
157
- throw new Error("bindAdmittedTicketNumber refuses to replace durable true-unbound ticket resolution");
158
- }
159
156
  if (admitted.ticketNumber !== undefined) {
160
157
  if (admitted.ticketNumber === ticketNumber)
161
158
  return;
@@ -167,25 +164,6 @@ export async function bindAdmittedTicketNumber(admitted, ticketNumber) {
167
164
  const current = JSON.parse(await readFile(admittedPath, "utf8"));
168
165
  await writeFile(admittedPath, `${JSON.stringify({ ...current, ticketNumber }, null, 2)}\n`, "utf8");
169
166
  }
170
- /**
171
- * Persist seat LLM true-unbound conclusion onto in-memory admitted + both durable
172
- * pages so later auto-resume attempts and manual resume never re-enter hermes (#635).
173
- */
174
- export async function recordTrueUnboundTicketResolution(admitted) {
175
- if (admitted.ticketNumber !== undefined) {
176
- throw new Error(`recordTrueUnboundTicketResolution refuses to overwrite ticket #${admitted.ticketNumber}`);
177
- }
178
- if (admitted.ticketResolution === "true-unbound")
179
- return;
180
- admitted.ticketResolution =
181
- "true-unbound";
182
- await mergeInvocationIdentityPage(admitted.runDirectory, {
183
- ticketResolution: "true-unbound",
184
- });
185
- const admittedPath = admitted.admittedRequestPath;
186
- const current = JSON.parse(await readFile(admittedPath, "utf8"));
187
- await writeFile(admittedPath, `${JSON.stringify({ ...current, ticketResolution: "true-unbound" }, null, 2)}\n`, "utf8");
188
- }
189
167
  /** Add the identity returned by the production Pi launch seam to its existing ledger page. */
190
168
  export async function recordLaunchedPiIdentity(runDirectory, identity) {
191
169
  await mergeInvocationIdentityPage(runDirectory, {