@sema-agent/server 7.55.0 → 7.57.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 +3 -3
- package/README.md +14 -7
- package/README.zh-CN.md +10 -6
- package/USAGE.md +207 -59
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/dist/approval-ask-audit-store.d.ts +100 -1
- package/dist/approval-ask-audit-store.js +103 -2
- package/dist/approval-card.d.ts +148 -13
- package/dist/approval-card.js +82 -13
- package/dist/approval-content-kind.d.ts +22 -0
- package/dist/approval-content-kind.js +5 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +18 -0
- package/dist/auto-mode-face.d.ts +111 -0
- package/dist/auto-mode-face.js +99 -0
- package/dist/bench/s1/arms.js +5 -5
- package/dist/bench/s1/live-deps.js +11 -11
- package/dist/bench/s1/run-firm.js +3 -0
- package/dist/bench/s1/runner-ctx.d.ts +4 -0
- package/dist/bench/s1/runner-ctx.js +7 -0
- package/dist/boot/config-center.js +23 -1
- package/dist/boot/coordinators.js +13 -1
- package/dist/boot/leader.d.ts +21 -0
- package/dist/boot/leader.js +6 -0
- package/dist/boot/parked-revive-gate.d.ts +7 -2
- package/dist/boot/parked-revive-gate.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +3 -1
- package/dist/boot/runner-deps.d.ts +12 -0
- package/dist/boot/runner-deps.js +28 -3
- package/dist/boot/runtime-caps.d.ts +19 -8
- package/dist/boot/runtime-caps.js +50 -20
- package/dist/boot/session-shell-gate-registry.d.ts +39 -0
- package/dist/boot/session-shell-gate-registry.js +21 -0
- package/dist/config-catalog.js +14 -5
- package/dist/config-center/types.d.ts +2 -1
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +41 -10
- package/dist/config.d.ts +21 -0
- package/dist/config.js +40 -5
- package/dist/http/route-ctx.d.ts +90 -3
- package/dist/http/routes/a2a-serve.js +5 -3
- package/dist/http/routes/approvals-assistant.js +21 -8
- package/dist/http/routes/capabilities.js +21 -4
- package/dist/http/routes/leader.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +2 -2
- package/dist/http/routes/rules.js +3 -2
- package/dist/http/routes/runs.d.ts +0 -14
- package/dist/http/routes/runs.js +20 -8
- package/dist/http/routes/tasks.js +34 -8
- package/dist/http/routes/workflows.js +20 -5
- package/dist/http/server.d.ts +11 -1
- package/dist/http/server.js +220 -34
- package/dist/http/wire-types.d.ts +9 -4
- package/dist/leader/diffout.js +2 -1
- package/dist/leader/endpoint.js +49 -15
- package/dist/leader/fanout.js +6 -5
- package/dist/leader/leader.js +17 -14
- package/dist/leader/merge.js +17 -12
- package/dist/leader/planner.js +3 -2
- package/dist/leader/repair-oracle.js +6 -4
- package/dist/leader/repair-wire.js +5 -3
- package/dist/leader/wire.d.ts +80 -1
- package/dist/leader/wire.js +55 -26
- package/dist/main.js +44 -8
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +10 -0
- package/dist/observability/fail-open.d.ts +40 -0
- package/dist/observability/fail-open.js +19 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.d.ts +120 -0
- package/dist/observability/run-terminal-log.js +360 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
- package/dist/orchestration/workflow-completion-inbox.js +8 -0
- package/dist/permission-rule-vocab.d.ts +40 -0
- package/dist/permission-rule-vocab.js +17 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +26 -0
- package/dist/plugins/checkpoint-store-sql.js +23 -4
- package/dist/plugins/file-run-store.d.ts +3 -34
- package/dist/plugins/file-run-store.js +21 -0
- package/dist/plugins/local-checkpoint-store.d.ts +10 -0
- package/dist/plugins/local-checkpoint-store.js +5 -0
- package/dist/plugins/local-session-store.d.ts +9 -9
- package/dist/plugins/local-session-store.js +5 -3
- package/dist/plugins/memory-run-store.d.ts +3 -15
- package/dist/plugins/memory-run-store.js +21 -0
- package/dist/plugins/permission-rule-store-file.d.ts +22 -4
- package/dist/plugins/permission-rule-store-file.js +25 -11
- package/dist/plugins/permission-rule-store-sql.d.ts +60 -30
- package/dist/plugins/permission-rule-store-sql.js +39 -23
- package/dist/plugins/pg-session-storage.js +17 -13
- package/dist/plugins/remote-scratchpad.js +3 -2
- package/dist/plugins/run-store-sql.d.ts +3 -42
- package/dist/plugins/run-store-sql.js +36 -5
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +70 -1
- package/dist/plugins/tidb-session-store.js +18 -14
- package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
- package/dist/plugins/workflow-run-store-sql.js +10 -2
- package/dist/rules-consent.d.ts +5 -4
- package/dist/rules-consent.js +25 -46
- package/dist/run-cancel-context.d.ts +13 -0
- package/dist/run-cancel-context.js +15 -0
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +4 -1
- package/dist/runs.js +41 -10
- package/dist/runtime-caps-resolver.d.ts +133 -17
- package/dist/runtime-caps-resolver.js +44 -4
- package/dist/security.d.ts +7 -0
- package/dist/task-settings.d.ts +57 -3
- package/dist/task-settings.js +78 -5
- package/dist/task-workflow.d.ts +32 -2
- package/dist/task-workflow.js +8 -3
- package/dist/tool-approval.d.ts +38 -63
- package/dist/tool-approval.js +102 -74
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +13 -4
- package/dist/trace/ledger-sink.js +14 -6
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +10 -1
- package/dist/trace/redact.d.ts +22 -11
- package/dist/trace/redact.js +655 -20
- package/dist/trace/sema-provenance.d.ts +26 -0
- package/dist/trace/sema-provenance.js +11 -0
- package/dist/turn-activity.d.ts +32 -2
- package/dist/turn-activity.js +29 -4
- package/package.json +3 -3
- package/skills/find-skills.md +1 -1
- package/skills/loop.md +1 -1
package/dist/http/routes/runs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { memoRedactedRead, memoRedactTerminalResult, redactErrorMessage, redactHead, redactLedgerEventData, redactTerminalResult } from "../../observability/run-terminal-log.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { uuidv7, mintCheckpointToken, CheckpointError, validatePendingSteer } from "@sema-agent/core";
|
|
3
4
|
import { isTerminalRunStatus, isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
@@ -22,10 +23,12 @@ import { normalizeRunEventType } from "../../trace/project.js";
|
|
|
22
23
|
import { sendJson, sendError, httpErrorCode, sseHeaders } from "../send.js";
|
|
23
24
|
import { buildActiveRunConflict } from "../active-run-conflict.js";
|
|
24
25
|
import { clearTurnActivity, readTurnActivityMs } from "../../turn-activity.js";
|
|
26
|
+
import { buildCancelContext } from "../../run-cancel-context.js";
|
|
25
27
|
import { buildCrossSliceUsage, readResourceWindow, recordResourceWindow } from "../../resource-window.js";
|
|
26
28
|
import { headerStr, gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
27
29
|
import { ActorAssertionWire } from "@sema-agent/settings-schema";
|
|
28
30
|
import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage } from "../route-ctx.js";
|
|
31
|
+
import { withPrincipal } from "../../observability/principal-context.js";
|
|
29
32
|
export const RUN_ID_RE = /^\/v1\/runs\/([^/]+)(\/events)?$/;
|
|
30
33
|
export const RUN_CANCEL_RE = /^\/v1\/runs\/([^/]+)\/cancel$/;
|
|
31
34
|
export const RUN_STEER_RE = /^\/v1\/runs\/([^/]+)\/steer$/;
|
|
@@ -136,7 +139,10 @@ async function streamRunEvents(req, res, deps, runStore, taskId, staleMs) {
|
|
|
136
139
|
retainedFrom: (id) => runStore.retainedFrom(id),
|
|
137
140
|
formatEvent: (ev) => {
|
|
138
141
|
const type = normalizeRunEventType(ev.type);
|
|
139
|
-
|
|
142
|
+
const data = type === "done" && ev.data && typeof ev.data === "object" && ev.data.result && typeof ev.data.result === "object"
|
|
143
|
+
? { ...ev.data, result: memoRedactTerminalResult("ev.done.result", ev.data.result) }
|
|
144
|
+
: type === "failed" ? memoRedactedRead("ev.failed", ev.data, (d) => redactLedgerEventData(type, d)) : redactLedgerEventData(type, ev.data);
|
|
145
|
+
return { id: ev.seq, event: type, data: { type, ...(data ?? {}) } };
|
|
140
146
|
},
|
|
141
147
|
staleFrame: () => ({ event: "failed", data: { type: "failed", errorMessage: "run stalled (instance lost?)" } }),
|
|
142
148
|
}, taskId, staleMs);
|
|
@@ -251,11 +257,11 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
251
257
|
status: stale ? "failed" : run.status,
|
|
252
258
|
...(lastActivityAt !== undefined ? { msSinceLastActivity: Math.max(0, Date.now() - lastActivityAt) } : {}),
|
|
253
259
|
...(crossSliceUsage !== undefined ? { crossSliceUsage } : {}),
|
|
254
|
-
result: run.result ?? undefined,
|
|
260
|
+
result: isTerminalRunStatus(run.status) ? memoRedactTerminalResult("run.result", run.result ?? undefined) : redactTerminalResult(run.result ?? undefined),
|
|
255
261
|
supervisorCost,
|
|
256
262
|
suggestions,
|
|
257
263
|
errorCode: stale ? undefined : (run.result?.errorCode ?? undefined),
|
|
258
|
-
error: stale ? "run stalled (instance lost?)" : run.error ?? undefined,
|
|
264
|
+
error: stale ? "run stalled (instance lost?)" : isTerminalRunStatus(run.status) ? memoRedactedRead("run.error", run.error ?? undefined, redactErrorMessage) : redactErrorMessage(run.error ?? undefined),
|
|
259
265
|
jobId: run.jobId ?? undefined,
|
|
260
266
|
source: run.source ?? undefined,
|
|
261
267
|
});
|
|
@@ -324,7 +330,8 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
324
330
|
}
|
|
325
331
|
const err = note;
|
|
326
332
|
try {
|
|
327
|
-
const
|
|
333
|
+
const cancelContext = buildCancelContext(taskId, Date.parse(run.createdAt));
|
|
334
|
+
const result = { taskId, sessionId: run.sessionId, status: "failed", errorCode: "cancelled", errorMessage: err, stats: { turns: 0, tokens: 0 }, ...(cancelContext ? { cancelContext } : {}) };
|
|
328
335
|
let lastErr;
|
|
329
336
|
for (let attempt = 0;; attempt++) {
|
|
330
337
|
try {
|
|
@@ -870,7 +877,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
870
877
|
deps.logger?.[level]?.("manual_compact_outcome", { taskId, outcome, note: outcome === "compacted" ? "a compacted{trigger:'manual'} event rode the run stream" : `no event (see compaction.${outcome === "mooted" ? "mooted" : outcome} trace / task terminal events)` });
|
|
871
878
|
}, (e) => {
|
|
872
879
|
if (e.code !== "steering.not_running") {
|
|
873
|
-
deps.logger?.warn?.("manual_compact_failed", { taskId, err: e instanceof Error ? e.message : String(e) });
|
|
880
|
+
deps.logger?.warn?.("manual_compact_failed", { taskId, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
|
|
874
881
|
}
|
|
875
882
|
else {
|
|
876
883
|
deps.logger?.info?.("manual_compact_not_running", { taskId, note: "stream settled between lookup and compact() — benign unless systematic" });
|
|
@@ -934,7 +941,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
934
941
|
}
|
|
935
942
|
async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
936
943
|
const { deps } = ctx;
|
|
937
|
-
const { readJson, rateLimited, quotaExceeded, leaseDenied, safeDecode, runSessionAcceptOk } = ctx.helpers;
|
|
944
|
+
const { readJson, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, safeDecode, runSessionAcceptOk } = ctx.helpers;
|
|
938
945
|
const subOutputMatch = req.method === "GET" ? RUN_SUBAGENT_OUTPUT_RE.exec(url) : null;
|
|
939
946
|
if (subOutputMatch) {
|
|
940
947
|
if (!deps.runStore || !deps.subagentTaskOutput) {
|
|
@@ -1159,7 +1166,12 @@ async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
|
1159
1166
|
send409("resume.retain_off", "the parent run did not retain sub-agent sessions (set retainSubagentSessions on the run to enable revival)");
|
|
1160
1167
|
return;
|
|
1161
1168
|
}
|
|
1162
|
-
|
|
1169
|
+
if (ownerQuotaDenied(res, run.owner ?? undefined))
|
|
1170
|
+
return;
|
|
1171
|
+
if (await ownerLeaseDenied(res, run.owner ?? undefined))
|
|
1172
|
+
return;
|
|
1173
|
+
const resumeFn = handle.resume;
|
|
1174
|
+
const marker = await withPrincipal(run.owner ?? undefined, () => resumeFn(redacted));
|
|
1163
1175
|
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.` });
|
|
1164
1176
|
return;
|
|
1165
1177
|
}
|
|
@@ -1593,7 +1605,7 @@ export async function createDurableRun(req, res, ctx, prepared, idemKey) {
|
|
|
1593
1605
|
backend: deps.backend,
|
|
1594
1606
|
parkFacility: deps.checkpointStore !== undefined,
|
|
1595
1607
|
}).active,
|
|
1596
|
-
windowMs: deps.config.streamApproval?.windowMs
|
|
1608
|
+
windowMs: deps.config.streamApproval?.windowMs,
|
|
1597
1609
|
windowMarginMs: deps.config.streamAskWindowMarginMs,
|
|
1598
1610
|
}
|
|
1599
1611
|
: undefined, deps.checkpointStore);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal-log.js";
|
|
1
2
|
import { runWithVerification, runCascade, uuidv7 } from "@sema-agent/core";
|
|
2
3
|
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, resumeAtHttpStatus, stripCheckpointToken, HEARTBEAT_MS } from "../../runs.js";
|
|
3
4
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
@@ -7,6 +8,7 @@ import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotific
|
|
|
7
8
|
import { createLedgerSink } from "../../trace/ledger-sink.js";
|
|
8
9
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
9
10
|
import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
|
|
11
|
+
import { buildCancelContext } from "../../run-cancel-context.js";
|
|
10
12
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
11
13
|
import { redactSecrets } from "../../trace/redact.js";
|
|
12
14
|
import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage } from "../../trace/project.js";
|
|
@@ -47,6 +49,13 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
47
49
|
const { prepareSpec, finalizeTaskResult } = ctx.legs;
|
|
48
50
|
const reqState = ctx.req;
|
|
49
51
|
const source = ctx.req.source;
|
|
52
|
+
const noteRunOutcome = (r) => {
|
|
53
|
+
if (!r?.status)
|
|
54
|
+
return;
|
|
55
|
+
reqState.runStatus = r.status;
|
|
56
|
+
if (r.errorCode)
|
|
57
|
+
reqState.runErrorCode = r.errorCode;
|
|
58
|
+
};
|
|
50
59
|
if (req.method === "POST" && (url === "/v1/tasks" || url === "/v1/tasks/stream")) {
|
|
51
60
|
const rawIdem = headerStr(req.headers["idempotency-key"]);
|
|
52
61
|
const idemKey = rawIdem ? scopedIdempotencyKey(rawIdem, source, gatedPrincipal(req, deps.config)) : undefined;
|
|
@@ -88,6 +97,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
88
97
|
return;
|
|
89
98
|
}
|
|
90
99
|
const earlyDurableTid = deps.runStore && prepared.spec.sessionId ? uuidv7() : undefined;
|
|
100
|
+
const streamLegStartedAt = Date.now();
|
|
91
101
|
const detachOnDisconnect = headerStr(req.headers["x-detach-on-disconnect"]) === "true";
|
|
92
102
|
if (detachOnDisconnect && earlyDurableTid === undefined) {
|
|
93
103
|
sendError(res, 400, "request.precondition_unmet", "x-detach-on-disconnect requires a durable run (a run store + sessionId) — without one the detached result would be unqueryable");
|
|
@@ -178,6 +188,19 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
178
188
|
let runRowSettled = false;
|
|
179
189
|
let durableTaskId;
|
|
180
190
|
let durableHeartbeat;
|
|
191
|
+
const mintCancelledResult = (errorMessage) => {
|
|
192
|
+
const ctx = durableTaskId === undefined ? undefined : buildCancelContext(durableTaskId, streamLegStartedAt);
|
|
193
|
+
return {
|
|
194
|
+
taskId: durableTaskId ?? "",
|
|
195
|
+
sessionId: prepared.spec.sessionId ?? "",
|
|
196
|
+
status: "failed",
|
|
197
|
+
result: "",
|
|
198
|
+
errorCode: "cancelled",
|
|
199
|
+
errorMessage,
|
|
200
|
+
stats: { turns: 0, tokens: 0 },
|
|
201
|
+
...(ctx ? { cancelContext: ctx } : {}),
|
|
202
|
+
};
|
|
203
|
+
};
|
|
181
204
|
let userMsgEntryId;
|
|
182
205
|
let anchorPut = false;
|
|
183
206
|
const putRewindAnchor = () => {
|
|
@@ -211,7 +234,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
211
234
|
if (detachOnDisconnect && durableTaskId && deps.runStore) {
|
|
212
235
|
const rs = deps.runStore;
|
|
213
236
|
const tid = durableTaskId;
|
|
214
|
-
ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking, onActivity: () => recordTurnActivity(tid) });
|
|
237
|
+
ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking, onActivity: (kind, statusData) => recordTurnActivity(tid, kind, statusData) });
|
|
215
238
|
usageKey = durableTaskId;
|
|
216
239
|
if (usageKey)
|
|
217
240
|
deps.modelUsage?.register(usageKey);
|
|
@@ -380,13 +403,14 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
380
403
|
if (ev.type === "message_committed" && ev.role === "user")
|
|
381
404
|
userMsgEntryId = ev.entryId;
|
|
382
405
|
if (ev.type === "done") {
|
|
383
|
-
finalResult = stripCheckpointToken(await graftParkToolCallId(ev.result, deps.checkpointStore));
|
|
406
|
+
finalResult = redactTerminalResult(stripCheckpointToken(await graftParkToolCallId(ev.result, deps.checkpointStore)));
|
|
384
407
|
if (durableTaskId && finalResult)
|
|
385
408
|
finalResult = { ...finalResult, taskId: durableTaskId };
|
|
386
409
|
if (durableTaskId && cancelledViaVerb.has(durableTaskId) && finalResult?.status === "failed" && !finalResult.errorCode) {
|
|
387
410
|
finalResult = { ...finalResult, errorCode: "cancelled" };
|
|
388
411
|
}
|
|
389
412
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
413
|
+
noteRunOutcome(finalResult);
|
|
390
414
|
putRewindAnchor();
|
|
391
415
|
settleFleet(finalResult?.status ?? "completed", fleetRunResiduals(finalResult));
|
|
392
416
|
if (ledgerSink && finalResult) {
|
|
@@ -530,7 +554,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
530
554
|
const legWalltimeMs = typeof prepared.spec.limits?.maxWalltimeMs === "number" ? prepared.spec.limits.maxWalltimeMs : undefined;
|
|
531
555
|
const approvalLeg = resolveApprovalLeg({
|
|
532
556
|
streamApprovalOn,
|
|
533
|
-
windowMs: deps.config.streamApproval?.windowMs
|
|
557
|
+
windowMs: deps.config.streamApproval?.windowMs,
|
|
534
558
|
windowMarginMs: deps.config.streamAskWindowMarginMs,
|
|
535
559
|
...(legWalltimeMs !== undefined ? { legWalltimeMs } : {}),
|
|
536
560
|
nowMonotonicMs: performance.now(),
|
|
@@ -618,13 +642,14 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
618
642
|
}
|
|
619
643
|
else {
|
|
620
644
|
const dcErr = "stream client disconnected before completion (run aborted)";
|
|
621
|
-
const dc =
|
|
645
|
+
const dc = mintCancelledResult(dcErr);
|
|
622
646
|
if (ledgerSink && !ledgerTerminal) {
|
|
623
647
|
ledgerTerminal = true;
|
|
624
648
|
await ledgerSink.flush().catch(() => undefined);
|
|
625
649
|
await ledgerSink.append("failed", { errorMessage: dcErr, errorCode: "cancelled" }).catch(() => undefined);
|
|
626
650
|
}
|
|
627
651
|
await deps.runStore.setTerminal(durableTaskId, "failed", dc, dcErr);
|
|
652
|
+
noteRunOutcome(dc);
|
|
628
653
|
settleFleet("failed");
|
|
629
654
|
}
|
|
630
655
|
}
|
|
@@ -668,7 +693,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
668
693
|
}
|
|
669
694
|
else if (ac.signal.aborted) {
|
|
670
695
|
const err = "stream aborted before completion (run cancelled)";
|
|
671
|
-
const c =
|
|
696
|
+
const c = mintCancelledResult(err);
|
|
672
697
|
if (ledgerSink && !ledgerTerminal) {
|
|
673
698
|
ledgerTerminal = true;
|
|
674
699
|
await ledgerSink.flush().catch(() => undefined);
|
|
@@ -699,7 +724,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
699
724
|
resp = await idemCache.run(idemKey, runStreamSubmitLeg, (r) => r.body?.status === "completed");
|
|
700
725
|
}
|
|
701
726
|
catch (e) {
|
|
702
|
-
deps.logger?.error("stream_error", { closed, err: e instanceof Error ? e.message : String(e) });
|
|
727
|
+
deps.logger?.error("stream_error", { closed, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
|
|
703
728
|
if (!closed)
|
|
704
729
|
throw e;
|
|
705
730
|
}
|
|
@@ -790,7 +815,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
790
815
|
const retryAfterSec = result.errorCode === "memory.admission_required" && result.retryAfterMs !== undefined
|
|
791
816
|
? Math.max(1, Math.ceil(result.retryAfterMs / 1000))
|
|
792
817
|
: undefined;
|
|
793
|
-
return { status: resumeAtStatus, body: { errorCode: result.errorCode, error: result.errorMessage ?? "resume-at failed", ...(retryAfterSec !== undefined ? { retryAfterSec } : {}) } };
|
|
818
|
+
return { status: resumeAtStatus, body: { errorCode: result.errorCode, error: redactErrorMessage(result.errorMessage) ?? "resume-at failed", ...(retryAfterSec !== undefined ? { retryAfterSec } : {}) } };
|
|
794
819
|
}
|
|
795
820
|
if (result.status === "suspended" && deps.checkpointStore && deps.runStore && prepared.spec.sessionId) {
|
|
796
821
|
if (durableTaskId)
|
|
@@ -805,7 +830,8 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
805
830
|
if (durableTaskId && deps.runStore)
|
|
806
831
|
await deps.runStore.setTerminal(durableTaskId, result.status, stripCheckpointToken(result), result.errorMessage ?? null);
|
|
807
832
|
finalizeTaskResult(result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
808
|
-
|
|
833
|
+
noteRunOutcome(result);
|
|
834
|
+
return { status: 200, body: withDiscoveryNotice(redactTerminalResult(stripCheckpointToken(result))) };
|
|
809
835
|
};
|
|
810
836
|
const resp = await idemCache.run(idemKey, runSyncSubmitLeg, (r) => r.status < 400);
|
|
811
837
|
sendSubmitResult(res, resp);
|
|
@@ -2,6 +2,10 @@ import { z } from "zod";
|
|
|
2
2
|
import { getWorkflowRun, subscribeWorkflow, deriveAgentDisplayStatus, callKeyOrdinal } from "@sema-agent/core";
|
|
3
3
|
import { redactSteerIn, STEER_IN_MAX_CHARS, STEER_IN_MAX_REQUEST_CHARS } from "../../orchestration/workflow-agent-steer.js";
|
|
4
4
|
import { redactSecrets } from "../../trace/redact.js";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { redactErrorMessage, redactHead } from "../../observability/run-terminal-log.js";
|
|
7
|
+
const JOURNAL_PROJECTION_MEMO_CAP = 1024;
|
|
8
|
+
const journalProjectionMemo = new Map();
|
|
5
9
|
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
6
10
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
7
11
|
const WORKFLOWS_RE = /^\/v1\/workflows(?:\/([^/]+)(\/stream|\/journal)?)?$/;
|
|
@@ -68,16 +72,27 @@ async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
|
|
|
68
72
|
const jLimit = Math.min(50, Math.max(1, Number(jq.get("limit") ?? 20) || 20));
|
|
69
73
|
const jOffset = Math.max(0, Number(jq.get("offset") ?? 0) || 0);
|
|
70
74
|
const MAX_ROW_BYTES = 64 * 1024;
|
|
71
|
-
const firstLine = (t) => (t ?
|
|
75
|
+
const firstLine = (t) => (t ? redactHead(t.split("\n")[0], 300) : undefined);
|
|
72
76
|
const MAX_RESULT_CHARS = 2000;
|
|
73
|
-
const projectResult = (callKey, r) =>
|
|
77
|
+
const projectResult = (callKey, r) => {
|
|
78
|
+
const memoKey = `${wfId}\u0000${callKey}\u0000${createHash("sha256").update(JSON.stringify(r)).digest("hex")}`;
|
|
79
|
+
const hit = journalProjectionMemo.get(memoKey);
|
|
80
|
+
if (hit)
|
|
81
|
+
return hit;
|
|
82
|
+
const out = projectResultUncached(callKey, r);
|
|
83
|
+
if (journalProjectionMemo.size >= JOURNAL_PROJECTION_MEMO_CAP)
|
|
84
|
+
journalProjectionMemo.delete(journalProjectionMemo.keys().next().value);
|
|
85
|
+
journalProjectionMemo.set(memoKey, out);
|
|
86
|
+
return out;
|
|
87
|
+
};
|
|
88
|
+
const projectResultUncached = (callKey, r) => ({
|
|
74
89
|
callKey,
|
|
75
90
|
ordinal: callKeyOrdinal(callKey),
|
|
76
91
|
status: r.status,
|
|
77
92
|
...(r.errorMessage ? { error: firstLine(r.errorMessage) } : {}),
|
|
78
93
|
...(r.result
|
|
79
94
|
? {
|
|
80
|
-
result:
|
|
95
|
+
result: redactHead(r.result, MAX_RESULT_CHARS),
|
|
81
96
|
...(r.result.length > MAX_RESULT_CHARS ? { resultTruncated: true, resultChars: r.result.length } : {}),
|
|
82
97
|
}
|
|
83
98
|
: {}),
|
|
@@ -196,7 +211,7 @@ async function handleWorkflowAgentSteerBody(req, res, url, ctx, miss) {
|
|
|
196
211
|
sendNotRunningWf(!wfRun
|
|
197
212
|
? "workflow agent is not running on this replica (no live handle)"
|
|
198
213
|
: wfRun.status === "running"
|
|
199
|
-
? "workflow agent
|
|
214
|
+
? "no live handle for this workflow agent on this replica — the run may be live on another replica (cross-replica live-steer is not yet supported), or this replica holds no steerable stream for it; read the workflow status faces for progress"
|
|
200
215
|
: `workflow is ${wfRun.status} — agent is not running`);
|
|
201
216
|
return;
|
|
202
217
|
}
|
|
@@ -310,7 +325,7 @@ function summarizeWorkflowDetail(run) {
|
|
|
310
325
|
createdAt: run.createdAt,
|
|
311
326
|
durationMs: durationOf(run),
|
|
312
327
|
rev: run.rev,
|
|
313
|
-
error: run.error,
|
|
328
|
+
...(run.error !== undefined ? { error: redactErrorMessage(run.error) ?? "" } : {}),
|
|
314
329
|
...(run.result !== undefined ? { result: redactSecrets(run.result) } : {}),
|
|
315
330
|
phases,
|
|
316
331
|
...(unphasedView ? { unphased: unphasedView } : {}),
|
package/dist/http/server.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { type FleetEventBus } from "../fleet/fleet-bus.js";
|
|
|
18
18
|
import type { ElicitationCoordinator } from "../elicitation.js";
|
|
19
19
|
import type { QuestionCoordinator } from "../question.js";
|
|
20
20
|
import { type ParkedAskRedeem, type ToolApprovalCoordinator } from "../tool-approval.js";
|
|
21
|
-
import type { ApprovalAskAuditReadFace } from "../approval-ask-audit-store.js";
|
|
21
|
+
import type { ApprovalAskAuditReadFace, ApprovalLateSettleSink } from "../approval-ask-audit-store.js";
|
|
22
22
|
import type { PlanCacheProbe } from "../plan-cache-probe.js";
|
|
23
23
|
import type { Logger } from "../observability/logger.js";
|
|
24
24
|
import type { Metrics } from "../observability/metrics.js";
|
|
@@ -210,6 +210,11 @@ export interface ServiceCoordinatorDeps {
|
|
|
210
210
|
* SQL 车道恒缺席(那里有真 ask 店,[ref])—— 键缺席 = 本部署无此面,`[]` = 有面且无崩溃残账,
|
|
211
211
|
* 两义与 `livePending` 同律可判别。 */
|
|
212
212
|
approvalAskAudit?: ApprovalAskAuditReadFace;
|
|
213
|
+
/** [ref] 迟到人工结算的**写面**(与 {@link approvalAskAudit} 同一个店实例,两把窄口:读面喂
|
|
214
|
+
* `GET /v1/approvals` 的 `crashConverged`,写面只有 `recordLateSettle` 一个动词 —— 铸造/决议/腿闭
|
|
215
|
+
* 三点的属主是协调器,路由层拿到它们只会长出第二个写者)。在场判据与读面逐字同源(local 车道 ∧
|
|
216
|
+
* 审批面开着);缺席 ⇒ 两条 decide 腿**不写、不抛**,行为与修前逐字相同(SQL 车道恒缺席)。 */
|
|
217
|
+
approvalLateSettle?: ApprovalLateSettleSink;
|
|
213
218
|
/** SVC-5 ([ref] CORE-5 #6): the process-local registry of STEERABLE workflow-agent handles
|
|
214
219
|
* (`ctx.agentStream`) live on THIS replica. Enables POST /v1/workflows/:id/agents/:label/steer. See
|
|
215
220
|
* workflow-agent-steer.ts for the handle-visibility seam status (the registry is real + ready; core does not
|
|
@@ -539,6 +544,11 @@ export interface ServiceGovernanceDeps {
|
|
|
539
544
|
* pre-admission 读窗用(主力 /v1/runs 是 202 异步,core 进场拒发生在 202 之后永远变不成 429;
|
|
540
545
|
* 这里是异步车道唯一的 429 出路)。enforcement 真源在 core(RunnerDeps 同店),此门是快速座。 */
|
|
541
546
|
usageWindowStore?: import("@sema-agent/core").UsageWindowStore;
|
|
547
|
+
/** [ref] ④([ref]/[ref],core [ref] 合修):auto 模式自查读面的三件装配事实(席位在不在 / 合成后的
|
|
548
|
+
* per-principal caps resolver / 分类器配置路由)。`/v1/capabilities.permissionModeAuto.{armed,reason,model}`
|
|
549
|
+
* 只消费它,不自算 —— 席位与 resolver 都必须是递给 `RunnerDeps` 的**同一只**(main.ts 装配),否则又是
|
|
550
|
+
* 「诊断说亮、位说暗」。缺席(桩/一次性 CLI)⇒ 读面如实答 `deployment_incapable`。 */
|
|
551
|
+
autoModeFace?: import("../auto-mode-face.js").AutoModeFaceDeps;
|
|
542
552
|
}
|
|
543
553
|
/** **部署自述面**:只被 /health、/v1/capabilities 与提交前置门读的部署事实(多为 live getter,
|
|
544
554
|
* 不是快照——/health 必须回答「此刻」)。 */
|