@akagilnc/pi-workflow-roles 0.1.4021 → 0.1.4035

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.
@@ -1,30 +1,12 @@
1
1
  import { sitianReport } from "./sitian-facade.js";
2
2
  import {
3
3
  NavigatorUnavailableError,
4
- navigatorProviderFailureFromDiagnostics,
4
+ navigatorProviderFailureFromPublicTerminal,
5
5
  navigatorProviderFailureFromError,
6
- navigatorProviderFailureFromStatus,
7
6
  navigatorUnavailableError,
8
7
  parseNavigatorModelSetting,
9
8
  resolveNavigatorSeatSelection
10
9
  } from "./navigator-session-contracts.js";
11
- import { lastRolePayloadRecord } from "./public-cli/terminal.js";
12
- function providerFailureFromPublicTerminal(outcome) {
13
- const facts = outcome.decisiveFacts;
14
- const secondary = typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null ? facts.secondaryEvidence : void 0;
15
- const httpStatus = typeof secondary?.httpStatus === "number" ? secondary.httpStatus : typeof facts.httpStatus === "number" ? facts.httpStatus : typeof facts.errorCode === "number" ? facts.errorCode : void 0;
16
- const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
17
- if (fromStatus !== void 0) return fromStatus;
18
- const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
19
- const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
20
- if (fromDiagnostics !== void 0) return fromDiagnostics;
21
- const fromCode = navigatorProviderFailureFromError({
22
- code: secondary?.code ?? facts.errorCode
23
- });
24
- if (fromCode !== void 0) return fromCode;
25
- if (outcome.cause === "provider") return { source: "transport", cause: "transport" };
26
- return { source: "session", cause: "session" };
27
- }
28
10
  function createNativeNavigatorSessionFactory() {
29
11
  return async ({ context, subject, tool }) => {
30
12
  const resolved = await resolveNavigatorSeatSelection(context);
@@ -80,14 +62,15 @@ function createNativeNavigatorSessionFactory() {
80
62
  const outcome = summoned.terminal?.roleOutcome;
81
63
  if (outcome === void 0) {
82
64
  const detail = summoned.stderr?.trim() || `exit ${summoned.exitCode}`;
83
- providerFailure = { source: "transport", cause: "transport" };
65
+ providerFailure = { source: "transport", cause: "unknown" };
84
66
  throw navigatorUnavailableError(
85
- "transport",
86
- new Error(`Navigator public summon produced no terminal (${detail})`)
67
+ providerFailure.source,
68
+ new Error(`Navigator public summon produced no terminal (${detail})`),
69
+ providerFailure.cause
87
70
  );
88
71
  }
89
72
  if (outcome.kind === "failure") {
90
- providerFailure = providerFailureFromPublicTerminal(outcome);
73
+ providerFailure = navigatorProviderFailureFromPublicTerminal(outcome);
91
74
  throw navigatorUnavailableError(
92
75
  providerFailure.source,
93
76
  new Error(outcome.diagnostic),
@@ -101,21 +84,22 @@ function createNativeNavigatorSessionFactory() {
101
84
  if (outcome.kind !== "accepted") {
102
85
  return;
103
86
  }
104
- const candidates = lastRolePayloadRecord(outcome.payloads ?? [])?.candidates;
105
- if (!Array.isArray(candidates)) {
106
- return;
87
+ for (const payload of outcome.payloads ?? []) {
88
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) continue;
89
+ const candidates = payload.candidates;
90
+ if (!Array.isArray(candidates)) continue;
91
+ await tool.execute(
92
+ "navigator-public-prepare",
93
+ { candidates },
94
+ void 0,
95
+ void 0,
96
+ context
97
+ );
107
98
  }
108
- await tool.execute(
109
- "navigator-public-prepare",
110
- { candidates },
111
- void 0,
112
- void 0,
113
- context
114
- );
115
99
  } catch (error) {
116
100
  if (error instanceof NavigatorUnavailableError) throw error;
117
101
  const fact = navigatorProviderFailureFromError(error);
118
- providerFailure = fact ?? { source: "transport", cause: "transport" };
102
+ providerFailure = fact ?? { source: "transport", cause: "unknown" };
119
103
  throw navigatorUnavailableError(providerFailure.source, error, providerFailure.cause);
120
104
  }
121
105
  })();
@@ -92,6 +92,24 @@ function navigatorProviderFailureFromDiagnostics(diagnostics) {
92
92
  }
93
93
  return void 0;
94
94
  }
95
+ function navigatorProviderFailureFromPublicTerminal(outcome) {
96
+ const facts = outcome.decisiveFacts;
97
+ const secondary = typeof facts.secondaryEvidence === "object" && facts.secondaryEvidence !== null ? facts.secondaryEvidence : void 0;
98
+ const httpStatus = typeof secondary?.httpStatus === "number" ? secondary.httpStatus : typeof facts.httpStatus === "number" ? facts.httpStatus : typeof facts.errorCode === "number" ? facts.errorCode : void 0;
99
+ const fromStatus = navigatorProviderFailureFromStatus(httpStatus);
100
+ if (fromStatus !== void 0) return fromStatus;
101
+ const diagnostics = secondary?.diagnostics ?? facts.diagnostics;
102
+ const fromDiagnostics = navigatorProviderFailureFromDiagnostics(diagnostics);
103
+ if (fromDiagnostics !== void 0) return fromDiagnostics;
104
+ const fromCode = navigatorProviderFailureFromError({
105
+ code: secondary?.code ?? facts.errorCode
106
+ });
107
+ if (fromCode !== void 0) return fromCode;
108
+ if (outcome.cause === "provider") return { source: "transport", cause: "unknown" };
109
+ const typed = navigatorUnavailableKey(outcome.cause);
110
+ if (typed !== void 0) return { source: typed, cause: typed };
111
+ return { source: "unknown", cause: "unknown" };
112
+ }
95
113
  const navigatorProviderFailureSchema = Type.Object({
96
114
  source: Type.Union([
97
115
  Type.Literal("context"),
@@ -208,6 +226,7 @@ export {
208
226
  navigatorProviderFailure,
209
227
  navigatorProviderFailureFromDiagnostics,
210
228
  navigatorProviderFailureFromError,
229
+ navigatorProviderFailureFromPublicTerminal,
211
230
  navigatorProviderFailureFromStatus,
212
231
  navigatorUnavailableError,
213
232
  parseNavigatorModelSetting,
@@ -35,9 +35,8 @@ function sessionStopDetails(input) {
35
35
  /**
36
36
  * Project a native session assistant stop onto the existing knownFailure chain.
37
37
  * Classification follows two-way testimony: typed HTTP status or SDK structure
38
- * keeps provider; stopReason, configured provider/model, or errorMessage prose
39
- * alone is the existing unrecognized value. Present upstream payload is preserved
40
- * in details without rewriting; missing fields are omitted.
38
+ * keeps provider; stopReason / errorMessage prose alone never invents a class (#881).
39
+ * Present upstream payload is preserved in details without rewriting; missing fields are omitted.
41
40
  */
42
41
  export function knownFailureFromProviderStop(input) {
43
42
  if (input.stopReason !== "error" && input.stopReason !== "aborted")
@@ -45,7 +44,7 @@ export function knownFailureFromProviderStop(input) {
45
44
  const diagnostic = nonEmptyString(input.errorMessage);
46
45
  const details = sessionStopDetails(input);
47
46
  return {
48
- cause: hasUpstreamErrorTestimony(input) ? "provider" : "unrecognized",
47
+ ...(hasUpstreamErrorTestimony(input) ? { cause: "provider" } : {}),
49
48
  ...(diagnostic === undefined ? {} : { diagnostic }),
50
49
  ...(Object.keys(details).length === 0 ? {} : { details }),
51
50
  };
@@ -18,7 +18,7 @@ import { join } from "node:path";
18
18
  import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunResumable, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
19
19
  import { parseAutoResumeLimit } from "./config.js";
20
20
  import { isLawfulTypedTerminalOutcome, formatTerminalResult } from "./terminal.js";
21
- import { presentFailureTerminal, presentStructuralRejection, resolveControlledFailureResumeObservation, } from "./settlement.js";
21
+ import { attachRecordedSubmissions, presentFailureTerminal, presentStructuralRejection, resolveControlledFailureResumeObservation, } from "./settlement.js";
22
22
  const dummyIo = { stdout: () => { }, stderr: () => { } };
23
23
  /**
24
24
  * Persist run-state after a host-turn result, outside the retried dispatch try.
@@ -280,6 +280,20 @@ function unwrapTurnDispatchedFailure(error) {
280
280
  }
281
281
  return current;
282
282
  }
283
+ async function attachDispatchExceptionTerminal(admitted, terminal, io) {
284
+ try {
285
+ return await attachRecordedSubmissions({
286
+ projectRoot: admitted.projectRoot,
287
+ runId: admitted.runId,
288
+ runDirectory: admitted.runDirectory,
289
+ }, terminal);
290
+ }
291
+ catch (error) {
292
+ // Existing diagnostic seam: attach true cause on stderr, original Terminal stays.
293
+ io.stderr(`dispatch exception ledger attach failed (best-effort continue): ${describeErrorIdentity(error)}\n`);
294
+ return terminal;
295
+ }
296
+ }
283
297
  /**
284
298
  * Typed failure terminal for a retry path that ended with only exceptions:
285
299
  * loud, non-lawful, carrying the last true cause and the pointers to the
@@ -293,8 +307,8 @@ function dispatchExceptionFailureTerminal(input) {
293
307
  ? "dispatch threw an exception on every attempt"
294
308
  : "the final dispatch threw an exception";
295
309
  const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(causeError)}`;
310
+ // #881: no fabricated cause class — original error identity + error-file pointers carry the fact.
296
311
  const decisiveFacts = {
297
- cause: "unrecognized",
298
312
  diagnostic,
299
313
  resumesUsed: input.autoResumeAttempts,
300
314
  dispatchErrorFiles: [...input.errorFiles],
@@ -316,7 +330,6 @@ function dispatchExceptionFailureTerminal(input) {
316
330
  roleOutcome: {
317
331
  kind: "failure",
318
332
  role: input.role,
319
- cause: "unrecognized",
320
333
  diagnostic,
321
334
  decisiveFacts,
322
335
  },
@@ -431,7 +444,7 @@ export async function runWithAutoResumeLoop(options) {
431
444
  else {
432
445
  // Exception path: continue through the identical budget/session gates.
433
446
  if (autoResumeAttempts >= limit) {
434
- const terminal = dispatchExceptionFailureTerminal({
447
+ const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
435
448
  role: options.admitted.role,
436
449
  runId: options.admitted.runId,
437
450
  causeError: lastThrownError,
@@ -439,7 +452,7 @@ export async function runWithAutoResumeLoop(options) {
439
452
  autoResumeAttempts,
440
453
  endReason: "auto-resume budget exhausted",
441
454
  everyAttemptThrew,
442
- });
455
+ }), options.io);
443
456
  await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
444
457
  presentTerminal(terminal, options.io);
445
458
  return {
@@ -448,7 +461,7 @@ export async function runWithAutoResumeLoop(options) {
448
461
  };
449
462
  }
450
463
  if (!(await isPrincipalAvailable(options.admitted.principal))) {
451
- const terminal = dispatchExceptionFailureTerminal({
464
+ const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
452
465
  role: options.admitted.role,
453
466
  runId: options.admitted.runId,
454
467
  causeError: lastThrownError,
@@ -456,7 +469,7 @@ export async function runWithAutoResumeLoop(options) {
456
469
  autoResumeAttempts,
457
470
  endReason: "session principal unavailable before further resume",
458
471
  everyAttemptThrew,
459
- });
472
+ }), options.io);
460
473
  await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
461
474
  presentTerminal(terminal, options.io);
462
475
  return {
@@ -5,8 +5,8 @@ import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissio
5
5
  import { loadResumableDiaristRun, markRunAdmitted, } from "./run-lifecycle.js";
6
6
  import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
7
7
  import { presentStructuralRejection, trySettleDiaristTerminalResult, } from "./settlement.js";
8
- import { lastRolePayloadRecord } from "./terminal.js";
9
8
  import { projectRoleTurnRequest, } from "./turn-request.js";
9
+ import { readRunTicketNumber } from "../run-ticket-number.js";
10
10
  /** Project admitted invocation onto the host-neutral turn request. */
11
11
  export function buildDiaristTurnRequest(admitted, options) {
12
12
  return projectRoleTurnRequest(admitted, { activation: { role: "diarist" } }, options);
@@ -110,29 +110,15 @@ export async function runPublicDiarist(argv, env, io, parseDiaristArgv) {
110
110
  adapters: diaristAdapters(),
111
111
  ...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
112
112
  }).then(async (result) => {
113
- // Accept may have bound ticket onto durable pages after LLM assertion.
114
- // Mirror only lawful non-escalate accepted terminals — escalate must not
115
- // leak an unverified ticketNumber into the caller-visible typed key.
113
+ // Accept hook binds ticket onto durable pages (#771). Mirror that page fact
114
+ // onto the caller-visible admitted object — never pick a ticketNumber out of
115
+ // the payload sequence (#881 sole-collapse ban on ticket/escalate).
116
116
  if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
117
- const roleOutcome = result.terminal?.roleOutcome;
118
- const facts = roleOutcome?.kind === "accepted"
119
- ? lastRolePayloadRecord(roleOutcome.payloads ?? [])
120
- : undefined;
121
- if (roleOutcome !== undefined &&
122
- roleOutcome.kind === "accepted" &&
123
- facts?.status !== "escalate") {
124
- const raw = typeof facts?.ticketNumber === "number"
125
- ? facts.ticketNumber
126
- : typeof facts?.sitian === "object"
127
- && facts.sitian !== null
128
- && typeof facts.sitian.ticketNumber === "number"
129
- ? facts.sitian.ticketNumber
130
- : undefined;
131
- if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) {
132
- admitted.ticketNumber = raw;
133
- if (result.admitted.ticketNumber === undefined) {
134
- result.admitted.ticketNumber = raw;
135
- }
117
+ const fromPages = await readRunTicketNumber(admitted.runDirectory);
118
+ if (fromPages !== undefined) {
119
+ await bindAdmittedTicketNumber(admitted, fromPages);
120
+ if (result.admitted.ticketNumber === undefined) {
121
+ result.admitted.ticketNumber = fromPages;
136
122
  }
137
123
  }
138
124
  }
@@ -36,7 +36,9 @@ function instructionSeatAdapters(options) {
36
36
  return infrastructureFailure === undefined
37
37
  ? result.knownFailure
38
38
  : {
39
- cause: infrastructureFailure.cause,
39
+ ...(infrastructureFailure.cause === undefined
40
+ ? {}
41
+ : { cause: infrastructureFailure.cause }),
40
42
  diagnostic: infrastructureFailure.diagnostic,
41
43
  ...(infrastructureFailure.identity === undefined
42
44
  ? {}
@@ -18,7 +18,9 @@ function judgeAdapters() {
18
18
  (infrastructureFailure === undefined
19
19
  ? undefined
20
20
  : {
21
- cause: infrastructureFailure.cause,
21
+ ...(infrastructureFailure.cause === undefined
22
+ ? {}
23
+ : { cause: infrastructureFailure.cause }),
22
24
  diagnostic: infrastructureFailure.diagnostic,
23
25
  ...(infrastructureFailure.identity === undefined
24
26
  ? {}