@sema-agent/server 7.111.0 → 7.113.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 +68 -0
- package/USAGE.md +3 -3
- package/dist/approval-ask-audit-store.d.ts +3 -4
- package/dist/approval-ask-audit-store.js +3 -3
- package/dist/approval-card.d.ts +4 -4
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/stores.js +4 -6
- package/dist/boot/workflow-orchestration.js +4 -4
- package/dist/config-types.d.ts +1 -1
- package/dist/config.js +2 -1
- package/dist/hooks/hook-runner.d.ts +7 -2
- package/dist/hooks/hook-runner.js +48 -4
- package/dist/host-decision.d.ts +5 -3
- package/dist/http/admission.js +12 -3
- package/dist/http/request-guards.js +2 -2
- package/dist/http/resume-legs.d.ts +14 -24
- package/dist/http/resume-legs.js +91 -29
- package/dist/http/route-ctx.d.ts +64 -17
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/background.js +2 -2
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/notify-wake.js +3 -3
- package/dist/http/routes/runs.js +22 -19
- package/dist/http/routes/session-sync.js +6 -6
- package/dist/http/routes/sessions.js +20 -20
- package/dist/http/routes/tasks.d.ts +3 -2
- package/dist/http/routes/tasks.js +38 -45
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +1 -1
- package/dist/http/server.js +1 -2
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/local-data-root.d.ts +14 -0
- package/dist/local-data-root.js +5 -0
- package/dist/observability/corrupt-read-seat.d.ts +46 -66
- package/dist/observability/corrupt-read-seat.js +74 -59
- package/dist/observability/fail-open.d.ts +29 -1
- package/dist/observability/fail-open.js +26 -1
- package/dist/observability/run-terminal.js +39 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +1 -5
- package/dist/orchestration/workflow-agent-session-index.js +2 -2
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -2
- package/dist/plugins/approval-ask-store-file.d.ts +1 -4
- package/dist/plugins/approval-ask-store-file.js +2 -2
- package/dist/plugins/file-resume-anchor-store.d.ts +1 -6
- package/dist/plugins/file-resume-anchor-store.js +2 -4
- package/dist/plugins/file-run-store.d.ts +0 -4
- package/dist/plugins/file-run-store.js +1 -3
- package/dist/plugins/send-file-ledger.d.ts +1 -5
- package/dist/plugins/send-file-ledger.js +2 -2
- package/dist/plugins/store-backend.js +8 -9
- package/dist/plugins/store-contracts.d.ts +40 -7
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/user-input-ledger-file.d.ts +1 -5
- package/dist/plugins/user-input-ledger-file.js +2 -2
- package/dist/run-cancel-context.d.ts +59 -7
- package/dist/run-cancel-context.js +26 -0
- package/dist/run-local.d.ts +1 -1
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +7 -5
- package/dist/runs.js +18 -13
- package/dist/spec-fields.js +0 -1
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.js +2 -3
- package/dist/trace/redact.d.ts +18 -0
- package/dist/trace/redact.js +4 -0
- package/dist/trace/self-minted-wire.d.ts +22 -0
- package/dist/trace/self-minted-wire.js +8 -0
- package/dist/unattended-permission-mode.d.ts +9 -2
- package/dist/unattended-permission-mode.js +5 -1
- package/dist/user-input-ledger.d.ts +24 -5
- package/dist/user-input-ledger.js +46 -11
- package/package.json +4 -3
- package/scripts/retired-mode-seed-drain-local.mjs +317 -0
|
@@ -9,7 +9,7 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SET
|
|
|
9
9
|
import { readWritableSessionPermissionRules, describeSessionPermissionRulesProblem } from "../../session-policy-wire.js";
|
|
10
10
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders } from "../send.js";
|
|
11
11
|
import { gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
|
|
12
|
-
import { assertNever } from "../route-ctx.js";
|
|
12
|
+
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
13
13
|
import { rosterAgentId } from "./agents-roster.js";
|
|
14
14
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
15
15
|
const MCP_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
@@ -68,15 +68,15 @@ async function readLastLegMcp(deps, sessionId, principal) {
|
|
|
68
68
|
export async function handleSessions(req, res, match, ctx) {
|
|
69
69
|
const { deps } = ctx;
|
|
70
70
|
const { readJson, safeDecode, runOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite } = ctx.helpers;
|
|
71
|
-
const readOwnedSessionView = async (sessionAudit, sessionId
|
|
71
|
+
const readOwnedSessionView = async (sessionAudit, sessionId) => {
|
|
72
72
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
73
73
|
if (ownerOf !== undefined) {
|
|
74
74
|
const owner = await ownerOf(sessionId);
|
|
75
75
|
if (owner === undefined) {
|
|
76
|
-
sendError(res, 404, "not_found.session",
|
|
76
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
77
77
|
return undefined;
|
|
78
78
|
}
|
|
79
|
-
if (!runOwnerOk(req, res, owner,
|
|
79
|
+
if (!runOwnerOk(req, res, owner, "session"))
|
|
80
80
|
return undefined;
|
|
81
81
|
}
|
|
82
82
|
let audit;
|
|
@@ -89,10 +89,10 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
89
89
|
throw new Error(`session view read failed before ownership could be established: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
90
90
|
}
|
|
91
91
|
if (!audit) {
|
|
92
|
-
sendError(res, 404, "not_found.session",
|
|
92
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
93
93
|
return undefined;
|
|
94
94
|
}
|
|
95
|
-
if (!runOwnerOk(req, res, audit.owner,
|
|
95
|
+
if (!runOwnerOk(req, res, audit.owner, "session"))
|
|
96
96
|
return undefined;
|
|
97
97
|
return audit;
|
|
98
98
|
};
|
|
@@ -261,21 +261,21 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
261
261
|
if (getHead) {
|
|
262
262
|
const head = await getHead(headSession);
|
|
263
263
|
if (head === undefined) {
|
|
264
|
-
sendError(res, 404, "not_found.session",
|
|
264
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
265
265
|
return;
|
|
266
266
|
}
|
|
267
267
|
owner = head.owner;
|
|
268
|
-
if (!runOwnerOk(req, res, owner ?? null))
|
|
268
|
+
if (!runOwnerOk(req, res, owner ?? null, "session"))
|
|
269
269
|
return;
|
|
270
270
|
leafId = head.leafId;
|
|
271
271
|
}
|
|
272
272
|
else {
|
|
273
273
|
owner = await ownerOf(headSession);
|
|
274
274
|
if (owner === undefined) {
|
|
275
|
-
sendError(res, 404, "not_found.session",
|
|
275
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
276
276
|
return;
|
|
277
277
|
}
|
|
278
|
-
if (!runOwnerOk(req, res, owner ?? null))
|
|
278
|
+
if (!runOwnerOk(req, res, owner ?? null, "session"))
|
|
279
279
|
return;
|
|
280
280
|
leafId = await getLeafId(headSession);
|
|
281
281
|
}
|
|
@@ -355,10 +355,10 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
355
355
|
}
|
|
356
356
|
if (owner === undefined) {
|
|
357
357
|
cleanup();
|
|
358
|
-
sendError(res, 404, "not_found.session",
|
|
358
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
359
359
|
return;
|
|
360
360
|
}
|
|
361
|
-
if (!runOwnerOk(req, res, owner ?? null)) {
|
|
361
|
+
if (!runOwnerOk(req, res, owner ?? null, "session")) {
|
|
362
362
|
cleanup();
|
|
363
363
|
return;
|
|
364
364
|
}
|
|
@@ -498,7 +498,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
498
498
|
sendError(res, 501, "capability.memory_engine_required", "memory provenance and erasure are not available on this deployment — they require a wired memory engine (MEMORY_ENGINE=on) whose backend owns the engine control plane (controlPlaneRoot); the file memory backend does, the pg/tidb memory backends do not (their control plane would land on a replica's local disk)");
|
|
499
499
|
return;
|
|
500
500
|
}
|
|
501
|
-
if ((await readOwnedSessionView(deps.sessionAudit, sessionId
|
|
501
|
+
if ((await readOwnedSessionView(deps.sessionAudit, sessionId)) === undefined)
|
|
502
502
|
return;
|
|
503
503
|
const faces = deps.memoryCompliance;
|
|
504
504
|
if (faces === undefined) {
|
|
@@ -550,7 +550,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
550
550
|
sendError(res, 501, "capability.memory_engine_required", "session memory status is not available on this deployment — it requires a wired memory engine (MEMORY_ENGINE=on) whose backend owns the engine control plane (controlPlaneRoot); the file memory backend does, the pg/tidb memory backends do not (their control plane would land on a replica's local disk)");
|
|
551
551
|
return;
|
|
552
552
|
}
|
|
553
|
-
if ((await readOwnedSessionView(deps.sessionAudit, sessionId
|
|
553
|
+
if ((await readOwnedSessionView(deps.sessionAudit, sessionId)) === undefined)
|
|
554
554
|
return;
|
|
555
555
|
const face = deps.sessionMemoryStatus;
|
|
556
556
|
if (face === undefined) {
|
|
@@ -594,7 +594,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
594
594
|
sendError(res, 404, "not_found.route", "not found");
|
|
595
595
|
return;
|
|
596
596
|
}
|
|
597
|
-
const audit = await readOwnedSessionView(deps.sessionAudit, match.params.sessionId
|
|
597
|
+
const audit = await readOwnedSessionView(deps.sessionAudit, match.params.sessionId);
|
|
598
598
|
if (audit === undefined)
|
|
599
599
|
return;
|
|
600
600
|
const q = new URLSearchParams(match.search);
|
|
@@ -665,17 +665,17 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
665
665
|
}
|
|
666
666
|
const owner = await ownerOf(sourceId);
|
|
667
667
|
if (owner === undefined) {
|
|
668
|
-
sendError(res, 404, "not_found.session",
|
|
668
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
669
669
|
return;
|
|
670
670
|
}
|
|
671
671
|
if (!scope.fleetWide && owner !== scope.gateOwner && !legacyUnownedSessionOk(deps.config.requirePrincipal, scope.gateOwner, owner)) {
|
|
672
|
-
sendError(res, 404, "not_found.session",
|
|
672
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
673
673
|
return;
|
|
674
674
|
}
|
|
675
675
|
const newOwner = scope.fleetWide ? (owner ?? null) : scope.gateOwner;
|
|
676
676
|
const newId = await fork(sourceId, newOwner);
|
|
677
677
|
if (newId === null) {
|
|
678
|
-
sendError(res, 404, "not_found.session",
|
|
678
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
679
679
|
return;
|
|
680
680
|
}
|
|
681
681
|
sendJson(res, 200, { sessionId: newId });
|
|
@@ -747,7 +747,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
747
747
|
}
|
|
748
748
|
const owner = await ownerOf(sessionId);
|
|
749
749
|
if (owner === undefined) {
|
|
750
|
-
sendError(res, 404, "not_found.session",
|
|
750
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
751
751
|
return;
|
|
752
752
|
}
|
|
753
753
|
if (owner === null) {
|
|
@@ -756,7 +756,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
756
756
|
}
|
|
757
757
|
const operator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
758
758
|
if (!operator && owner !== (principal ?? null)) {
|
|
759
|
-
sendError(res, 404, "not_found.session",
|
|
759
|
+
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
760
760
|
return;
|
|
761
761
|
}
|
|
762
762
|
const target = owner;
|
|
@@ -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
|
*/
|
|
@@ -10,7 +10,7 @@ import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotific
|
|
|
10
10
|
import { createLedgerSink } from "../../trace/ledger-sink.js";
|
|
11
11
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
12
12
|
import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
|
|
13
|
-
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
13
|
+
import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../../run-cancel-context.js";
|
|
14
14
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
15
15
|
import { redactSecrets } from "../../trace/redact.js";
|
|
16
16
|
import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestFaceEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData, liveIdentityFields } from "../../trace/project.js";
|
|
@@ -57,7 +57,7 @@ function sendSubmitResult(res, resp) {
|
|
|
57
57
|
const MULTI_ATTEMPT_NO_EVENT_CHANNEL_NOTICE = "verify / cascade ran with their normal multi-attempt semantics, but the synchronous /v1/tasks endpoint has no event channel — no status/progress frames exist to replay on this shape. For status / progress / delegated-child frames and the terminal done, submit via POST /v1/runs and read GET /v1/runs/:id/events; note that verify / cascade legs carry no per-turn stream there either (no wiring_manifest / text / turn_end frames — the engine runs those legs as result-returning attempts).";
|
|
58
58
|
export async function handleTasks(req, res, match, ctx) {
|
|
59
59
|
const { deps } = ctx;
|
|
60
|
-
const { idemCache, inflightRuns,
|
|
60
|
+
const { idemCache, inflightRuns, steerableRuns, counters } = ctx.registry;
|
|
61
61
|
const { rateLimited, quotaExceeded, leaseDenied, usageWindowDenied } = ctx.helpers;
|
|
62
62
|
const { prepareSpec, finalizeTaskResult } = ctx.legs;
|
|
63
63
|
const reqState = ctx.req;
|
|
@@ -136,7 +136,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
136
136
|
if (earlyDurableTid) {
|
|
137
137
|
res.write(`event: meta\ndata: ${JSON.stringify({ type: "meta", taskId: earlyDurableTid, ...(prepared.spec.sessionId ? { sessionId: prepared.spec.sessionId } : {}) })}\n\n`);
|
|
138
138
|
}
|
|
139
|
-
const ac =
|
|
139
|
+
const ac = createRunAbort();
|
|
140
140
|
let closed = false;
|
|
141
141
|
let detachLogged = false;
|
|
142
142
|
let disconnectParkGrace;
|
|
@@ -158,14 +158,14 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
158
158
|
deps.logger?.info("stream_disconnect_ask_parked", { taskId: earlyDurableTid, sessionId: prepared.spec.sessionId, asks: parked, graceMs: DISCONNECT_PARK_GRACE_MS });
|
|
159
159
|
disconnectParkGrace = setTimeout(() => {
|
|
160
160
|
closed = true;
|
|
161
|
-
ac.abort();
|
|
161
|
+
ac.abort("ask_park_grace_exhausted");
|
|
162
162
|
}, DISCONNECT_PARK_GRACE_MS);
|
|
163
163
|
disconnectParkGrace.unref?.();
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
closed = true;
|
|
168
|
-
ac.abort();
|
|
168
|
+
ac.abort("client_disconnected");
|
|
169
169
|
};
|
|
170
170
|
req.on("close", onDisconnect);
|
|
171
171
|
res.on("close", () => {
|
|
@@ -213,13 +213,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
213
213
|
let runRowSettled = false;
|
|
214
214
|
let durableTaskId;
|
|
215
215
|
let durableHeartbeat;
|
|
216
|
-
const cancelledResult = (errorMessage,
|
|
217
|
-
taskId: durableTaskId ?? "",
|
|
218
|
-
sessionId: prepared.spec.sessionId ?? "",
|
|
219
|
-
startedAtMs: streamLegStartedAt,
|
|
220
|
-
message: errorMessage,
|
|
221
|
-
...(engineResult !== undefined ? { engineResult } : {}),
|
|
222
|
-
});
|
|
216
|
+
const cancelledResult = (errorMessage, cause) => mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId ?? "", startedAtMs: streamLegStartedAt, message: errorMessage, cause });
|
|
223
217
|
let userMsgEntryId;
|
|
224
218
|
let anchorPut = false;
|
|
225
219
|
const putRewindAnchor = () => {
|
|
@@ -267,10 +261,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
267
261
|
usageKey = tid;
|
|
268
262
|
deps.modelUsage?.register(tid);
|
|
269
263
|
const owner = principal ?? null;
|
|
270
|
-
const abortFromVerb = () =>
|
|
271
|
-
cancelledViaVerb.add(tid);
|
|
272
|
-
ac.abort();
|
|
273
|
-
};
|
|
264
|
+
const abortFromVerb = () => ac.abort("cancel_verb");
|
|
274
265
|
durableHeartbeat = setInterval(() => {
|
|
275
266
|
void rs.heartbeat(tid, owner).catch(() => undefined);
|
|
276
267
|
if (!ac.signal.aborted) {
|
|
@@ -292,10 +283,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
292
283
|
catch (e) {
|
|
293
284
|
if (durableHeartbeat)
|
|
294
285
|
clearInterval(durableHeartbeat);
|
|
295
|
-
if (durableTaskId && inflightRuns.get(durableTaskId) === ac)
|
|
286
|
+
if (durableTaskId && inflightRuns.get(durableTaskId) === ac)
|
|
296
287
|
inflightRuns.delete(durableTaskId);
|
|
297
|
-
cancelledViaVerb.delete(durableTaskId);
|
|
298
|
-
}
|
|
299
288
|
throw e;
|
|
300
289
|
}
|
|
301
290
|
let fleetSettled = false;
|
|
@@ -315,10 +304,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
315
304
|
catch (e) {
|
|
316
305
|
if (durableHeartbeat)
|
|
317
306
|
clearInterval(durableHeartbeat);
|
|
318
|
-
if (durableTaskId && inflightRuns.get(durableTaskId) === ac)
|
|
307
|
+
if (durableTaskId && inflightRuns.get(durableTaskId) === ac)
|
|
319
308
|
inflightRuns.delete(durableTaskId);
|
|
320
|
-
cancelledViaVerb.delete(durableTaskId);
|
|
321
|
-
}
|
|
322
309
|
settleFleet("failed");
|
|
323
310
|
throw e;
|
|
324
311
|
}
|
|
@@ -430,13 +417,14 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
430
417
|
sseData(res, end);
|
|
431
418
|
if (ev.type === "message_committed" && ev.role === "user")
|
|
432
419
|
userMsgEntryId = ev.entryId;
|
|
433
|
-
const
|
|
420
|
+
const causeAtDone = ev.type === "done" ? ac.cause() : undefined;
|
|
421
|
+
await deps.userInputLedger?.observeLegEvent(liveStream, ev);
|
|
434
422
|
if (ev.type === "done") {
|
|
435
423
|
finalResult = await mintSubmitExitResult(ev.result, deps.checkpointStore);
|
|
436
424
|
if (durableTaskId && finalResult)
|
|
437
425
|
finalResult = { ...finalResult, taskId: durableTaskId };
|
|
438
|
-
if (
|
|
439
|
-
finalResult =
|
|
426
|
+
if (causeAtDone !== undefined && finalResult !== undefined) {
|
|
427
|
+
finalResult = mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId ?? "", startedAtMs: streamLegStartedAt, message: "cancelled by user", cause: causeAtDone, engineResult: finalResult });
|
|
440
428
|
}
|
|
441
429
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
442
430
|
noteRunOutcome(finalResult);
|
|
@@ -672,6 +660,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
672
660
|
else {
|
|
673
661
|
putRewindAnchor();
|
|
674
662
|
const pending = await deps.checkpointStore?.findPendingTokenBySession(prepared.spec.sessionId);
|
|
663
|
+
const dcCause = ac.cause();
|
|
675
664
|
if (pending) {
|
|
676
665
|
if (ledgerSink && !ledgerTerminal) {
|
|
677
666
|
ledgerTerminal = true;
|
|
@@ -681,18 +670,28 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
681
670
|
await deps.runStore.setSuspended(durableTaskId, null);
|
|
682
671
|
settleFleet("suspended");
|
|
683
672
|
}
|
|
684
|
-
else {
|
|
673
|
+
else if (dcCause !== undefined) {
|
|
685
674
|
const dcErr = "stream client disconnected before completion (run aborted)";
|
|
686
|
-
const dc = cancelledResult(dcErr);
|
|
675
|
+
const dc = cancelledResult(dcErr, dcCause);
|
|
687
676
|
if (ledgerSink && !ledgerTerminal) {
|
|
688
677
|
ledgerTerminal = true;
|
|
689
678
|
await ledgerSink.flush().catch(() => undefined);
|
|
690
|
-
await ledgerSink.append("failed",
|
|
679
|
+
await ledgerSink.append("failed", buildCancelledLedgerRow(dc)).catch(() => undefined);
|
|
691
680
|
}
|
|
692
681
|
await deps.runStore.setTerminal(durableTaskId, "failed", dc, dcErr);
|
|
693
682
|
noteRunOutcome(dc);
|
|
694
683
|
settleFleet("failed");
|
|
695
684
|
}
|
|
685
|
+
else {
|
|
686
|
+
const err = "stream ended before a terminal event";
|
|
687
|
+
if (ledgerSink && !ledgerTerminal) {
|
|
688
|
+
ledgerTerminal = true;
|
|
689
|
+
await ledgerSink.flush().catch(() => undefined);
|
|
690
|
+
await ledgerSink.append("failed", { errorMessage: err }).catch(() => undefined);
|
|
691
|
+
}
|
|
692
|
+
await deps.runStore.setTerminal(durableTaskId, "failed", null, err);
|
|
693
|
+
settleFleet("failed");
|
|
694
|
+
}
|
|
696
695
|
}
|
|
697
696
|
runRowSettled = true;
|
|
698
697
|
}
|
|
@@ -706,10 +705,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
706
705
|
steerableRuns.delete(durableTaskId);
|
|
707
706
|
if (liveStreamRef !== undefined)
|
|
708
707
|
await deps.userInputLedger?.settleLeg(liveStreamRef);
|
|
709
|
-
if (durableTaskId && inflightRuns.get(durableTaskId) === ac)
|
|
708
|
+
if (durableTaskId && inflightRuns.get(durableTaskId) === ac)
|
|
710
709
|
inflightRuns.delete(durableTaskId);
|
|
711
|
-
cancelledViaVerb.delete(durableTaskId);
|
|
712
|
-
}
|
|
713
710
|
for (const evict of subagentHandleEvictions)
|
|
714
711
|
evict();
|
|
715
712
|
if (!runRowSettled && durableTaskId && deps.runStore) {
|
|
@@ -717,6 +714,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
717
714
|
const row = await deps.runStore.getRun(durableTaskId);
|
|
718
715
|
if (row?.status === "running") {
|
|
719
716
|
const pending = prepared.spec.sessionId ? await deps.checkpointStore?.findPendingTokenBySession(prepared.spec.sessionId) : undefined;
|
|
717
|
+
const backstopCause = ac.cause();
|
|
720
718
|
if (pending) {
|
|
721
719
|
if (ledgerSink && !ledgerTerminal) {
|
|
722
720
|
ledgerTerminal = true;
|
|
@@ -735,13 +733,13 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
735
733
|
await deps.runStore.setTerminal(durableTaskId, retryPlane.status, finalResult, retryPlane.errorMessage ?? null);
|
|
736
734
|
settleFleet(retryPlane.status, fleetRunResiduals(finalResult));
|
|
737
735
|
}
|
|
738
|
-
else if (
|
|
736
|
+
else if (backstopCause !== undefined) {
|
|
739
737
|
const err = "stream aborted before completion (run cancelled)";
|
|
740
|
-
const c = cancelledResult(err);
|
|
738
|
+
const c = cancelledResult(err, backstopCause);
|
|
741
739
|
if (ledgerSink && !ledgerTerminal) {
|
|
742
740
|
ledgerTerminal = true;
|
|
743
741
|
await ledgerSink.flush().catch(() => undefined);
|
|
744
|
-
await ledgerSink.append("failed",
|
|
742
|
+
await ledgerSink.append("failed", buildCancelledLedgerRow(c)).catch(() => undefined);
|
|
745
743
|
}
|
|
746
744
|
await deps.runStore.setTerminal(durableTaskId, "failed", c, err);
|
|
747
745
|
}
|
|
@@ -808,17 +806,14 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
808
806
|
if (deps.checkpointStore && prepared.spec.sessionId) {
|
|
809
807
|
await deps.checkpointStore.putCtx(prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope });
|
|
810
808
|
}
|
|
811
|
-
const syncCancelCtrl =
|
|
809
|
+
const syncCancelCtrl = createRunAbort();
|
|
812
810
|
if (prepared.spec.sessionId)
|
|
813
811
|
markChildrenStoppedByUserOnAbort(syncCancelCtrl.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
814
812
|
if (durableTaskId && deps.runStore) {
|
|
815
813
|
const rs = deps.runStore;
|
|
816
814
|
const tid = durableTaskId;
|
|
817
815
|
const owner = principal ?? null;
|
|
818
|
-
const abortFromVerb = () =>
|
|
819
|
-
cancelledViaVerb.add(tid);
|
|
820
|
-
syncCancelCtrl.abort();
|
|
821
|
-
};
|
|
816
|
+
const abortFromVerb = () => syncCancelCtrl.abort("cancel_verb");
|
|
822
817
|
durableHeartbeat = setInterval(() => {
|
|
823
818
|
void rs.heartbeat(tid, owner).catch(() => undefined);
|
|
824
819
|
if (!syncCancelCtrl.signal.aborted) {
|
|
@@ -841,7 +836,6 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
841
836
|
wiringReceipt = wiringManifestFaceEventData(ev, wiringOperatorFace, instructionLeg.segment());
|
|
842
837
|
};
|
|
843
838
|
let result;
|
|
844
|
-
let cancelVerbLabel = false;
|
|
845
839
|
counters.uncountedBillableInflight++;
|
|
846
840
|
try {
|
|
847
841
|
const verifyLeg = (v) => runWithVerification(prepared.runner, specWithSignal, v);
|
|
@@ -858,18 +852,17 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
858
852
|
counters.uncountedBillableInflight--;
|
|
859
853
|
if (durableHeartbeat)
|
|
860
854
|
clearInterval(durableHeartbeat);
|
|
861
|
-
if (durableTaskId && inflightRuns.get(durableTaskId) === syncCancelCtrl)
|
|
855
|
+
if (durableTaskId && inflightRuns.get(durableTaskId) === syncCancelCtrl)
|
|
862
856
|
inflightRuns.delete(durableTaskId);
|
|
863
|
-
cancelVerbLabel = cancelledViaVerb.has(durableTaskId);
|
|
864
|
-
cancelledViaVerb.delete(durableTaskId);
|
|
865
|
-
}
|
|
866
857
|
}
|
|
867
|
-
|
|
858
|
+
const syncAbortCause = syncCancelCtrl.cause();
|
|
859
|
+
if (syncAbortCause !== undefined) {
|
|
868
860
|
result = mintCancelledResult({
|
|
869
861
|
taskId: durableTaskId ?? "",
|
|
870
862
|
sessionId: prepared.spec.sessionId ?? "",
|
|
871
863
|
startedAtMs: syncLegStartedAt,
|
|
872
864
|
message: "cancelled by user",
|
|
865
|
+
cause: syncAbortCause,
|
|
873
866
|
engineResult: result,
|
|
874
867
|
});
|
|
875
868
|
}
|
package/dist/http/send.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export declare function sendSessionPolicyError(res: ServerResponse, e: unknown):
|
|
|
80
80
|
* 句子:属主给了固定句就发它(import 面 `invalid_session` 那一句),否则发 core 的原句过既有脱敏(`redactErrorMessage`:
|
|
81
81
|
* 不抛、有上限)。core 的 `corrupt_session` 原句只点名会话 id 与跳过计数(「Session <id> is unreadable: its log has no
|
|
82
82
|
* meta header (N of M records skipped as corrupt)」),SQL 两店的原句点名 `<表>.<列>` 与行号 —— 两者都不含盘上路径
|
|
83
|
-
* (
|
|
83
|
+
* (路径只进运维面:`server.session-repo.corrupt-record-skipped` 的探针行,S-990;SQL 两店走各自的拒绝句)。
|
|
84
84
|
*/
|
|
85
85
|
export declare function sendSessionError(res: ServerResponse, e: unknown, face: SessionErrorFace): boolean;
|
|
86
86
|
export declare function msg(e: unknown): string;
|
package/dist/http/send.js
CHANGED
|
@@ -43,7 +43,7 @@ export function sendSessionPolicyError(res, e) {
|
|
|
43
43
|
if (!(e instanceof SessionPolicyError))
|
|
44
44
|
return false;
|
|
45
45
|
const h = sessionPolicyErrorHttp(e.code);
|
|
46
|
-
sendError(res, h.status, h.errorCode, e.message);
|
|
46
|
+
sendError(res, h.status, h.errorCode, redactErrorMessage(e.message) ?? "session policy error");
|
|
47
47
|
return true;
|
|
48
48
|
}
|
|
49
49
|
export function sendSessionError(res, e, face) {
|
package/dist/http/server.js
CHANGED
|
@@ -43,7 +43,6 @@ export function createHttpServer(rawDeps) {
|
|
|
43
43
|
}
|
|
44
44
|
const idemCache = new IdempotencyCache();
|
|
45
45
|
const inflightRuns = new Map();
|
|
46
|
-
const cancelledViaVerb = new Set();
|
|
47
46
|
const preemptableRuns = new Map();
|
|
48
47
|
const steerableRuns = new Map();
|
|
49
48
|
const wakeParkMints = new Map();
|
|
@@ -97,7 +96,7 @@ export function createHttpServer(rawDeps) {
|
|
|
97
96
|
}
|
|
98
97
|
const { quotaExceeded, ownerCostQuotaDenied, ownerFleetLeaseDenied, ownerLeaseDenied, ownerQuotaDenied, leaseDenied, usageWindowDenialFor, usageWindowDenied, rateLimited, safeDecode, isFleetWide, sessionOwnerScope, sessionOwnerScopeForWrite, runOwnerOk, runSessionAcceptOk, readJson, readRawBody } = createRequestGuards({ deps });
|
|
99
98
|
const { prepareSpec } = createAdmission({ deps, readJson, runnerFor });
|
|
100
|
-
const { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, mayDiscloseRowError, resumePreempted, startFreshHostTurn, resumeWake } = createResumeLegs({ deps, runnerFor, idemCache, inflightRuns, preemptableRuns,
|
|
99
|
+
const { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, mayDiscloseRowError, resumePreempted, startFreshHostTurn, resumeWake } = createResumeLegs({ 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 });
|
|
101
100
|
const { denyExpiredApprovals, redeemParkedAsk } = createApprovalSweeps({ deps, resumeCheckpoint, mayDiscloseRowError });
|
|
102
101
|
const { handle } = createDispatch({ deps, noteActivity, routeCtxBase, counters });
|
|
103
102
|
const server = http.createServer((req, res) => {
|
|
@@ -233,13 +233,13 @@ export interface TaskRequestBody {
|
|
|
233
233
|
* core 默认开列表注入的通道)与三个 deadline opt-out 在类型面失踪。agentListing/skillsListing 是
|
|
234
234
|
* boolean 透传(core DEFAULT-ON,explicit false 才关);budgetUsd/mcpInstructions 只认 literal true;
|
|
235
235
|
* limits 三个 opt-out 只认 literal false(normalizer 同判)。 */
|
|
236
|
+
/** core 7.39.0 #1254:`planModeReminder` 退役(硬破坏,零别名)—— 提交即 400 点名退役;plan 姿态由引擎从 `permissionMode:"plan"` 自己陈述。 */
|
|
236
237
|
attachments?: {
|
|
237
238
|
todoReminder?: boolean;
|
|
238
239
|
todoReminderMode?: "baseline" | "off";
|
|
239
240
|
changedFiles?: boolean | {
|
|
240
241
|
maxFiles?: number;
|
|
241
242
|
};
|
|
242
|
-
planModeReminder?: boolean;
|
|
243
243
|
budgetUsd?: true;
|
|
244
244
|
backgroundTasks?: boolean;
|
|
245
245
|
toolsDelta?: boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **本机数据根的唯一解析**(`DB_BACKEND=local` 的 file 店把全部落盘件放在这个目录下;`config.localDataRoot` 就是它的值)。
|
|
3
|
+
*
|
|
4
|
+
* 链(第一个非空者胜,空串视作缺席):`LOCAL_DATA_ROOT` → `CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `<home>/.ai-agent`。
|
|
5
|
+
*
|
|
6
|
+
* 为什么是一只**纯函数、env 与 home 都由调用方递入**:本函数有两个读者 —— 服务自己的 `loadConfig`(递 `process.env` / `os.homedir()`)与随包发的运维脚本
|
|
7
|
+
* `scripts/retired-mode-seed-drain-local.mjs`(经 `dist/local-data-root.js` 取同一只函数,缺省数据根不再自抄一份链:抄本只对了一半,
|
|
8
|
+
* 设了 `AGENT_DATA_DIR` 的部署不带参数跑脚本会读到另一个目录并报「干净」)。纯叶、零反向依赖 ⇒ 脚本引它不会把整个服务装载起来。
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ 与**配置根**(`configLocalRoot`:`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`,不吃 `LOCAL_DATA_ROOT`)不是同一条链 —— 两根分家的那一形由
|
|
11
|
+
* boot 期的 `local_roots_split` 告警说出来,本函数不改那条解析。
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveLocalDataRoot(env: Readonly<Record<string, string | undefined>>, home: string): string;
|
|
14
|
+
//# sourceMappingURL=local-data-root.d.ts.map
|