@sema-agent/server 7.92.1 → 7.93.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/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +10 -7
- package/dist/approval-card.js +3 -3
- package/dist/bench/s1/arms.js +5 -6
- package/dist/bench/s1/live-deps.js +2 -2
- package/dist/boot/resolve-spec.js +1 -0
- package/dist/boot/stage-06-runners.js +3 -2
- package/dist/boot/stores.js +9 -0
- package/dist/capabilities/mcp-probe.d.ts +290 -0
- package/dist/capabilities/mcp-probe.js +318 -0
- package/dist/http/resume-legs.js +12 -10
- package/dist/http/route-ctx.d.ts +3 -0
- package/dist/http/routes/capabilities.d.ts +17 -0
- package/dist/http/routes/capabilities.js +93 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/rules.js +49 -13
- package/dist/http/routes/runs.js +3 -2
- package/dist/http/routes/session-sync.js +24 -12
- package/dist/http/routes/sessions.js +12 -8
- package/dist/http/routes/tasks.js +13 -13
- package/dist/http/send.d.ts +11 -0
- package/dist/http/send.js +9 -0
- package/dist/http/server.js +3 -2
- package/dist/http/wire-types.d.ts +17 -1
- 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/observability/metrics.js +2 -0
- package/dist/permission-rule-vocab.d.ts +9 -9
- package/dist/plugins/file-run-store.js +3 -3
- package/dist/plugins/memory-run-store.js +3 -3
- package/dist/plugins/remote-env-host.d.ts +5 -0
- package/dist/plugins/remote-env-host.js +39 -37
- 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/request-key-closure.js +1 -1
- package/dist/rules-consent.d.ts +42 -4
- package/dist/rules-consent.js +23 -7
- package/dist/run-local.js +2 -2
- package/dist/runs.js +14 -14
- package/dist/task-mcp.d.ts +32 -0
- package/dist/task-mcp.js +7 -0
- package/dist/terminal.d.ts +58 -4
- package/dist/terminal.js +28 -6
- package/dist/tool-approval.d.ts +22 -1
- package/dist/tool-approval.js +1 -0
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/ledger-sink.js +3 -2
- package/dist/trace/project.d.ts +24 -0
- package/dist/trace/project.js +40 -37
- package/dist/trace/wire-projection-faces.d.ts +209 -0
- package/dist/trace/wire-projection-faces.js +62 -0
- package/package.json +3 -3
|
@@ -3,7 +3,7 @@ import { sendJson, sendError, httpErrorCode } from "../send.js";
|
|
|
3
3
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
4
4
|
import { assertNever } from "../route-ctx.js";
|
|
5
5
|
import { createLogger } from "../../observability/logger.js";
|
|
6
|
-
import { RULE_IMPORT_RETRY_AFTER_SEC, MAX_RULE_SCOPE_CHARS, parseRuleScope,
|
|
6
|
+
import { RULE_IMPORT_RETRY_AFTER_SEC, MAX_RULE_SCOPE_CHARS, parseRuleScope, serializeRuleScope } from "../../rules-consent.js";
|
|
7
7
|
import { CONTROL_AND_BIDI_CHARS } from "../../text-bidi.js";
|
|
8
8
|
import { HttpError } from "../../security.js";
|
|
9
9
|
import { MAX_RULE_TEXT_CHARS, RULE_BEHAVIORS } from "@sema-agent/core";
|
|
@@ -246,6 +246,40 @@ async function handleRuleList(res, search, caller, lane, operatorPrincipals) {
|
|
|
246
246
|
: undefined;
|
|
247
247
|
sendJson(res, 200, { rules: page, rev: listing.rev, ...(nextCursor !== undefined ? { nextCursor } : {}) });
|
|
248
248
|
}
|
|
249
|
+
function sendRuleWriteFailure(res, verb, committed) {
|
|
250
|
+
switch (verb) {
|
|
251
|
+
case "write":
|
|
252
|
+
switch (committed) {
|
|
253
|
+
case "no":
|
|
254
|
+
sendError(res, 503, "state.rule_write_failed", "this rule was not written and nothing landed in the store — send the same request again", { committed });
|
|
255
|
+
return;
|
|
256
|
+
case "unknown":
|
|
257
|
+
sendError(res, 503, "state.rule_write_failed", "this write may or may not have landed — reconcile with GET /v1/rules before sending it again", { committed });
|
|
258
|
+
return;
|
|
259
|
+
default: {
|
|
260
|
+
const unknownWord = committed;
|
|
261
|
+
throw new Error(`unknown rule-write committed word: ${JSON.stringify(unknownWord)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
case "revoke":
|
|
265
|
+
switch (committed) {
|
|
266
|
+
case "no":
|
|
267
|
+
sendError(res, 503, "state.rule_remove_failed", "this rule was not revoked and nothing landed in the store — send the same request again", { committed });
|
|
268
|
+
return;
|
|
269
|
+
case "unknown":
|
|
270
|
+
sendError(res, 503, "state.rule_remove_failed", "this revocation may or may not have landed — reconcile with GET /v1/rules before sending it again", { committed });
|
|
271
|
+
return;
|
|
272
|
+
default: {
|
|
273
|
+
const unknownWord = committed;
|
|
274
|
+
throw new Error(`unknown rule-remove committed word: ${JSON.stringify(unknownWord)}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
default: {
|
|
278
|
+
const unknownVerb = verb;
|
|
279
|
+
throw new Error(`unknown rule write verb: ${JSON.stringify(unknownVerb)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
249
283
|
async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
|
|
250
284
|
const parsed = RuleIdentityBodySchema.safeParse(rawBody);
|
|
251
285
|
if (!parsed.success) {
|
|
@@ -271,7 +305,9 @@ async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
|
|
|
271
305
|
switch (written.status) {
|
|
272
306
|
case "persisted":
|
|
273
307
|
case "no-op":
|
|
274
|
-
sendJson(res, 200,
|
|
308
|
+
sendJson(res, 200, written.row === undefined
|
|
309
|
+
? { status: written.status, rev: written.rev, stillLive: written.stillLive }
|
|
310
|
+
: { status: written.status, rev: written.rev, stillLive: written.stillLive, rule: written.row });
|
|
275
311
|
return;
|
|
276
312
|
case "rejected":
|
|
277
313
|
logger.info("rule write refused", { reason: written.reason, detail: written.detail });
|
|
@@ -280,9 +316,9 @@ async function handleRuleWrite(res, rawBody, caller, lane, operatorPrincipals) {
|
|
|
280
316
|
detail: written.detail,
|
|
281
317
|
});
|
|
282
318
|
return;
|
|
283
|
-
case "
|
|
284
|
-
logger.info("rule write
|
|
285
|
-
|
|
319
|
+
case "failed":
|
|
320
|
+
logger.info("rule write failed", { detail: written.detail, committed: written.committed });
|
|
321
|
+
sendRuleWriteFailure(res, "write", written.committed);
|
|
286
322
|
return;
|
|
287
323
|
default: {
|
|
288
324
|
const unknown = written;
|
|
@@ -311,17 +347,17 @@ async function handleRuleRevoke(res, rawBody, caller, lane, operatorPrincipals)
|
|
|
311
347
|
case "removed":
|
|
312
348
|
sendJson(res, 200, { status: "removed", rev: outcome.rev, stillLive: outcome.stillLive });
|
|
313
349
|
return;
|
|
314
|
-
case "no-op":
|
|
315
|
-
|
|
316
|
-
? "yes"
|
|
317
|
-
: "no";
|
|
318
|
-
sendJson(res, 200, { status: "no-op", rev: outcome.rev, stillLive });
|
|
350
|
+
case "no-op":
|
|
351
|
+
sendJson(res, 200, { status: "no-op", rev: outcome.rev, stillLive: outcome.stillLive });
|
|
319
352
|
return;
|
|
320
|
-
}
|
|
321
353
|
case "failed":
|
|
322
|
-
logger.info("rule revoke failed", { error: outcome.error });
|
|
323
|
-
|
|
354
|
+
logger.info("rule revoke failed", { error: outcome.error, committed: outcome.committed });
|
|
355
|
+
sendRuleWriteFailure(res, "revoke", outcome.committed);
|
|
324
356
|
return;
|
|
357
|
+
default: {
|
|
358
|
+
const unknown = outcome;
|
|
359
|
+
throw new Error(`unknown rule revoke outcome: ${JSON.stringify(unknown)}`);
|
|
360
|
+
}
|
|
325
361
|
}
|
|
326
362
|
}
|
|
327
363
|
export const RULES_ROUTES = [
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -2,7 +2,7 @@ import { persistedPlaneOf } from "../../terminal.js";
|
|
|
2
2
|
import { memoRedactedRead, memoRedactTerminalResult, redactErrorMessage, redactHead, redactLedgerEventData, redactTerminalResult } from "../../observability/run-terminal.js";
|
|
3
3
|
import { isModelTextLedgerEventType } from "../../trace/ledger-events.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { uuidv7, mintCheckpointToken, CheckpointError, validatePendingSteer } from "@sema-agent/core";
|
|
5
|
+
import { uuidv7, mintCheckpointToken, CheckpointError, validatePendingSteer, TASK_OUTPUT_REPLAY_KINDS } from "@sema-agent/core";
|
|
6
6
|
import { isTerminalRunStatus, isParkedRunStatus, runProvenElsewhere } from "../../plugins/store-contracts.js";
|
|
7
7
|
import { LIVE_RUN_GATE_KINDS, TERMINAL_PARK_GATE_KINDS } from "../../plugins/checkpoint-store-sql.js";
|
|
8
8
|
import { redactSecrets } from "../../trace/redact.js";
|
|
@@ -1058,8 +1058,9 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1058
1058
|
const g14 = (() => {
|
|
1059
1059
|
const d = out.details;
|
|
1060
1060
|
const replay = d?.details?.replay;
|
|
1061
|
-
if (replay === "
|
|
1061
|
+
if (typeof replay === "string" && TASK_OUTPUT_REPLAY_KINDS.includes(replay)) {
|
|
1062
1062
|
return { cursorSemantics: replay };
|
|
1063
|
+
}
|
|
1063
1064
|
return {};
|
|
1064
1065
|
})();
|
|
1065
1066
|
sendJson(res, 200, { taskId: runId, target, content: out.content, output: out.details, ...g14 });
|
|
@@ -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";
|
|
@@ -47,7 +47,7 @@ function sendSubmitResult(res, resp) {
|
|
|
47
47
|
: raw;
|
|
48
48
|
sendJson(res, 200, body);
|
|
49
49
|
}
|
|
50
|
-
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
|
|
50
|
+
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).";
|
|
51
51
|
export async function handleTasks(req, res, match, ctx) {
|
|
52
52
|
const { deps } = ctx;
|
|
53
53
|
const { idemCache, inflightRuns, cancelledViaVerb, steerableRuns, counters } = ctx.registry;
|
|
@@ -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);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* cut: ONLY TaskRequestBody (its field types are inline or core imports — no other server.ts-local satellites);
|
|
7
7
|
* sibling request/response shapes can migrate here later on the same pattern.
|
|
8
8
|
*/
|
|
9
|
-
import type { A2aServerSpec, AgentDefinition, McpServerSpec, SkillSpec } from "@sema-agent/core";
|
|
9
|
+
import type { A2aServerSpec, AgentDefinition, ApproverPosture, McpServerSpec, SkillSpec } from "@sema-agent/core";
|
|
10
10
|
import type { SettingsPermissionMode } from "../task-settings.js";
|
|
11
11
|
import type { wiringManifestFaceEventData } from "../trace/project.js";
|
|
12
12
|
export interface TaskRequestBody {
|
|
@@ -316,6 +316,22 @@ export interface TaskRequestBody {
|
|
|
316
316
|
* `SETTINGS_PERMISSION_MODES`(受理腿 `isSettingsPermissionMode` 与两句 400 文案同读那一份)。
|
|
317
317
|
* 改前这里是第 6 份手抄的五词联合 —— 加/退一个模式时 wire 型面与受理腿会各走各的。 */
|
|
318
318
|
permissionMode?: SettingsPermissionMode;
|
|
319
|
+
/**
|
|
320
|
+
* **#1000**(core 7.26.0 `TaskSpec.approverPosture`;CC-116 装配口)—— 宿主对这个 run 的**审批姿态声明**:
|
|
321
|
+
* 「这些卡由我自己自动答」。一个词(`"bypass"`),闭集属主在 core(`ApproverPosture` / `APPROVER_POSTURES`)。
|
|
322
|
+
*
|
|
323
|
+
* 🔴 **它不授权任何东西,也不是一只门**(core 头注逐字:"a statement about who answers this run's approval
|
|
324
|
+
* cards, never an instruction to the gate";引擎只把它**渲**给模型,没有任何裁决腿读它)。所以它与
|
|
325
|
+
* {@link permissionMode} 的 `"bypassPermissions"` **不是**一回事,也不能互相派生:后者选的是**门的形状**
|
|
326
|
+
* (且只能收紧、deny-wins),前者只说「谁来答卡」。两者在语义上同族,声明它而不开 `bypassPermissions`
|
|
327
|
+
* 只是对模型说了一句与实际不符的话 —— 见契约文档那一段的「谁受伤」。
|
|
328
|
+
*
|
|
329
|
+
* 🔴 **本仓零判据、原样过境**(`boot/resolve-spec.ts` 写点):词的拒绝逻辑属引擎
|
|
330
|
+
* (`refuseInvalidApproverPosture` 响亮拒一个不认识的词),server **不加第二道门** —— 在下游再判一次就是
|
|
331
|
+
* 同一语义面的第二个判官(源头修复纪律 [ref]),而且本仓抄一份词表的那天 core 加第二个词就会被静默 400 掉。
|
|
332
|
+
* 型也从 core 的 `ApproverPosture` 派生(不抄字面联合)。
|
|
333
|
+
*/
|
|
334
|
+
approverPosture?: ApproverPosture;
|
|
319
335
|
/** MF-30 memory PAUSE (shell-host contract, option B per-request — clay 2026-06-27): `false` makes THIS run
|
|
320
336
|
* read-only over long-term memory (`TaskSpec.memory.writeScope:null` — the [ref] memory ENGINE materializes/
|
|
321
337
|
* reads but its harvest commits nothing). Absent/`true` ⇒ normal read+write. The shell's `/memory` pause carries
|
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,5 +1,6 @@
|
|
|
1
1
|
import { DENIED_BY_VALUES } from "@sema-agent/core";
|
|
2
2
|
import { buildPermissionRuleEventsHelp } from "./permission-rule-events.js";
|
|
3
|
+
import { MCP_PROBE_OUTCOMES } from "../capabilities/mcp-probe.js";
|
|
3
4
|
const DEFAULT_BUCKETS = [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60];
|
|
4
5
|
function labelKey(labels) {
|
|
5
6
|
return Object.keys(labels)
|
|
@@ -214,6 +215,7 @@ export function createMetrics() {
|
|
|
214
215
|
m.counter("http_requests_total", "HTTP requests by route/method/status");
|
|
215
216
|
m.histogram("http_request_duration_seconds", "HTTP request duration");
|
|
216
217
|
m.counter("rate_limited_total", "Requests rejected by the rate limiter");
|
|
218
|
+
m.counter("mcp_probe_total", `No-run MCP status face outcomes by outcome (${MCP_PROBE_OUTCOMES.join("/")} — see src/capabilities/mcp-probe.ts MCP_PROBE_OUTCOMES; rate_limited has TWO causes: the per-identity fixed window AND the replica-wide in-flight cap, which can refuse caller B because of caller A)`);
|
|
217
219
|
m.counter("tasks_total", "Finished tasks by terminal status");
|
|
218
220
|
m.counter("task_tokens_total", "Total model tokens consumed by tasks");
|
|
219
221
|
m.histogram("task_duration_seconds", "Async run wall-clock duration");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RuleApprovalRecord, RuleBehavior } from "@sema-agent/core";
|
|
2
2
|
/**
|
|
3
3
|
* `PersistedRuleTool` **不再是闭词表**(core 7.9.0 / [ref]):工具名自 core 的**目录**派生
|
|
4
4
|
* (`pathTarget` 声明 ⇒ 路径文法;`Bash` ⇒ 命令文法),型是 `string`。于是本文件对它的处置从
|
|
@@ -61,13 +61,13 @@ export declare const RULE_APPROVAL_RECORD_SCHEMA: RuleApprovalRecord["schema"];
|
|
|
61
61
|
* `"unknown"` = **回答这一问的那次读回没做成** —— 这个域唯一不肯说的那句话是「看不见 ⇒ 没有」,
|
|
62
62
|
* 第三个词就是它的反面。
|
|
63
63
|
*
|
|
64
|
-
* 🔴 **型从 core
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* (
|
|
64
|
+
* 🔴 **型从 core 的包根导出接过来,本仓零抄**(core 7.26.0 S-538 ⓐ:`RemovalLiveness` 随请托上了导出面)。
|
|
65
|
+
* 上一版是 `Extract<RemoveResult, {status:"removed"}>["stillLive"]` —— 那是派生而非手抄(值不会漂),但它把
|
|
66
|
+
* 一只**独立的闭集**寄生在某一个臂的形状上:core 哪天给 `removed` 臂改名、或让这只词表先在别的臂上落地,
|
|
67
|
+
* 派生式就会指向一个与词表无关的东西。现在两者同名同源。
|
|
68
|
+
* 🔴 **运行期词表 `REMOVAL_LIVENESS_WORDS` 刻意不 re-export**:本仓对这三个词零运行期判据(值由 core 在两只
|
|
69
|
+
* 200 体上原样透传,分支全走编译期穷尽),开一扇没人走的门只会让台账多一行「引用而不消费」的例外。
|
|
70
|
+
* 哪天真要判「这个串在不在表里」,那时从 core 直接读它。
|
|
69
71
|
*/
|
|
70
|
-
export type RemovalLiveness
|
|
71
|
-
status: "removed";
|
|
72
|
-
}>["stillLive"];
|
|
72
|
+
export type { RemovalLiveness } from "@sema-agent/core";
|
|
73
73
|
//# sourceMappingURL=permission-rule-vocab.d.ts.map
|
|
@@ -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
|
}
|
|
@@ -134,6 +134,11 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
134
134
|
* ② 把读拉进来会让 teardown 去等一次可能**永不 settle** 的读(workspace 里的 FIFO / 特殊文件),
|
|
135
135
|
* 把 destroy 挂死 —— 拿一条新的可用性病换一条不存在的正确性收益。
|
|
136
136
|
* 判据因此是一句话:**「可能建路径或改路径的那些面」进在飞集合**,不是「过了 fsReady 的都进」。
|
|
137
|
+
*
|
|
138
|
+
* ── 不止文件面(S-541)─────────────────────────────────────────────────────────────────────────
|
|
139
|
+
* 判据是「会不会建/改路径」,**不是**「在不在 `env` 的文件面上」。后台 shell 的 `launch`(`makeBgDriver`)
|
|
140
|
+
* 在 spool 根下 `mkdir` 一棵子树,而它整段期间 `bgManager.hasShells()` 还是 false(`shells.set` 在
|
|
141
|
+
* `await launch(ctx)` 之后)⇒ 同一条竞态的另一张脸,同一只集合治。见那处头注。
|
|
137
142
|
*/
|
|
138
143
|
private withInflightFs;
|
|
139
144
|
/**
|