@akagilnc/pi-workflow-roles 0.1.5245 → 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.
Files changed (34) hide show
  1. package/dist/acp-host/production-host.js +125 -217
  2. package/dist/headless-host/production-host.js +381 -470
  3. package/dist/host-descriptions.js +0 -2
  4. package/dist/navigator-invocation-identity.js +0 -57
  5. package/dist/navigator-public-session.js +6 -1
  6. package/dist/pi/durable-principal.js +1 -12
  7. package/dist/public-cli/auto-resume.js +59 -87
  8. package/dist/public-cli/instruction-seat-run.js +3 -3
  9. package/dist/public-cli/main.js +86 -206
  10. package/dist/public-cli/post-admission.js +7 -15
  11. package/dist/public-cli/run-lifecycle.js +5 -15
  12. package/dist/public-cli/settlement.js +10 -59
  13. package/dist/public-cli/terminal.js +4 -2
  14. package/dist/session-identity.js +0 -18
  15. package/package.json +1 -1
  16. package/src/acp-host/description.ts +0 -2
  17. package/src/acp-host/production-host.ts +0 -1
  18. package/src/acp-host/role-turn-host.ts +12 -6
  19. package/src/headless-host/role-turn-host.ts +11 -3
  20. package/src/host-contracts.ts +0 -1
  21. package/src/host-descriptions.ts +0 -2
  22. package/src/navigator-invocation-identity.ts +4 -121
  23. package/src/navigator-public-session.ts +12 -1
  24. package/src/pi/durable-principal.ts +1 -11
  25. package/src/prepared-role-turn.ts +1 -1
  26. package/src/public-cli/auto-resume.ts +67 -109
  27. package/src/public-cli/instruction-seat-run.ts +3 -3
  28. package/src/public-cli/post-admission.ts +7 -21
  29. package/src/public-cli/run-lifecycle.ts +3 -21
  30. package/src/public-cli/settlement.ts +8 -75
  31. package/src/public-cli/terminal.ts +4 -2
  32. package/src/role-envelope.ts +3 -3
  33. package/src/role-runtime.ts +23 -5
  34. package/src/session-identity.ts +0 -22
@@ -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",
@@ -146,47 +146,6 @@ function findLatestDurablePackagedRoleTerminal(entries) {
146
146
  }
147
147
  return void 0;
148
148
  }
149
- function bindCurrentDurableTerminalToMarker(entries) {
150
- const terminal = findLatestDurablePackagedRoleTerminal(entries);
151
- if (terminal === void 0) return { kind: "absent" };
152
- let markerIndex = -1;
153
- for (let i = terminal.index - 1; i >= 0; i -= 1) {
154
- if (isInvocationMarkerEntry(entries[i])) {
155
- markerIndex = i;
156
- break;
157
- }
158
- }
159
- if (markerIndex < 0) {
160
- return { kind: "unbound", terminal };
161
- }
162
- const marker = parseInvocationMarkerIdentity(entries[markerIndex]?.data);
163
- if (marker === void 0) {
164
- return { kind: "unbound", terminal };
165
- }
166
- let windowEnd = entries.length;
167
- for (let i = markerIndex + 1; i < entries.length; i += 1) {
168
- if (isInvocationMarkerEntry(entries[i])) {
169
- windowEnd = i;
170
- break;
171
- }
172
- }
173
- let durableCount = 0;
174
- for (let i = markerIndex + 1; i < windowEnd; i += 1) {
175
- if (durableTerminalAt(entries, i) !== void 0) durableCount += 1;
176
- }
177
- if (durableCount !== 1) return { kind: "ambiguous" };
178
- if (terminal.index <= markerIndex || terminal.index >= windowEnd) {
179
- return { kind: "ambiguous" };
180
- }
181
- return {
182
- kind: "bound",
183
- terminal,
184
- marker: { ...marker, index: markerIndex }
185
- };
186
- }
187
- function isReceiptSettlementBindingClear(entries) {
188
- return bindCurrentDurableTerminalToMarker(entries).kind !== "ambiguous";
189
- }
190
149
  function resolveLifecycleInvocationPrincipal(entries, expected) {
191
150
  const markerIndex = latestInvocationMarkerIndex(entries);
192
151
  if (markerIndex < 0) {
@@ -206,35 +165,19 @@ function resolveLifecycleInvocationPrincipal(entries, expected) {
206
165
  }
207
166
  return { invocationId: marker.invocationId, resume: true };
208
167
  }
209
- function currentInvocationPrincipalFromSession(entries, beforeIndex = entries.length) {
210
- return currentInvocationMarkerFromSession(entries, beforeIndex)?.invocationId;
211
- }
212
- function currentInvocationMarkerFromSession(entries, beforeIndex = entries.length) {
213
- const limit = Math.min(Math.max(beforeIndex, 0), entries.length);
214
- for (let i = limit - 1; i >= 0; i -= 1) {
215
- const entry = entries[i];
216
- if (!isInvocationMarkerEntry(entry)) continue;
217
- return parseInvocationMarkerIdentity(entry?.data);
218
- }
219
- return void 0;
220
- }
221
168
  export {
222
169
  NAVIGATOR_INFRASTRUCTURE_FAILURE_EVIDENCE_KEYS,
223
170
  NAVIGATOR_INFRASTRUCTURE_FAILURE_KIND,
224
171
  NAVIGATOR_INVOCATION_ENTRY,
225
172
  NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY,
226
- bindCurrentDurableTerminalToMarker,
227
173
  buildNavigatorInfrastructureFailureFact,
228
174
  classifyPackagedRoleTerminalResult,
229
- currentInvocationMarkerFromSession,
230
- currentInvocationPrincipalFromSession,
231
175
  extractInfrastructureFailureEvidence,
232
176
  findLatestDurablePackagedRoleTerminal,
233
177
  hasNavigatorInfrastructureFailureBase,
234
178
  isAcceptedPackagedRoleTerminalResult,
235
179
  isDurablePackagedRoleTerminalResult,
236
180
  isNavigatorInfrastructureFailureFact,
237
- isReceiptSettlementBindingClear,
238
181
  markerMatchesExpectedIdentity,
239
182
  mintNavigatorInvocationId,
240
183
  parseInvocationMarkerIdentity,
@@ -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");