@akagilnc/pi-workflow-roles 0.1.6017 → 0.1.6021
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/acp-host/production-host.js +172 -249
- package/dist/headless-host/production-host.js +181 -258
- package/dist/public-cli/instruction-seat-run.js +9 -146
- package/dist/public-cli/main.js +125 -213
- package/dist/public-cli/post-admission.js +1 -19
- package/dist/report-ticket-tool.js +1 -4
- package/dist/submission-ledger.js +16 -1
- package/package.json +1 -1
- package/resources/ticket-at-receipt.md +1 -0
- package/src/public-cli/instruction-seat-run.ts +9 -182
- package/src/public-cli/post-admission.ts +1 -37
- package/src/report-ticket-tool.ts +1 -5
- package/src/submission-ledger.ts +16 -1
- package/resources/missing-ticket-reask.md +0 -1
|
@@ -684,31 +684,13 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
684
684
|
pendingTerminal !== undefined &&
|
|
685
685
|
result.terminal === pendingTerminal) {
|
|
686
686
|
try {
|
|
687
|
-
|
|
688
|
-
// preserves the sealed terminal face. ticketReasksSpent is budget across
|
|
689
|
-
// rounds — after audit continue, ordinary no-volume turns publish
|
|
690
|
-
// no_receipt honestly. previewOnly skips the terminal pointer;
|
|
691
|
-
// recordAttemptHistory still appends.
|
|
692
|
-
const softTicketReaskNoSeal = pendingSettlement !== "sealed" && env.softTicketReaskTurn === true;
|
|
693
|
-
const noSealScope = softTicketReaskNoSeal
|
|
694
|
-
? {
|
|
695
|
-
...courtScope,
|
|
696
|
-
previewOnly: true,
|
|
697
|
-
recordAttemptHistory: true,
|
|
698
|
-
}
|
|
699
|
-
: { ...courtScope, recordAttemptHistory: true };
|
|
687
|
+
const noSealScope = { ...courtScope, recordAttemptHistory: true };
|
|
700
688
|
const terminal = pendingSettlement === "sealed"
|
|
701
689
|
? await adapters.trySettle(admitted, env.principalAuthority, { ...courtScope, recordAttemptHistory: true })
|
|
702
690
|
: await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, noSealScope, receiptDelivery.issuedDeliveryRequests()), noSealScope);
|
|
703
691
|
if (terminal === undefined)
|
|
704
692
|
throw new Error("settled host attempt vanished before publication");
|
|
705
693
|
result = { ...result, terminal, exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome) };
|
|
706
|
-
// #1171 F2-R7: ownership evidence at this soft-reask turn's seal —
|
|
707
|
-
// mutate the live ownership cell carried on env (survives spreads).
|
|
708
|
-
if (pendingSettlement === "sealed" && env.softTicketReaskTurn === true
|
|
709
|
-
&& env.softTicketReaskOwnership !== undefined) {
|
|
710
|
-
env.softTicketReaskOwnership.sealedSubstitute = true;
|
|
711
|
-
}
|
|
712
694
|
if (pendingSettlement === "sealed" && terminal.roleOutcome.kind === "accepted"
|
|
713
695
|
&& request.courtAttemptId !== undefined && request.courtAttemptId.length > 0) {
|
|
714
696
|
try {
|
|
@@ -20,8 +20,7 @@ const reportTicketArgsSchema = Type.Object({
|
|
|
20
20
|
}),
|
|
21
21
|
}, { additionalProperties: true });
|
|
22
22
|
export const REPORT_TICKET_TOOL_DESCRIPTION = "向包申报本票号。已知票号时开工第一件事调用;包当场把本腿归到票号目录。"
|
|
23
|
-
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。"
|
|
24
|
-
+ "交卷时若仍未报过且交卷亦未带票号,包会经既有重交通道再问一次。";
|
|
23
|
+
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。";
|
|
25
24
|
function pageString(page, key) {
|
|
26
25
|
const value = page?.[key];
|
|
27
26
|
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
@@ -153,5 +152,3 @@ export function registerReportTicketTool(roleHost, hostActions) {
|
|
|
153
152
|
},
|
|
154
153
|
});
|
|
155
154
|
}
|
|
156
|
-
/** Soft re-ask when a sealed submission still leaves the leg unbound (#1171 / ADR 0073). */
|
|
157
|
-
export const MISSING_TICKET_REASK_MATERIAL = "resources/missing-ticket-reask.md";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { sessionFileIn, sessionFileOf } from "./role-run-placement.js";
|
|
1
|
+
import { isUnboundRunDirectory, sessionFileIn, sessionFileOf } from "./role-run-placement.js";
|
|
2
|
+
import { readPackageMaterial } from "./session-opening-materials.js";
|
|
2
3
|
import { resolveActivationLedgerHome, tryHomeFromAkRolesPath, } from "./activation-ledger-topology.js";
|
|
3
4
|
import { courtAttemptIdFromHostContext, runDirectoryFromHostContext, } from "./host-contracts.js";
|
|
4
5
|
import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
|
|
@@ -491,6 +492,20 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
|
|
|
491
492
|
? { lens: rawLens }
|
|
492
493
|
: {}),
|
|
493
494
|
});
|
|
495
|
+
// Native structured-output hosts have no tool-result reply to the model.
|
|
496
|
+
// Keep the conversation open only after the receipt is durably sealed.
|
|
497
|
+
const runDirectory = runDirectoryFromHostContext(context);
|
|
498
|
+
if (context.host !== "claude" && context.host !== "codex"
|
|
499
|
+
&& runDirectory !== undefined && isUnboundRunDirectory(runDirectory)) {
|
|
500
|
+
result = {
|
|
501
|
+
...result,
|
|
502
|
+
terminate: false,
|
|
503
|
+
content: [...result.content, {
|
|
504
|
+
type: "text",
|
|
505
|
+
text: (await readPackageMaterial("resources/ticket-at-receipt.md")).trim(),
|
|
506
|
+
}],
|
|
507
|
+
};
|
|
508
|
+
}
|
|
494
509
|
}
|
|
495
510
|
catch (error) {
|
|
496
511
|
if (toolThrew)
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
卷已收下,不用重交。本腿还没有票号:知道就调 ak_report_ticket 报上;不知道就直接结束。
|
|
@@ -4,10 +4,7 @@
|
|
|
4
4
|
* starts two ordinary single-axis runs here.
|
|
5
5
|
*/
|
|
6
6
|
import { dirname, resolve } from "node:path";
|
|
7
|
-
import { MISSING_TICKET_REASK_MATERIAL } from "../report-ticket-tool.ts";
|
|
8
7
|
import { isUnboundRunDirectory, sessionFileOf } from "../role-run-placement.ts";
|
|
9
|
-
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.ts";
|
|
10
|
-
import { readPackageMaterial } from "../session-opening-materials.ts";
|
|
11
8
|
import { createPiDoctorAuditor } from "../doctor-auditor.ts";
|
|
12
9
|
|
|
13
10
|
import type { DurablePrincipalAuthority, HostContext, RoleTurnRequest } from "../host-contracts.ts";
|
|
@@ -193,21 +190,10 @@ function unreadablePostSubmissionStatus(
|
|
|
193
190
|
* status: the caller presents the volumes already in hand and does not advance
|
|
194
191
|
* review. ADR 0007 audit continues are not this shape.
|
|
195
192
|
*/
|
|
196
|
-
/**
|
|
197
|
-
* Drop turn-scoped soft-reask identity; keep cross-turn budgets and ownership cell.
|
|
198
|
-
* #1171 F2-R5 / F2-R9: ordinary status reask and court continue must not inherit
|
|
199
|
-
* softTicketReaskTurn (that flag only governs the soft-reask turn's own settle).
|
|
200
|
-
*/
|
|
201
|
-
function withoutSoftTicketReaskTurn(env: InstructionSeatRunEnv): InstructionSeatRunEnv {
|
|
202
|
-
const { softTicketReaskTurn: _softTicketReaskTurn, ...rest } = env;
|
|
203
|
-
return rest;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
193
|
function withUnreadableReask(env: InstructionSeatRunEnv): InstructionSeatRunEnv | undefined {
|
|
207
194
|
const spent = env.unreadableReasksSpent ?? 0;
|
|
208
195
|
if (spent >= deliveryLimitFromConfig(env.autoResumeLimit)) return undefined;
|
|
209
|
-
|
|
210
|
-
return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: spent + 1 };
|
|
196
|
+
return { ...env, unreadableReasksSpent: spent + 1 };
|
|
211
197
|
}
|
|
212
198
|
|
|
213
199
|
/** Budget spent on an unreadable status. Distinct from "status is readable". */
|
|
@@ -406,144 +392,12 @@ async function reaskUnreadablePostSubmissionStatus(
|
|
|
406
392
|
);
|
|
407
393
|
}
|
|
408
394
|
|
|
409
|
-
/**
|
|
410
|
-
* Soft-reask call outcome at the missing-ticket seam (#1171 F2-R6 / F2-R7).
|
|
411
|
-
* Ownership is decided here once from this nested chain's settlement evidence
|
|
412
|
-
* (soft-reask turn sealed → ownership cell; or returned terminal still carries volume);
|
|
413
|
-
* callers must not re-judge the same criterion.
|
|
414
|
-
*/
|
|
415
|
-
type SoftTicketReaskOutcome =
|
|
416
|
-
| { readonly kind: "none" }
|
|
417
|
-
| { readonly kind: "nested_owns"; readonly result: SeatRunResult }
|
|
418
|
-
| { readonly kind: "no_substitute"; readonly result: SeatRunResult };
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* #1171: sealed submission still leaves the leg unbound → soft reask once.
|
|
422
|
-
* Does not reject the sealed receipt; exhaustion leaves unbound as filed.
|
|
423
|
-
* Reask copy lives in package resources (ADR 0073).
|
|
424
|
-
* #1171 F2-R7: ownership comes from this nested resume's settlement seam
|
|
425
|
-
* (softTicketReaskOwnership.sealedSubstitute set when the soft-reask turn itself seals),
|
|
426
|
-
* not full-leg sealed count / time-window deltas that can absorb another court's seal.
|
|
427
|
-
*/
|
|
428
|
-
async function reaskMissingTicketOnce(
|
|
429
|
-
admitted: AdmittedRoleInvocation,
|
|
430
|
-
terminal: TerminalResult | undefined,
|
|
431
|
-
env: InstructionSeatRunEnv,
|
|
432
|
-
io: CliIo,
|
|
433
|
-
): Promise<SoftTicketReaskOutcome> {
|
|
434
|
-
if ((env.ticketReasksSpent ?? 0) >= 1) return { kind: "none" };
|
|
435
|
-
// #1192: a table lookup has no one to ask, and a second turn would re-seal.
|
|
436
|
-
if (isNavigatorSeat(admitted.role)) return { kind: "none" };
|
|
437
|
-
if (admitted.ticketNumber !== undefined) return { kind: "none" };
|
|
438
|
-
if (!isUnboundRunDirectory(admitted.runDirectory)) return { kind: "none" };
|
|
439
|
-
if (terminal?.roleOutcome.kind !== "accepted" && terminal?.roleOutcome.kind !== "audit_escalation") {
|
|
440
|
-
return { kind: "none" };
|
|
441
|
-
}
|
|
442
|
-
const payloads = terminal.roleOutcome.payloads ?? [];
|
|
443
|
-
if (payloads.length === 0) return { kind: "none" };
|
|
444
|
-
// Sealed-with-ticket skips via admitted.ticketNumber after bindSealedTicketNumber
|
|
445
|
-
// (post-admission). Do not re-parse payload.ticketNumber here — that would
|
|
446
|
-
// duplicate the existing ticket seam (#1171 notary: no rule copy).
|
|
447
|
-
const instruction = (await readPackageMaterial(MISSING_TICKET_REASK_MATERIAL)).trim();
|
|
448
|
-
// Spend budget and mark this turn as the soft reask (settlement identity).
|
|
449
|
-
// Ownership cell is set at that turn's settlement; env spreads keep the same
|
|
450
|
-
// ref through audit continue — see envForCourtContinue (#1171 F2-R5 / F2-R7).
|
|
451
|
-
const softTicketReaskOwnership = { sealedSubstitute: false };
|
|
452
|
-
const softReaskEnv: InstructionSeatRunEnv = {
|
|
453
|
-
...env,
|
|
454
|
-
ticketReasksSpent: 1,
|
|
455
|
-
softTicketReaskTurn: true,
|
|
456
|
-
softTicketReaskOwnership,
|
|
457
|
-
};
|
|
458
|
-
// Named #1171 missing-ticket: summons opens a new court; message carries the
|
|
459
|
-
// package reask copy (not arbitrary summons.instruction passthrough).
|
|
460
|
-
const result = await runPublicInstructionSeatResume(
|
|
461
|
-
{
|
|
462
|
-
runId: admitted.runId,
|
|
463
|
-
runDirectory: admitted.runDirectory,
|
|
464
|
-
message: instruction,
|
|
465
|
-
summons: { instructionEmpty: true },
|
|
466
|
-
},
|
|
467
|
-
softReaskEnv,
|
|
468
|
-
io,
|
|
469
|
-
);
|
|
470
|
-
if (
|
|
471
|
-
softTicketReaskOwnership.sealedSubstitute
|
|
472
|
-
|| terminalCarriesSealedSubmission(result.terminal)
|
|
473
|
-
) {
|
|
474
|
-
return { kind: "nested_owns", result };
|
|
475
|
-
}
|
|
476
|
-
return { kind: "no_substitute", result };
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/** Court continue: keep cross-turn budgets + ownership cell; drop turn-scoped soft-reask identity. */
|
|
395
|
+
/** Court continue resets the unreadable-status budget. */
|
|
480
396
|
function envForCourtContinue(env: InstructionSeatRunEnv): InstructionSeatRunEnv {
|
|
481
|
-
return { ...
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/** True when a terminal still carries a sealed accepted / audit_escalation volume. */
|
|
485
|
-
function terminalCarriesSealedSubmission(terminal: TerminalResult | undefined): boolean {
|
|
486
|
-
const kind = terminal?.roleOutcome.kind;
|
|
487
|
-
if (kind !== "accepted" && kind !== "audit_escalation") return false;
|
|
488
|
-
return (terminal?.roleOutcome.payloads ?? []).length > 0;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* #1171 F2-R2: kept original terminal may still name pre-relocate artifact
|
|
493
|
-
* paths after a report-only reask moved the leg. Project onto live admitted
|
|
494
|
-
* via the same rewrite seam post-admission uses after relocate.
|
|
495
|
-
*/
|
|
496
|
-
function projectKeptOriginalArtifactsOntoLiveAdmitted(
|
|
497
|
-
original: SeatRunResult,
|
|
498
|
-
kept: SeatRunResult,
|
|
499
|
-
): void {
|
|
500
|
-
const oldRunDirectory = original.admitted?.runDirectory;
|
|
501
|
-
const newRunDirectory = kept.admitted?.runDirectory;
|
|
502
|
-
if (
|
|
503
|
-
oldRunDirectory === undefined
|
|
504
|
-
|| newRunDirectory === undefined
|
|
505
|
-
|| oldRunDirectory === newRunDirectory
|
|
506
|
-
|| kept.terminal === undefined
|
|
507
|
-
) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
for (const artifact of kept.terminal.artifacts) {
|
|
511
|
-
artifact.path = rewriteRunDirectoryPathValue(
|
|
512
|
-
artifact.path,
|
|
513
|
-
oldRunDirectory,
|
|
514
|
-
newRunDirectory,
|
|
515
|
-
) as string;
|
|
516
|
-
}
|
|
397
|
+
return { ...env, unreadableReasksSpent: 0 };
|
|
517
398
|
}
|
|
518
399
|
|
|
519
|
-
/**
|
|
520
|
-
* #1171 F2: missing-ticket reask did not seal a substitute — keep original
|
|
521
|
-
* terminal; take live placement from the reask when it relocated.
|
|
522
|
-
* #1171 F2-R1: silence / report-only stays on the original chain; a real host
|
|
523
|
-
* failure (nonzero exit) must be delivered honestly — do not wash it into the
|
|
524
|
-
* original accepted result. Sealed original volume remains on disk either way.
|
|
525
|
-
* #1171 F2-R2: after keep, project report artifact paths onto the live
|
|
526
|
-
* admitted directory so public result, durable face, and artifact refs agree.
|
|
527
|
-
* Substitute ownership is decided once at the soft-reask call seam (F2-R6);
|
|
528
|
-
* this helper only finishes the keep path.
|
|
529
|
-
*/
|
|
530
|
-
function mergeMissingTicketReaskKeepOriginal(
|
|
531
|
-
original: SeatRunResult,
|
|
532
|
-
ticketReask: SeatRunResult,
|
|
533
|
-
): SeatRunResult {
|
|
534
|
-
if (ticketReask.exitCode !== 0) return ticketReask;
|
|
535
|
-
const admitted = ticketReask.admitted ?? original.admitted;
|
|
536
|
-
if (admitted === undefined) return { ...original };
|
|
537
|
-
const kept = { ...original, admitted };
|
|
538
|
-
projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept);
|
|
539
|
-
return kept;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Post-submission status reask, then missing-ticket soft reask, then audit.
|
|
544
|
-
* #1171 F3: status exhaustion must not skip the one missing-ticket reask.
|
|
545
|
-
* #1171 F2: ticket reask without a substitute seal continues the original chain.
|
|
546
|
-
*/
|
|
400
|
+
/** Post-submission status reask, then audit. */
|
|
547
401
|
async function continueAfterPostSubmissionGuards(
|
|
548
402
|
result: SeatRunResult,
|
|
549
403
|
admittedRole: PackagedRole,
|
|
@@ -562,44 +416,22 @@ async function continueAfterPostSubmissionGuards(
|
|
|
562
416
|
env,
|
|
563
417
|
io,
|
|
564
418
|
);
|
|
565
|
-
let
|
|
419
|
+
let continued = result;
|
|
566
420
|
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
567
421
|
const terminal = result.terminal === undefined
|
|
568
422
|
? undefined
|
|
569
423
|
: withUnsettledDirection(result.terminal);
|
|
570
|
-
|
|
424
|
+
continued = terminal === undefined
|
|
571
425
|
? { ...result, admitted: live }
|
|
572
426
|
: { ...result, admitted: live, terminal };
|
|
573
|
-
// Fall through: missing-ticket reask still owed on the original sealed volume.
|
|
574
427
|
} else if (unreadableReask !== undefined) {
|
|
575
428
|
return unreadableReask;
|
|
576
429
|
}
|
|
577
|
-
const forTicket = afterStatus.admitted ?? live;
|
|
578
|
-
// Ownership is decided once inside reaskMissingTicketOnce (#1171 F2-R6).
|
|
579
|
-
const softReask = await reaskMissingTicketOnce(
|
|
580
|
-
forTicket,
|
|
581
|
-
afterStatus.terminal ?? result.terminal,
|
|
582
|
-
env,
|
|
583
|
-
io,
|
|
584
|
-
);
|
|
585
|
-
if (softReask.kind === "nested_owns") {
|
|
586
|
-
// Nested resume already finished guards + audit/present for the substitute.
|
|
587
|
-
// Do not run a second outer settlement of the same public call (#1171 F2-R4b).
|
|
588
|
-
return softReask.result;
|
|
589
|
-
}
|
|
590
|
-
const continued = softReask.kind === "none"
|
|
591
|
-
? afterStatus
|
|
592
|
-
: mergeMissingTicketReaskKeepOriginal(afterStatus, softReask.result);
|
|
593
430
|
const continuedAdmitted = continued.admitted ?? live;
|
|
594
|
-
// Spent budget rides the outer audit/continue chain (#1171 F2-R4a). Soft-reask
|
|
595
|
-
// settlement identity does not — ordinary continue turns publish no_receipt (#1171 F2-R5).
|
|
596
|
-
const continuedEnv = softReask.kind !== "none"
|
|
597
|
-
? { ...env, ticketReasksSpent: 1 }
|
|
598
|
-
: env;
|
|
599
431
|
if (AUDITED_ROLES.has(admittedRole) && continued.terminal?.roleOutcome.kind === "accepted") {
|
|
600
432
|
return auditSubmittedRole(
|
|
601
433
|
{ ...continued, admitted: continuedAdmitted },
|
|
602
|
-
|
|
434
|
+
env,
|
|
603
435
|
io,
|
|
604
436
|
);
|
|
605
437
|
}
|
|
@@ -1168,8 +1000,7 @@ export async function runPublicInstructionSeat(
|
|
|
1168
1000
|
const result = await runCountersignBody(parsed, env, {
|
|
1169
1001
|
stdout: (value) => held.push(value), stderr: io.stderr,
|
|
1170
1002
|
});
|
|
1171
|
-
//
|
|
1172
|
-
// soft reask once, then audit — no early return that skips the reask.
|
|
1003
|
+
// Apply the same post-submission guards and audit as every other seat.
|
|
1173
1004
|
return continueAfterPostSubmissionGuards(result, "countersign", env, io, held);
|
|
1174
1005
|
}
|
|
1175
1006
|
|
|
@@ -1345,9 +1176,7 @@ export async function runPublicInstructionSeat(
|
|
|
1345
1176
|
await persistAdmittedSourceRunPath(admitted, auditorSource, auditorSubject);
|
|
1346
1177
|
admitted = { ...admitted, sourceRunPath: auditorSource } as AdmittedRoleInvocation;
|
|
1347
1178
|
}
|
|
1348
|
-
// #1208 C2: same helper as resume —
|
|
1349
|
-
// source binds and relocates before the missing-ticket soft reask can
|
|
1350
|
-
// fire. True no-ticket sources stay unbound (#1171 asks once).
|
|
1179
|
+
// #1208 C2: same helper as resume — inherit the audited source's known ticket.
|
|
1351
1180
|
admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
|
|
1352
1181
|
|
|
1353
1182
|
const runAdmitted = async (): Promise<SeatRunResult> => {
|
|
@@ -1736,8 +1565,6 @@ async function auditSubmittedRole(
|
|
|
1736
1565
|
resumedOfficer?: AdmittedRoleInvocation,
|
|
1737
1566
|
passedReceipt?: unknown,
|
|
1738
1567
|
): Promise<SeatRunResult> {
|
|
1739
|
-
// Ordinary audit chain: drop soft-reask turn identity; keep budgets/ownership (#1171 F2-R9).
|
|
1740
|
-
env = withoutSoftTicketReaskTurn(env);
|
|
1741
1568
|
const admitted = turn.admitted;
|
|
1742
1569
|
if (admitted === undefined || !AUDITED_ROLES.has(admitted.role)
|
|
1743
1570
|
|| turn.terminal?.roleOutcome.kind !== "accepted") return turn;
|
|
@@ -449,23 +449,6 @@ export type PostAdmissionEnv = {
|
|
|
449
449
|
* keeps this env's autoResumeLimit; the counter is the only thing that moves.
|
|
450
450
|
*/
|
|
451
451
|
unreadableReasksSpent?: number;
|
|
452
|
-
/**
|
|
453
|
-
* #1171: missing-ticket soft reasks already issued on this chain. Hard ceiling
|
|
454
|
-
* is one — still no ticket after that stays unbound with the sealed receipt.
|
|
455
|
-
* Budget only across rounds; not the current turn's settlement identity.
|
|
456
|
-
*/
|
|
457
|
-
ticketReasksSpent?: number;
|
|
458
|
-
/**
|
|
459
|
-
* #1171 F2-R5: this turn is the one soft reask. Keep the sealed terminal face
|
|
460
|
-
* when it ends without a substitute seal. Not carried into audit continue.
|
|
461
|
-
*/
|
|
462
|
-
softTicketReaskTurn?: true;
|
|
463
|
-
/**
|
|
464
|
-
* #1171 F2-R7: live ownership cell for this soft-reask nested chain.
|
|
465
|
-
* Created at soft-reask dispatch; settlement of that turn sets sealedSubstitute.
|
|
466
|
-
* Shared ref survives env spreads — not a second ledger.
|
|
467
|
-
*/
|
|
468
|
-
softTicketReaskOwnership?: { sealedSubstitute: boolean };
|
|
469
452
|
createRunId?: () => string;
|
|
470
453
|
/**
|
|
471
454
|
* Parent cancellation for a nested public summon (#675). Every dispatched turn
|
|
@@ -1220,20 +1203,7 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1220
1203
|
result.terminal === pendingTerminal
|
|
1221
1204
|
) {
|
|
1222
1205
|
try {
|
|
1223
|
-
|
|
1224
|
-
// preserves the sealed terminal face. ticketReasksSpent is budget across
|
|
1225
|
-
// rounds — after audit continue, ordinary no-volume turns publish
|
|
1226
|
-
// no_receipt honestly. previewOnly skips the terminal pointer;
|
|
1227
|
-
// recordAttemptHistory still appends.
|
|
1228
|
-
const softTicketReaskNoSeal =
|
|
1229
|
-
pendingSettlement !== "sealed" && env.softTicketReaskTurn === true;
|
|
1230
|
-
const noSealScope = softTicketReaskNoSeal
|
|
1231
|
-
? {
|
|
1232
|
-
...courtScope,
|
|
1233
|
-
previewOnly: true as const,
|
|
1234
|
-
recordAttemptHistory: true as const,
|
|
1235
|
-
}
|
|
1236
|
-
: { ...courtScope, recordAttemptHistory: true as const };
|
|
1206
|
+
const noSealScope = { ...courtScope, recordAttemptHistory: true as const };
|
|
1237
1207
|
const terminal = pendingSettlement === "sealed"
|
|
1238
1208
|
? await adapters.trySettle(admitted, env.principalAuthority,
|
|
1239
1209
|
{ ...courtScope, recordAttemptHistory: true })
|
|
@@ -1243,12 +1213,6 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1243
1213
|
noSealScope);
|
|
1244
1214
|
if (terminal === undefined) throw new Error("settled host attempt vanished before publication");
|
|
1245
1215
|
result = { ...result, terminal, exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome) };
|
|
1246
|
-
// #1171 F2-R7: ownership evidence at this soft-reask turn's seal —
|
|
1247
|
-
// mutate the live ownership cell carried on env (survives spreads).
|
|
1248
|
-
if (pendingSettlement === "sealed" && env.softTicketReaskTurn === true
|
|
1249
|
-
&& env.softTicketReaskOwnership !== undefined) {
|
|
1250
|
-
env.softTicketReaskOwnership.sealedSubstitute = true;
|
|
1251
|
-
}
|
|
1252
1216
|
if (pendingSettlement === "sealed" && terminal.roleOutcome.kind === "accepted"
|
|
1253
1217
|
&& request.courtAttemptId !== undefined && request.courtAttemptId.length > 0) {
|
|
1254
1218
|
try {
|
|
@@ -40,8 +40,7 @@ const reportTicketArgsSchema = Type.Object(
|
|
|
40
40
|
|
|
41
41
|
export const REPORT_TICKET_TOOL_DESCRIPTION =
|
|
42
42
|
"向包申报本票号。已知票号时开工第一件事调用;包当场把本腿归到票号目录。"
|
|
43
|
-
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。"
|
|
44
|
-
+ "交卷时若仍未报过且交卷亦未带票号,包会经既有重交通道再问一次。";
|
|
43
|
+
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。";
|
|
45
44
|
|
|
46
45
|
function pageString(page: Record<string, unknown> | undefined, key: string): string | undefined {
|
|
47
46
|
const value = page?.[key];
|
|
@@ -199,6 +198,3 @@ export function registerReportTicketTool(
|
|
|
199
198
|
},
|
|
200
199
|
});
|
|
201
200
|
}
|
|
202
|
-
|
|
203
|
-
/** Soft re-ask when a sealed submission still leaves the leg unbound (#1171 / ADR 0073). */
|
|
204
|
-
export const MISSING_TICKET_REASK_MATERIAL = "resources/missing-ticket-reask.md" as const;
|
package/src/submission-ledger.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { sessionFileIn, sessionFileOf } from "./role-run-placement.ts";
|
|
1
|
+
import { isUnboundRunDirectory, sessionFileIn, sessionFileOf } from "./role-run-placement.ts";
|
|
2
|
+
import { readPackageMaterial } from "./session-opening-materials.ts";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
resolveActivationLedgerHome,
|
|
@@ -676,6 +677,20 @@ export function createSubmissionLedgerHost(
|
|
|
676
677
|
? { lens: rawLens }
|
|
677
678
|
: {}),
|
|
678
679
|
});
|
|
680
|
+
// Native structured-output hosts have no tool-result reply to the model.
|
|
681
|
+
// Keep the conversation open only after the receipt is durably sealed.
|
|
682
|
+
const runDirectory = runDirectoryFromHostContext(context);
|
|
683
|
+
if (context.host !== "claude" && context.host !== "codex"
|
|
684
|
+
&& runDirectory !== undefined && isUnboundRunDirectory(runDirectory)) {
|
|
685
|
+
result = {
|
|
686
|
+
...result,
|
|
687
|
+
terminate: false,
|
|
688
|
+
content: [...result.content, {
|
|
689
|
+
type: "text",
|
|
690
|
+
text: (await readPackageMaterial("resources/ticket-at-receipt.md")).trim(),
|
|
691
|
+
}],
|
|
692
|
+
};
|
|
693
|
+
}
|
|
679
694
|
} catch (error) {
|
|
680
695
|
if (toolThrew) throw error;
|
|
681
696
|
return failInfrastructure(error, context, toolCallId);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
本腿仍无票号。请调用 ak_report_ticket 申报,或在交卷里带上 ticketNumber。只问这一次。
|