@sema-agent/server 7.92.1 → 7.92.2
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/dist/approval-ask-machine.d.ts +1 -1
- package/dist/bench/s1/arms.js +5 -6
- package/dist/bench/s1/live-deps.js +2 -2
- package/dist/boot/stage-06-runners.js +3 -2
- package/dist/boot/stores.js +9 -0
- package/dist/http/resume-legs.js +10 -9
- package/dist/http/routes/rules.js +7 -1
- package/dist/http/routes/session-sync.js +24 -12
- package/dist/http/routes/sessions.js +12 -8
- package/dist/http/routes/tasks.js +12 -12
- package/dist/http/send.d.ts +11 -0
- package/dist/http/send.js +9 -0
- package/dist/http/server.js +3 -2
- package/dist/leader/fanout.js +3 -3
- package/dist/leader/leader.js +4 -4
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/wire.js +2 -2
- package/dist/plugins/file-run-store.js +3 -3
- package/dist/plugins/memory-run-store.js +3 -3
- package/dist/plugins/run-store-sql.js +3 -3
- package/dist/plugins/schema-consistency.d.ts +88 -0
- package/dist/plugins/schema-consistency.js +202 -0
- package/dist/plugins/schema-ensure-legs.d.ts +55 -0
- package/dist/plugins/schema-ensure-legs.js +87 -0
- package/dist/plugins/session-policy-store-sql.js +23 -5
- package/dist/rules-consent.d.ts +23 -2
- package/dist/rules-consent.js +6 -4
- package/dist/run-local.js +2 -2
- package/dist/runs.js +14 -14
- package/dist/terminal.d.ts +58 -4
- package/dist/terminal.js +28 -6
- package/dist/trace/ledger-sink.js +3 -2
- package/package.json +3 -3
|
@@ -34,7 +34,7 @@ export declare function canBatchTransition(from: BatchState, to: BatchState): bo
|
|
|
34
34
|
* ── `legKey`(取代数值 `leg`,一腿一凭据)─────────────────────────────────────────────────────────────
|
|
35
35
|
* = `sha256(resume checkpoint token)` 的 hex,**首腿 = 空串**。一次 park→resume 的 token 就是这条腿的
|
|
36
36
|
* 天然身份:同 token 重投 = 同一腿(幂等,正确);新 park ⇒ 新 token ⇒ 新腿 ⇒ 新 askId。
|
|
37
|
-
* 取摘要而非原始 token 是因为 token 是能力凭据(本仓有 `
|
|
37
|
+
* 取摘要而非原始 token 是因为 token 是能力凭据(本仓有 `sealResultForExit` 专门把它从可重放账本里
|
|
38
38
|
* 剥掉),而 `askId` 是 wire 可见值 ⇒ **只存/只喂摘要**。用它当轴顺带消掉了原裁需要的 `task_run` 加列
|
|
39
39
|
* 与 `markResuming` 返回加宽(后者有第二个调用点 `http/routes/runs.ts:463`,加宽会误杀已成功 resume 的
|
|
40
40
|
* running run)。残留边界:同一 run 内**不经 checkpoint token** 的重入形若未来出现会得到同一 legKey ——
|
package/dist/bench/s1/arms.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { pausedCauseOf } from "../../terminal.js";
|
|
1
|
+
import { pausedCauseOf, outwardPlane } from "../../terminal.js";
|
|
3
2
|
import { leafBudgetFields, s1DebugGit } from "./runner-ctx.js";
|
|
4
3
|
import {} from "./tasks.js";
|
|
5
4
|
import { decideApproval, decidePlan } from "./reviewer.js";
|
|
@@ -78,12 +77,12 @@ export async function driveSupSuspendResume(initial, deps, implSpec, trap, advan
|
|
|
78
77
|
let exitedViaBreak = false;
|
|
79
78
|
for (let leg = 0; leg < MAX_LEGS; leg++) {
|
|
80
79
|
foldCost(vr.stats);
|
|
81
|
-
const legPlane =
|
|
80
|
+
const legPlane = outwardPlane(vr.terminal);
|
|
82
81
|
if (process.env.S1_DEBUG_SUPCOST)
|
|
83
82
|
console.error(`[SUPCOST leg=${leg}] status=${legPlane.status} reason=${vr.verification?.unverifiedReason} c1µ=${vr.stats?.costBreakdown?.llmRootMicroUsd} accC1µ=${c1Acc.llmRootMicroUsd} hrGates=${vr.stats?.humanReview?.gates?.length}`);
|
|
84
83
|
const paused = pausedCauseOf(vr.terminal);
|
|
85
84
|
const gateKind = paused?.gate.kind;
|
|
86
|
-
const parkWord = paused === undefined ? undefined :
|
|
85
|
+
const parkWord = paused === undefined ? undefined : outwardPlane(paused).status;
|
|
87
86
|
if (parkWord === "suspended") {
|
|
88
87
|
const cp = await deps.getCheckpoint(implSpec.checkpointStore, paused.token, implSpec.durableApprovalScope);
|
|
89
88
|
const binding = bindingFromCheckpoint(cp);
|
|
@@ -178,7 +177,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
178
177
|
finishedAt: ctx.clock.now(),
|
|
179
178
|
});
|
|
180
179
|
}
|
|
181
|
-
const soloPlane =
|
|
180
|
+
const soloPlane = outwardPlane(vr.terminal);
|
|
182
181
|
if (s1DebugGit())
|
|
183
182
|
console.error("[S1 SOLO vr.status]", soloPlane.status, "| stats:", JSON.stringify(vr.stats)?.slice(0, 200));
|
|
184
183
|
const oracle = pickOracleVerdicts(await deps.runOracle());
|
|
@@ -225,7 +224,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
225
224
|
finishedAt: ctx.clock.now(),
|
|
226
225
|
});
|
|
227
226
|
}
|
|
228
|
-
const supPlane =
|
|
227
|
+
const supPlane = outwardPlane(vr.terminal);
|
|
229
228
|
if (s1DebugGit())
|
|
230
229
|
console.error(`[S1 SUP vr.status driver=${trap.supDriver}]`, supPlane.status, "| repairTerminal:", repairTerminal, "| verdict:", vr.verification?.verdict, "| unverifiedReason:", vr.verification?.unverifiedReason, "| checkpointGate:", JSON.stringify(pausedCauseOf(vr.terminal)?.gate), "| error:", vr.error, "| stats:", JSON.stringify(vr.stats)?.slice(0, 180));
|
|
231
230
|
const oracle = pickOracleVerdicts(await deps.runOracle());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { outwardPlane } from "../../terminal.js";
|
|
2
2
|
import { Runner, runWithVerification, resumeWithVerification, runRepairLoop, combinePolicies, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, } from "@sema-agent/core";
|
|
3
3
|
import { loadConfig } from "../../config.js";
|
|
4
4
|
import { createBrain } from "../../brain.js";
|
|
@@ -211,7 +211,7 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
|
|
|
211
211
|
runWithVerificationSup: async (implSpec) => {
|
|
212
212
|
const { runner } = await ensureWorker();
|
|
213
213
|
const result = await runner.runTask(toSupTaskSpec(baseSpec, implSpec, trap));
|
|
214
|
-
const pausePlane = result.terminal.kind === "paused" ?
|
|
214
|
+
const pausePlane = result.terminal.kind === "paused" ? outwardPlane(result.terminal).status : undefined;
|
|
215
215
|
const unverifiedReason = pausePlane === "needs_review" ? "needs_review" : pausePlane === "suspended" ? "suspended" : "no_verdict";
|
|
216
216
|
return { ...result, verification: { verdict: "unverified", unverifiedReason, rounds: 0, findings: [] } };
|
|
217
217
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Runner,
|
|
1
|
+
import { Runner, InMemoryToolResultStore, TtlSessionStore, uuidv7, createAllowDenyPolicy, workflowsCapability } from "@sema-agent/core";
|
|
2
|
+
import { outwardPlane } from "../terminal.js";
|
|
2
3
|
import { createSessionTitler } from "../session-titler.js";
|
|
3
4
|
import { cardRuleScopeRoot } from "../task-cwd.js";
|
|
4
5
|
import { ForkRoutingSessionStore } from "../plugins/fork-routing-session-store.js";
|
|
@@ -149,7 +150,7 @@ export function runStage06(ctx) {
|
|
|
149
150
|
});
|
|
150
151
|
metrics.inc("hook_llm_calls_total", { type: "agent" });
|
|
151
152
|
if (result.terminal.kind !== "completed") {
|
|
152
|
-
const plane =
|
|
153
|
+
const plane = outwardPlane(result.terminal);
|
|
153
154
|
return { ok: false, error: `hook agent ${plane.status}: ${plane.errorMessage ?? ""}` };
|
|
154
155
|
}
|
|
155
156
|
return { ok: true, text: result.result ?? "" };
|
package/dist/boot/stores.js
CHANGED
|
@@ -27,6 +27,7 @@ import { buildMemoryRemoteLaneWarn, memoryEngineBackendFor, memoryEngineRemoteLa
|
|
|
27
27
|
import { assertToolResultProvenanceSchema } from "../plugins/tool-result-store-sql.js";
|
|
28
28
|
import { assertPermissionRuleApprovalSchema, assertPermissionRuleTicketSchema } from "../plugins/permission-rule-store-sql.js";
|
|
29
29
|
import { assertApprovalAskRuleMaterialSchema } from "../plugins/approval-ask-store-sql.js";
|
|
30
|
+
import { assertSchemaMatchesCode } from "../plugins/schema-consistency.js";
|
|
30
31
|
import { assertCheckpointColumnSchema } from "../plugins/checkpoint-store-sql.js";
|
|
31
32
|
import { assertFileHistorySchema } from "../plugins/file-history-store-sql.js";
|
|
32
33
|
import { resolveStreamApprovalGate } from "../tool-approval.js";
|
|
@@ -402,6 +403,14 @@ export async function openStores(ctx) {
|
|
|
402
403
|
await assertDeviceAuditRebindEventSchema(mysqlDriver(mysqlPool));
|
|
403
404
|
}
|
|
404
405
|
}
|
|
406
|
+
{
|
|
407
|
+
const mysqlPool = backend?.mysqlPool?.();
|
|
408
|
+
const pgPool = backend?.pgPool?.();
|
|
409
|
+
if (pgPool)
|
|
410
|
+
await assertSchemaMatchesCode(async (sql) => (await pgPool.query(sql)).rows, "pg");
|
|
411
|
+
else if (mysqlPool)
|
|
412
|
+
await assertSchemaMatchesCode(async (sql) => (await mysqlPool.query(sql))[0], "mysql");
|
|
413
|
+
}
|
|
405
414
|
const sessionStore = createSessionStore(config, backend, metrics);
|
|
406
415
|
if (config.requirePrincipal && backend?.kind === "local") {
|
|
407
416
|
throw new Error("REQUIRE_PRINCIPAL=true is not supported on the local file backend (DB_BACKEND=local): session/run CONTENT is " +
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, validatePendingSteer, GLOBAL_USAGE_KEY,
|
|
3
|
+
import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
|
|
4
4
|
import { decideParkedAgent, denyWorkflowParksUnreadable, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
5
5
|
import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
6
6
|
import { evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
7
|
-
import { failureCodeOf, nestedPauseOf,
|
|
7
|
+
import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
|
|
8
8
|
import { mintCancelledResult } from "../run-cancel-context.js";
|
|
9
9
|
import {} from "../host-decision.js";
|
|
10
10
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
@@ -550,7 +550,7 @@ export function createResumeLegs(ctx) {
|
|
|
550
550
|
resumeLegLive = true;
|
|
551
551
|
const vr = await resumeWithVerification(legRunner, token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, resumeObjective, verifyRounds, verifyInternals);
|
|
552
552
|
await commitResumeCtx();
|
|
553
|
-
let safe =
|
|
553
|
+
let safe = sealResultForExit(vr);
|
|
554
554
|
const vrNested = nestedPauseOf(vr.terminal);
|
|
555
555
|
const reParkStatus = nestedParkStatusOf(vr);
|
|
556
556
|
const reSuspended = vrNested !== undefined;
|
|
@@ -679,7 +679,7 @@ export function createResumeLegs(ctx) {
|
|
|
679
679
|
if (taskId)
|
|
680
680
|
steerableRuns.set(taskId, stream);
|
|
681
681
|
if (!taskId || !rs)
|
|
682
|
-
return await stream.result();
|
|
682
|
+
return sealResultForExit(await stream.result());
|
|
683
683
|
onAccepted?.(acceptedResumingReceipt(taskId, sessionId));
|
|
684
684
|
for await (const ev of stream) {
|
|
685
685
|
recordTurnActivity(taskId);
|
|
@@ -790,7 +790,7 @@ export function createResumeLegs(ctx) {
|
|
|
790
790
|
}
|
|
791
791
|
await flush();
|
|
792
792
|
const r = await stream.result();
|
|
793
|
-
const rPark = r.terminal.kind === "paused" ?
|
|
793
|
+
const rPark = r.terminal.kind === "paused" ? outwardPlane(r.terminal).status : undefined;
|
|
794
794
|
if (rPark === "suspended") {
|
|
795
795
|
const parkCallId = await parkToolCallId(r, deps.checkpointStore);
|
|
796
796
|
await append("suspended", { gate: r.terminal.kind === "paused" ? r.terminal.gate : null, ...(parkCallId !== undefined ? { toolCallId: parkCallId } : {}) });
|
|
@@ -812,7 +812,7 @@ export function createResumeLegs(ctx) {
|
|
|
812
812
|
}
|
|
813
813
|
return r;
|
|
814
814
|
}
|
|
815
|
-
const safe = await attachModelUsage(
|
|
815
|
+
const safe = await attachModelUsage(sealResultForExit(r), { append, getEvents: (id, a) => rs.getEvents(id, a), modelUsage: deps.modelUsage, taskId });
|
|
816
816
|
await append("done", { result: safe });
|
|
817
817
|
if (taskConfig.suggestNextPrompts && safe.terminal.kind === "completed") {
|
|
818
818
|
const suggestions = await stream.suggestions().catch(() => []);
|
|
@@ -951,16 +951,17 @@ export function createResumeLegs(ctx) {
|
|
|
951
951
|
}
|
|
952
952
|
evictIfConflict(deps.runner, sessionId, result);
|
|
953
953
|
const checkpointReopened = reopenConfirmedPark;
|
|
954
|
-
const
|
|
954
|
+
const sealed = sealResultForExit(result);
|
|
955
|
+
const resumePlane = outwardPlane(sealed.terminal);
|
|
955
956
|
if (taskId && deps.runStore) {
|
|
956
957
|
if (resumePlane.status === "suspended" || checkpointReopened)
|
|
957
958
|
await deps.runStore.setSuspended(taskId);
|
|
958
959
|
else if (resumePlane.status === "needs_review")
|
|
959
960
|
await deps.runStore.setNeedsReview(taskId);
|
|
960
961
|
else
|
|
961
|
-
await deps.runStore.setTerminal(taskId, resumePlane.status,
|
|
962
|
+
await deps.runStore.setTerminal(taskId, resumePlane.status, sealed, resumePlane.errorMessage ?? null);
|
|
962
963
|
}
|
|
963
|
-
settleFleet(checkpointReopened ? "suspended" : resumePlane.status, fleetRunResiduals(
|
|
964
|
+
settleFleet(checkpointReopened ? "suspended" : resumePlane.status, fleetRunResiduals(sealed));
|
|
964
965
|
return {
|
|
965
966
|
status: 200,
|
|
966
967
|
body: {
|
|
@@ -271,7 +271,9 @@ async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
|
|
|
271
271
|
switch (written.status) {
|
|
272
272
|
case "persisted":
|
|
273
273
|
case "no-op":
|
|
274
|
-
sendJson(res, 200,
|
|
274
|
+
sendJson(res, 200, written.row === undefined
|
|
275
|
+
? { status: written.status, rev: written.rev, stillLive: written.stillLive }
|
|
276
|
+
: { status: written.status, rev: written.rev, stillLive: written.stillLive, rule: written.row });
|
|
275
277
|
return;
|
|
276
278
|
case "rejected":
|
|
277
279
|
logger.info("rule write refused", { reason: written.reason, detail: written.detail });
|
|
@@ -322,6 +324,10 @@ async function handleRuleRevoke(res, rawBody, caller, lane, operatorPrincipals)
|
|
|
322
324
|
logger.info("rule revoke failed", { error: outcome.error });
|
|
323
325
|
sendError(res, 503, "state.rule_remove_failed", "the rule could not be revoked right now — retry, then reconcile with GET /v1/rules");
|
|
324
326
|
return;
|
|
327
|
+
default: {
|
|
328
|
+
const unknown = outcome;
|
|
329
|
+
throw new Error(`unknown rule revoke outcome: ${JSON.stringify(unknown)}`);
|
|
330
|
+
}
|
|
325
331
|
}
|
|
326
332
|
}
|
|
327
333
|
export const RULES_ROUTES = [
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { once } from "node:events";
|
|
2
2
|
import { randomBytes, createHash } from "node:crypto";
|
|
3
|
-
import { sessionLogDigest, sessionLogDigestsComparable, StreamingImportValidator, SessionError,
|
|
3
|
+
import { sessionLogDigest, sessionLogDigestsComparable, StreamingImportValidator, SessionError, validateFileHistoryExport } from "@sema-agent/core";
|
|
4
4
|
import { StringDecoder } from "node:string_decoder";
|
|
5
5
|
import { exportSessionManifest, fileHistoryImportFace, replayFileHistory, replayPolicyRecords, overwriteWipeIncapable, wipeSessionAttendants } from "../../session-sync.js";
|
|
6
6
|
import { classifySyncRelationshipByIds, SyncConflictError } from "../../session-sync-kernel.js";
|
|
7
7
|
import { isUuidShape } from "../../security.js";
|
|
8
|
-
import { sendJson, sendError, msg } from "../send.js";
|
|
8
|
+
import { sendJson, sendError, sendSessionPolicyError, msg } from "../send.js";
|
|
9
9
|
import { buildDisplaySafeUrl } from "../../trace/redact.js";
|
|
10
10
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
11
11
|
const SYNC_BLOB_MAX_BYTES = 64 * 1024 * 1024;
|
|
@@ -13,7 +13,7 @@ const SYNC_NDJSON_LINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
|
13
13
|
const SYNC_IMPORT_BATCH = 500;
|
|
14
14
|
import { SHA256_HEX_RE } from "../../digest-form.js";
|
|
15
15
|
import { assertNever } from "../route-ctx.js";
|
|
16
|
-
import { readSessionRulesRecords, describeSessionRulesRecordProblem
|
|
16
|
+
import { readSessionRulesRecords, describeSessionRulesRecordProblem } from "../../session-policy-wire.js";
|
|
17
17
|
export function createSessionSyncLocal() {
|
|
18
18
|
const pendingStagings = new Map();
|
|
19
19
|
const importLeases = new Map();
|
|
@@ -74,7 +74,15 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
74
74
|
if (match.decl.id === "session-sync-manifest") {
|
|
75
75
|
if (!pullGateOk())
|
|
76
76
|
return;
|
|
77
|
-
|
|
77
|
+
let out;
|
|
78
|
+
try {
|
|
79
|
+
out = await exportSessionManifest(sessionId, deps.backend);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
if (sendSessionPolicyError(res, e))
|
|
83
|
+
return;
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
78
86
|
if (out === null) {
|
|
79
87
|
sendError(res, 404, "not_found.session", "session not found");
|
|
80
88
|
return;
|
|
@@ -281,8 +289,16 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
281
289
|
}
|
|
282
290
|
}
|
|
283
291
|
const opOk = explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config.operatorPrincipals);
|
|
284
|
-
if (deps.sessionPolicyStore)
|
|
285
|
-
|
|
292
|
+
if (deps.sessionPolicyStore) {
|
|
293
|
+
try {
|
|
294
|
+
await replayPolicyRecords(deps.sessionPolicyStore, sessionId, policy, opOk);
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
if (sendSessionPolicyError(res, e))
|
|
298
|
+
return;
|
|
299
|
+
throw e;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
286
302
|
const importingP = scope.fleetWide ? (owner ?? null) : scope.gateOwner;
|
|
287
303
|
if (deps.resumeAnchorStore)
|
|
288
304
|
for (const a of anchors)
|
|
@@ -499,13 +515,9 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
499
515
|
sendError(res, 409, "conflict", "sync conflict", { relation: e.relation });
|
|
500
516
|
return;
|
|
501
517
|
}
|
|
502
|
-
if (e instanceof SessionPolicyError) {
|
|
503
|
-
cleanupStaging(stagingId);
|
|
504
|
-
const h = sessionPolicyErrorHttp(e.code);
|
|
505
|
-
sendError(res, h.status, h.errorCode, e.message);
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
518
|
cleanupStaging(stagingId);
|
|
519
|
+
if (sendSessionPolicyError(res, e))
|
|
520
|
+
return;
|
|
509
521
|
throw e;
|
|
510
522
|
}
|
|
511
523
|
return;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { materializeA2aTools, materializeMcpTools, sanitizePathComponent
|
|
1
|
+
import { materializeA2aTools, materializeMcpTools, sanitizePathComponent } from "@sema-agent/core";
|
|
2
2
|
import { a2aForScenario, mcpForScenario } from "../../config-center/facade.js";
|
|
3
3
|
import { isUuidV7, isUuidShape, legacyUnownedSessionOk } from "../../security.js";
|
|
4
4
|
import { windowMessages, truncateMessageBlobs } from "../../audit.js";
|
|
5
5
|
import { redactSecrets } from "../../trace/redact.js";
|
|
6
6
|
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
7
7
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
8
|
-
import { readSessionPermissionRules, describeSessionPermissionRulesProblem
|
|
9
|
-
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
8
|
+
import { readSessionPermissionRules, describeSessionPermissionRulesProblem } from "../../session-policy-wire.js";
|
|
9
|
+
import { sendJson, sendError, sendSessionPolicyError, sseHeaders } from "../send.js";
|
|
10
10
|
import { gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal } from "../principal-gate.js";
|
|
11
11
|
import { assertNever } from "../route-ctx.js";
|
|
12
12
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
@@ -731,7 +731,14 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
731
731
|
}
|
|
732
732
|
const target = owner;
|
|
733
733
|
if (match.decl.id === "session-policy-get") {
|
|
734
|
-
|
|
734
|
+
try {
|
|
735
|
+
sendJson(res, 200, { rules: (await store.getRules(sessionId, target)) ?? { rev: 0 } });
|
|
736
|
+
}
|
|
737
|
+
catch (e) {
|
|
738
|
+
if (sendSessionPolicyError(res, e))
|
|
739
|
+
return;
|
|
740
|
+
throw e;
|
|
741
|
+
}
|
|
735
742
|
return;
|
|
736
743
|
}
|
|
737
744
|
let body;
|
|
@@ -767,11 +774,8 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
767
774
|
sendJson(res, 200, { rules: stored });
|
|
768
775
|
}
|
|
769
776
|
catch (e) {
|
|
770
|
-
if (e
|
|
771
|
-
const h = sessionPolicyErrorHttp(e.code);
|
|
772
|
-
sendError(res, h.status, h.errorCode, e.message);
|
|
777
|
+
if (sendSessionPolicyError(res, e))
|
|
773
778
|
return;
|
|
774
|
-
}
|
|
775
779
|
throw e;
|
|
776
780
|
}
|
|
777
781
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal.js";
|
|
2
|
-
import { runWithVerification, runCascade, uuidv7
|
|
2
|
+
import { runWithVerification, runCascade, uuidv7 } from "@sema-agent/core";
|
|
3
3
|
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, resumeAtHttpStatus, HEARTBEAT_MS } from "../../runs.js";
|
|
4
|
-
import {
|
|
4
|
+
import { sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
5
5
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
6
6
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
|
|
7
7
|
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
@@ -58,7 +58,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
58
58
|
const noteRunOutcome = (r) => {
|
|
59
59
|
if (!r?.terminal)
|
|
60
60
|
return;
|
|
61
|
-
const plane =
|
|
61
|
+
const plane = outwardPlane(r.terminal);
|
|
62
62
|
reqState.runStatus = plane.status;
|
|
63
63
|
if (plane.errorCode)
|
|
64
64
|
reqState.runErrorCode = plane.errorCode;
|
|
@@ -421,7 +421,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
421
421
|
userMsgEntryId = ev.entryId;
|
|
422
422
|
const cancelVerbAtDone = ev.type === "done" && durableTaskId !== undefined && cancelledViaVerb.has(durableTaskId);
|
|
423
423
|
if (ev.type === "done") {
|
|
424
|
-
finalResult = redactTerminalResult(
|
|
424
|
+
finalResult = redactTerminalResult(sealResultForExit(await graftParkToolCallId(ev.result, deps.checkpointStore)));
|
|
425
425
|
if (durableTaskId && finalResult)
|
|
426
426
|
finalResult = { ...finalResult, taskId: durableTaskId };
|
|
427
427
|
if (cancelVerbAtDone && finalResult !== undefined) {
|
|
@@ -430,7 +430,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
430
430
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
431
431
|
noteRunOutcome(finalResult);
|
|
432
432
|
putRewindAnchor();
|
|
433
|
-
settleFleet(
|
|
433
|
+
settleFleet(outwardPlane(finalResult.terminal).status, fleetRunResiduals(finalResult));
|
|
434
434
|
if (ledgerSink && finalResult) {
|
|
435
435
|
if (usageKey && deps.modelUsage && deps.runStore && durableTaskId) {
|
|
436
436
|
const rs2 = deps.runStore;
|
|
@@ -650,7 +650,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
650
650
|
}, withApproval)
|
|
651
651
|
: withApproval());
|
|
652
652
|
if (durableTaskId && deps.runStore) {
|
|
653
|
-
const finalPlane = finalResult ?
|
|
653
|
+
const finalPlane = finalResult ? outwardPlane(finalResult.terminal) : undefined;
|
|
654
654
|
if (finalPlane?.status === "suspended" && deps.checkpointStore)
|
|
655
655
|
await deps.runStore.setSuspended(durableTaskId);
|
|
656
656
|
else if (finalPlane?.status === "needs_review" && deps.checkpointStore)
|
|
@@ -717,7 +717,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
717
717
|
ledgerTerminal = true;
|
|
718
718
|
await ledgerSink.onDone(finalResult).catch(() => undefined);
|
|
719
719
|
}
|
|
720
|
-
const retryPlane =
|
|
720
|
+
const retryPlane = outwardPlane(finalResult.terminal);
|
|
721
721
|
await deps.runStore.setTerminal(durableTaskId, retryPlane.status, finalResult, retryPlane.errorMessage ?? null);
|
|
722
722
|
settleFleet(retryPlane.status, fleetRunResiduals(finalResult));
|
|
723
723
|
}
|
|
@@ -860,15 +860,15 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
860
860
|
}
|
|
861
861
|
const resumeAtStatus = resumeAtHttpStatus(result);
|
|
862
862
|
if (resumeAtStatus) {
|
|
863
|
-
const resumeAtPlane =
|
|
863
|
+
const resumeAtPlane = outwardPlane(result.terminal);
|
|
864
864
|
if (durableTaskId && deps.runStore)
|
|
865
|
-
await deps.runStore.setTerminal(durableTaskId, "failed",
|
|
865
|
+
await deps.runStore.setTerminal(durableTaskId, "failed", sealResultForExit(result), resumeAtPlane.errorMessage ?? null);
|
|
866
866
|
const retryAfterSec = resumeAtPlane.errorCode === "memory.admission_required" && result.retryAfterMs !== undefined
|
|
867
867
|
? Math.max(1, Math.ceil(result.retryAfterMs / 1000))
|
|
868
868
|
: undefined;
|
|
869
869
|
return { status: resumeAtStatus, body: { errorCode: resumeAtPlane.errorCode, error: redactErrorMessage(resumeAtPlane.errorMessage) ?? "resume-at failed", ...(retryAfterSec !== undefined ? { retryAfterSec } : {}) } };
|
|
870
870
|
}
|
|
871
|
-
const syncPlane =
|
|
871
|
+
const syncPlane = outwardPlane(result.terminal);
|
|
872
872
|
if (syncPlane.status === "suspended" && deps.checkpointStore && deps.runStore && prepared.spec.sessionId) {
|
|
873
873
|
if (durableTaskId)
|
|
874
874
|
await deps.runStore.setSuspended(durableTaskId);
|
|
@@ -880,10 +880,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
880
880
|
return { status: 200, body: withSyncSubmitKeys({ taskId: durableTaskId, sessionId: prepared.spec.sessionId, status: "needs_review" }) };
|
|
881
881
|
}
|
|
882
882
|
if (durableTaskId && deps.runStore)
|
|
883
|
-
await deps.runStore.setTerminal(durableTaskId, syncPlane.status,
|
|
883
|
+
await deps.runStore.setTerminal(durableTaskId, syncPlane.status, sealResultForExit(result), syncPlane.errorMessage ?? null);
|
|
884
884
|
finalizeTaskResult(result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
885
885
|
noteRunOutcome(result);
|
|
886
|
-
return { status: 200, body: withSyncSubmitKeys(redactTerminalResult(
|
|
886
|
+
return { status: 200, body: withSyncSubmitKeys(redactTerminalResult(sealResultForExit(result))) };
|
|
887
887
|
};
|
|
888
888
|
const resp = await idemCache.run(idemKey, async () => withRunReceipt(await runSyncSubmitLeg()), (r) => r.status < 400);
|
|
889
889
|
sendSubmitResult(res, resp);
|
package/dist/http/send.d.ts
CHANGED
|
@@ -60,5 +60,16 @@ export declare function sendError(res: ServerResponse, status: number, errorCode
|
|
|
60
60
|
* throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码**
|
|
61
61
|
* 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */
|
|
62
62
|
export declare function httpErrorCode(status: number, code?: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* 🔴 B1 / S-535 —— `SessionPolicyError` → 应答,经 {@link sessionPolicyErrorHttp} 那**一只**映射
|
|
65
|
+
* (`src/session-policy-wire.ts`)。回 `true` = 已应答,调用点 `return`。
|
|
66
|
+
*
|
|
67
|
+
* 为什么是一只共享腿而不是每个站点一句 `instanceof`:映射本身早就收成一处了,而**咨询它的站点**只有两条
|
|
68
|
+
* **写**腿 —— 偏偏 core 7.25.0 那个新词 `corrupt` 是在**读**上抛出来的(店读不出它自己写的那一行:
|
|
69
|
+
* S-529 的两道读回拒 + C1 的体形拒)。于是 `GET /v1/sessions/:id/policy` 与 2c 的 manifest 导出腿把它交给了
|
|
70
|
+
* 域外通用臂 ⇒ `500 internal.error "internal error"`:码丢,连带**修复路径**也丢(同一行 `PUT` 却答
|
|
71
|
+
* `500 corrupt` 并点名那一列)。一只腿之后,新站点只会忘记调它,不会再各自发明一份映射。
|
|
72
|
+
*/
|
|
73
|
+
export declare function sendSessionPolicyError(res: ServerResponse, e: unknown): boolean;
|
|
63
74
|
export declare function msg(e: unknown): string;
|
|
64
75
|
//# sourceMappingURL=send.d.ts.map
|
package/dist/http/send.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SessionPolicyError } from "@sema-agent/core";
|
|
2
|
+
import { sessionPolicyErrorHttp } from "../session-policy-wire.js";
|
|
1
3
|
export const SSE_RESPONSE_MARK = Symbol("sema.http.sse-response");
|
|
2
4
|
export function isSseResponse(res) {
|
|
3
5
|
return res[SSE_RESPONSE_MARK] === true;
|
|
@@ -35,6 +37,13 @@ export function httpErrorCode(status, code) {
|
|
|
35
37
|
default: return status >= 500 ? "internal.error" : "request.rejected";
|
|
36
38
|
}
|
|
37
39
|
}
|
|
40
|
+
export function sendSessionPolicyError(res, e) {
|
|
41
|
+
if (!(e instanceof SessionPolicyError))
|
|
42
|
+
return false;
|
|
43
|
+
const h = sessionPolicyErrorHttp(e.code);
|
|
44
|
+
sendError(res, h.status, h.errorCode, e.message);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
38
47
|
export function msg(e) {
|
|
39
48
|
return e instanceof Error ? e.message : String(e);
|
|
40
49
|
}
|
package/dist/http/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
+
import { outwardPlane } from "../terminal.js";
|
|
2
3
|
import { redactHead } from "../observability/run-terminal.js";
|
|
3
|
-
import { validatePendingSteer
|
|
4
|
+
import { validatePendingSteer } from "@sema-agent/core";
|
|
4
5
|
import {} from "../config-center/facade.js";
|
|
5
6
|
import { HttpError } from "../security.js";
|
|
6
7
|
import {} from "../memory-sync.js";
|
|
@@ -117,7 +118,7 @@ export function createHttpServer(rawDeps) {
|
|
|
117
118
|
return deps.runnerFor?.(spec) ?? deps.runner;
|
|
118
119
|
}
|
|
119
120
|
function recordTaskResult(result) {
|
|
120
|
-
const plane =
|
|
121
|
+
const plane = outwardPlane(result.terminal);
|
|
121
122
|
deps.metrics?.inc("tasks_total", { status: plane.status });
|
|
122
123
|
if (result.stats.tokens)
|
|
123
124
|
deps.metrics?.inc("task_tokens_total", {}, result.stats.tokens);
|
package/dist/leader/fanout.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {} from "@sema-agent/core";
|
|
2
2
|
import { redactErrorMessage, redactWorkerReportError } from "../observability/run-terminal.js";
|
|
3
|
-
import { pausedCauseOf } from "../terminal.js";
|
|
3
|
+
import { pausedCauseOf, outwardPlane } from "../terminal.js";
|
|
4
4
|
class Semaphore {
|
|
5
5
|
max;
|
|
6
6
|
active = 0;
|
|
@@ -68,7 +68,7 @@ export async function fanOut(subtasks, opts) {
|
|
|
68
68
|
let report;
|
|
69
69
|
try {
|
|
70
70
|
const result = await stream.result();
|
|
71
|
-
const plane =
|
|
71
|
+
const plane = outwardPlane(result.terminal);
|
|
72
72
|
report = { ...base, status: mapStatus(result.terminal), stats: result.stats, ...(plane.errorMessage ? { error: redactErrorMessage(plane.errorMessage) ?? "" } : {}) };
|
|
73
73
|
}
|
|
74
74
|
catch (e) {
|
package/dist/leader/leader.js
CHANGED
|
@@ -2,8 +2,8 @@ import { fanOut } from "./fanout.js";
|
|
|
2
2
|
import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
|
|
3
3
|
import { pullDiff } from "./diffout.js";
|
|
4
4
|
import { mergeBranches } from "./merge.js";
|
|
5
|
-
import { Runner, runWithVerification, resumeWithVerification
|
|
6
|
-
import { pausedCauseOf } from "../terminal.js";
|
|
5
|
+
import { Runner, runWithVerification, resumeWithVerification } from "@sema-agent/core";
|
|
6
|
+
import { pausedCauseOf, outwardPlane } from "../terminal.js";
|
|
7
7
|
const MAX_RESUME_SLICES = 200;
|
|
8
8
|
export async function autoResumeOnResource(initial, signal, resume, maxSlices = MAX_RESUME_SLICES) {
|
|
9
9
|
let vr = initial;
|
|
@@ -59,7 +59,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
59
59
|
deps.logger?.info?.("worker_resource_resume", { workerId: w.workerId, slice });
|
|
60
60
|
return runner.resume(token, { gate: "resource_limit", decision: "continue" }, { ...w.spec, sessionId: w.sessionId, signal });
|
|
61
61
|
});
|
|
62
|
-
const plane =
|
|
62
|
+
const plane = outwardPlane(r.terminal);
|
|
63
63
|
const paused = pausedCauseOf(r.terminal);
|
|
64
64
|
if (r.terminal.kind !== "completed") {
|
|
65
65
|
deps.logger?.warn?.("worker_failed", {
|
|
@@ -92,7 +92,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
|
|
|
92
92
|
deps.logger?.info?.("worker_resource_resume", { workerId: w.workerId, slice });
|
|
93
93
|
return resumeWithVerification(w.runner, token, { gate: "resource_limit", decision: "continue" }, { ...w.spec, sessionId: w.sessionId, signal }, w.spec.objective ?? "", cfg);
|
|
94
94
|
});
|
|
95
|
-
const vrPlane =
|
|
95
|
+
const vrPlane = outwardPlane(vr.terminal);
|
|
96
96
|
deps.logger?.info?.("verify_outcome", { workerId: w.workerId, status: vrPlane.status, verdict: vr.verification.verdict, rounds: vr.verification.rounds, finding: redactHead(vr.verification.findings[0] ?? "", 220) });
|
|
97
97
|
const pass = vr.terminal.kind === "completed" && vr.verification.verdict === "PASS";
|
|
98
98
|
const vrPaused = pausedCauseOf(vr.terminal);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runRepairLoop, delimitUntrusted, } from "@sema-agent/core";
|
|
2
2
|
import { mkRepairOracle } from "./repair-oracle.js";
|
|
3
|
-
import {
|
|
3
|
+
import { outwardPlane } from "../terminal.js";
|
|
4
4
|
import { redactHead } from "../observability/run-terminal.js";
|
|
5
5
|
function statusForTerminal(t) {
|
|
6
6
|
return t === "fixed" || t === "candidate_only" ? "completed" : "failed";
|
|
@@ -52,7 +52,7 @@ async function runRepairLoopForLeaderInner(solo, implSpec, cfg) {
|
|
|
52
52
|
return { report: { ...base, status: "failed", error: safe }, result: undefined };
|
|
53
53
|
}
|
|
54
54
|
const terminal = result.repairTerminal;
|
|
55
|
-
const plane =
|
|
55
|
+
const plane = outwardPlane(result.terminal);
|
|
56
56
|
const report = {
|
|
57
57
|
...base,
|
|
58
58
|
status: terminal === undefined ? "failed" : statusForTerminal(terminal),
|
package/dist/leader/wire.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { outwardPlane } from "../terminal.js";
|
|
2
2
|
import { parseNumOrFail } from "../config.js";
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -176,7 +176,7 @@ export function createLeaderRunner(cfg) {
|
|
|
176
176
|
})
|
|
177
177
|
.result();
|
|
178
178
|
if (res.terminal.kind !== "completed")
|
|
179
|
-
throw new Error(`repair round ${round} did not complete (status=${
|
|
179
|
+
throw new Error(`repair round ${round} did not complete (status=${outwardPlane(res.terminal).status})`);
|
|
180
180
|
return { costUsd: res.stats?.costMicroUsd !== undefined ? res.stats.costMicroUsd / 1e6 : undefined, note: res.result?.slice(0, 200) };
|
|
181
181
|
}
|
|
182
182
|
finally {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { redactErrorMessage, redactLedgerEventData, redactTerminalResult } from "../observability/run-terminal.js";
|
|
2
|
+
import { outwardPlane } from "../terminal.js";
|
|
2
3
|
import { projectUsageStats, USAGE_SCAN_LIMIT } from "../usage-analytics.js";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
5
6
|
import { randomBytes } from "node:crypto";
|
|
6
|
-
import { terminalProjection } from "@sema-agent/core";
|
|
7
7
|
import { AppendLog, atomicWriteFile, ensureDir, isProcessLive, processFingerprint, readJsonlRecords, sanitizePathComponent, writeThenLink } from "@sema-agent/core";
|
|
8
8
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
9
9
|
import { StoreBootRefusalError } from "./store-contracts.js";
|
|
@@ -371,7 +371,7 @@ export class FileRunStore {
|
|
|
371
371
|
r.status = status;
|
|
372
372
|
r.result = result;
|
|
373
373
|
r.error = error;
|
|
374
|
-
r.errorCode = result === null ? null : (
|
|
374
|
+
r.errorCode = result === null ? null : (outwardPlane(result.terminal).errorCode ?? null);
|
|
375
375
|
r.updatedAt = new Date();
|
|
376
376
|
this.persist(r);
|
|
377
377
|
pending = {
|
|
@@ -382,7 +382,7 @@ export class FileRunStore {
|
|
|
382
382
|
resultTaskId: result?.taskId,
|
|
383
383
|
model: result?.model,
|
|
384
384
|
errorCode: r.errorCode ?? undefined,
|
|
385
|
-
errorMessage: (result === null ? undefined :
|
|
385
|
+
errorMessage: (result === null ? undefined : outwardPlane(result.terminal).errorMessage) ?? error ?? undefined,
|
|
386
386
|
elapsedMs: Math.max(0, Date.now() - r.createdAt.getTime()),
|
|
387
387
|
};
|
|
388
388
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { redactErrorMessage, redactLedgerEventData, redactTerminalResult } from "../observability/run-terminal.js";
|
|
2
|
+
import { outwardPlane } from "../terminal.js";
|
|
2
3
|
import { projectUsageStats, USAGE_SCAN_LIMIT } from "../usage-analytics.js";
|
|
3
|
-
import { terminalProjection } from "@sema-agent/core";
|
|
4
4
|
import { notifyRunTerminal } from "../observability/run-terminal.js";
|
|
5
5
|
const ownerEq = (a, b) => (a ?? null) === (b ?? null);
|
|
6
6
|
export class MemoryRunStore {
|
|
@@ -121,7 +121,7 @@ export class MemoryRunStore {
|
|
|
121
121
|
r.status = status;
|
|
122
122
|
r.result = result;
|
|
123
123
|
r.error = error;
|
|
124
|
-
r.errorCode = result === null ? null : (
|
|
124
|
+
r.errorCode = result === null ? null : (outwardPlane(result.terminal).errorCode ?? null);
|
|
125
125
|
r.updatedAt = new Date();
|
|
126
126
|
pending = {
|
|
127
127
|
runId: taskId,
|
|
@@ -131,7 +131,7 @@ export class MemoryRunStore {
|
|
|
131
131
|
resultTaskId: result?.taskId,
|
|
132
132
|
model: result?.model,
|
|
133
133
|
errorCode: r.errorCode ?? undefined,
|
|
134
|
-
errorMessage: (result === null ? undefined :
|
|
134
|
+
errorMessage: (result === null ? undefined : outwardPlane(result.terminal).errorMessage) ?? error ?? undefined,
|
|
135
135
|
elapsedMs: Math.max(0, Date.now() - r.createdAt.getTime()),
|
|
136
136
|
};
|
|
137
137
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { redactErrorMessage, redactLedgerEventData, redactTerminalResult } from "../observability/run-terminal.js";
|
|
2
|
-
import {
|
|
2
|
+
import { outwardPlane } from "../terminal.js";
|
|
3
3
|
import { projectUsageStats, USAGE_SCAN_LIMIT } from "../usage-analytics.js";
|
|
4
4
|
import { escapeLike } from "./sql-escape.js";
|
|
5
5
|
import { pgSanitizeText } from "./pg-safe-json.js";
|
|
@@ -120,7 +120,7 @@ export class SqlRunStore {
|
|
|
120
120
|
async setTerminal(taskId, status, result, error) {
|
|
121
121
|
result = redactTerminalResult(result);
|
|
122
122
|
error = error === null ? null : (redactErrorMessage(error) ?? null);
|
|
123
|
-
const errorCode = result === null ? null : (
|
|
123
|
+
const errorCode = result === null ? null : (outwardPlane(result.terminal).errorCode ?? null);
|
|
124
124
|
let won = false;
|
|
125
125
|
let observed;
|
|
126
126
|
await this.tx(async (conn) => {
|
|
@@ -153,7 +153,7 @@ export class SqlRunStore {
|
|
|
153
153
|
resultTaskId: result?.taskId,
|
|
154
154
|
model: result?.model,
|
|
155
155
|
errorCode: errorCode ?? undefined,
|
|
156
|
-
errorMessage: (result === null ? undefined :
|
|
156
|
+
errorMessage: (result === null ? undefined : outwardPlane(result.terminal).errorMessage) ?? error ?? undefined,
|
|
157
157
|
elapsedMs: observed?.createdAt === undefined ? undefined : Math.max(0, Date.now() - observed.createdAt),
|
|
158
158
|
});
|
|
159
159
|
}
|