@akagilnc/pi-workflow-roles 0.1.3902 → 0.1.3936
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.
- package/README.md +3 -3
- package/README.zh-CN.md +4 -4
- package/dist/acp-host/production-host.js +2540 -1994
- package/dist/compliance-transport.js +4 -1
- package/dist/headless-host/production-host.js +2274 -1728
- package/dist/prepared-role-turn.js +5 -0
- package/dist/public-cli/auto-resume.js +115 -13
- package/dist/public-cli/diarist-run.js +154 -0
- package/dist/public-cli/main.js +7082 -5854
- package/dist/public-cli/option-definitions.js +1 -1
- package/dist/public-cli/post-admission.js +455 -188
- package/dist/public-cli/role-turn-host-resolution.js +59 -0
- package/dist/public-cli/run-lifecycle.js +11 -6
- package/dist/public-cli/turn-request.js +1 -0
- package/dist/public-role-summons.js +48 -42
- package/dist/role-activation-flags.js +3 -0
- package/dist/role-runtime.js +14 -3
- package/dist/session-identity.js +58 -0
- package/package.json +1 -1
- package/src/compliance-transport.ts +6 -1
- package/src/host-contracts.ts +2 -0
- package/src/public-cli/auto-resume.ts +157 -11
- package/src/public-cli/cli.ts +25 -85
- package/src/public-cli/countersign-run.ts +26 -33
- package/src/public-cli/option-definitions.ts +1 -1
- package/src/public-cli/post-admission.ts +514 -159
- package/src/public-cli/reviewer-run.ts +0 -2
- package/src/public-cli/role-turn-host-resolution.ts +122 -0
- package/src/public-cli/run-lifecycle.ts +15 -10
- package/src/public-cli/turn-request.ts +3 -0
- package/src/public-role-summons.ts +71 -54
- package/src/role-activation-flags.ts +3 -0
- package/src/role-runtime.ts +14 -2
- package/src/session-identity.ts +34 -0
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
freezeAttachmentsIntoRun,
|
|
23
23
|
} from "./invocation.ts";
|
|
24
24
|
import { pathContainedIn } from "../activation-ledger-topology.ts";
|
|
25
|
+
import { resolveHostAwareSessionAvailability } from "../session-identity.ts";
|
|
25
26
|
|
|
26
27
|
import type {
|
|
27
28
|
ControlledFailureCause,
|
|
@@ -56,9 +57,8 @@ import {
|
|
|
56
57
|
acquireRunWriterLease,
|
|
57
58
|
clearCurrentCourt,
|
|
58
59
|
clearTypedProviderHttpObservation,
|
|
59
|
-
|
|
60
|
+
describeErrorIdentity,
|
|
60
61
|
markRunRunning,
|
|
61
|
-
markRunTerminal,
|
|
62
62
|
readCurrentCourt,
|
|
63
63
|
recordCurrentCourt,
|
|
64
64
|
renderResumeCommand,
|
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
} from "./run-lifecycle.ts";
|
|
71
71
|
import { homeFromRunDirectory } from "../activation-ledger-topology.ts";
|
|
72
72
|
import { readSealedSubmission } from "../submission-ledger.ts";
|
|
73
|
+
import { clearReviewerDispatchRejection } from "./reviewer-dispatch-rejection.ts";
|
|
73
74
|
import {
|
|
74
75
|
classifyPostAdmissionFailure,
|
|
75
76
|
controlledFailureInputFromResolution,
|
|
@@ -88,10 +89,97 @@ import {
|
|
|
88
89
|
} from "./settlement.ts";
|
|
89
90
|
import type { CliIo } from "./cli-io.ts";
|
|
90
91
|
import type { AdmittedRoleInvocation } from "./invocation.ts";
|
|
92
|
+
import type { NamedRoleTurnHostAdapter } from "./role-turn-host-resolution.ts";
|
|
91
93
|
import {
|
|
92
94
|
type TerminalResult,
|
|
93
95
|
} from "./terminal.ts";
|
|
94
|
-
import {
|
|
96
|
+
import {
|
|
97
|
+
ensureRealArtifactsDirectory,
|
|
98
|
+
persistReturnedRunState,
|
|
99
|
+
runWithAutoResumeLoop,
|
|
100
|
+
TurnDispatchedFailure,
|
|
101
|
+
} from "./auto-resume.ts";
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Nested station-child role already finished its own call-local loop.
|
|
105
|
+
* Parent records that failure once and must not auto-resume into a re-summon
|
|
106
|
+
* (#840 父子不层叠). Other beforeDispatch failures keep the shared #416 budget.
|
|
107
|
+
*/
|
|
108
|
+
export class StationChildExhaustedError extends Error {
|
|
109
|
+
override readonly name = "StationChildExhaustedError";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function withOnceSuccessfulBeforeDispatch<
|
|
113
|
+
A extends AdmittedRoleInvocation,
|
|
114
|
+
T extends TerminalResult = TerminalResult,
|
|
115
|
+
>(adapters: PostAdmissionAdapters<A, T>): PostAdmissionAdapters<A, T> {
|
|
116
|
+
const hook = adapters.beforeDispatch;
|
|
117
|
+
if (hook === undefined) return adapters;
|
|
118
|
+
let succeeded = false;
|
|
119
|
+
return {
|
|
120
|
+
...adapters,
|
|
121
|
+
beforeDispatch: async (admitted) => {
|
|
122
|
+
if (succeeded) return;
|
|
123
|
+
await hook(admitted);
|
|
124
|
+
succeeded = true;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Session custom-entry type for a best-effort post-dispatch cleanup
|
|
131
|
+
* diagnostic (#840 r9 判词 class 1). Every auto-resume attempt dispatches
|
|
132
|
+
* with dummyIo (src/public-cli/auto-resume.ts), so an io.stderr-only
|
|
133
|
+
* diagnostic never reaches a real caller — the dossier is the durable
|
|
134
|
+
* channel that does (失败诚实宪法 真因必须落痕).
|
|
135
|
+
*/
|
|
136
|
+
export const POST_ADMISSION_CLEANUP_DIAGNOSTIC_ENTRY_TYPE =
|
|
137
|
+
"ak_post_admission_cleanup_diagnostic" as const;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Best-effort post-dispatch diagnostic that must still leave a real trace
|
|
141
|
+
* even though the attempt's own io may be dummyIo (#840 r9 判词 class 1).
|
|
142
|
+
* Single authoritative durable channel — a dossier custom entry via
|
|
143
|
+
* sessionAppender — not a standing duplicate. Only when that one write
|
|
144
|
+
* itself fails does this fall back to a plain run-artifacts file (reusing
|
|
145
|
+
* auto-resume.ts's existing dispatch-error retention helper — no new
|
|
146
|
+
* mechanism), so an unhealthy session never leaves this diagnostic with
|
|
147
|
+
* zero durable trace (#840 bounce class 2); a healthy session never gets a
|
|
148
|
+
* redundant second copy (#840 bounce class 1: 同一业务规则只保留一个权威实现).
|
|
149
|
+
*/
|
|
150
|
+
async function recordBestEffortPostDispatchDiagnostic<A extends AdmittedRoleInvocation>(
|
|
151
|
+
admitted: A,
|
|
152
|
+
env: PostAdmissionEnv,
|
|
153
|
+
diagnostic: string,
|
|
154
|
+
io: CliIo,
|
|
155
|
+
): Promise<void> {
|
|
156
|
+
io.stderr(formatCliDiagnostic(diagnostic));
|
|
157
|
+
const payload = { diagnostic, recordedAt: new Date().toISOString() };
|
|
158
|
+
try {
|
|
159
|
+
await env.sessionAppender(
|
|
160
|
+
env.principalAuthority,
|
|
161
|
+
admitted.principal,
|
|
162
|
+
POST_ADMISSION_CLEANUP_DIAGNOSTIC_ENTRY_TYPE,
|
|
163
|
+
payload,
|
|
164
|
+
);
|
|
165
|
+
return;
|
|
166
|
+
} catch (appendError) {
|
|
167
|
+
try {
|
|
168
|
+
const artifactsDir = await ensureRealArtifactsDirectory(admitted.runDirectory);
|
|
169
|
+
await writeFile(
|
|
170
|
+
join(artifactsDir, `post-admission-diagnostic-${randomUUID()}.json`),
|
|
171
|
+
`${JSON.stringify({ version: 1, ...payload }, null, 2)}\n`,
|
|
172
|
+
{ encoding: "utf8", flag: "wx" },
|
|
173
|
+
);
|
|
174
|
+
} catch (artifactError) {
|
|
175
|
+
io.stderr(
|
|
176
|
+
formatCliDiagnostic(
|
|
177
|
+
`post-dispatch diagnostic durable retention failed on both channels (best-effort continue): dossier=${describeErrorIdentity(appendError)}; artifact=${describeErrorIdentity(artifactError)}`,
|
|
178
|
+
),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
95
183
|
|
|
96
184
|
/** Previous main-session host recorded on invocation.json, if any. */
|
|
97
185
|
async function readInvocationHost(runDirectory: string): Promise<string | undefined> {
|
|
@@ -113,6 +201,11 @@ export type PostAdmissionEnv = {
|
|
|
113
201
|
cwd: string;
|
|
114
202
|
correlationId?: string;
|
|
115
203
|
roleTurnHost: RoleTurnHost;
|
|
204
|
+
/**
|
|
205
|
+
* Composition-root adapter table. Nested summons select by the child seat
|
|
206
|
+
* from this table — they must not inherit the already-selected parent host.
|
|
207
|
+
*/
|
|
208
|
+
hostAdapters?: readonly NamedRoleTurnHostAdapter[];
|
|
116
209
|
model?: SeatModelConfig;
|
|
117
210
|
engine?: string;
|
|
118
211
|
/** Effective main-session host for this run (#595 admission / #617 resume seat). */
|
|
@@ -134,6 +227,8 @@ export type PostAdmissionEnv = {
|
|
|
134
227
|
* and mint a new run. Absent on ordinary role commands and on `ak-role resume`.
|
|
135
228
|
*/
|
|
136
229
|
freshSummons?: true;
|
|
230
|
+
/** Station child role run (#840): omit automatic navigator attendance. */
|
|
231
|
+
stationChild?: boolean;
|
|
137
232
|
};
|
|
138
233
|
|
|
139
234
|
/**
|
|
@@ -243,6 +338,7 @@ export async function presentControlledFailure<
|
|
|
243
338
|
adapters: PostAdmissionAdapters<A, T>,
|
|
244
339
|
authority: DurablePrincipalAuthority,
|
|
245
340
|
io: CliIo,
|
|
341
|
+
persistRunState = true,
|
|
246
342
|
): Promise<{
|
|
247
343
|
exitCode: number;
|
|
248
344
|
admitted: A;
|
|
@@ -303,10 +399,8 @@ export async function presentControlledFailure<
|
|
|
303
399
|
resumable = sessionPrincipalAvailable && typedHttp429 !== undefined;
|
|
304
400
|
}
|
|
305
401
|
|
|
306
|
-
if (
|
|
307
|
-
await
|
|
308
|
-
} else {
|
|
309
|
-
await markRunTerminal(admitted.runDirectory);
|
|
402
|
+
if (persistRunState) {
|
|
403
|
+
await persistReturnedRunState(admitted, authority);
|
|
310
404
|
}
|
|
311
405
|
|
|
312
406
|
const terminal = await settleFailureTerminalResult(
|
|
@@ -325,6 +419,42 @@ export async function presentControlledFailure<
|
|
|
325
419
|
};
|
|
326
420
|
}
|
|
327
421
|
|
|
422
|
+
/**
|
|
423
|
+
* presentControlledFailure, called only where the host turn has already
|
|
424
|
+
* genuinely started (#840 r9 判词 class 1 boundary). If presentControlledFailure
|
|
425
|
+
* itself fails, this never fabricates a replacement terminal (ADR 0080
|
|
426
|
+
* single-settlement-disposition — presentControlledFailure /
|
|
427
|
+
* settleFailureTerminalResult stays the one authority); it re-throws a
|
|
428
|
+
* TurnDispatchedFailure so runWithAutoResumeLoop still learns the turn
|
|
429
|
+
* started and selects a resume payload on the next attempt, while settling
|
|
430
|
+
* the true cause through its own existing dispatch-exception machinery once
|
|
431
|
+
* the retry budget is exhausted.
|
|
432
|
+
*/
|
|
433
|
+
async function settleAfterTurnStarted<
|
|
434
|
+
A extends AdmittedRoleInvocation,
|
|
435
|
+
T extends TerminalResult = TerminalResult,
|
|
436
|
+
>(
|
|
437
|
+
admitted: A,
|
|
438
|
+
failureInput: ControlledFailureInput,
|
|
439
|
+
adapters: PostAdmissionAdapters<A, T>,
|
|
440
|
+
authority: DurablePrincipalAuthority,
|
|
441
|
+
io: CliIo,
|
|
442
|
+
persistRunState: boolean,
|
|
443
|
+
): Promise<{ exitCode: number; admitted: A; terminal: T }> {
|
|
444
|
+
try {
|
|
445
|
+
return (await presentControlledFailure(
|
|
446
|
+
admitted,
|
|
447
|
+
failureInput,
|
|
448
|
+
adapters,
|
|
449
|
+
authority,
|
|
450
|
+
io,
|
|
451
|
+
persistRunState,
|
|
452
|
+
)) as { exitCode: number; admitted: A; terminal: T };
|
|
453
|
+
} catch (error) {
|
|
454
|
+
throw new TurnDispatchedFailure(error);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
328
458
|
export async function dispatchPostAdmissionTurn<
|
|
329
459
|
A extends AdmittedRoleInvocation,
|
|
330
460
|
T extends TerminalResult = TerminalResult,
|
|
@@ -336,12 +466,18 @@ export async function dispatchPostAdmissionTurn<
|
|
|
336
466
|
lease: RunWriterLease;
|
|
337
467
|
adapters: PostAdmissionAdapters<A, T>;
|
|
338
468
|
effectiveEngine?: string;
|
|
469
|
+
persistRunState?: boolean;
|
|
339
470
|
}): Promise<{
|
|
340
471
|
exitCode: number;
|
|
341
472
|
admitted: A;
|
|
342
473
|
terminal?: T;
|
|
474
|
+
skipAutoResume?: true;
|
|
475
|
+
turnDispatched?: true;
|
|
476
|
+
needsPersist?: true;
|
|
343
477
|
}> {
|
|
344
478
|
const { admitted, env, io, request, lease, adapters, effectiveEngine } = input;
|
|
479
|
+
const persistRunState = input.persistRunState !== false;
|
|
480
|
+
const deferredPersist = persistRunState ? {} : { needsPersist: true as const };
|
|
345
481
|
const shouldPresent =
|
|
346
482
|
adapters.shouldPresentSettled ?? ((terminal: T) => isLawfulTypedTerminalOutcome(terminal.roleOutcome));
|
|
347
483
|
try {
|
|
@@ -350,13 +486,17 @@ export async function dispatchPostAdmissionTurn<
|
|
|
350
486
|
env.credentials,
|
|
351
487
|
);
|
|
352
488
|
if (missingCredential !== undefined) {
|
|
353
|
-
return
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
489
|
+
return {
|
|
490
|
+
...(await presentControlledFailure(
|
|
491
|
+
admitted,
|
|
492
|
+
missingCredential,
|
|
493
|
+
adapters,
|
|
494
|
+
env.principalAuthority,
|
|
495
|
+
io,
|
|
496
|
+
persistRunState,
|
|
497
|
+
)) as { exitCode: number; admitted: A; terminal: T },
|
|
498
|
+
...deferredPersist,
|
|
499
|
+
};
|
|
360
500
|
}
|
|
361
501
|
// #617 DK-4: capture previous invocation host before markRunRunning overwrites it.
|
|
362
502
|
// Single authority projectHostTransitionPriorNative classifies the prior native volume.
|
|
@@ -380,29 +520,43 @@ export async function dispatchPostAdmissionTurn<
|
|
|
380
520
|
: undefined;
|
|
381
521
|
} catch (error) {
|
|
382
522
|
// prior-native IO is on the public one-shot path — controlled failure, not bare throw.
|
|
383
|
-
return
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
523
|
+
return {
|
|
524
|
+
...(await presentControlledFailure(
|
|
525
|
+
admitted,
|
|
526
|
+
{
|
|
527
|
+
timedOut: false,
|
|
528
|
+
code: null,
|
|
529
|
+
stderr: "",
|
|
530
|
+
thrown: error,
|
|
531
|
+
},
|
|
532
|
+
adapters,
|
|
533
|
+
env.principalAuthority,
|
|
534
|
+
io,
|
|
535
|
+
persistRunState,
|
|
536
|
+
)) as { exitCode: number; admitted: A; terminal: T },
|
|
537
|
+
...deferredPersist,
|
|
538
|
+
};
|
|
395
539
|
}
|
|
396
540
|
|
|
397
|
-
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine, env.host);
|
|
398
541
|
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
399
|
-
//
|
|
400
|
-
//
|
|
542
|
+
// Per-attempt hygiene: stale Reviewer rejection pages must not ride into
|
|
543
|
+
// auto-resume. ENOENT-safe for every seat.
|
|
544
|
+
await clearReviewerDispatchRejection(admitted.runDirectory);
|
|
545
|
+
// The authoritative host write (markRunRunning) is delayed to just before
|
|
546
|
+
// executeTurn — not merely past beforeDispatch (#840 r9 判词 class 2). Any
|
|
547
|
+
// pre-turn retry (beforeDispatch, dossier projection, continuation
|
|
548
|
+
// assembly) must still see the prior invocation host on its next attempt;
|
|
549
|
+
// committing env.host earlier would make readInvocationHost read back the
|
|
550
|
+
// new host on the retry and silently drop hostTransition.
|
|
551
|
+
// Failures settle the run (presentControlledFailure); they must not leave it
|
|
552
|
+
// permanently running. Call-local auto-resume retries this hook until it
|
|
553
|
+
// succeeds; only an exhausted nested station child skips the parent loop
|
|
554
|
+
// (#840 父子不层叠).
|
|
401
555
|
if (adapters.beforeDispatch !== undefined) {
|
|
402
556
|
try {
|
|
403
557
|
await adapters.beforeDispatch(admitted);
|
|
404
558
|
} catch (error) {
|
|
405
|
-
|
|
559
|
+
const settled = (await presentControlledFailure(
|
|
406
560
|
admitted,
|
|
407
561
|
{
|
|
408
562
|
timedOut: false,
|
|
@@ -413,7 +567,12 @@ export async function dispatchPostAdmissionTurn<
|
|
|
413
567
|
adapters,
|
|
414
568
|
env.principalAuthority,
|
|
415
569
|
io,
|
|
570
|
+
persistRunState,
|
|
416
571
|
)) as { exitCode: number; admitted: A; terminal: T };
|
|
572
|
+
if (error instanceof StationChildExhaustedError) {
|
|
573
|
+
return { ...settled, skipAutoResume: true as const, ...deferredPersist };
|
|
574
|
+
}
|
|
575
|
+
return { ...settled, ...deferredPersist };
|
|
417
576
|
}
|
|
418
577
|
}
|
|
419
578
|
|
|
@@ -425,6 +584,9 @@ export async function dispatchPostAdmissionTurn<
|
|
|
425
584
|
// attachments and the seat's own prompt bytes are never rewritten.
|
|
426
585
|
let turnRequest: RoleTurnRequest =
|
|
427
586
|
env.signal === undefined ? request : { ...request, signal: env.signal };
|
|
587
|
+
if (env.stationChild !== undefined) {
|
|
588
|
+
turnRequest = { ...turnRequest, stationChild: env.stationChild };
|
|
589
|
+
}
|
|
428
590
|
if (hostTransition !== undefined) {
|
|
429
591
|
turnRequest = { ...turnRequest, hostTransition };
|
|
430
592
|
}
|
|
@@ -443,11 +605,16 @@ export async function dispatchPostAdmissionTurn<
|
|
|
443
605
|
};
|
|
444
606
|
}
|
|
445
607
|
|
|
608
|
+
// Authoritative host write happens here, at the real dispatch boundary —
|
|
609
|
+
// immediately before the turn actually starts, after every retryable
|
|
610
|
+
// pre-turn step above has succeeded on this attempt (#840 r9 判词 class 2).
|
|
611
|
+
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine, env.host);
|
|
612
|
+
|
|
446
613
|
let result: RoleTurnResult;
|
|
447
614
|
try {
|
|
448
615
|
result = await env.roleTurnHost.executeTurn(turnRequest);
|
|
449
616
|
} catch (error) {
|
|
450
|
-
|
|
617
|
+
const settled = await settleAfterTurnStarted(
|
|
451
618
|
admitted,
|
|
452
619
|
{
|
|
453
620
|
timedOut: false,
|
|
@@ -458,17 +625,43 @@ export async function dispatchPostAdmissionTurn<
|
|
|
458
625
|
adapters,
|
|
459
626
|
env.principalAuthority,
|
|
460
627
|
io,
|
|
461
|
-
|
|
628
|
+
persistRunState,
|
|
629
|
+
);
|
|
630
|
+
return { ...settled, turnDispatched: true as const, ...deferredPersist };
|
|
462
631
|
}
|
|
463
632
|
|
|
633
|
+
// Everything below runs after the host turn genuinely started (#840 r9
|
|
634
|
+
// 判词 class 1 boundary — 覆盖 executeTurn 已启动后至返回带 turnDispatched
|
|
635
|
+
// 结果前的全部异常). Each fallible step routes any failure through the
|
|
636
|
+
// single existing settlement authority (presentControlledFailure /
|
|
637
|
+
// settleFailureTerminalResult) rather than a second one (ADR 0080
|
|
638
|
+
// single-settlement-disposition) — never by fabricating a terminal here.
|
|
639
|
+
// A cleanup step that runs only after a real settlement (clearCurrentCourt)
|
|
640
|
+
// is protected by logging and keeping that already-obtained result, never
|
|
641
|
+
// by discarding it (#840 已交劳动只整理终局不重做). A failure inside the
|
|
642
|
+
// settlement authority itself (presentControlledFailure's own reads) goes
|
|
643
|
+
// through settleAfterTurnStarted: still no fabricated terminal, but the
|
|
644
|
+
// re-thrown TurnDispatchedFailure still tells runWithAutoResumeLoop the
|
|
645
|
+
// turn genuinely started, so the next retry sends a resume payload — the
|
|
646
|
+
// true cause settles through the loop's own existing dispatch-exception
|
|
647
|
+
// machinery once the retry budget is exhausted.
|
|
464
648
|
try {
|
|
465
649
|
await writeFile(
|
|
466
650
|
join(admitted.runDirectory, "stderr.log"),
|
|
467
651
|
result.stderr,
|
|
468
652
|
"utf8",
|
|
469
653
|
);
|
|
470
|
-
} catch {
|
|
471
|
-
//
|
|
654
|
+
} catch (error) {
|
|
655
|
+
// Best-effort: the turn's own stderr capture is secondary to lawful /
|
|
656
|
+
// controlled-failure settlement below, but the failure itself must
|
|
657
|
+
// still leave a real trace (失败诚实宪法 真因必须落痕) — durably, since
|
|
658
|
+
// every auto-resume attempt's io is dummyIo (#840 r9 判词 class 1).
|
|
659
|
+
await recordBestEffortPostDispatchDiagnostic(
|
|
660
|
+
admitted,
|
|
661
|
+
env,
|
|
662
|
+
`stderr.log write failed (best-effort continue): ${describeErrorIdentity(error)}`,
|
|
663
|
+
io,
|
|
664
|
+
);
|
|
472
665
|
}
|
|
473
666
|
|
|
474
667
|
let settled: T | undefined;
|
|
@@ -478,11 +671,55 @@ export async function dispatchPostAdmissionTurn<
|
|
|
478
671
|
request.courtAttemptId === undefined || request.courtAttemptId.length === 0
|
|
479
672
|
? undefined
|
|
480
673
|
: { courtAttemptId: request.courtAttemptId };
|
|
674
|
+
// Single complete boundary (#840 r9 判词 class 1): trySettle, its
|
|
675
|
+
// shouldPresent gate, and the accepted-settlement cleanup all settle
|
|
676
|
+
// through this one catch. shouldPresent used to sit outside every
|
|
677
|
+
// TurnDispatchedFailure wrap, so a throw from it looked like an ordinary
|
|
678
|
+
// pre-turn throw to the caller (turnStartedBeforeThrow stayed false) and
|
|
679
|
+
// replayed the initial payload even though the host turn had already
|
|
680
|
+
// genuinely run.
|
|
681
|
+
let settledOutcome:
|
|
682
|
+
| { exitCode: number; admitted: A; terminal: T; turnDispatched: true }
|
|
683
|
+
| undefined;
|
|
481
684
|
try {
|
|
482
685
|
settled = await adapters.trySettle(admitted, env.principalAuthority, courtScope);
|
|
686
|
+
if (settled !== undefined && shouldPresent(settled)) {
|
|
687
|
+
// This court sealed — drop open-court pointer (bare resume no longer continues it).
|
|
688
|
+
if (
|
|
689
|
+
settled.roleOutcome.kind === "accepted" &&
|
|
690
|
+
request.courtAttemptId !== undefined &&
|
|
691
|
+
request.courtAttemptId.length > 0
|
|
692
|
+
) {
|
|
693
|
+
try {
|
|
694
|
+
await clearCurrentCourt(admitted.runDirectory, request.courtAttemptId);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
// Settlement already sealed accepted — a cleanup failure here must
|
|
697
|
+
// not erase that fact or make the caller replay this court's
|
|
698
|
+
// summons over already-delivered work (#840 已交劳动只整理终局不重做).
|
|
699
|
+
// A later bare resume self-heals: buildRequestAfterLease finds the
|
|
700
|
+
// open court already sealed and clears it then (documented
|
|
701
|
+
// continue-under-failure contract, not a swallow — 失败诚实宪法 真因
|
|
702
|
+
// 必须落痕) — durably, since every auto-resume attempt's io here is
|
|
703
|
+
// dummyIo (#840 r9 判词 class 1).
|
|
704
|
+
await recordBestEffortPostDispatchDiagnostic(
|
|
705
|
+
admitted,
|
|
706
|
+
env,
|
|
707
|
+
`current-court cleanup failed after accepted settlement (best-effort continue, self-heals on next resume): ${describeErrorIdentity(error)}`,
|
|
708
|
+
io,
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
settledOutcome = {
|
|
713
|
+
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
714
|
+
admitted,
|
|
715
|
+
terminal: settled,
|
|
716
|
+
turnDispatched: true as const,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
483
719
|
} catch (error) {
|
|
484
|
-
// Settle throw is a real failure fact —
|
|
485
|
-
|
|
720
|
+
// Settle (or its shouldPresent gate) throw is a real failure fact —
|
|
721
|
+
// never swallow into undefined.
|
|
722
|
+
const settledFailure = await settleAfterTurnStarted(
|
|
486
723
|
admitted,
|
|
487
724
|
{
|
|
488
725
|
timedOut: false,
|
|
@@ -493,59 +730,81 @@ export async function dispatchPostAdmissionTurn<
|
|
|
493
730
|
adapters,
|
|
494
731
|
env.principalAuthority,
|
|
495
732
|
io,
|
|
496
|
-
|
|
733
|
+
persistRunState,
|
|
734
|
+
);
|
|
735
|
+
return { ...settledFailure, turnDispatched: true as const, ...deferredPersist };
|
|
497
736
|
}
|
|
498
|
-
if (
|
|
499
|
-
//
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
request.courtAttemptId !== undefined &&
|
|
503
|
-
request.courtAttemptId.length > 0
|
|
504
|
-
) {
|
|
505
|
-
await clearCurrentCourt(admitted.runDirectory, request.courtAttemptId);
|
|
506
|
-
}
|
|
507
|
-
await markRunTerminal(admitted.runDirectory);
|
|
508
|
-
io.stdout(formatTerminalResult(settled));
|
|
509
|
-
return {
|
|
510
|
-
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
511
|
-
admitted,
|
|
512
|
-
terminal: settled,
|
|
513
|
-
};
|
|
737
|
+
if (settledOutcome !== undefined) {
|
|
738
|
+
// Lawful persist + present is the caller's stop seam (auto-resume loop /
|
|
739
|
+
// manual resume), not this retried host-turn function.
|
|
740
|
+
return { ...settledOutcome, ...deferredPersist };
|
|
514
741
|
}
|
|
515
742
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
743
|
+
// Any exception while resolving the failure facts below — including the
|
|
744
|
+
// session-file decode itself — still happened after the host turn
|
|
745
|
+
// genuinely started (#840 r9 判词 class 1). Fold it into the same single
|
|
746
|
+
// controlled-failure settlement instead of losing turnDispatched to an
|
|
747
|
+
// uncaught throw.
|
|
748
|
+
let failureInput: ControlledFailureInput;
|
|
749
|
+
try {
|
|
750
|
+
const sessionFile =
|
|
751
|
+
admitted.principal !== undefined
|
|
752
|
+
? env.principalAuthority.decode(admitted.principal).sessionFile
|
|
753
|
+
: "";
|
|
754
|
+
const runnerKnownFailure =
|
|
755
|
+
adapters.resolveRunnerKnownFailure !== undefined && sessionFile !== ""
|
|
756
|
+
? await adapters.resolveRunnerKnownFailure({ result, sessionFile })
|
|
757
|
+
: result.knownFailure;
|
|
758
|
+
const credentialFailure = postRunMissingCredentialFailure(
|
|
759
|
+
result,
|
|
760
|
+
env.model,
|
|
761
|
+
env.credentials,
|
|
762
|
+
);
|
|
763
|
+
const resolution = await resolveAuditedRunnerFailureResolution({
|
|
764
|
+
runner: runnerKnownFailure,
|
|
765
|
+
sessionFile,
|
|
766
|
+
credential: credentialFailure,
|
|
767
|
+
runDirectory: admitted.runDirectory,
|
|
768
|
+
});
|
|
769
|
+
failureInput = {
|
|
538
770
|
timedOut: result.timedOut,
|
|
539
771
|
code: result.code,
|
|
540
772
|
stderr: result.stderr,
|
|
541
773
|
...controlledFailureInputFromResolution(resolution),
|
|
542
|
-
}
|
|
774
|
+
};
|
|
775
|
+
} catch (error) {
|
|
776
|
+
failureInput = {
|
|
777
|
+
timedOut: false,
|
|
778
|
+
code: result.code,
|
|
779
|
+
stderr: result.stderr,
|
|
780
|
+
thrown: error,
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
const failed = await settleAfterTurnStarted(
|
|
784
|
+
admitted,
|
|
785
|
+
failureInput,
|
|
543
786
|
adapters,
|
|
544
787
|
env.principalAuthority,
|
|
545
788
|
io,
|
|
546
|
-
|
|
789
|
+
persistRunState,
|
|
790
|
+
);
|
|
791
|
+
return { ...failed, turnDispatched: true as const, ...deferredPersist };
|
|
547
792
|
} finally {
|
|
548
|
-
|
|
793
|
+
try {
|
|
794
|
+
await lease.release();
|
|
795
|
+
} catch (error) {
|
|
796
|
+
// lease.release() is documented best-effort and never rejects in the
|
|
797
|
+
// production acquireRunWriterLease implementation (createWriterLease
|
|
798
|
+
// reports cleanup failures via callback, never throws) — this guard is
|
|
799
|
+
// structural only: an uncaught throw from a finally block silently
|
|
800
|
+
// replaces whatever the try already returned, including a properly
|
|
801
|
+
// tagged turnDispatched:true result (#840 r9 判词 class 1 boundary).
|
|
802
|
+
io.stderr(
|
|
803
|
+
formatCliDiagnostic(
|
|
804
|
+
`writer lease release failed unexpectedly (best-effort continue): ${describeErrorIdentity(error)}`,
|
|
805
|
+
),
|
|
806
|
+
);
|
|
807
|
+
}
|
|
549
808
|
}
|
|
550
809
|
}
|
|
551
810
|
|
|
@@ -623,9 +882,32 @@ export function resumeTurnRequestProjectionOptions(
|
|
|
623
882
|
prompt,
|
|
624
883
|
},
|
|
625
884
|
...(request.message === undefined ? {} : { courtAttemptId: randomUUID() }),
|
|
885
|
+
...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
|
|
626
886
|
};
|
|
627
887
|
}
|
|
628
888
|
|
|
889
|
+
/**
|
|
890
|
+
* Hold the writer lease through after-lease build, then hand off to dispatch.
|
|
891
|
+
* Builder (or any throw before dispatch) must release here — dispatch's finally
|
|
892
|
+
* only runs after this handoff (manual resume and station-child auto-resume).
|
|
893
|
+
*/
|
|
894
|
+
async function dispatchAfterWriterLease<T>(input: {
|
|
895
|
+
lease: RunWriterLease;
|
|
896
|
+
build: () => Promise<RoleTurnRequest>;
|
|
897
|
+
dispatch: (request: RoleTurnRequest) => Promise<T>;
|
|
898
|
+
}): Promise<T> {
|
|
899
|
+
let handedOffToDispatch = false;
|
|
900
|
+
try {
|
|
901
|
+
const request = await input.build();
|
|
902
|
+
handedOffToDispatch = true;
|
|
903
|
+
return await input.dispatch(request);
|
|
904
|
+
} finally {
|
|
905
|
+
if (!handedOffToDispatch) {
|
|
906
|
+
await input.lease.release();
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
629
911
|
function isAlreadyFrozenSummonsAttachment(
|
|
630
912
|
runDirectory: string,
|
|
631
913
|
attachmentPath: string,
|
|
@@ -677,7 +959,8 @@ export async function prepareSummonsResumeMaterials(
|
|
|
677
959
|
* Shared manual-resume orchestration for seats whose continuation is the
|
|
678
960
|
* package resume envelope (#599 / #633): load once → structural rejection →
|
|
679
961
|
* optional seat afterAdmittedLoad (method material / controlled failure) →
|
|
680
|
-
* seat turn projection →
|
|
962
|
+
* seat turn projection → station-child auto-resume or public manual resume.
|
|
963
|
+
* Seat-owned loader
|
|
681
964
|
* validation, turn builder, and adapters stay on the seat.
|
|
682
965
|
*
|
|
683
966
|
* Court open/recovery transaction (#637): under the existing writer lease,
|
|
@@ -739,17 +1022,7 @@ export async function runPostAdmissionSeatResume<
|
|
|
739
1022
|
adapters = prepared.adapters;
|
|
740
1023
|
}
|
|
741
1024
|
|
|
742
|
-
|
|
743
|
-
try {
|
|
744
|
-
return await runPostAdmissionManualResume({
|
|
745
|
-
admitted: loaded.admitted,
|
|
746
|
-
env: input.env,
|
|
747
|
-
io: input.io,
|
|
748
|
-
adapters,
|
|
749
|
-
...(input.effectiveEngine === undefined
|
|
750
|
-
? {}
|
|
751
|
-
: { effectiveEngine: input.effectiveEngine }),
|
|
752
|
-
buildRequestAfterLease: async () => {
|
|
1025
|
+
const buildRequestAfterLease = async (): Promise<RoleTurnRequest> => {
|
|
753
1026
|
let openCourtAttemptId: string | undefined;
|
|
754
1027
|
// Build uses the admitted judged under this lease (rehydrated when open
|
|
755
1028
|
// court materials ride). Settlement identity stays on the outer admitted.
|
|
@@ -843,8 +1116,88 @@ export async function runPostAdmissionSeatResume<
|
|
|
843
1116
|
await recordCurrentCourt(admittedForBuild.runDirectory, court);
|
|
844
1117
|
}
|
|
845
1118
|
}
|
|
846
|
-
|
|
847
|
-
|
|
1119
|
+
return turnRequest;
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
// Court recovery / open under lease, then dispatch.
|
|
1123
|
+
// Station-child same-ticket/same-parent resume is call-local auto-resume
|
|
1124
|
+
// (#840 / #416). Public `ak-role resume` stays one-shot (ADR 0080).
|
|
1125
|
+
try {
|
|
1126
|
+
if (input.env.stationChild === true) {
|
|
1127
|
+
let firstTurn: RoleTurnRequest | undefined;
|
|
1128
|
+
const stationAdapters = withOnceSuccessfulBeforeDispatch(adapters);
|
|
1129
|
+
type StationChildAttempt = { readonly resumeTurn: boolean };
|
|
1130
|
+
return await runWithAutoResumeLoop({
|
|
1131
|
+
admitted: loaded.admitted,
|
|
1132
|
+
principalAuthority: input.env.principalAuthority,
|
|
1133
|
+
isPrincipalAvailable: resolveHostAwareSessionAvailability(
|
|
1134
|
+
input.env.host,
|
|
1135
|
+
input.env.principalAuthority,
|
|
1136
|
+
),
|
|
1137
|
+
io: input.io,
|
|
1138
|
+
sessionAppender: input.env.sessionAppender,
|
|
1139
|
+
autoResumeLimit: input.env.autoResumeLimit,
|
|
1140
|
+
buildInitialPayload: (): StationChildAttempt => ({ resumeTurn: false }),
|
|
1141
|
+
buildResumePayload: (): StationChildAttempt => ({ resumeTurn: true }),
|
|
1142
|
+
// Same as public manual resume: prior-court sealed acceptance is not a
|
|
1143
|
+
// redispatch brake (#833). New-court station-child turns still auto-resume.
|
|
1144
|
+
dispatch: async (payload, lease, _isFirst, attemptIo) =>
|
|
1145
|
+
dispatchAfterWriterLease({
|
|
1146
|
+
lease,
|
|
1147
|
+
build: async () => {
|
|
1148
|
+
// #840 r8 判词 class 2: this call-local retry must keep this
|
|
1149
|
+
// court's frozen summons / 交卷 body / attachments verbatim
|
|
1150
|
+
// (same object as firstTurn) and project only the minimal
|
|
1151
|
+
// host-needed resume trigger — never the manual-resume engine
|
|
1152
|
+
// handbook (buildResumeContinuationPrompt), which would replace
|
|
1153
|
+
// a 审核循环 same-ticket continuation with a bare outsourcing
|
|
1154
|
+
// 「重新读」 envelope (#755 contract, resumeTurnRequestProjectionOptions
|
|
1155
|
+
// above). RESUME_TRANSPORT_ENVELOPE is the same package-owned,
|
|
1156
|
+
// non-semantic trigger that projection already uses for a
|
|
1157
|
+
// same-ticket summons carrying no instruction/attachments.
|
|
1158
|
+
if (payload.resumeTurn && firstTurn !== undefined) {
|
|
1159
|
+
return {
|
|
1160
|
+
...firstTurn,
|
|
1161
|
+
continuation: {
|
|
1162
|
+
kind: "resume",
|
|
1163
|
+
prompt: RESUME_TRANSPORT_ENVELOPE,
|
|
1164
|
+
},
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
const turnRequest = await buildRequestAfterLease();
|
|
1168
|
+
firstTurn = turnRequest;
|
|
1169
|
+
return turnRequest;
|
|
1170
|
+
},
|
|
1171
|
+
dispatch: (turnRequest) =>
|
|
1172
|
+
dispatchPostAdmissionTurn({
|
|
1173
|
+
admitted: loaded.admitted,
|
|
1174
|
+
env: {
|
|
1175
|
+
...input.env,
|
|
1176
|
+
...(loaded.admitted.correlationId === undefined
|
|
1177
|
+
? {}
|
|
1178
|
+
: { correlationId: loaded.admitted.correlationId }),
|
|
1179
|
+
},
|
|
1180
|
+
io: attemptIo,
|
|
1181
|
+
request: turnRequest,
|
|
1182
|
+
lease,
|
|
1183
|
+
adapters: stationAdapters,
|
|
1184
|
+
persistRunState: false,
|
|
1185
|
+
...(input.effectiveEngine === undefined
|
|
1186
|
+
? {}
|
|
1187
|
+
: { effectiveEngine: input.effectiveEngine }),
|
|
1188
|
+
}),
|
|
1189
|
+
}),
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
return await runPostAdmissionManualResume({
|
|
1193
|
+
admitted: loaded.admitted,
|
|
1194
|
+
env: input.env,
|
|
1195
|
+
io: input.io,
|
|
1196
|
+
adapters,
|
|
1197
|
+
...(input.effectiveEngine === undefined
|
|
1198
|
+
? {}
|
|
1199
|
+
: { effectiveEngine: input.effectiveEngine }),
|
|
1200
|
+
buildRequestAfterLease,
|
|
848
1201
|
});
|
|
849
1202
|
} catch (error) {
|
|
850
1203
|
// Open-court rehydrate load under lease may still surface seat structural
|
|
@@ -858,7 +1211,8 @@ export async function runPostAdmissionSeatResume<
|
|
|
858
1211
|
}
|
|
859
1212
|
|
|
860
1213
|
/**
|
|
861
|
-
* Shared post-admission one-shot path:
|
|
1214
|
+
* Shared post-admission one-shot path: folds into runPostAdmissionResumable (#840 / #416).
|
|
1215
|
+
* All callable roles share the single auto-resume loop.
|
|
862
1216
|
* Initial facades own the durable admitted mark (markRunAdmitted) before
|
|
863
1217
|
* entering; manual resume never re-admits.
|
|
864
1218
|
*/
|
|
@@ -877,29 +1231,24 @@ export async function runPostAdmissionOneShot<
|
|
|
877
1231
|
admitted?: A;
|
|
878
1232
|
terminal?: T;
|
|
879
1233
|
}> {
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
)
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
io,
|
|
899
|
-
request,
|
|
900
|
-
lease,
|
|
901
|
-
adapters,
|
|
902
|
-
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1234
|
+
const engine = input.effectiveEngine ?? input.env.engine;
|
|
1235
|
+
return await runPostAdmissionResumable({
|
|
1236
|
+
admitted: input.admitted,
|
|
1237
|
+
env: input.env,
|
|
1238
|
+
io: input.io,
|
|
1239
|
+
buildInitialRequest: () => input.request,
|
|
1240
|
+
buildResumeRequest: () => ({
|
|
1241
|
+
...input.request,
|
|
1242
|
+
continuation: {
|
|
1243
|
+
kind: "resume",
|
|
1244
|
+
prompt: buildResumeContinuationPrompt({
|
|
1245
|
+
packageRoot: input.env.packageRoot,
|
|
1246
|
+
...(engine === undefined ? {} : { engine }),
|
|
1247
|
+
}),
|
|
1248
|
+
},
|
|
1249
|
+
}),
|
|
1250
|
+
adapters: input.adapters,
|
|
1251
|
+
...(input.effectiveEngine === undefined ? {} : { effectiveEngine: input.effectiveEngine }),
|
|
903
1252
|
});
|
|
904
1253
|
}
|
|
905
1254
|
|
|
@@ -923,11 +1272,13 @@ export async function runPostAdmissionResumable<
|
|
|
923
1272
|
admitted?: A;
|
|
924
1273
|
terminal?: T;
|
|
925
1274
|
}> {
|
|
926
|
-
const { admitted, env, io, buildInitialRequest, buildResumeRequest,
|
|
1275
|
+
const { admitted, env, io, buildInitialRequest, buildResumeRequest, effectiveEngine } = input;
|
|
1276
|
+
const adapters = withOnceSuccessfulBeforeDispatch(input.adapters);
|
|
927
1277
|
|
|
928
1278
|
return runWithAutoResumeLoop({
|
|
929
1279
|
admitted,
|
|
930
1280
|
principalAuthority: env.principalAuthority,
|
|
1281
|
+
isPrincipalAvailable: resolveHostAwareSessionAvailability(env.host, env.principalAuthority),
|
|
931
1282
|
io,
|
|
932
1283
|
sessionAppender: env.sessionAppender,
|
|
933
1284
|
autoResumeLimit: env.autoResumeLimit,
|
|
@@ -946,6 +1297,7 @@ export async function runPostAdmissionResumable<
|
|
|
946
1297
|
request,
|
|
947
1298
|
lease,
|
|
948
1299
|
adapters,
|
|
1300
|
+
persistRunState: false,
|
|
949
1301
|
// #600: every attempt (initial + auto-resume) writes seat engine when present.
|
|
950
1302
|
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
951
1303
|
}),
|
|
@@ -956,6 +1308,7 @@ export async function runPostAdmissionResumable<
|
|
|
956
1308
|
* Manual resume: lease + dispatch. Pass-through to the host — no sealed-accepted
|
|
957
1309
|
* short-circuit (#833 / #416). Court open (summons / message / open court) is
|
|
958
1310
|
* built under lease when using buildRequestAfterLease; sole-final stays per-attempt.
|
|
1311
|
+
* After-lease build shares dispatchAfterWriterLease with station-child auto-resume.
|
|
959
1312
|
*/
|
|
960
1313
|
export async function runPostAdmissionManualResume<
|
|
961
1314
|
A extends AdmittedRoleInvocation,
|
|
@@ -1015,48 +1368,50 @@ export async function runPostAdmissionManualResume<
|
|
|
1015
1368
|
throw error;
|
|
1016
1369
|
}
|
|
1017
1370
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
);
|
|
1371
|
+
const result = await dispatchAfterWriterLease({
|
|
1372
|
+
lease,
|
|
1373
|
+
build: async () => {
|
|
1374
|
+
if (request === undefined) {
|
|
1375
|
+
if (buildRequestAfterLease === undefined) {
|
|
1376
|
+
throw new Error(
|
|
1377
|
+
"runPostAdmissionManualResume requires request or buildRequestAfterLease",
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
request = await buildRequestAfterLease();
|
|
1028
1381
|
}
|
|
1029
|
-
request
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
};
|
|
1057
|
-
} finally {
|
|
1058
|
-
if (!handedOffToDispatch) {
|
|
1059
|
-
await lease.release();
|
|
1060
|
-
}
|
|
1382
|
+
return request;
|
|
1383
|
+
},
|
|
1384
|
+
dispatch: (turnRequest) =>
|
|
1385
|
+
dispatchPostAdmissionTurn({
|
|
1386
|
+
admitted,
|
|
1387
|
+
env: {
|
|
1388
|
+
...env,
|
|
1389
|
+
...(effectiveModel === undefined ? {} : { model: effectiveModel }),
|
|
1390
|
+
...(admitted.correlationId === undefined
|
|
1391
|
+
? {}
|
|
1392
|
+
: { correlationId: admitted.correlationId }),
|
|
1393
|
+
},
|
|
1394
|
+
io,
|
|
1395
|
+
request: turnRequest,
|
|
1396
|
+
lease,
|
|
1397
|
+
adapters,
|
|
1398
|
+
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1399
|
+
}),
|
|
1400
|
+
});
|
|
1401
|
+
if (
|
|
1402
|
+
result.terminal !== undefined &&
|
|
1403
|
+
isLawfulTypedTerminalOutcome(result.terminal.roleOutcome)
|
|
1404
|
+
) {
|
|
1405
|
+
await persistReturnedRunState(admitted, env.principalAuthority, { lawful: true });
|
|
1406
|
+
io.stdout(formatTerminalResult(result.terminal));
|
|
1407
|
+
}
|
|
1408
|
+
if (result.terminal !== undefined) {
|
|
1409
|
+
(result.terminal as { autoResumeCount?: number }).autoResumeCount = 0;
|
|
1061
1410
|
}
|
|
1411
|
+
return {
|
|
1412
|
+
...result,
|
|
1413
|
+
...(staleWriterLeaseReclaimed === true
|
|
1414
|
+
? { staleWriterLeaseReclaimed: true as const }
|
|
1415
|
+
: {}),
|
|
1416
|
+
};
|
|
1062
1417
|
}
|