@akagilnc/pi-workflow-roles 0.1.5252 → 0.1.5270

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.
@@ -9,7 +9,6 @@ export const HOST_DESCRIPTIONS = Object.freeze({
9
9
  modelFlag: "--model",
10
10
  }),
11
11
  modelPassing: "argv",
12
- boundResume: "session/load",
13
12
  sessionBindingFile: "grok-acp-session.json",
14
13
  }),
15
14
  /**
@@ -27,7 +26,6 @@ export const HOST_DESCRIPTIONS = Object.freeze({
27
26
  thinkingFlag: "--reasoning",
28
27
  }),
29
28
  modelPassing: "set_model",
30
- boundResume: "session/load",
31
29
  sessionBindingFile: "hermes-acp-session.json",
32
30
  seatProfileSoul: Object.freeze({
33
31
  flag: "-p",
@@ -136,9 +136,14 @@ function createNativeNavigatorSessionFactory(deps) {
136
136
  }
137
137
  if (outcome.kind === "failure") {
138
138
  providerFailure = navigatorProviderFailureFromPublicTerminal(outcome);
139
+ const failedAttempts = outcome.decisiveFacts.failedAttempts;
140
+ const attemptReasons = Array.isArray(failedAttempts) ? failedAttempts.map((entry, index) => {
141
+ const fact = entry !== null && typeof entry === "object" ? entry : void 0;
142
+ return `${typeof fact?.attempt === "number" ? fact.attempt + 1 : index + 1}: ${typeof fact?.diagnostic === "string" ? fact.diagnostic : outcome.diagnostic}`;
143
+ }) : [];
139
144
  throw navigatorUnavailableError(
140
145
  providerFailure.source,
141
- new Error(outcome.diagnostic),
146
+ new Error(attemptReasons.length > 0 ? attemptReasons.join("\n") : outcome.diagnostic),
142
147
  providerFailure.cause
143
148
  );
144
149
  }
@@ -1,4 +1,3 @@
1
- import { lstat } from "node:fs/promises";
2
1
  import { join } from "node:path";
3
2
  import { resolveBookKeyFromGit } from "../activation-ledger-git.js";
4
3
  import { resolveActivationLedgerHome } from "../activation-ledger-topology.js";
@@ -21,7 +20,7 @@ export function issuePiDurablePrincipalCoordinates(request) {
21
20
  ...placement,
22
21
  };
23
22
  }
24
- /** Pi's durable-principal codec and availability authority. */
23
+ /** Pi's durable-principal codec. */
25
24
  export const piDurablePrincipalAuthority = {
26
25
  issue(request) {
27
26
  const coordinates = issuePiDurablePrincipalCoordinates(request);
@@ -51,14 +50,4 @@ export const piDurablePrincipalAuthority = {
51
50
  : join(record.sessionDirectory, "session.jsonl"),
52
51
  };
53
52
  },
54
- async isAvailable(principal) {
55
- const { sessionFile } = this.decode(principal);
56
- try {
57
- const stat = await lstat(sessionFile);
58
- return stat.isFile() && !stat.isSymbolicLink();
59
- }
60
- catch {
61
- return false;
62
- }
63
- },
64
53
  };
@@ -30,7 +30,7 @@ const dummyIo = { stdout: () => { }, stderr: () => { } };
30
30
  * Write failure must escape the retried dispatch try instead of becoming a
31
31
  * synthetic host-turn terminal.
32
32
  */
