@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
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { renderAgentStartMaterials } from "./agent-start-materials.js";
|
|
2
|
+
/** Fold structured system-prompt authority into the provider-visible ACP override. */
|
|
3
|
+
export function renderSystemPromptOverride(authority) {
|
|
4
|
+
return renderAgentStartMaterials(authority.body, authority.materials);
|
|
5
|
+
}
|
|
@@ -15,11 +15,35 @@ import { constants as fsConstants } from "node:fs";
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
import { lstat, mkdir, open } from "node:fs/promises";
|
|
17
17
|
import { join } from "node:path";
|
|
18
|
-
import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
|
|
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, } from "./settlement.js";
|
|
21
|
+
import { presentFailureTerminal, presentStructuralRejection, resolveControlledFailureResumeObservation, } from "./settlement.js";
|
|
22
22
|
const dummyIo = { stdout: () => { }, stderr: () => { } };
|
|
23
|
+
/**
|
|
24
|
+
* Persist run-state after a host-turn result, outside the retried dispatch try.
|
|
25
|
+
* Lawful settlement always seals terminal — a typed 429 observation must not
|
|
26
|
+
* win (#416 成功即停). 429 only marks resumable on the controlled-failure path.
|
|
27
|
+
* Write failure must escape the retried dispatch try instead of becoming a
|
|
28
|
+
* synthetic host-turn terminal.
|
|
29
|
+
*/
|
|
30
|
+
export async function persistReturnedRunState(admitted, authority, options) {
|
|
31
|
+
if (options?.lawful === true) {
|
|
32
|
+
await markRunTerminal(admitted.runDirectory);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const resumeObservation = await resolveControlledFailureResumeObservation({
|
|
36
|
+
runDirectory: admitted.runDirectory,
|
|
37
|
+
});
|
|
38
|
+
const typedHttp429 = resumeObservation.typedHttp429;
|
|
39
|
+
if (admitted.principal !== undefined && typedHttp429 !== undefined) {
|
|
40
|
+
if (await authority.isAvailable(admitted.principal)) {
|
|
41
|
+
await markRunResumable(admitted.runDirectory, typedHttp429);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
await markRunTerminal(admitted.runDirectory);
|
|
46
|
+
}
|
|
23
47
|
function presentTerminal(terminal, io) {
|
|
24
48
|
if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
|
|
25
49
|
presentFailureTerminal(terminal, io);
|
|
@@ -42,6 +66,25 @@ async function finalizeExceptionRunBestEffort(runDirectory, io) {
|
|
|
42
66
|
io.stderr(`run terminal-state finalization failed (best-effort continue): ${describeErrorIdentity(error)}\n`);
|
|
43
67
|
}
|
|
44
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Thrown by dispatchPostAdmissionTurn when a failure happens inside its own
|
|
71
|
+
* settlement authority (presentControlledFailure) after the host turn
|
|
72
|
+
* genuinely started — never to fabricate a replacement terminal (ADR 0080
|
|
73
|
+
* single-settlement-disposition: settlement stays exactly presentControlledFailure
|
|
74
|
+
* / settleFailureTerminalResult, or — once the retry budget is exhausted —
|
|
75
|
+
* this loop's own dispatchExceptionFailureTerminal). Its only job is to carry
|
|
76
|
+
* the "turn already started" fact across the throw boundary so this loop
|
|
77
|
+
* selects a resume payload on the next attempt instead of replaying the
|
|
78
|
+
* initial one (#840 r9 判词 class 1 boundary — 覆盖 executeTurn 已启动后至
|
|
79
|
+
* 返回带 turnDispatched 结果前的全部异常). `cause` is the true failure;
|
|
80
|
+
* retention below serializes it whole via the standard Error.cause chain.
|
|
81
|
+
*/
|
|
82
|
+
export class TurnDispatchedFailure extends Error {
|
|
83
|
+
name = "TurnDispatchedFailure";
|
|
84
|
+
constructor(cause) {
|
|
85
|
+
super(`settlement failed after the host turn genuinely started: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
45
88
|
/** Session custom-entry type carrying the pointer to one dispatch error file. */
|
|
46
89
|
export const DISPATCH_ERROR_RETENTION_ENTRY_TYPE = "ak_run_dispatch_error_retention";
|
|
47
90
|
/** Artifacts subdirectory of a run directory (established run-artifacts location). */
|
|
@@ -51,20 +94,23 @@ function runArtifactsDirectory(runDirectory) {
|
|
|
51
94
|
/**
|
|
52
95
|
* #182-A hardened path identity, mirrored from settlement.ts's
|
|
53
96
|
* ensureAuditEvidenceDirectory: a planted symlink at the run directory or the
|
|
54
|
-
* artifacts path must not receive
|
|
97
|
+
* artifacts path must not receive a durable run artifact (O_NOFOLLOW only
|
|
55
98
|
* protects the final file name; recursive mkdir would accept a symlinked
|
|
56
|
-
* parent). Fails loudly with the true cause instead.
|
|
99
|
+
* parent). Fails loudly with the true cause instead. Shared by every caller
|
|
100
|
+
* that needs a durable run-artifacts write independent of session/dossier
|
|
101
|
+
* health — dispatch-error retention here, and post-admission's best-effort
|
|
102
|
+
* cleanup diagnostic (#840 bounce class 2).
|
|
57
103
|
*/
|
|
58
|
-
async function ensureRealArtifactsDirectory(runDirectory) {
|
|
104
|
+
export async function ensureRealArtifactsDirectory(runDirectory) {
|
|
59
105
|
const runStat = await lstat(runDirectory);
|
|
60
106
|
if (runStat.isSymbolicLink() || !runStat.isDirectory()) {
|
|
61
|
-
throw new Error("
|
|
107
|
+
throw new Error("run artifact retention: run directory is not a real directory");
|
|
62
108
|
}
|
|
63
109
|
const artifactsDir = runArtifactsDirectory(runDirectory);
|
|
64
110
|
try {
|
|
65
111
|
const existing = await lstat(artifactsDir);
|
|
66
112
|
if (existing.isSymbolicLink() || !existing.isDirectory()) {
|
|
67
|
-
throw new Error("
|
|
113
|
+
throw new Error("run artifact retention: artifacts path is not a real directory");
|
|
68
114
|
}
|
|
69
115
|
}
|
|
70
116
|
catch (error) {
|
|
@@ -73,7 +119,7 @@ async function ensureRealArtifactsDirectory(runDirectory) {
|
|
|
73
119
|
await mkdir(artifactsDir, { recursive: true });
|
|
74
120
|
const created = await lstat(artifactsDir);
|
|
75
121
|
if (created.isSymbolicLink() || !created.isDirectory()) {
|
|
76
|
-
throw new Error("
|
|
122
|
+
throw new Error("run artifact retention: artifacts directory is not a real directory");
|
|
77
123
|
}
|
|
78
124
|
}
|
|
79
125
|
return artifactsDir;
|
|
@@ -212,18 +258,35 @@ async function retainDispatchError(admitted, principalAuthority, sessionAppender
|
|
|
212
258
|
}
|
|
213
259
|
return pointerError === undefined ? { file: filePath } : { file: filePath, pointerError };
|
|
214
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Unwrap TurnDispatchedFailure before this loop's own final presentation
|
|
263
|
+
* (#840 r9 判词 class 1 — the auto-resume.ts final presentation boundary).
|
|
264
|
+
* The wrapper only carries the "turn already genuinely started" signal
|
|
265
|
+
* across the throw boundary so the loop above selects a resume payload; the
|
|
266
|
+
* typed decisiveFacts and diagnostic text below must name the real cause it
|
|
267
|
+
* wraps (接住可以,洗白不行 — 未识别异常不得冒用具体标签,真因必须落痕), not
|
|
268
|
+
* the internal signal's own identity.
|
|
269
|
+
*/
|
|
270
|
+
function unwrapTurnDispatchedFailure(error) {
|
|
271
|
+
let current = error;
|
|
272
|
+
while (current instanceof TurnDispatchedFailure) {
|
|
273
|
+
current = current.cause;
|
|
274
|
+
}
|
|
275
|
+
return current;
|
|
276
|
+
}
|
|
215
277
|
/**
|
|
216
278
|
* Typed failure terminal for a retry path that ended with only exceptions:
|
|
217
279
|
* loud, non-lawful, carrying the last true cause and the pointers to the
|
|
218
280
|
* full per-attempt error files. Never rethrows the raw exception at callers.
|
|
219
281
|
*/
|
|
220
282
|
function dispatchExceptionFailureTerminal(input) {
|
|
283
|
+
const causeError = unwrapTurnDispatchedFailure(input.causeError);
|
|
221
284
|
// #426 review: this terminal fires whenever the FINAL dispatch throws, not
|
|
222
285
|
// only when every attempt threw — do not misrepresent a mixed retry history.
|
|
223
286
|
const history = input.everyAttemptThrew
|
|
224
287
|
? "dispatch threw an exception on every attempt"
|
|
225
288
|
: "the final dispatch threw an exception";
|
|
226
|
-
const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(
|
|
289
|
+
const diagnostic = `${history} (${input.endReason}; resumes used ${input.autoResumeAttempts}); last cause: ${describeErrorIdentity(causeError)}`;
|
|
227
290
|
const decisiveFacts = {
|
|
228
291
|
cause: "unrecognized",
|
|
229
292
|
diagnostic,
|
|
@@ -233,7 +296,7 @@ function dispatchExceptionFailureTerminal(input) {
|
|
|
233
296
|
if (input.errorFiles.length > 0) {
|
|
234
297
|
decisiveFacts.lastDispatchErrorFile = input.errorFiles[input.errorFiles.length - 1];
|
|
235
298
|
}
|
|
236
|
-
const candidate =
|
|
299
|
+
const candidate = causeError;
|
|
237
300
|
if (typeof candidate?.name === "string")
|
|
238
301
|
decisiveFacts.errorName = candidate.name;
|
|
239
302
|
if (typeof candidate?.code === "string" || typeof candidate?.code === "number") {
|
|
@@ -262,6 +325,8 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
262
325
|
// `attempts >= limit` comparison (always false) — reject here, before any dispatch.
|
|
263
326
|
const limit = options.autoResumeLimit ?? AUTO_RESUME_LIMIT;
|
|
264
327
|
parseAutoResumeLimit(limit);
|
|
328
|
+
const isPrincipalAvailable = options.isPrincipalAvailable ??
|
|
329
|
+
((principal) => options.principalAuthority.isAvailable(principal));
|
|
265
330
|
let autoResumeAttempts = 0;
|
|
266
331
|
let isFirst = true;
|
|
267
332
|
let currentPayload = options.buildInitialPayload();
|
|
@@ -282,6 +347,11 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
282
347
|
throw error;
|
|
283
348
|
}
|
|
284
349
|
let result;
|
|
350
|
+
// Set only when the caught throw is a TurnDispatchedFailure (#840 r9 判词
|
|
351
|
+
// class 1): the host turn genuinely started this attempt even though
|
|
352
|
+
// dispatch produced no result — the next payload must still be a resume,
|
|
353
|
+
// not a replay of the initial one.
|
|
354
|
+
let turnStartedBeforeThrow = false;
|
|
285
355
|
try {
|
|
286
356
|
result = await options.dispatch(currentPayload, lease, isFirst, dummyIo);
|
|
287
357
|
}
|
|
@@ -292,6 +362,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
292
362
|
// diagnostic-sink-isolation precedent). The dispatcher owns lease release
|
|
293
363
|
// in its own finally, so the retry round starts with the lock free.
|
|
294
364
|
lastThrownError = error;
|
|
365
|
+
turnStartedBeforeThrow = error instanceof TurnDispatchedFailure;
|
|
295
366
|
const attempt = dispatchOrdinal;
|
|
296
367
|
try {
|
|
297
368
|
// Track the file as soon as it is durably written (#426 review):
|
|
@@ -309,6 +380,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
309
380
|
}
|
|
310
381
|
}
|
|
311
382
|
dispatchOrdinal += 1;
|
|
383
|
+
let persistFailure;
|
|
312
384
|
if (result !== undefined) {
|
|
313
385
|
everyAttemptThrew = false;
|
|
314
386
|
const terminal = result.terminal;
|
|
@@ -316,6 +388,21 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
316
388
|
terminal.autoResumeCount = autoResumeAttempts;
|
|
317
389
|
}
|
|
318
390
|
const lawful = terminal !== undefined && isLawfulTypedTerminalOutcome(terminal.roleOutcome);
|
|
391
|
+
if (result.needsPersist === true) {
|
|
392
|
+
// Persist on the loop seam, not inside retried dispatch.
|
|
393
|
+
try {
|
|
394
|
+
await persistReturnedRunState(options.admitted, { isAvailable: isPrincipalAvailable }, lawful ? { lawful: true } : undefined);
|
|
395
|
+
}
|
|
396
|
+
catch (persistError) {
|
|
397
|
+
// Lawful persist stays loud (no fake terminal). Non-lawful persist is
|
|
398
|
+
// not a host-turn failure — do not auto-resume it — but sealed stop
|
|
399
|
+
// must still see the already-settled result (#648).
|
|
400
|
+
if (lawful)
|
|
401
|
+
throw persistError;
|
|
402
|
+
persistFailure = { error: persistError };
|
|
403
|
+
lastThrownError = persistError;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
319
406
|
if (lawful) {
|
|
320
407
|
if (terminal !== undefined) {
|
|
321
408
|
// Present lawful terminal once to real io (dummy was used inside dispatch)
|
|
@@ -323,6 +410,11 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
323
410
|
}
|
|
324
411
|
return result;
|
|
325
412
|
}
|
|
413
|
+
if (result.skipAutoResume === true) {
|
|
414
|
+
if (terminal !== undefined)
|
|
415
|
+
presentTerminal(terminal, options.io);
|
|
416
|
+
return result;
|
|
417
|
+
}
|
|
326
418
|
}
|
|
327
419
|
// Settlement-owned sealed disposition before any redispatch (#648 / #672):
|
|
328
420
|
// shared for non-lawful return and direct throw. Only entry presentation
|
|
@@ -357,6 +449,9 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
357
449
|
};
|
|
358
450
|
}
|
|
359
451
|
}
|
|
452
|
+
if (persistFailure !== undefined) {
|
|
453
|
+
throw persistFailure.error;
|
|
454
|
+
}
|
|
360
455
|
if (result !== undefined) {
|
|
361
456
|
const terminal = result.terminal;
|
|
362
457
|
if (autoResumeAttempts >= limit) {
|
|
@@ -364,7 +459,8 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
364
459
|
presentTerminal(terminal, options.io);
|
|
365
460
|
return result;
|
|
366
461
|
}
|
|
367
|
-
if (
|
|
462
|
+
if (result.turnDispatched === true
|
|
463
|
+
&& !(await isPrincipalAvailable(options.admitted.principal))) {
|
|
368
464
|
if (terminal !== undefined)
|
|
369
465
|
presentTerminal(terminal, options.io);
|
|
370
466
|
return result;
|
|
@@ -389,7 +485,7 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
389
485
|
terminal,
|
|
390
486
|
};
|
|
391
487
|
}
|
|
392
|
-
if (!(await
|
|
488
|
+
if (!(await isPrincipalAvailable(options.admitted.principal))) {
|
|
393
489
|
const terminal = dispatchExceptionFailureTerminal({
|
|
394
490
|
role: options.admitted.role,
|
|
395
491
|
runId: options.admitted.runId,
|
|
@@ -408,7 +504,13 @@ export async function runWithAutoResumeLoop(options) {
|
|
|
408
504
|
}
|
|
409
505
|
}
|
|
410
506
|
autoResumeAttempts++;
|
|
411
|
-
|
|
507
|
+
// Resume payload only after a host turn actually started — either a
|
|
508
|
+
// returned result says so, or the attempt threw a TurnDispatchedFailure
|
|
509
|
+
// after genuinely starting the turn (#840 r9 判词 class 1). Pre-turn
|
|
510
|
+
// throws and beforeDispatch failures retry the initial payload (#840 / #416).
|
|
511
|
+
if (result?.turnDispatched === true || turnStartedBeforeThrow) {
|
|
512
|
+
currentPayload = options.buildResumePayload();
|
|
513
|
+
}
|
|
412
514
|
isFirst = false;
|
|
413
515
|
}
|
|
414
516
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.js";
|
|
2
|
+
import { CliUsageError } from "./cli-errors.js";
|
|
3
|
+
import { admitDiaristInvocation, bindAdmittedTicketNumber, buildInstructionTransportPrompt, } from "./invocation.js";
|
|
4
|
+
import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissionSeatResume, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
|
|
5
|
+
import { loadResumableDiaristRun, markRunAdmitted, } from "./run-lifecycle.js";
|
|
6
|
+
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
|
|
7
|
+
import { presentStructuralRejection, trySettleDiaristTerminalResult, } from "./settlement.js";
|
|
8
|
+
import { projectRoleTurnRequest, } from "./turn-request.js";
|
|
9
|
+
/** Project admitted invocation onto the host-neutral turn request. */
|
|
10
|
+
export function buildDiaristTurnRequest(admitted, options) {
|
|
11
|
+
return projectRoleTurnRequest(admitted, { activation: { role: "diarist" } }, options);
|
|
12
|
+
}
|
|
13
|
+
function diaristAdapters() {
|
|
14
|
+
return {
|
|
15
|
+
trySettle: (admitted, authority, scope) => trySettleDiaristTerminalResult(admitted, authority, scope),
|
|
16
|
+
shouldPresentSettled: () => true,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export async function runPublicDiarist(argv, env, io, parseDiaristArgv) {
|
|
20
|
+
let parsed;
|
|
21
|
+
try {
|
|
22
|
+
parsed = parseDiaristArgv(argv);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof CliUsageError) {
|
|
26
|
+
presentStructuralRejection(error, io);
|
|
27
|
+
return { exitCode: 2 };
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
// #637 / #771 / ADR 0075 / #779: ticket identity is the LLM's typed assertion
|
|
32
|
+
// on this turn, OR a typed handoff key already held by the caller (countersign
|
|
33
|
+
// refresh). Code never pre-judges the summons text and never freezes a
|
|
34
|
+
// candidate catalog. First summons without handoff stays unbound until assert.
|
|
35
|
+
const projectRoot = parsed.project ?? env.cwd;
|
|
36
|
+
const handoffTicket = env.boundTicketNumber;
|
|
37
|
+
if (typeof handoffTicket === "number" &&
|
|
38
|
+
Number.isSafeInteger(handoffTicket) &&
|
|
39
|
+
handoffTicket >= 1) {
|
|
40
|
+
const summons = {
|
|
41
|
+
instruction: parsed.instruction,
|
|
42
|
+
instructionEmpty: parsed.instruction.trim() === "",
|
|
43
|
+
attachmentPaths: parsed.attachmentPaths,
|
|
44
|
+
};
|
|
45
|
+
const resumed = await tryResumeSameTicketSeatRun({
|
|
46
|
+
home: env.home,
|
|
47
|
+
projectRoot,
|
|
48
|
+
role: "diarist",
|
|
49
|
+
ticketNumber: handoffTicket,
|
|
50
|
+
freshSummons: env.freshSummons,
|
|
51
|
+
summons,
|
|
52
|
+
resume: (runId, materials) => runPublicDiaristResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
53
|
+
});
|
|
54
|
+
if (resumed !== undefined)
|
|
55
|
+
return resumed;
|
|
56
|
+
}
|
|
57
|
+
let admitted;
|
|
58
|
+
try {
|
|
59
|
+
admitted = await admitDiaristInvocation({
|
|
60
|
+
home: env.home,
|
|
61
|
+
principalAuthority: env.principalAuthority,
|
|
62
|
+
cwd: env.cwd,
|
|
63
|
+
instruction: parsed.instruction,
|
|
64
|
+
attachmentPaths: parsed.attachmentPaths,
|
|
65
|
+
...(parsed.project === undefined ? {} : { project: parsed.project }),
|
|
66
|
+
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
67
|
+
...(env.model === undefined ? {} : { model: env.model }),
|
|
68
|
+
...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof CliUsageError) {
|
|
73
|
+
presentStructuralRejection(error, io);
|
|
74
|
+
return { exitCode: 2 };
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
await markRunAdmitted(admitted, env.principalAuthority);
|
|
79
|
+
// Typed handoff: bind before the turn so identity is on durable pages.
|
|
80
|
+
if (typeof handoffTicket === "number" &&
|
|
81
|
+
Number.isSafeInteger(handoffTicket) &&
|
|
82
|
+
handoffTicket >= 1) {
|
|
83
|
+
await bindAdmittedTicketNumber(admitted, handoffTicket);
|
|
84
|
+
}
|
|
85
|
+
const turnProjection = {
|
|
86
|
+
packageRoot: env.packageRoot,
|
|
87
|
+
home: env.home,
|
|
88
|
+
agentDir: env.agentDir,
|
|
89
|
+
...(env.model === undefined ? {} : { model: env.model }),
|
|
90
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
91
|
+
...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
|
|
92
|
+
...(env.correlationId === undefined || env.correlationId.trim() === ""
|
|
93
|
+
? {}
|
|
94
|
+
: { correlationId: env.correlationId }),
|
|
95
|
+
continuation: {
|
|
96
|
+
kind: "initial",
|
|
97
|
+
prompt: buildInstructionTransportPrompt(admitted, engineSessionMaterialFromOptions({
|
|
98
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
99
|
+
packageRoot: env.packageRoot,
|
|
100
|
+
})),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const turnRequest = buildDiaristTurnRequest(admitted, turnProjection);
|
|
104
|
+
return await runPostAdmissionOneShot({
|
|
105
|
+
admitted,
|
|
106
|
+
env,
|
|
107
|
+
io,
|
|
108
|
+
request: turnRequest,
|
|
109
|
+
adapters: diaristAdapters(),
|
|
110
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
111
|
+
}).then(async (result) => {
|
|
112
|
+
// Accept may have bound ticket onto durable pages after LLM assertion.
|
|
113
|
+
// Mirror only lawful non-escalate accepted terminals — escalate must not
|
|
114
|
+
// leak an unverified ticketNumber into the caller-visible typed key.
|
|
115
|
+
if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
|
|
116
|
+
const roleOutcome = result.terminal?.roleOutcome;
|
|
117
|
+
if (roleOutcome !== undefined &&
|
|
118
|
+
roleOutcome.kind === "accepted" &&
|
|
119
|
+
roleOutcome.status !== "escalate") {
|
|
120
|
+
const facts = roleOutcome.decisiveFacts;
|
|
121
|
+
const raw = typeof facts.ticketNumber === "number"
|
|
122
|
+
? facts.ticketNumber
|
|
123
|
+
: typeof facts.sitian?.ticketNumber === "number"
|
|
124
|
+
? facts.sitian.ticketNumber
|
|
125
|
+
: undefined;
|
|
126
|
+
if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) {
|
|
127
|
+
admitted.ticketNumber = raw;
|
|
128
|
+
if (result.admitted.ticketNumber === undefined) {
|
|
129
|
+
result.admitted.ticketNumber = raw;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Resume a previously admitted Diarist run (#708 / ADR 0079 同票传召 = resume).
|
|
139
|
+
* LLM re-finds materials; entry identity keeps the volume idempotent.
|
|
140
|
+
*/
|
|
141
|
+
export async function runPublicDiaristResume(request, env, io) {
|
|
142
|
+
return await runPostAdmissionSeatResume({
|
|
143
|
+
request,
|
|
144
|
+
env,
|
|
145
|
+
io,
|
|
146
|
+
load: (effective) => loadResumableDiaristRun(env.home, effective.runId, env.principalAuthority),
|
|
147
|
+
buildTurnRequest: async (admitted, effective) => {
|
|
148
|
+
const summonsPrepared = await prepareSummonsResumeMaterials(admitted.runDirectory, effective.summons);
|
|
149
|
+
return buildDiaristTurnRequest(admitted, resumeTurnRequestProjectionOptions(admitted, effective, env, summonsPrepared));
|
|
150
|
+
},
|
|
151
|
+
adapters: diaristAdapters(),
|
|
152
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
153
|
+
});
|
|
154
|
+
}
|