@sema-agent/server 7.110.0 → 7.112.0
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/MIGRATION.md +16 -0
- package/dist/approval-ask-audit-store.d.ts +38 -25
- package/dist/approval-ask-audit-store.js +26 -59
- package/dist/bench/s1/run-firm.d.ts +11 -0
- package/dist/bench/s1/run-firm.js +47 -32
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/session-faces.d.ts +4 -0
- package/dist/boot/session-faces.js +3 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +4 -3
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/boot/workflow-orchestration.js +1 -1
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +45 -1
- package/dist/http/admission.js +6 -1
- package/dist/http/resume-legs.d.ts +6 -15
- package/dist/http/resume-legs.js +33 -14
- package/dist/http/route-ctx.d.ts +28 -11
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/runs.js +17 -7
- package/dist/http/routes/tasks.d.ts +3 -2
- package/dist/http/routes/tasks.js +45 -51
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -2
- package/dist/index.d.ts +1 -1
- package/dist/instruction-selection.d.ts +34 -0
- package/dist/instruction-selection.js +23 -0
- package/dist/observability/corrupt-read-seat.d.ts +18 -0
- package/dist/observability/corrupt-read-seat.js +18 -0
- package/dist/observability/fail-open.d.ts +20 -4
- package/dist/observability/fail-open.js +17 -4
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -23
- package/dist/orchestration/workflow-agent-session-index.js +11 -50
- package/dist/orchestration/workflow-notify-journal.d.ts +11 -8
- package/dist/orchestration/workflow-notify-journal.js +20 -36
- package/dist/plugins/file-outcome-sink.d.ts +4 -1
- package/dist/plugins/file-outcome-sink.js +9 -3
- package/dist/plugins/file-run-store.js +3 -3
- package/dist/plugins/retention-store-sql.d.ts +1 -1
- package/dist/plugins/retention-store-sql.js +2 -0
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +59 -9
- package/dist/plugins/store-contracts.js +10 -0
- package/dist/plugins/user-input-ledger-file.d.ts +4 -2
- package/dist/plugins/user-input-ledger-file.js +11 -1
- package/dist/plugins/user-input-ledger-sql.d.ts +14 -4
- package/dist/plugins/user-input-ledger-sql.js +46 -18
- package/dist/project-memory.d.ts +10 -2
- package/dist/project-memory.js +5 -2
- package/dist/run-cancel-context.d.ts +59 -7
- package/dist/run-cancel-context.js +26 -0
- package/dist/runs.d.ts +7 -5
- package/dist/runs.js +22 -16
- package/dist/trace/ledger-sink.d.ts +4 -0
- package/dist/trace/ledger-sink.js +1 -1
- package/dist/trace/project.d.ts +11 -3
- package/dist/trace/project.js +6 -5
- package/dist/trace/self-minted-wire.d.ts +48 -0
- package/dist/trace/self-minted-wire.js +17 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +4 -2
- package/dist/user-input-ledger.d.ts +101 -48
- package/dist/user-input-ledger.js +52 -35
- package/package.json +3 -3
package/dist/http/admission.js
CHANGED
|
@@ -7,7 +7,7 @@ import { taskBodyKeyIssue } from "../request-key-closure.js";
|
|
|
7
7
|
import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
8
8
|
import { selfOrchestrationDenial } from "../task-workflow.js";
|
|
9
9
|
import { centerEntitlementSourceWired } from "../runtime-caps-resolver.js";
|
|
10
|
-
import { parseHooksConfig } from "../hooks/hook-runner.js";
|
|
10
|
+
import { deadToolMatcherIssue, parseHooksConfig } from "../hooks/hook-runner.js";
|
|
11
11
|
import { judgeRequestWebSearch } from "../plugins/web-search.js";
|
|
12
12
|
import { normalizeApproachNotice, unknownTaskAgentModelRef, validateTaskAgents } from "../spec-fields.js";
|
|
13
13
|
import { isValidCwd, MAX_ADDITIONAL_DIRS, MAX_CWD_CHARS } from "../task-cwd.js";
|
|
@@ -200,6 +200,11 @@ export function createAdmission(ctx) {
|
|
|
200
200
|
sendError(res, 400, "request.field_invalid", hooksParsed.error ?? "settings.hooks: invalid shape");
|
|
201
201
|
return null;
|
|
202
202
|
}
|
|
203
|
+
const deadMatcher = deadToolMatcherIssue(hooksParsed.config);
|
|
204
|
+
if (deadMatcher !== undefined) {
|
|
205
|
+
sendError(res, 400, "request.field_invalid", deadMatcher);
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
203
208
|
}
|
|
204
209
|
{
|
|
205
210
|
const webSearchVerdict = judgeRequestWebSearch(st.webSearch);
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* 字节不变)。同居是第三条,也是唯一一条既保住静态序又保住 move-only 的。序由 `const` 的 TDZ 亲自执法:
|
|
8
8
|
* 腿是提升的函数声明、ctx 是文件尾的 `const`,谁也不会在工厂执行期读到对方。
|
|
9
9
|
*
|
|
10
|
-
* ctx 字段
|
|
11
|
-
*
|
|
10
|
+
* ctx 字段 27 只(registry 6 + helpers 14 + deps/runnerFor/prepareSpec/finalizeTaskResult + 三只配额/租约/用量窗
|
|
11
|
+
* 判据;S-947 删 `cancelledViaVerb` 旁表后 28 → 27,与 `test/split-ctx-parity.test.ts` 的读数同)—— 这是**如实披露**的耦合读数(设计稿 §9:把它变小是波 3 的事,本刀不承诺)。型一律从既有型派生
|
|
12
12
|
* (`RunRegistry` / `RouteHelpers` / `RouteLegs` / `ReturnType<typeof createRequestGuards>`),不手抄签名 ⇒ 不会漂。
|
|
13
13
|
*/
|
|
14
14
|
import type { IncomingMessage } from "node:http";
|
|
@@ -17,7 +17,7 @@ import type { DecideBinding } from "./wire-types.js";
|
|
|
17
17
|
import type { CheckpointStoreFull } from "../plugins/store-backend.js";
|
|
18
18
|
import { type HostDecision } from "../host-decision.js";
|
|
19
19
|
import { type PlanApprovePermissionModeAfter } from "../task-settings.js";
|
|
20
|
-
import type { RouteCtxBase, DriveResumeArgs, PreparedPlanReview } from "./route-ctx.js";
|
|
20
|
+
import type { RouteCtxBase, DriveResumeArgs, PreparedPlanReview, ResumeLegOutcome } from "./route-ctx.js";
|
|
21
21
|
import { type TrustedRunInternals } from "./durable-run-leg.js";
|
|
22
22
|
import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
|
|
23
23
|
import type { createRequestGuards } from "./request-guards.js";
|
|
@@ -33,21 +33,12 @@ export declare function createResumeLegs(ctx: {
|
|
|
33
33
|
}) => Promise<void>, acceptEarly?: boolean, decider?: {
|
|
34
34
|
principal?: string;
|
|
35
35
|
explicitOperator: boolean;
|
|
36
|
-
}) => Promise<
|
|
37
|
-
|
|
38
|
-
body: object;
|
|
39
|
-
}>;
|
|
40
|
-
driveResumeIntoRunLog: (args: DriveResumeArgs) => Promise<{
|
|
41
|
-
status: number;
|
|
42
|
-
body: object;
|
|
43
|
-
}>;
|
|
36
|
+
}) => Promise<ResumeLegOutcome>;
|
|
37
|
+
driveResumeIntoRunLog: (args: DriveResumeArgs) => Promise<ResumeLegOutcome>;
|
|
44
38
|
driveResumeLeg: (args: DriveResumeArgs, onAccepted?: (receipt: {
|
|
45
39
|
status: number;
|
|
46
40
|
body: object;
|
|
47
|
-
}) => void) => Promise<
|
|
48
|
-
status: number;
|
|
49
|
-
body: object;
|
|
50
|
-
}>;
|
|
41
|
+
}) => void) => Promise<ResumeLegOutcome>;
|
|
51
42
|
mayDiscloseRowError: (cs: CheckpointStoreFull, token: CheckpointToken, decider: {
|
|
52
43
|
principal?: string;
|
|
53
44
|
explicitOperator: boolean;
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -8,7 +8,7 @@ const sessionFaceUnavailable = async (sessionId) => {
|
|
|
8
8
|
import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
9
9
|
import { evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
10
10
|
import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
|
|
11
|
-
import { mintCancelledResult } from "../run-cancel-context.js";
|
|
11
|
+
import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
|
|
12
12
|
import {} from "../host-decision.js";
|
|
13
13
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
14
14
|
import { recordResumeResourceWindow } from "../resource-window.js";
|
|
@@ -20,6 +20,7 @@ import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
|
|
|
20
20
|
import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
21
21
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
22
22
|
import { withModelReachScope } from "../model-reach-scope.js";
|
|
23
|
+
import { createInstructionSelectionLeg } from "../instruction-selection.js";
|
|
23
24
|
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
24
25
|
import { redactSecrets } from "../trace/redact.js";
|
|
25
26
|
import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
@@ -74,7 +75,7 @@ function notResumableAnswer() {
|
|
|
74
75
|
return { status: 409, body: { error: "run is not in a resumable (suspended) state (already resumed, decided, or expired)", errorCode: "conflict.not_resumable" } };
|
|
75
76
|
}
|
|
76
77
|
export function createResumeLegs(ctx) {
|
|
77
|
-
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns,
|
|
78
|
+
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, isFleetWide, runOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
|
|
78
79
|
const resumeFleetScope = (req, auth) => req ? (gatedPrincipal(req, deps.config) ?? "default") : (auth.principal ?? "default");
|
|
79
80
|
async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider) {
|
|
80
81
|
const cs = deps.checkpointStore;
|
|
@@ -224,7 +225,7 @@ export function createResumeLegs(ctx) {
|
|
|
224
225
|
const noteLateSettleIfAccepted = (out) => {
|
|
225
226
|
if (lateSettleSink === undefined || lateSettleAnchor === undefined || out.status !== 200)
|
|
226
227
|
return;
|
|
227
|
-
if (out.body?.errorCode === "cancelled")
|
|
228
|
+
if (out.body?.errorCode === "cancelled" && out.decisionCommitted !== true)
|
|
228
229
|
return;
|
|
229
230
|
try {
|
|
230
231
|
lateSettleSink.recordLateSettle({ ...lateSettleAnchor, outcome: decision === "approve" ? "allowed" : "denied", decidedBy: hostDecision.decidedBy, ...(hostDecision.approver !== undefined ? { approver: hostDecision.approver } : {}), settledAtMs: Date.now() });
|
|
@@ -471,7 +472,8 @@ export function createResumeLegs(ctx) {
|
|
|
471
472
|
let resumeLegLive = false;
|
|
472
473
|
const subagentHandleEvictions = [];
|
|
473
474
|
let claimedRow = false;
|
|
474
|
-
const cancelCtrl =
|
|
475
|
+
const cancelCtrl = createRunAbort();
|
|
476
|
+
let cancelLedgerAppend;
|
|
475
477
|
const preemptCtrl = new AbortController();
|
|
476
478
|
if (taskId && deps.runStore) {
|
|
477
479
|
claimedRow = freshRunClaimed ? true : await deps.runStore.markResuming(taskId, deps.instanceId ?? "default");
|
|
@@ -487,7 +489,7 @@ export function createResumeLegs(ctx) {
|
|
|
487
489
|
void rs.heartbeat(taskId, owner).catch(() => undefined);
|
|
488
490
|
if (!cancelCtrl.signal.aborted) {
|
|
489
491
|
void rs.isCancelRequested(taskId, owner).then((c) => { if (c)
|
|
490
|
-
cancelCtrl.abort(); }).catch(() => undefined);
|
|
492
|
+
cancelCtrl.abort("cancel_verb"); }).catch(() => undefined);
|
|
491
493
|
}
|
|
492
494
|
if (!preemptCtrl.signal.aborted) {
|
|
493
495
|
void rs.isPreemptRequested(taskId, owner).then((p) => { if (p)
|
|
@@ -503,7 +505,8 @@ export function createResumeLegs(ctx) {
|
|
|
503
505
|
await args.onResumeCommitted();
|
|
504
506
|
let unregisterEngineNotice;
|
|
505
507
|
try {
|
|
506
|
-
|
|
508
|
+
const instructionLeg = createInstructionSelectionLeg();
|
|
509
|
+
result = await instructionLeg.run(sessionId, () => withModelReachScope(sessionId, () => withPrincipal(principal, async () => {
|
|
507
510
|
const resumeNotifiedKeys = new NotifiedKeys();
|
|
508
511
|
if (deps.runStore && taskId) {
|
|
509
512
|
const rs0 = deps.runStore;
|
|
@@ -526,8 +529,10 @@ export function createResumeLegs(ctx) {
|
|
|
526
529
|
persistThinking: deps.config.traceThinking,
|
|
527
530
|
notifiedKeys: resumeNotifiedKeys,
|
|
528
531
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
532
|
+
instructionsSegment: instructionLeg.segment,
|
|
529
533
|
});
|
|
530
534
|
const vAppend = (type, data) => verifySink.append(type, data);
|
|
535
|
+
cancelLedgerAppend = vAppend;
|
|
531
536
|
const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
532
537
|
const verifyInternals = {
|
|
533
538
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
@@ -576,7 +581,9 @@ export function createResumeLegs(ctx) {
|
|
|
576
581
|
: {}),
|
|
577
582
|
};
|
|
578
583
|
resumeLegLive = true;
|
|
579
|
-
const
|
|
584
|
+
const vrEngine = await resumeWithVerification(legRunner, token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, resumeObjective, verifyRounds, verifyInternals);
|
|
585
|
+
const vrCause = cancelCtrl.cause();
|
|
586
|
+
const vr = vrCause !== undefined && taskId !== undefined ? mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", engineResult: vrEngine, cause: vrCause }) : vrEngine;
|
|
580
587
|
await commitResumeCtx();
|
|
581
588
|
let safe = sealResultForExit(vr);
|
|
582
589
|
const vrNested = nestedPauseOf(vr.terminal);
|
|
@@ -619,6 +626,7 @@ export function createResumeLegs(ctx) {
|
|
|
619
626
|
const persistThinking = deps.config.traceThinking;
|
|
620
627
|
const serialAppend = createSerialLedgerAppend((s, type, data) => rs.appendEvent(taskId, s, type, data), startSeq);
|
|
621
628
|
const append = (type, data) => (rs && taskId ? serialAppend(type, data) : Promise.resolve());
|
|
629
|
+
cancelLedgerAppend = append;
|
|
622
630
|
unregisterEngineNotice = registerEngineNoticeLeg({
|
|
623
631
|
sessionId,
|
|
624
632
|
...(args.engineNotices !== undefined && args.engineNotices.length > 0 ? { pending: [...args.engineNotices] } : {}),
|
|
@@ -712,7 +720,10 @@ export function createResumeLegs(ctx) {
|
|
|
712
720
|
if (!taskId || !rs)
|
|
713
721
|
return sealResultForExit(await stream.result());
|
|
714
722
|
onAccepted?.(acceptedResumingReceipt(taskId, sessionId));
|
|
723
|
+
let causeAtDone;
|
|
715
724
|
for await (const ev of stream) {
|
|
725
|
+
if (ev.type === "done")
|
|
726
|
+
causeAtDone = cancelCtrl.cause();
|
|
716
727
|
recordTurnActivity(taskId);
|
|
717
728
|
fleetPub?.onEvent(ev);
|
|
718
729
|
await appendPromptManifest(append, deps.promptManifests, taskId);
|
|
@@ -771,7 +782,7 @@ export function createResumeLegs(ctx) {
|
|
|
771
782
|
break;
|
|
772
783
|
case "wiring_manifest":
|
|
773
784
|
await flush();
|
|
774
|
-
await append("wiring_manifest", wiringManifestEventData(ev));
|
|
785
|
+
await append("wiring_manifest", wiringManifestEventData(ev, instructionLeg.segment()));
|
|
775
786
|
break;
|
|
776
787
|
case "tool_roster_delta": {
|
|
777
788
|
await flush();
|
|
@@ -820,7 +831,8 @@ export function createResumeLegs(ctx) {
|
|
|
820
831
|
}
|
|
821
832
|
}
|
|
822
833
|
await flush();
|
|
823
|
-
const
|
|
834
|
+
const rEngine = await stream.result();
|
|
835
|
+
const r = causeAtDone !== undefined ? mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", engineResult: rEngine, cause: causeAtDone }) : rEngine;
|
|
824
836
|
const rPark = r.terminal.kind === "paused" ? outwardPlane(r.terminal).status : undefined;
|
|
825
837
|
if (rPark === "suspended") {
|
|
826
838
|
const parkCallId = await parkToolCallId(r, deps.checkpointStore);
|
|
@@ -875,7 +887,7 @@ export function createResumeLegs(ctx) {
|
|
|
875
887
|
return await (deps.sendUserFile && taskId
|
|
876
888
|
? deps.sendUserFile.runWithContext({ taskId, emit: async (frame) => { const { type, ...rest } = frame; await append(type, rest); } }, withElicitation)
|
|
877
889
|
: withElicitation());
|
|
878
|
-
}));
|
|
890
|
+
})));
|
|
879
891
|
}
|
|
880
892
|
catch (e) {
|
|
881
893
|
if (e instanceof CheckpointError) {
|
|
@@ -895,7 +907,11 @@ export function createResumeLegs(ctx) {
|
|
|
895
907
|
}
|
|
896
908
|
}
|
|
897
909
|
if (claimedRow && taskId && deps.runStore) {
|
|
898
|
-
const
|
|
910
|
+
const handleCause = cancelCtrl.cause();
|
|
911
|
+
if (handleCause === undefined)
|
|
912
|
+
recordFailOpen("server.runs.abort-cause-missing", `task=${taskId} arm=resume-aborted`);
|
|
913
|
+
const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: e.message, cause: handleCause ?? "cancel_verb" });
|
|
914
|
+
await cancelLedgerAppend?.("failed", buildCancelledLedgerRow(c)).catch((err) => recordFailOpen("server.runs.terminal-ledger-write-failed", `arm=resume-append-resume-aborted ${errorText(err)}`));
|
|
899
915
|
await deps.runStore.setTerminal(taskId, "failed", c, e.message).catch(() => undefined);
|
|
900
916
|
}
|
|
901
917
|
settleFleet("failed");
|
|
@@ -950,9 +966,11 @@ export function createResumeLegs(ctx) {
|
|
|
950
966
|
},
|
|
951
967
|
};
|
|
952
968
|
}
|
|
953
|
-
|
|
969
|
+
const thrownCause = cancelCtrl.cause();
|
|
970
|
+
if (thrownCause !== undefined) {
|
|
954
971
|
if (taskId && deps.runStore) {
|
|
955
|
-
const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user" });
|
|
972
|
+
const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", cause: thrownCause });
|
|
973
|
+
await cancelLedgerAppend?.("failed", buildCancelledLedgerRow(c)).catch((err) => recordFailOpen("server.runs.terminal-ledger-write-failed", `arm=resume-append-thrown-cancel ${errorText(err)}`));
|
|
956
974
|
await deps.runStore.setTerminal(taskId, "failed", c, "cancelled by user").catch(() => undefined);
|
|
957
975
|
}
|
|
958
976
|
settleFleet("failed");
|
|
@@ -999,6 +1017,7 @@ export function createResumeLegs(ctx) {
|
|
|
999
1017
|
settleFleet(checkpointReopened ? "suspended" : resumePlane.status, fleetRunResiduals(sealed));
|
|
1000
1018
|
return {
|
|
1001
1019
|
status: 200,
|
|
1020
|
+
decisionCommitted: true,
|
|
1002
1021
|
body: {
|
|
1003
1022
|
taskId, sessionId, status: resumePlane.status,
|
|
1004
1023
|
bindingEnforced: true,
|
|
@@ -1298,7 +1317,7 @@ export function createResumeLegs(ctx) {
|
|
|
1298
1317
|
}
|
|
1299
1318
|
const routeCtxBase = {
|
|
1300
1319
|
deps,
|
|
1301
|
-
registry: { idemCache, inflightRuns, preemptableRuns,
|
|
1320
|
+
registry: { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters },
|
|
1302
1321
|
helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, isFleetWide, runOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite, runSessionAcceptOk },
|
|
1303
1322
|
local: { images: createImagesLocal(deps), sessions: createSessionsLocal(), sessionSync: createSessionSyncLocal(), capabilities: createCapabilitiesLocal() },
|
|
1304
1323
|
legs: { prepareSpec, finalizeTaskResult, resumeCheckpoint, driveResumeIntoRunLog, resumePreempted, resumeWake, preparePlanReview, drivePlanReview },
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -26,12 +26,15 @@ import type { SessionsLocal } from "./routes/sessions.js";
|
|
|
26
26
|
import type { SessionSyncLocal } from "./routes/session-sync.js";
|
|
27
27
|
import type { PlanApprovePermissionModeAfter } from "../task-settings.js";
|
|
28
28
|
import type { VerifyRoundsSpec } from "./verify-rounds.js";
|
|
29
|
-
|
|
29
|
+
import type { RunAbort } from "../run-cancel-context.js";
|
|
30
|
+
/** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
|
|
31
|
+
* S-947:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
|
|
32
|
+
* {@link RunAbort} 句柄上(`inflightRuns` 的值),同寿同删,不再有一张与腿不同寿的旁表。 */
|
|
30
33
|
export interface RunRegistry {
|
|
31
34
|
idemCache: IdempotencyCache<SubmitOutcome>;
|
|
32
|
-
|
|
35
|
+
/** 本副本在飞的 durable 腿 → 它的中止句柄(取消动词的同副本快路在这里按 `cancel_verb` 中止它)。 */
|
|
36
|
+
inflightRuns: Map<string, RunAbort>;
|
|
33
37
|
preemptableRuns: Map<string, AbortController>;
|
|
34
|
-
cancelledViaVerb: Set<string>;
|
|
35
38
|
steerableRuns: Map<string, TaskStream>;
|
|
36
39
|
wakeParkMints: Map<string, Promise<void>>;
|
|
37
40
|
/** 裸 number 的登记簿(`let` 拆不出闭包)—— 装进盒子才能跨模块自增/自减。 */
|
|
@@ -380,15 +383,9 @@ export interface RouteLegs {
|
|
|
380
383
|
decider?: {
|
|
381
384
|
principal?: string;
|
|
382
385
|
explicitOperator: boolean;
|
|
383
|
-
}): Promise<
|
|
384
|
-
status: number;
|
|
385
|
-
body: object;
|
|
386
|
-
}>;
|
|
386
|
+
}): Promise<ResumeLegOutcome>;
|
|
387
387
|
/** 全 resume 家族的共同下半场(lease admission + 写 run log + fleet 发布)。 */
|
|
388
|
-
driveResumeIntoRunLog(args: DriveResumeArgs): Promise<
|
|
389
|
-
status: number;
|
|
390
|
-
body: object;
|
|
391
|
-
}>;
|
|
388
|
+
driveResumeIntoRunLog(args: DriveResumeArgs): Promise<ResumeLegOutcome>;
|
|
392
389
|
/** assistant 抢占腿的复位(gate-kind 守卫 + 行级属主复核 + 驱动)。 */
|
|
393
390
|
resumePreempted(sessionId: string, req: IncomingMessage | undefined,
|
|
394
391
|
/** [ref] 件 S-1:透传给 {@link DriveResumeArgs.acceptEarly}(200 受理语义)。HTTP `/resume` 腿传 `true`。 */
|
|
@@ -628,6 +625,26 @@ export type RouteDecl = RouteDispatchDecl | RouteGateDecl;
|
|
|
628
625
|
* 两臂逐字节同形,指路价值不丢。钉=audit-fixes-http「[ref]」格(两臂全文比对);文案变更走
|
|
629
626
|
* api-error-text-freeze 基线流程。
|
|
630
627
|
*/
|
|
628
|
+
/**
|
|
629
|
+
* resume 家族一条腿的结局:HTTP 应答(`status` / `body`)+ 一位**进程内**事实(不上 wire)。
|
|
630
|
+
*
|
|
631
|
+
* `decisionCommitted` —— **这条腿的终局是在引擎受理了这次决定之后才结出来的**([ref] X5,7.112.0):本腿走到了「引擎交回结果」那一步
|
|
632
|
+
* (`resumeStream` / `resumeWithVerification` 解析 = core 的全部 pre-CAS 门 + checkpoint resolve CAS 都过了 —— 受理点的判据属主
|
|
633
|
+
* = {@link DriveResumeArgs.acceptEarly} 顶注),之后才收场。只由 `driveResumeLeg` 走完引擎那一步的终局出口铸;`CheckpointError` 臂、
|
|
634
|
+
* 「中止抛穿」臂、一切 4xx / 拒绝出口一律**不铸**(缺席 ⇒ 证不出提交,读者按「没提交」读 —— fail-closed)。
|
|
635
|
+
*
|
|
636
|
+
* 读者:`/decide` 的两道「这次决定提交了没有」门 —— [ref] 迟到结算留痕(`noteLateSettleIfAccepted`)与 remember 豁免落店
|
|
637
|
+
* (`routes/approvals-assistant.ts`)。它们原先只读 wire 体的 `errorCode !== "cancelled"`(「取消 = 决定没提交」),S-947 给续跑腿补了
|
|
638
|
+
* 「引擎受理之后才被取消」的取消终局之后,`cancelled` 不再等价「没提交」⇒ 两门改读本位:`200 ∧ (errorCode≠cancelled ∨ decisionCommitted)`。
|
|
639
|
+
* 🔴 为什么不放进 `result.cancelContext`(主会话初稿的落点):那只快照的契约是「分诊材料,不是状态机输入:任何门 / CAS / resume 判定都不许
|
|
640
|
+
* 读它」(`plugins/store-contracts.ts` `RunCancelContext` 顶注)—— 这两道门正是判定;而且门读的是 `/decide` 的平面体、看不到结果对象。
|
|
641
|
+
* 受理与否是腿自己的控制流事实,在腿上说,零 wire 变更。
|
|
642
|
+
*/
|
|
643
|
+
export interface ResumeLegOutcome {
|
|
644
|
+
status: number;
|
|
645
|
+
body: object;
|
|
646
|
+
decisionCommitted?: true;
|
|
647
|
+
}
|
|
631
648
|
/**
|
|
632
649
|
* **「决定/续跑已受理」那一枚 200 收据的单一铸点**(S-218)。
|
|
633
650
|
*
|
|
@@ -764,7 +764,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
764
764
|
: undefined;
|
|
765
765
|
const out = await resumeCheckpoint(sessionId, decision, body.reason ?? undefined, hostDecisionFacts(settledBy, deciderPrincipal), req, answer, binding, grantOnCommit, true, { principal: deciderPrincipal, explicitOperator });
|
|
766
766
|
const outErrorCode = out.body.errorCode;
|
|
767
|
-
if (pendingGrant !== undefined && out.status === 200 && outErrorCode !== "cancelled" && deps.approvalExemptionStore) {
|
|
767
|
+
if (pendingGrant !== undefined && out.status === 200 && (outErrorCode !== "cancelled" || out.decisionCommitted === true) && deps.approvalExemptionStore) {
|
|
768
768
|
try {
|
|
769
769
|
await deps.approvalExemptionStore.grant(pendingGrant.sessionId, pendingGrant.toolName, deciderPrincipal ?? null);
|
|
770
770
|
rememberApplied = true;
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -148,7 +148,7 @@ async function acceptLiveUserInput(deps, res, at) {
|
|
|
148
148
|
}
|
|
149
149
|
export async function handleRuns(req, res, match, ctx) {
|
|
150
150
|
const { deps } = ctx;
|
|
151
|
-
const { idemCache, inflightRuns, preemptableRuns,
|
|
151
|
+
const { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints } = ctx.registry;
|
|
152
152
|
const { readJson, rateLimited, quotaExceeded, leaseDenied, usageWindowDenied, runOwnerOk, runSessionAcceptOk } = ctx.helpers;
|
|
153
153
|
const { prepareSpec } = ctx.legs;
|
|
154
154
|
const source = ctx.req.source;
|
|
@@ -319,7 +319,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
319
319
|
}
|
|
320
320
|
const err = note;
|
|
321
321
|
try {
|
|
322
|
-
const result = mintCancelledResult({ taskId, sessionId: run.sessionId, startedAtMs: Date.parse(run.createdAt), message: err });
|
|
322
|
+
const result = mintCancelledResult({ taskId, sessionId: run.sessionId, startedAtMs: Date.parse(run.createdAt), message: err, cause: "cancel_verb" });
|
|
323
323
|
let lastErr;
|
|
324
324
|
for (let attempt = 0;; attempt++) {
|
|
325
325
|
try {
|
|
@@ -368,10 +368,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
368
368
|
sendJson(res, 202, { taskId, status: now?.status ?? "failed", cancelRequested: now?.cancelRequested ?? false, note: "run already terminal — cancel is a no-op" });
|
|
369
369
|
return;
|
|
370
370
|
}
|
|
371
|
-
|
|
372
|
-
cancelledViaVerb.add(taskId);
|
|
373
|
-
inflightRuns.get(taskId).abort();
|
|
374
|
-
}
|
|
371
|
+
inflightRuns.get(taskId)?.abort("cancel_verb");
|
|
375
372
|
sendJson(res, 202, { taskId, status: "cancelling", cancelRequested: true });
|
|
376
373
|
}
|
|
377
374
|
else if (isParkedRunStatus(run.status)) {
|
|
@@ -1155,6 +1152,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1155
1152
|
}
|
|
1156
1153
|
const handle = resolution?.handle;
|
|
1157
1154
|
if (handle) {
|
|
1155
|
+
let acceptance;
|
|
1158
1156
|
try {
|
|
1159
1157
|
if (verb === "resume") {
|
|
1160
1158
|
if (!handle.resume) {
|
|
@@ -1170,11 +1168,23 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1170
1168
|
sendJson(res, 200, { taskId: runId, target, status: "running", delivery: "applied", marker, note: `Message queued for delivery to ${handle.agentName ?? target}; it will continue in the background.` });
|
|
1171
1169
|
return;
|
|
1172
1170
|
}
|
|
1171
|
+
const messageId = uuidv7();
|
|
1172
|
+
const accepted = await acceptLiveUserInput(deps, res, { messageId, sessionId: run.sessionId, taskId: runId, leg: handle });
|
|
1173
|
+
if (accepted === "refused")
|
|
1174
|
+
return;
|
|
1175
|
+
acceptance = accepted;
|
|
1173
1176
|
const marker = await handle.steer(redacted);
|
|
1174
|
-
|
|
1177
|
+
if (acceptance !== undefined) {
|
|
1178
|
+
const ledger = deps.userInputLedger;
|
|
1179
|
+
const settle = () => ledger.settleLeg(handle);
|
|
1180
|
+
void handle.settled.then(settle, settle);
|
|
1181
|
+
}
|
|
1182
|
+
sendJson(res, 200, { taskId: runId, target, status: "running", delivery: "applied", messageId, marker, note: `Message queued for delivery to ${handle.agentName ?? target} at its next tool round.` });
|
|
1175
1183
|
return;
|
|
1176
1184
|
}
|
|
1177
1185
|
catch (e) {
|
|
1186
|
+
if (acceptance !== undefined)
|
|
1187
|
+
await deps.userInputLedger.withdraw(acceptance);
|
|
1178
1188
|
const code = e.code;
|
|
1179
1189
|
if (code === "steering.not_running") {
|
|
1180
1190
|
send409(code, "sub-agent just finished — no longer accepting steers");
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* · {@link handleTasksBody} 内的 `runStreamSubmitLeg` —— plain-stream 腿(原 703 行匿名体);
|
|
12
12
|
* · `runSyncSubmitLeg` —— 同步腿(原 128 行匿名体);
|
|
13
13
|
* · 同步腿内 `verifyLeg` / `cascadeLeg` / `plainLeg` —— 三条模型驱动腿,判别顺序与短路语义逐字不变。
|
|
14
|
-
*
|
|
15
|
-
* (
|
|
14
|
+
* 提名**只改名字不改行为**:闭包捕获的自由变量、语句顺序、settle/registry 生命周期注释全部随代码原样迁移。
|
|
15
|
+
* (S-947 起「这次是不是被 server 中止、为什么」读每腿自己的中止句柄 `RunAbort`,不再有按 taskId 键控的
|
|
16
|
+
* `cancelledViaVerb` 旁表与它「身份守卫内先取后删」的取值时点。)
|
|
16
17
|
*
|
|
17
18
|
* 与 runs 域共用 `ctx.legs.prepareSpec`(读体/校验/建 spec 的同一条前段)。
|
|
18
19
|
*/
|