33
- export async function persistReturnedRunState(admitted, authority, options) {
33
+ export async function persistReturnedRunState(admitted, options) {
34
34
  if (options?.lawful === true) {
35
35
  await markRunTerminal(admitted.runDirectory);
36
36
  return;
@@ -40,10 +40,8 @@ export async function persistReturnedRunState(admitted, authority, options) {
40
40
  });
41
41
  const typedHttp429 = resumeObservation.typedHttp429;
42
42
  if (admitted.principal !== undefined && typedHttp429 !== undefined) {
43
- if (await authority.isAvailable(admitted.principal)) {
44
- await markRunResumable(admitted.runDirectory, typedHttp429);
45
- return;
46
- }
43
+ await markRunResumable(admitted.runDirectory, typedHttp429);
44
+ return;
47
45
  }
48
46
  await markRunTerminal(admitted.runDirectory);
49
47
  }
@@ -286,8 +284,6 @@ export async function runWithAutoResumeLoop(options) {
286
284
  // `attempts >= limit` comparison (always false) — reject here, before any dispatch.
287
285
  const limit = options.autoResumeLimit ?? AUTO_RESUME_LIMIT;
288
286
  parseAutoResumeLimit(limit);
289
- const isPrincipalAvailable = options.isPrincipalAvailable ??
290
- ((principal) => options.principalAuthority.isAvailable(principal));
291
287
  let autoResumeAttempts = 0;
292
288
  let isFirst = true;
293
289
  let currentPayload = options.buildInitialPayload();
@@ -295,6 +291,9 @@ export async function runWithAutoResumeLoop(options) {
295
291
  let lastThrownError;
296
292
  let everyAttemptThrew = true;
297
293
  const retainedErrorFiles = [];
294
+ const failedAttempts = [];
295
+ let stoppedResult;
296
+ let endReason;
298
297
  while (true) {
299
298
  let result;
300
299
  // Set only when the caught throw is a TurnDispatchedFailure (#840 r9 判词
@@ -317,6 +316,11 @@ export async function runWithAutoResumeLoop(options) {
317
316
  // diagnostic-sink-isolation precedent). When a lease is held inside
318
317
  // dispatch, that path owns release in its own finally.
319
318
  lastThrownError = error;
319
+ const failedAttempt = {
320
+ attempt: dispatchOrdinal,
321
+ diagnostic: describeErrorIdentity(unwrapTurnDispatchedFailure(error)),
322
+ };
323
+ failedAttempts.push(failedAttempt);
320
324
  turnStartedBeforeThrow = error instanceof TurnDispatchedFailure;
321
325
  const attempt = dispatchOrdinal;
322
326
  try {
@@ -325,6 +329,7 @@ export async function runWithAutoResumeLoop(options) {
325
329
  // never orphan the retained file (#426 fix_now #5).
326
330
  const { file, pointerError } = await retainDispatchError(options.admitted, options.principalAuthority, options.sessionAppender, attempt, error);
327
331
  retainedErrorFiles.push(file);
332
+ failedAttempt.errorFile = file;
328
333
  options.io.stderr(`dispatch attempt ${attempt} threw (${describeErrorIdentity(error)}); full error retained at ${file}\n`);
329
334
  if (pointerError !== undefined) {
330
335
  options.io.stderr(`dispatch error retention failed (best-effort continue): ${describeErrorIdentity(pointerError)}\n`);
@@ -348,95 +353,31 @@ export async function runWithAutoResumeLoop(options) {
348
353
  const terminal = result.terminal;
349
354
  if (terminal !== undefined) {
350
355
  terminal.autoResumeCount = autoResumeAttempts;
351
- }
352
- const lawful = terminal !== undefined && isLawfulTypedTerminalOutcome(terminal.roleOutcome);
353
- if (lawful) {
354
- if (terminal !== undefined) {
355
- // Present lawful terminal once to real io (dummy was used inside dispatch)
356
- options.io.stdout(formatTerminalResult(terminal));
356
+ if (terminal.roleOutcome.kind === "failure") {
357
+ failedAttempts.push({
358
+ attempt: dispatchOrdinal - 1,
359
+ diagnostic: terminal.roleOutcome.diagnostic,
360
+ decisiveFacts: terminal.roleOutcome.decisiveFacts,
361
+ });
357
362
  }
358
- return result;
359
- }
360
- if (result.skipAutoResume === true) {
361
- if (terminal !== undefined)
362
- presentTerminal(terminal, options.io);
363
- return result;
364
- }
365
- // #855: process cancel — stop even if dispatch forgot skipAutoResume.
366
- if (processCancelSignalName(options.signal) !== undefined) {
367
- if (terminal !== undefined)
368
- presentTerminal(terminal, options.io);
369
- return result;
370
- }
371
- }
372
- if (result !== undefined) {
373
- const terminal = result.terminal;
374
- if (autoResumeAttempts >= limit) {
375
- if (terminal !== undefined)
376
- presentTerminal(terminal, options.io);
377
- return result;
378
363
  }
379
- if (result.turnDispatched === true
380
- && !(await isPrincipalAvailable(options.admitted.principal))) {
381
- if (terminal !== undefined)
382
- presentTerminal(terminal, options.io);
383
- return result;
364
+ if (terminal !== undefined && isLawfulTypedTerminalOutcome(terminal.roleOutcome)
365
+ || result.skipAutoResume === true
366
+ || autoResumeAttempts >= limit
367
+ || processCancelSignalName(options.signal) !== undefined) {
368
+ stoppedResult = result;
369
+ break;
384
370
  }
385
371
  }
386
372
  else {
387
- // Exception path: continue through the identical budget/session gates.
388
373
  if (autoResumeAttempts >= limit) {
389
- const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
390
- role: options.admitted.role,
391
- runId: options.admitted.runId,
392
- causeError: lastThrownError,
393
- errorFiles: retainedErrorFiles,
394
- autoResumeAttempts,
395
- endReason: "auto-resume budget exhausted",
396
- everyAttemptThrew,
397
- }), options.io);
398
- await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
399
- presentTerminal(terminal, options.io);
400
- return {
401
- exitCode: 1,
402
- terminal,
403
- };
374
+ endReason = "auto-resume budget exhausted";
375
+ break;
404
376
  }
405
- // #855: process cancel on the throw path — do not re-dispatch; name the signal.
406
377
  const cancelName = processCancelSignalName(options.signal);
407
378
  if (cancelName !== undefined) {
408
- const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
409
- role: options.admitted.role,
410
- runId: options.admitted.runId,
411
- causeError: lastThrownError,
412
- errorFiles: retainedErrorFiles,
413
- autoResumeAttempts,
414
- endReason: `ak-role terminated by ${cancelName}`,
415
- everyAttemptThrew,
416
- }), options.io);
417
- await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
418
- presentTerminal(terminal, options.io);
419
- return {
420
- exitCode: 1,
421
- terminal,
422
- };
423
- }
424
- if (!(await isPrincipalAvailable(options.admitted.principal))) {
425
- const terminal = await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
426
- role: options.admitted.role,
427
- runId: options.admitted.runId,
428
- causeError: lastThrownError,
429
- errorFiles: retainedErrorFiles,
430
- autoResumeAttempts,
431
- endReason: "session principal unavailable before further resume",
432
- everyAttemptThrew,
433
- }), options.io);
434
- await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
435
- presentTerminal(terminal, options.io);
436
- return {
437
- exitCode: 1,
438
- terminal,
439
- };
379
+ endReason = `ak-role terminated by ${cancelName}`;
380
+ break;
440
381
  }
441
382
  }
442
383
  autoResumeAttempts++;
@@ -449,4 +390,35 @@ export async function runWithAutoResumeLoop(options) {
449
390
  isFirst = false;
450
391
  }
451
392
  }
393
+ const terminal = stoppedResult === undefined
394
+ ? await attachDispatchExceptionTerminal(options.admitted, dispatchExceptionFailureTerminal({
395
+ role: options.admitted.role,
396
+ runId: options.admitted.runId,
397
+ causeError: lastThrownError,
398
+ errorFiles: retainedErrorFiles,
399
+ autoResumeAttempts,
400
+ endReason: endReason,
401
+ everyAttemptThrew,
402
+ }), options.io)
403
+ : stoppedResult.terminal;
404
+ if (stoppedResult === undefined) {
405
+ await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
406
+ }
407
+ const complete = terminal !== undefined && failedAttempts.length > 0
408
+ ? { ...terminal, roleOutcome: {
409
+ ...terminal.roleOutcome,
410
+ decisiveFacts: { ...terminal.roleOutcome.decisiveFacts, failedAttempts },
411
+ } }
412
+ : terminal;
413
+ if (complete !== undefined) {
414
+ if (isLawfulTypedTerminalOutcome(complete.roleOutcome)) {
415
+ options.io.stdout(formatTerminalResult(complete));
416
+ }
417
+ else {
418
+ presentTerminal(complete, options.io);
419
+ }
420
+ }
421
+ return stoppedResult === undefined
422
+ ? { exitCode: 1, terminal: complete }
423
+ : { ...stoppedResult, ...(complete === undefined ? {} : { terminal: complete }) };
452
424
  }
@@ -753,7 +753,7 @@ export async function runPublicInstructionSeatResume(request, env, io) {
753
753
  };
754
754
  let binding;
755
755
  try {
756
- const loaded = await loadResumablePublicRole(env.home, request.runId, env.principalAuthority, true);
756
+ const loaded = await loadResumablePublicRole(env.home, request.runId, env.principalAuthority);
757
757
  binding = loaded.admitted.role === "auditor"
758
758
  ? await readAuditorResumeBinding(loaded.admitted.runDirectory)
759
759
  : undefined;
@@ -805,7 +805,7 @@ async function queueConclusionFromChild(child, env, io) {
805
805
  }
806
806
  /** Continue the parent once a child has submitted. The child words ride the existing resume. */
807
807
  export async function continueParentAfterChild(parentRunId, child, env, io) {
808
- const loaded = await loadResumablePublicRole(env.home, parentRunId, env.principalAuthority, true);
808
+ const loaded = await loadResumablePublicRole(env.home, parentRunId, env.principalAuthority);
809
809
  if (loaded.run.state !== "admitted" && !AUDITED_ROLES.has(loaded.admitted.role)) {
810
810
  return runPublicInstructionSeatResume({ runId: parentRunId }, env, io);
811
811
  }
@@ -1005,7 +1005,7 @@ async function auditSubmittedRole(turn, env, io, passedOfficer, passedReceipt, p
1005
1005
  ? undefined : runIdFromRunDirectory(escalation.runDirectory));
1006
1006
  if (escalatedRunId === undefined)
1007
1007
  throw new Error("escalated audit has no resumable run identity");
1008
- const officer = await loadResumablePublicRole(env.home, escalatedRunId, env.principalAuthority, true);
1008
+ const officer = await loadResumablePublicRole(env.home, escalatedRunId, env.principalAuthority);
1009
1009
  const terminal = await trySettlePublicSeat(officer.admitted, env.principalAuthority, undefined);
1010
1010
  if (terminal === undefined)
1011
1011
  throw new Error("escalated audit has no terminal result");