@sema-agent/server 7.84.0 → 7.86.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/USAGE.md +24 -2
- package/dist/approval-card.d.ts +7 -0
- package/dist/approval-card.js +2 -2
- package/dist/approval-hmac.d.ts +28 -1
- package/dist/approval-hmac.js +2 -1
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/capabilities/execution-lane.d.ts +22 -0
- package/dist/capabilities/execution-lane.js +8 -0
- package/dist/capabilities/memory-notice.d.ts +4 -1
- package/dist/config-catalog.js +3 -2
- package/dist/config-types.d.ts +5 -1
- package/dist/execution-lane-caps.d.ts +13 -0
- package/dist/execution-lane-caps.js +1 -0
- package/dist/fleet/fleet-reconciler.js +2 -2
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +21 -6
- package/dist/http/route-ctx.d.ts +51 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.d.ts +11 -0
- package/dist/http/routes/approvals-assistant.js +61 -16
- package/dist/http/routes/capabilities.js +3 -0
- package/dist/http/routes/images.js +38 -8
- package/dist/http/routes/mcp-reconnect.d.ts +56 -0
- package/dist/http/routes/mcp-reconnect.js +139 -0
- package/dist/http/routes/memory-policy.js +3 -6
- package/dist/http/routes/notify-wake.js +11 -3
- package/dist/http/routes/run-memory-capture.js +3 -5
- package/dist/http/routes/runs.js +21 -23
- package/dist/http/routes/sessions.js +11 -2
- package/dist/http/routes/shared-memory.js +5 -1
- package/dist/http/server.d.ts +14 -0
- package/dist/http/wire-types.d.ts +5 -1
- package/dist/memory-scope.d.ts +16 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +8 -2
- package/dist/plugins/store-contracts.d.ts +23 -0
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/principal-jwt.d.ts +15 -7
- package/dist/principal-jwt.js +1 -1
- package/dist/read-face-posture.d.ts +6 -2
- package/dist/task-settings.d.ts +35 -0
- package/dist/task-settings.js +7 -0
- package/dist/trace/core-keyset-guard.d.ts +49 -3
- package/dist/trace/project.d.ts +12 -0
- package/dist/trace/project.js +14 -0
- package/package.json +2 -2
package/dist/http/routes/runs.js
CHANGED
|
@@ -3,7 +3,7 @@ import { memoRedactedRead, memoRedactTerminalResult, redactErrorMessage, redactH
|
|
|
3
3
|
import { isModelTextLedgerEventType } from "../../trace/ledger-events.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { uuidv7, mintCheckpointToken, CheckpointError, validatePendingSteer } from "@sema-agent/core";
|
|
6
|
-
import { isTerminalRunStatus, isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
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";
|
|
9
9
|
import { HttpError, verifiedPrincipal, isUuidV7, encodeCheckpointScope } from "../../security.js";
|
|
@@ -64,7 +64,8 @@ export function projectDecisionActor(raw) {
|
|
|
64
64
|
const parsed = AskDecisionActorSchema.safeParse(raw);
|
|
65
65
|
return parsed.success ? parsed.data : undefined;
|
|
66
66
|
}
|
|
67
|
-
async function subagentReadGate(req, res,
|
|
67
|
+
async function subagentReadGate(req, res, ctx, runId, search) {
|
|
68
|
+
const { deps } = ctx;
|
|
68
69
|
const principal = gatedPrincipal(req, deps.config);
|
|
69
70
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
70
71
|
sendPrincipalRefusal(res, req, deps.config);
|
|
@@ -72,10 +73,13 @@ async function subagentReadGate(req, res, deps, runId, search) {
|
|
|
72
73
|
}
|
|
73
74
|
const trusted = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
74
75
|
const run = await deps.runStore.getRun(runId);
|
|
75
|
-
if (!run
|
|
76
|
+
if (!run) {
|
|
76
77
|
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
77
78
|
return null;
|
|
78
79
|
}
|
|
80
|
+
if (!trusted && !ctx.helpers.runOwnerOk(req, res, run.owner)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
79
83
|
const callerSession = new URLSearchParams(search).get("session");
|
|
80
84
|
if (!trusted && run.sessionId && callerSession !== run.sessionId) {
|
|
81
85
|
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
@@ -88,7 +92,7 @@ function sendHandleMiss(res, run, thisInstanceId) {
|
|
|
88
92
|
sendError(res, 404, "not_found.task_owner_settled", "no such background task handle here. Routing hint about its OWNER (not about this handle): the run has already settled, and background handles live in the replica-local registry for the parent's lifetime, so none of its handles are readable any more. Read the run's own outcome via GET /v1/runs/:id");
|
|
89
93
|
return true;
|
|
90
94
|
}
|
|
91
|
-
if (run.status === "running" &&
|
|
95
|
+
if (run.status === "running" && runProvenElsewhere(run, thisInstanceId)) {
|
|
92
96
|
sendError(res, 404, "not_found.task_owner_elsewhere", "no such background task handle here. Routing hint about its OWNER (not about this handle): the run is executing on another replica, and this one's registry holds none of its handles. Ask the replica that owns the run, or read the run's own outcome via GET /v1/runs/:id");
|
|
93
97
|
return true;
|
|
94
98
|
}
|
|
@@ -429,10 +433,8 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
429
433
|
sendError(res, 404, "not_found.run", runNotFoundMessage(taskId));
|
|
430
434
|
return;
|
|
431
435
|
}
|
|
432
|
-
if (!trusted &&
|
|
433
|
-
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
436
|
+
if (!trusted && !runOwnerOk(req, res, run.owner))
|
|
434
437
|
return;
|
|
435
|
-
}
|
|
436
438
|
if (!runSessionAcceptOk(req, res, run, "run.steer"))
|
|
437
439
|
return;
|
|
438
440
|
let parkQueueFull;
|
|
@@ -532,7 +534,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
532
534
|
return;
|
|
533
535
|
}
|
|
534
536
|
if (run.status === "running") {
|
|
535
|
-
const provenElsewhere =
|
|
537
|
+
const provenElsewhere = runProvenElsewhere(run, deps.instanceId);
|
|
536
538
|
if (provenElsewhere) {
|
|
537
539
|
sendNotRunning("run is live on another replica — a live steer must reach the replica holding the stream; resend it there, or use POST /v1/runs/:id/cancel for a run-level stop");
|
|
538
540
|
}
|
|
@@ -714,10 +716,8 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
714
716
|
sendError(res, 404, "not_found.run", runNotFoundMessage(taskId));
|
|
715
717
|
return;
|
|
716
718
|
}
|
|
717
|
-
if (!trusted &&
|
|
718
|
-
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
719
|
+
if (!trusted && !runOwnerOk(req, res, run.owner))
|
|
719
720
|
return;
|
|
720
|
-
}
|
|
721
721
|
if (!runSessionAcceptOk(req, res, run, "run.interrupt"))
|
|
722
722
|
return;
|
|
723
723
|
const sendInterruptNotRunning = (error) => sendError(res, 409, "steering.not_running", error);
|
|
@@ -731,7 +731,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
731
731
|
return;
|
|
732
732
|
}
|
|
733
733
|
if (status === "running") {
|
|
734
|
-
const provenElsewhere =
|
|
734
|
+
const provenElsewhere = runProvenElsewhere(run, deps.instanceId);
|
|
735
735
|
if (provenElsewhere) {
|
|
736
736
|
sendError(res, 409, "interrupt.not_held", "run is live on another replica — interrupt must reach the replica holding the stream; resend it there, or use POST /v1/runs/:id/cancel for a run-level stop");
|
|
737
737
|
}
|
|
@@ -812,10 +812,8 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
812
812
|
return;
|
|
813
813
|
}
|
|
814
814
|
const operator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
815
|
-
if (!operator &&
|
|
816
|
-
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
815
|
+
if (!operator && !runOwnerOk(req, res, run.owner))
|
|
817
816
|
return;
|
|
818
|
-
}
|
|
819
817
|
if (!runSessionAcceptOk(req, res, run, "run.compact"))
|
|
820
818
|
return;
|
|
821
819
|
let compactBody;
|
|
@@ -894,10 +892,8 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
894
892
|
return;
|
|
895
893
|
}
|
|
896
894
|
const operator = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
897
|
-
if (!operator &&
|
|
898
|
-
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
895
|
+
if (!operator && !runOwnerOk(req, res, run.owner))
|
|
899
896
|
return;
|
|
900
|
-
}
|
|
901
897
|
if (!runSessionAcceptOk(req, res, run, "run.detach"))
|
|
902
898
|
return;
|
|
903
899
|
const live = steerableRuns.get(taskId);
|
|
@@ -913,7 +909,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
913
909
|
}
|
|
914
910
|
export async function handleRunVerbs(req, res, match, ctx) {
|
|
915
911
|
const { deps } = ctx;
|
|
916
|
-
const { readJson, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, safeDecode, runSessionAcceptOk } = ctx.helpers;
|
|
912
|
+
const { readJson, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, safeDecode, runOwnerOk, runSessionAcceptOk } = ctx.helpers;
|
|
917
913
|
if (match.decl.id === "run-subagent-output") {
|
|
918
914
|
if (!deps.runStore || !deps.subagentTaskOutput) {
|
|
919
915
|
sendError(res, 501, "capability.run_store_required", "subagent output reads require the run store");
|
|
@@ -925,7 +921,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
925
921
|
sendError(res, 400, "request.path_malformed", "malformed subagent path (invalid percent-encoding)");
|
|
926
922
|
return;
|
|
927
923
|
}
|
|
928
|
-
const gated = await subagentReadGate(req, res,
|
|
924
|
+
const gated = await subagentReadGate(req, res, ctx, runId, match.search);
|
|
929
925
|
if (!gated)
|
|
930
926
|
return;
|
|
931
927
|
const { run } = gated;
|
|
@@ -950,7 +946,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
950
946
|
sendError(res, 400, "request.path_malformed", "malformed subagent path (invalid percent-encoding)");
|
|
951
947
|
return;
|
|
952
948
|
}
|
|
953
|
-
const gatedTail = await subagentReadGate(req, res,
|
|
949
|
+
const gatedTail = await subagentReadGate(req, res, ctx, runId, match.search);
|
|
954
950
|
if (!gatedTail)
|
|
955
951
|
return;
|
|
956
952
|
const { run } = gatedTail;
|
|
@@ -1015,7 +1011,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1015
1011
|
sendError(res, 400, "request.path_malformed", "malformed task path (invalid percent-encoding)");
|
|
1016
1012
|
return;
|
|
1017
1013
|
}
|
|
1018
|
-
const gatedB = await subagentReadGate(req, res,
|
|
1014
|
+
const gatedB = await subagentReadGate(req, res, ctx, runId, match.search);
|
|
1019
1015
|
if (!gatedB)
|
|
1020
1016
|
return;
|
|
1021
1017
|
const { run } = gatedB;
|
|
@@ -1113,10 +1109,12 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1113
1109
|
}
|
|
1114
1110
|
const trusted = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
1115
1111
|
const run = await deps.runStore.getRun(runId);
|
|
1116
|
-
if (!run
|
|
1112
|
+
if (!run) {
|
|
1117
1113
|
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
1118
1114
|
return;
|
|
1119
1115
|
}
|
|
1116
|
+
if (!trusted && !runOwnerOk(req, res, run.owner))
|
|
1117
|
+
return;
|
|
1120
1118
|
if (!runSessionAcceptOk(req, res, run, `subagent.${verb}`))
|
|
1121
1119
|
return;
|
|
1122
1120
|
const send409 = (errorCode, error) => sendError(res, 409, errorCode, error);
|
|
@@ -219,7 +219,11 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
219
219
|
sendError(res, 501, "capability.session_store_required", "session head probe requires a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
222
|
-
const headSession =
|
|
222
|
+
const headSession = safeDecode(match.params.sessionId);
|
|
223
|
+
if (headSession === null) {
|
|
224
|
+
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
223
227
|
const getHead = deps.sessionStorage?.getHead?.bind(deps.sessionStorage);
|
|
224
228
|
let owner;
|
|
225
229
|
let leafId;
|
|
@@ -262,7 +266,11 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
262
266
|
sendError(res, 501, "capability.session_store_required", "session events require a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
263
267
|
return;
|
|
264
268
|
}
|
|
265
|
-
const evSession =
|
|
269
|
+
const evSession = safeDecode(match.params.sessionId);
|
|
270
|
+
if (evSession === null) {
|
|
271
|
+
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
266
274
|
if (sse.sseConnections >= (deps.sessionEventsMaxConnections ?? 256)) {
|
|
267
275
|
res.setHeader("retry-after", "5");
|
|
268
276
|
sendError(res, 503, "state.sse_connection_cap", "session-events connection cap reached — fall back to /head polling");
|
|
@@ -544,6 +552,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
544
552
|
const s = await entry.status;
|
|
545
553
|
sendJson(res, 200, {
|
|
546
554
|
sessionId,
|
|
555
|
+
autoConsolidationArmed: deps.memoryAutoConsolidationArmed,
|
|
547
556
|
...(s.captureOptedOut !== undefined ? { captureOptedOut: s.captureOptedOut } : {}),
|
|
548
557
|
...(s.committedCount !== undefined ? { committedCount: s.committedCount } : {}),
|
|
549
558
|
...(s.foldedCount !== undefined ? { foldedCount: s.foldedCount } : {}),
|
|
@@ -90,7 +90,11 @@ export async function handleSharedMemory(req, res, match, ctx) {
|
|
|
90
90
|
}
|
|
91
91
|
case "shared-memory-store-documents":
|
|
92
92
|
case "shared-memory-store-document": {
|
|
93
|
-
const storeId =
|
|
93
|
+
const storeId = ctx.helpers.safeDecode(match.params.store);
|
|
94
|
+
if (storeId === null) {
|
|
95
|
+
sendError(res, 400, "request.path_malformed", "malformed id (invalid percent-encoding)");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
94
98
|
const query = new URLSearchParams(match.search);
|
|
95
99
|
const snapshot = await bindSnapshot(req, res, ctx);
|
|
96
100
|
if (!snapshot)
|
package/dist/http/server.d.ts
CHANGED
|
@@ -417,6 +417,20 @@ export interface ServiceSeamDeps {
|
|
|
417
417
|
sessionMemoryStatus?: {
|
|
418
418
|
status: (sessionId: string) => Promise<SessionMemoryStatus>;
|
|
419
419
|
};
|
|
420
|
+
/**
|
|
421
|
+
* S-403(core 7.22.0 [ref] W1)—— 本**部署**的自动整理武装事实,`GET /v1/sessions/:id/memory-status`
|
|
422
|
+
* 的 `autoConsolidationArmed` 格。
|
|
423
|
+
*
|
|
424
|
+
* 🔴 **唯一读法**:值必须由 core 的 `autoRunOnRecommendationEffective(RunnerDeps)` 组
|
|
425
|
+
* (契约 `memory.auto_consolidation.arming_has_one_resolver`),在 **boot 期算一次**
|
|
426
|
+
* (`boot/stage-10-http-server.ts` 单点)。`RunnerDeps.autoRunOnRecommendation` 的**缺席不是「关」**
|
|
427
|
+
* (core 7.21.1 起具名缺省是开,且只在 `memoryConsolidation` ∧ 可解整理模型席都接线时才真武装)——
|
|
428
|
+
* 所以「按席在不在场判」与「按席值判」**两个方向都错**,本仓不自铸第二套派生。
|
|
429
|
+
* 🔴 **必填,不是可选**:它是一个纯函数在 boot 期的答案,任何装配点都算得出;做成可选就等于在
|
|
430
|
+
* wire 上多出一个「答不出」的第三态,而这一格读错的方向是**把一台正在外流记忆的部署报成没在外流**。
|
|
431
|
+
* 装配漏接 ⇒ tsc 红(本行就是通知);测试桩在 `test/helpers/boot.ts` 显式给 false。
|
|
432
|
+
*/
|
|
433
|
+
memoryAutoConsolidationArmed: boolean;
|
|
420
434
|
/** [ref] 件②(core 5.59.0 `MemoryEngine.listExternalOriginEntries` / `listOriginClearances` /
|
|
421
435
|
* `clearEntryOrigin`):**外源标记人面**。启用 `GET /v1/memory/origin/external`、
|
|
422
436
|
* `GET /v1/memory/origin/clearances`、`POST /v1/memory/origin/entries/:entryId/clear`(三口 operator-only)。
|
|
@@ -340,7 +340,11 @@ export interface TaskRequestBody {
|
|
|
340
340
|
* (`request-value-closure.ts` 的 `singleMemberOffFieldIssue`,与 `memoryCapture` 同表)。
|
|
341
341
|
* 记忆面暗的部署(引擎未接线 / 多租户)上 `memory` 整键本就缺席,本声明无处落也无事可关。
|
|
342
342
|
* Rides the persisted body onto resume legs(与 `memoryWrite` 同姿势)。
|
|
343
|
-
*
|
|
343
|
+
* ✅ **子代地板自 core 7.22.0 到货,本仓零接线**:core 的 `parentSpecSeatOf` 从**这条 run 自己的**
|
|
344
|
+
* `spec.memory.enabled === false` 铸 `ParentSpecSeat.memoryOff`,`applyParentSpecSeat` 作**无条件
|
|
345
|
+
* REMOVAL** 施加(改写成 `{ …子代自己的 scopes, enabled:false }`,永不删键)⇒ 父关 ⇒ 子代 / 孙代 /
|
|
346
|
+
* workflow 子 spec / 跨 run 唤醒与保留 resume 一律无记忆面。🚧 **tier-3 耐久复活不带**(core 已记残余)。
|
|
347
|
+
* 链路的机器锚:`test/memory-off-request.test.ts` ⑥ 段(亲读 core dist 的铸点与施加点)。 */
|
|
344
348
|
memory?: "off";
|
|
345
349
|
/** 142-S4 projectId 线程化([ref] §2):这个 run 归属的项目(center 登记簿键,generic lowercase
|
|
346
350
|
* UUID — core S1 `resolveProjectId` marker 或 S3 无仓 mint 的产物;客户端只透传,零铸造权威)。是「哪个
|
package/dist/memory-scope.d.ts
CHANGED
|
@@ -221,6 +221,22 @@ declarations?: {
|
|
|
221
221
|
* `normalizeMemorySpec(spec)?.enabled === true`。S-405 之后 `memory:"off"` 铸的是「键在场 + `enabled:false`」(保留事实),
|
|
222
222
|
* 任何按「键在不在场」判挂没挂的读者从那一天起都读错(改前 `memory-notice` 正是那样,把无记忆面的披露一起吞掉)。
|
|
223
223
|
* 消费方拿本函数的布尔,不拿 `spec.memory` 自己判。
|
|
224
|
+
*
|
|
225
|
+
* ⚠️ **本函数会抛,而「抛」本身就是一条判据**(S-424 补注)。`normalizeMemorySpec` 对两形直接 throw
|
|
226
|
+
* (亲核安装态 core 7.22.0 `dist/core/memory.js:367-372` 与 `:373-385`):
|
|
227
|
+
* · 单数 `scope` 键在场 ⇒ `code:"config.memory_scope_spelling"`(core 已删的旧拼写;它的报错原话
|
|
228
|
+
* 是「Refusing to start with memory silently off」—— 静默关闭正是那次 HIGH 的伤害面);
|
|
229
|
+
* · `capture` 在场且不是字面 `"off"` ⇒ `code:"config.memory_capture_spelling"`。
|
|
230
|
+
*
|
|
231
|
+
* 这两形在本仓的 wire 面上**都到不了这里**,各有属主:
|
|
232
|
+
* · 请求体侧 = 值闭包门 `singleMemberOffFieldIssue`(`src/request-value-closure.ts`;fresh 400 与 resume
|
|
233
|
+
* 重放共用的同一只)—— 到得了下游的 `memory`/`memoryCapture` 只可能是字面 `"off"` 或缺席;
|
|
234
|
+
* · spec 侧 = 唯一铸点 {@link memorySpecForRequest}(本文件)—— 它恒产 `scopes: [scope, ...extras]`,
|
|
235
|
+
* 从不产单数 `scope`,`capture` 也只在声明为 `"off"` 时才铸。
|
|
236
|
+
*
|
|
237
|
+
* 所以这里抛 = **调用方没先过值闭包门**(手搓 spec、或绕过铸点直接喂一份 `TaskSpec.memory`),不是这一腿
|
|
238
|
+
* 的运行期异常,**不要在本函数里加吞异常的臂**:吞掉就退回「memory 静默整体关闭」,即上面那条报错原话
|
|
239
|
+
* 拒绝的那件事。要放宽/收紧值域,改上面两个属主。
|
|
224
240
|
*/
|
|
225
241
|
export declare function memoryFaceMounted(memory: TaskSpec["memory"]): boolean;
|
|
226
242
|
//# sourceMappingURL=memory-scope.d.ts.map
|
|
@@ -754,7 +754,9 @@ export declare class SqlCheckpointStore implements CheckpointStore {
|
|
|
754
754
|
*/
|
|
755
755
|
peekScopeByToken(token: CheckpointToken): Promise<string | null | undefined>;
|
|
756
756
|
peekPendingScope(sessionId: string, binding?: PendingRowBinding): Promise<string | null | undefined>;
|
|
757
|
-
/** Upsert the resume rebuild inputs for a session
|
|
757
|
+
/** Upsert the resume rebuild inputs for a session — written at SUBMIT (durable mode on) and, since S-433 F2,
|
|
758
|
+
* once per resume leg at its CAS win point (`DriveResumeArgs.resumeCtx`). Unconditional per `session_id`:
|
|
759
|
+
* the ordering fence lives at the CALLER's CAS win, not in a version column here. */
|
|
758
760
|
putCtx(sessionId: string, ctx: unknown): Promise<void>;
|
|
759
761
|
getCtx<T = unknown>(sessionId: string): Promise<T | null>;
|
|
760
762
|
/**
|
|
@@ -763,7 +765,11 @@ export declare class SqlCheckpointStore implements CheckpointStore {
|
|
|
763
765
|
*
|
|
764
766
|
* The extra `task_active` guard closes a multi-gate window: between a resume's CAS (pending→resolved) and
|
|
765
767
|
* the task hitting its NEXT gate (writing a new pending checkpoint), the session has no pending checkpoint —
|
|
766
|
-
* yet `putCtx`
|
|
768
|
+
* yet `putCtx` stamps `updated_at_ms` at submit and — since S-433 F2 (server 7.86.0) — once more on EVERY
|
|
769
|
+
* resume leg, at the moment that leg wins the resume CAS (`DriveResumeArgs.resumeCtx`; before that fix only
|
|
770
|
+
* the plan approval's mode write-back re-stamped it, and it did so AFTER acceptance). Both extra writes only
|
|
771
|
+
* ever REFRESH the stamp, so they lengthen a ctx's life and touch none of the three guards — the direction is
|
|
772
|
+
* "keep it longer", never "reap it sooner". So the ctx could be older than `cutoff` and was being
|
|
767
773
|
* reaped, making the next resume 409 ("resume context missing"). `task_active` holds the session lock for
|
|
768
774
|
* the WHOLE submit→last-resume-complete span (running OR suspended), so excluding sessions with an active
|
|
769
775
|
* claim keeps the ctx exactly as long as a run still needs it, while still GC'ing it once the run is terminal.
|
|
@@ -240,6 +240,29 @@ export declare function isUnsuccessfulRunStatus(status: RunRecord["status"]): bo
|
|
|
240
240
|
* 动作(cancel 的恢复把手会 CAS-expire 一张 checkpoint);对一个本进程读不懂的状态,少动一次远好过错杀。
|
|
241
241
|
*/
|
|
242
242
|
export declare function isParkedRunStatus(status: RunRecord["status"]): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* 「**证得出**这条 run 活在别的副本上」——「他副本」那一句话的**唯一判据**(S-381 收编;S-430 迁居)。
|
|
245
|
+
*
|
|
246
|
+
* 成案:这个合取式此前在四处**逐字手抄**(`http/routes/runs.ts` 的 steer 臂与 interrupt 臂、同文件
|
|
247
|
+
* `sendHandleMiss`、`http/routes/run-memory-capture.ts`),`POST /v1/sessions/:id/mcp/reconnect` 是第五处、`fleet/fleet-reconciler.ts` 是第六处 ——
|
|
248
|
+
* 而它的每一个合取项都是一条**被复审逐条挣出来的**纪律,不是随手的判空:
|
|
249
|
+
* · {@link RunRecord.instanceId} 缺席(`null` / 建列之前的行 / 替身行的 `undefined`)= **不知道它在哪**,
|
|
250
|
+
* 不是「在别处」;
|
|
251
|
+
* · `thisInstanceId` 缺席 = 本副本连自己是谁都说不上,更不能断言别人;
|
|
252
|
+
* · 两者都在场且**不等**才够格说「他副本」(`markResuming` 认领后该列语义 = **当前持有副本**)。
|
|
253
|
+
* 抄一份 = 多一个写者;收成一只谓词之后,纪律改一次就处处同改。**`status` 不进本谓词**:
|
|
254
|
+
* `sendHandleMiss` 只对 `running` 行说这句话,而 steer 族在 park/终态两臂之后才走到这里 —— 各调用点
|
|
255
|
+
* 的前置臂不同族,合进来会让某一处悄悄改判。
|
|
256
|
+
*
|
|
257
|
+
* 🔴 **为什么住在这里而不是 `http/route-ctx.ts`**(S-430):`src/fleet/fleet-reconciler.ts` 是第六个手抄点,
|
|
258
|
+
* 而 `src/fleet/` 今天对 `src/http/` **零依赖**(边向恒是 http → fleet)。把消费方拉去 http 取一只纯谓词
|
|
259
|
+
* 会凭空造一条反向装载边;而本文件正是 {@link RunRecord} 与它的兄弟谓词({@link isTerminalRunStatus} /
|
|
260
|
+
* {@link isParkedRunStatus})的所在 —— 「关于 run 行的判词」本来就该在行的属主这里,http 与 fleet 都已经
|
|
261
|
+
* 依赖它。**无别名、无再导出**:旧拼法删干净,消费方的 tsc 红就是通知。
|
|
262
|
+
*/
|
|
263
|
+
export declare function runProvenElsewhere(run: {
|
|
264
|
+
instanceId?: string | null;
|
|
265
|
+
}, thisInstanceId: string | undefined): boolean;
|
|
243
266
|
/** One row of the GET /v1/sessions list (CC /resume picker): a DISTINCT session (sessionId) aggregated from its
|
|
244
267
|
* task_run rows — newest-first by last activity. The preview/status are the LATEST run's ("continue where I left
|
|
245
268
|
* off"); the counts/timestamps span the whole session. Owner included so a fleet-wide (ops) listing is attributable. */
|
|
@@ -23,6 +23,9 @@ export function isUnsuccessfulRunStatus(status) {
|
|
|
23
23
|
export function isParkedRunStatus(status) {
|
|
24
24
|
return runStatusClassOf(status) === "parked";
|
|
25
25
|
}
|
|
26
|
+
export function runProvenElsewhere(run, thisInstanceId) {
|
|
27
|
+
return typeof run.instanceId === "string" && run.instanceId.length > 0 && thisInstanceId !== undefined && run.instanceId !== thisInstanceId;
|
|
28
|
+
}
|
|
26
29
|
export function mapBakeRow(r) {
|
|
27
30
|
return {
|
|
28
31
|
bakeId: r.bake_id,
|
package/dist/principal-jwt.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
|
|
2
|
+
import { type ApprovalEnvelope } from "./approval-hmac.js";
|
|
2
3
|
/**
|
|
3
4
|
* 验签允许的**时钟不一致容差**(秒)—— 本仓这条轴上的单一来源。
|
|
4
5
|
*
|
|
@@ -47,6 +48,14 @@ export declare function verifyPrincipalJwt(token: string, keys: PrincipalJwtKey[
|
|
|
47
48
|
/** 良构 Unicode(无孤立代理项)。`u` 旗下配对代理项是一个码点、不匹配 `\p{Surrogate}`;只有孤立的才命中。 */
|
|
48
49
|
export declare function isWellFormedUnicode(s: string): boolean;
|
|
49
50
|
export declare function editedPlanBinding(editedPlan: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* JWT `cnf.bnd` = **「这份身份凭据被签发去决哪一张卡」**(动作身份),故只哈希前三位。
|
|
53
|
+
*
|
|
54
|
+
* 🔴 **与 HMAC 信封的分工**(S-433 F1 立此,属主注在 `approval-hmac.ts` 顶):HMAC 绑的是**「决了什么」**
|
|
55
|
+
* —— `decision` / `reason` / `permissionModeAfter` 这些**会改变后果**的字段全在那一把里。所以「同一张卡上
|
|
56
|
+
* 把 approve 的档偷偷调宽」只由 HMAC 拦;本函数**刻意不跟着加位**:决定内容改了 `cnf.bnd` 不该变(否则
|
|
57
|
+
* 一份合法 JWT 会被绑死在一个它签发时还不知道的值上,而 `permissionModeAfter` 恰恰是决裁那一刻才定的)。
|
|
58
|
+
*/
|
|
50
59
|
export declare function approvalBnd(env: {
|
|
51
60
|
sessionId: string;
|
|
52
61
|
boundCallId?: string | null;
|
|
@@ -109,13 +118,12 @@ export declare function verifyDirectDoorProof(proof: {
|
|
|
109
118
|
jwt?: string;
|
|
110
119
|
mac?: string;
|
|
111
120
|
kid?: string;
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}, config: {
|
|
121
|
+
},
|
|
122
|
+
/** 🔴 **命名类型,不是结构等价的内联字面**(S-433 F1 [high] 的**根因**,验真后修):本位此前是一份手抄的
|
|
123
|
+
* 五字段内联形 —— 结构类型让**多带一个字段的调用方**照样编译通过,而下面那次手抄再把它**丢掉**,于是
|
|
124
|
+
* `approval-hmac.ts` 上新增的签名位在这条唯一的真实验签链上**静默失效**,tsc 一个字都不响。
|
|
125
|
+
* 用 {@link ApprovalEnvelope} 之后,信封的形只有**一个**声明点,新增一位自动到达验签器。 */
|
|
126
|
+
envelope: ApprovalEnvelope, config: {
|
|
119
127
|
principalJwtPubkeys: PrincipalJwtKey[];
|
|
120
128
|
principalJwtIss?: string;
|
|
121
129
|
principalJwtAud?: string;
|
package/dist/principal-jwt.js
CHANGED
|
@@ -101,7 +101,7 @@ export function verifyDirectDoorProof(proof, envelope, config, opts) {
|
|
|
101
101
|
return { ok: false, status: 401, errorCode: "principal_unbound", error: "principal token is not bound to this decision (cnf.bnd mismatch)" };
|
|
102
102
|
if (!proof.mac)
|
|
103
103
|
return { ok: false, status: 401, errorCode: "approval_mac_required", error: "direct door requires an approval MAC (X-Approval-Mac)" };
|
|
104
|
-
const ok = verifyApprovalHmac(
|
|
104
|
+
const ok = verifyApprovalHmac(envelope, proof.mac, proof.kid, config.approvalHmacKeys);
|
|
105
105
|
if (!ok)
|
|
106
106
|
return { ok: false, status: 401, errorCode: "approval_mac_invalid", error: "approval envelope MAC invalid" };
|
|
107
107
|
return actionBinding
|
|
@@ -23,8 +23,12 @@
|
|
|
23
23
|
*
|
|
24
24
|
* ## 定界
|
|
25
25
|
* 本模块**只**管 `face` 一档。内建读拒表(`readDenyPatterns` / `readDenyBuiltinTiers` /
|
|
26
|
-
* `readDenyBuiltinExclude`)一个字节不动 ——
|
|
27
|
-
*
|
|
26
|
+
* `readDenyBuiltinExclude`)一个字节不动 —— 放开容纳面不等于放开那几档 deny,两者在 core 里是**并列
|
|
27
|
+
* 机制**(有活行的 deny 表在 open 面上照样拦)。
|
|
28
|
+
* ⚠️ **但别把这句话读成「凭证/shell 历史/钱包那几档一直在岗」**:core 7.22.0([ref])起内建表的**缺省
|
|
29
|
+
* 选择是空** —— 一台没写 `READ_DENY_BUILTIN_TIERS` 的部署上,那张表**一行都不生效**(要老姿态就显式写
|
|
30
|
+
* `credentials,browser,wallet,agent-config`;运维面全文见 `docs/DEPLOY-PREREQS.md` 的 core 7.22.0 段)。
|
|
31
|
+
* 本模块的定界不变,变的是「并列的那一侧默认开着」这个**前提**。core 的 `config.read_face_deployment_clamped`
|
|
28
32
|
* (只读挂载钳制)同样不受本件影响。
|
|
29
33
|
*/
|
|
30
34
|
import { type PostureSource } from "./posture-source.js";
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -70,6 +70,41 @@ export declare function coercePermissionMode(raw: unknown): SettingsPermissionMo
|
|
|
70
70
|
/** 五模式闭集的**严格**读法([ref] ④:`/v1/capabilities?permissionMode=` 查询串用它 —— 读面对词表外的值 400,
|
|
71
71
|
* 不走上面那条给 resume 重放留的 `default` 宽臂)。上面的宽读只是本函数外再包一层折叠,词表单点在此。 */
|
|
72
72
|
export declare function coercePermissionModeStrict(raw: unknown): SettingsPermissionMode | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* S-433 —— 一份**被批准的计划**之后,这条会话继续用的 permission mode(`plan_review` 决裁体的
|
|
75
|
+
* `permissionModeAfter`)。core 的契约句是 `present-plan-tool.js`:「If the plan is approved, the run
|
|
76
|
+
* continues (and any read-only restriction is lifted)」—— 而 core 顶层 resume 把宿主的续跑配置**整体**
|
|
77
|
+
* 展开成新一段 spec、不继承停靠点的 `handsReadOnly` / `enablePlanMode`(`runtask.ts`),所以「松开」这
|
|
78
|
+
* 件事的属主在**本仓装配续跑配置的那一处**。批准者要说的正是「松到哪一档」。
|
|
79
|
+
*
|
|
80
|
+
* 🔴 **闭集为什么只有这两个词**:它是 CC `ExitPlanMode` 三选卡上那两张 Yes 卡(「Yes, and auto-accept
|
|
81
|
+
* edits」/「Yes, and manually approve edits」)的对位;`plan` 自己(= 不松开)由 `edit`/`reject` 两个
|
|
82
|
+
* 决裁表达,`bypassPermissions` / `auto` 不在这张卡上 —— 一个批准动作不该顺带把部署的 ask 面整个关掉。
|
|
83
|
+
*
|
|
84
|
+
* 🔴 **不手抄词表**:`satisfies readonly SettingsPermissionMode[]` 让「本表的成员必须仍是本仓受理的
|
|
85
|
+
* 模式词」成为**编译期**事实 —— 五词表某天改名/删词,这里就是红的,而不是静默留一个没人认的词。
|
|
86
|
+
*/
|
|
87
|
+
export declare const PLAN_APPROVE_PERMISSION_MODES_AFTER: readonly ["default", "acceptEdits"];
|
|
88
|
+
/** {@link PLAN_APPROVE_PERMISSION_MODES_AFTER} 的类型形(值表单点,类型从值派生 —— 两处各写一份就会漂)。 */
|
|
89
|
+
export type PlanApprovePermissionModeAfter = (typeof PLAN_APPROVE_PERMISSION_MODES_AFTER)[number];
|
|
90
|
+
/** S-433 值闭集判据(wire 门用)。**宽臂一个都没有**:词表外的值 400 响亮拒 —— 这一位选的是批准之后
|
|
91
|
+
* 模型有没有手,把一个拼错的词折成任何一档都是安全轴上的静默降级([ref]「无静默 fail-open」)。 */
|
|
92
|
+
export declare function isPlanApprovePermissionModeAfter(raw: unknown): raw is PlanApprovePermissionModeAfter;
|
|
93
|
+
/**
|
|
94
|
+
* S-433 —— **一条已持久化的提交体的生效 permission mode**,单点。
|
|
95
|
+
*
|
|
96
|
+
* 它只是把 {@link effectivePermissionMode} 与「拿哪一份 settings 去问」这**一对**绑在一起:后者是
|
|
97
|
+
* `parseTaskSettings(body.settings)`(resume 重放共用的宽容解析层)。两处各写一遍这一对,就会在
|
|
98
|
+
* 「某天改成用别的 settings 去算生效模式」的那一拍静默分家 —— 而分家在这条轴上的形态是「wire 门说
|
|
99
|
+
* 这条任务不是只读起步、装配腿却按只读起步重建」,两个 4xx/行为各说各话,tsc 一声不响。
|
|
100
|
+
*
|
|
101
|
+
* ⚠️ 射程:**只管持久化体这一路**。首跑那一路的生效模式在 `boot/resolve-spec.ts` 单点算(它吃的是
|
|
102
|
+
* 场景折叠后的 settings),本函数刻意不去冒充那一处的属主。
|
|
103
|
+
*/
|
|
104
|
+
export declare function storedBodyPermissionMode(body: {
|
|
105
|
+
permissionMode?: unknown;
|
|
106
|
+
settings?: unknown;
|
|
107
|
+
}): SettingsPermissionMode | undefined;
|
|
73
108
|
/** L2 ultracode ([ref]): the effective `thinking` level given the explicit `reasoningEffort` and the ultracode
|
|
74
109
|
* preset. ultracode FLOORS at the "ultra" tier — core's single source `resolveReasoningProfile("ultra").thinking`
|
|
75
110
|
* (= "max"), never a hardcoded literal — so an explicit higher tier still wins (none exists above max today;
|
package/dist/task-settings.js
CHANGED
|
@@ -16,6 +16,13 @@ export function coercePermissionModeStrict(raw) {
|
|
|
16
16
|
return raw;
|
|
17
17
|
return undefined;
|
|
18
18
|
}
|
|
19
|
+
export const PLAN_APPROVE_PERMISSION_MODES_AFTER = ["default", "acceptEdits"];
|
|
20
|
+
export function isPlanApprovePermissionModeAfter(raw) {
|
|
21
|
+
return PLAN_APPROVE_PERMISSION_MODES_AFTER.includes(raw);
|
|
22
|
+
}
|
|
23
|
+
export function storedBodyPermissionMode(body) {
|
|
24
|
+
return effectivePermissionMode(body, parseTaskSettings(body.settings).settings);
|
|
25
|
+
}
|
|
19
26
|
export function effectiveThinking(reasoningEffort, ultracode) {
|
|
20
27
|
const explicit = isThinkingLevel(reasoningEffort) ? reasoningEffort : undefined;
|
|
21
28
|
if (!ultracode)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
|
|
18
18
|
* TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
|
|
19
19
|
*/
|
|
20
|
-
import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun } from "@sema-agent/core";
|
|
20
|
+
import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus } from "@sema-agent/core";
|
|
21
21
|
import type { ToolApprovalFrame } from "../tool-approval.js";
|
|
22
22
|
/** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。
|
|
23
23
|
* 🔴 **导出**([ref] U1/U2):本仓「词表闭集,未处置成员=编译红」的钉法只许有**一个**形 ——
|
|
@@ -60,7 +60,7 @@ type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peer
|
|
|
60
60
|
type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
|
|
61
61
|
type _GuardMailboxAppendReverse = AssertAllKeysHandled<Exclude<MailboxAppendProjected, keyof MailboxAppendMessage>>;
|
|
62
62
|
type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection";
|
|
63
|
-
type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation";
|
|
63
|
+
type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny";
|
|
64
64
|
type _GuardWiringSections = AssertAllKeysHandled<Exclude<keyof WiringManifest, WiringSectionProjected | WiringSectionExcluded>>;
|
|
65
65
|
type _GuardWiringSectionsReverse = AssertAllKeysHandled<Exclude<WiringSectionProjected | WiringSectionExcluded, keyof WiringManifest>>;
|
|
66
66
|
type WiringSection<K extends keyof WiringManifest> = NonNullable<WiringManifest[K]>;
|
|
@@ -103,6 +103,9 @@ type _GuardWiringRulesReverse = AssertAllKeysHandled<Exclude<WiringRulesHandled,
|
|
|
103
103
|
type WiringWriteProtectionHandled = "targetView";
|
|
104
104
|
type _GuardWiringWriteProtection = AssertAllKeysHandled<Exclude<keyof WiringSection<"writeProtection">, WiringWriteProtectionHandled>>;
|
|
105
105
|
type _GuardWiringWriteProtectionReverse = AssertAllKeysHandled<Exclude<WiringWriteProtectionHandled, keyof WiringSection<"writeProtection">>>;
|
|
106
|
+
type WiringReadDenyHandled = "builtinTiers";
|
|
107
|
+
type _GuardWiringReadDeny = AssertAllKeysHandled<Exclude<keyof WiringSection<"readDeny">, WiringReadDenyHandled>>;
|
|
108
|
+
type _GuardWiringReadDenyReverse = AssertAllKeysHandled<Exclude<WiringReadDenyHandled, keyof WiringSection<"readDeny">>>;
|
|
106
109
|
type WiringAutoConsolidationHandled = "onRecommendation";
|
|
107
110
|
type _GuardWiringAutoConsolidation = AssertAllKeysHandled<Exclude<keyof WiringSection<"autoConsolidation">, WiringAutoConsolidationHandled>>;
|
|
108
111
|
type _GuardWiringAutoConsolidationReverse = AssertAllKeysHandled<Exclude<WiringAutoConsolidationHandled, keyof WiringSection<"autoConsolidation">>>;
|
|
@@ -164,6 +167,32 @@ type ParkRowWithheld = "token";
|
|
|
164
167
|
type _GuardParkRow = AssertAllKeysHandled<Exclude<ParkRowKeys, ParkRowProjected | ParkRowWithheld>>;
|
|
165
168
|
type _NoDoubleBookParkRow = AssertAllKeysHandled<Extract<ParkRowProjected, ParkRowWithheld>>;
|
|
166
169
|
type _GuardParkRowReverse = AssertAllKeysHandled<Exclude<ParkRowProjected | ParkRowWithheld, ParkRowKeys>>;
|
|
170
|
+
type ReadRootCandidateProjected = "dir" | "clearsThisAsk" | "covers";
|
|
171
|
+
type _GuardReadRootCandidate = AssertAllKeysHandled<Exclude<keyof ReadRootGrantCandidate, ReadRootCandidateProjected>>;
|
|
172
|
+
type _GuardReadRootCandidateReverse = AssertAllKeysHandled<Exclude<ReadRootCandidateProjected, keyof ReadRootGrantCandidate>>;
|
|
173
|
+
type AskRiskAxes = NonNullable<AskRequest["riskAxes"]>;
|
|
174
|
+
type RiskAxesProjected = "irreversible" | "egress";
|
|
175
|
+
type _GuardRiskAxes = AssertAllKeysHandled<Exclude<keyof AskRiskAxes, RiskAxesProjected>>;
|
|
176
|
+
type _GuardRiskAxesReverse = AssertAllKeysHandled<Exclude<RiskAxesProjected, keyof AskRiskAxes>>;
|
|
177
|
+
type McpReconnectProjected = "server" | "prefix" | "toolCount" | "added" | "removed" | "outcome" | "tools" | "reason" | "status" | "listingIncomplete";
|
|
178
|
+
type McpReconnectWithheld = "axes" | "dropped" | "warnings";
|
|
179
|
+
type _GuardMcpReconnect = AssertAllKeysHandled<Exclude<keyof McpReconnectResult, McpReconnectProjected | McpReconnectWithheld>>;
|
|
180
|
+
type _NoDoubleBookMcpReconnect = AssertAllKeysHandled<Extract<McpReconnectProjected, McpReconnectWithheld>>;
|
|
181
|
+
type _GuardMcpReconnectReverse = AssertAllKeysHandled<Exclude<McpReconnectProjected | McpReconnectWithheld, keyof McpReconnectResult>>;
|
|
182
|
+
type McpRedialStatusProjected = "name" | "status" | "errorCode" | "httpStatus" | "delivered" | "serverInfo" | "error" | "transportClosed";
|
|
183
|
+
type McpRedialStatusWithheld = "toolNames";
|
|
184
|
+
type _GuardMcpRedialStatus = AssertAllKeysHandled<Exclude<keyof McpServerStatus, McpRedialStatusProjected | McpRedialStatusWithheld>>;
|
|
185
|
+
type _NoDoubleBookMcpRedialStatus = AssertAllKeysHandled<Extract<McpRedialStatusProjected, McpRedialStatusWithheld>>;
|
|
186
|
+
type _GuardMcpRedialStatusReverse = AssertAllKeysHandled<Exclude<McpRedialStatusProjected | McpRedialStatusWithheld, keyof McpServerStatus>>;
|
|
187
|
+
type McpPanelStatusProjected = "name" | "status" | "serverInfo" | "toolNames" | "error";
|
|
188
|
+
type McpPanelStatusWithheld = "errorCode" | "httpStatus" | "delivered" | "transportClosed";
|
|
189
|
+
type _GuardMcpPanelStatus = AssertAllKeysHandled<Exclude<keyof McpServerStatus, McpPanelStatusProjected | McpPanelStatusWithheld>>;
|
|
190
|
+
type _NoDoubleBookMcpPanelStatus = AssertAllKeysHandled<Extract<McpPanelStatusProjected, McpPanelStatusWithheld>>;
|
|
191
|
+
type _GuardMcpPanelStatusReverse = AssertAllKeysHandled<Exclude<McpPanelStatusProjected | McpPanelStatusWithheld, keyof McpServerStatus>>;
|
|
192
|
+
type McpListingIncompleteKeys = keyof NonNullable<McpReconnectResult["listingIncomplete"]>;
|
|
193
|
+
type McpListingIncompleteProjected = "reason" | "pages" | "budgetMs" | "error";
|
|
194
|
+
type _GuardMcpListingIncomplete = AssertAllKeysHandled<Exclude<McpListingIncompleteKeys, McpListingIncompleteProjected>>;
|
|
195
|
+
type _GuardMcpListingIncompleteReverse = AssertAllKeysHandled<Exclude<McpListingIncompleteProjected, McpListingIncompleteKeys>>;
|
|
167
196
|
export type CoreKeysetGuards = [
|
|
168
197
|
_GuardNotification,
|
|
169
198
|
_GuardNotificationReverse,
|
|
@@ -188,6 +217,8 @@ export type CoreKeysetGuards = [
|
|
|
188
217
|
_GuardWiringSections,
|
|
189
218
|
_GuardWiringWriteProtection,
|
|
190
219
|
_GuardWiringWriteProtectionReverse,
|
|
220
|
+
_GuardWiringReadDeny,
|
|
221
|
+
_GuardWiringReadDenyReverse,
|
|
191
222
|
_GuardWiringAutoConsolidation,
|
|
192
223
|
_GuardWiringAutoConsolidationReverse,
|
|
193
224
|
_GuardWiringSectionsReverse,
|
|
@@ -242,7 +273,22 @@ export type CoreKeysetGuards = [
|
|
|
242
273
|
_GuardWorkflowRunReverse,
|
|
243
274
|
_GuardParkRow,
|
|
244
275
|
_NoDoubleBookParkRow,
|
|
245
|
-
_GuardParkRowReverse
|
|
276
|
+
_GuardParkRowReverse,
|
|
277
|
+
_GuardReadRootCandidate,
|
|
278
|
+
_GuardReadRootCandidateReverse,
|
|
279
|
+
_GuardRiskAxes,
|
|
280
|
+
_GuardRiskAxesReverse,
|
|
281
|
+
_GuardMcpReconnect,
|
|
282
|
+
_NoDoubleBookMcpReconnect,
|
|
283
|
+
_GuardMcpReconnectReverse,
|
|
284
|
+
_GuardMcpRedialStatus,
|
|
285
|
+
_NoDoubleBookMcpRedialStatus,
|
|
286
|
+
_GuardMcpRedialStatusReverse,
|
|
287
|
+
_GuardMcpPanelStatus,
|
|
288
|
+
_NoDoubleBookMcpPanelStatus,
|
|
289
|
+
_GuardMcpPanelStatusReverse,
|
|
290
|
+
_GuardMcpListingIncomplete,
|
|
291
|
+
_GuardMcpListingIncompleteReverse
|
|
246
292
|
];
|
|
247
293
|
export {};
|
|
248
294
|
//# sourceMappingURL=core-keyset-guard.d.ts.map
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -613,6 +613,18 @@ export declare function workspaceChangedEventData(ev: {
|
|
|
613
613
|
* 并且它对调用方**本就不可观测**(整理跑在终端 harvest 之后、fire-and-forget、宿主侧),剥掉它不收走
|
|
614
614
|
* 任何能力 —— 这正是 `writeProtection` 判据①**不成立**的那一格。
|
|
615
615
|
* ⚠️ **缺席 = 未武装**(core present-iff),不折 `false`、不读「未知」。
|
|
616
|
+
* 🔴 **S-403(7.85.0)起本条要连着 `core-keyset-guard.ts` ⑥b 判据②的补句读**:同一个事实在会话面
|
|
617
|
+
* (`GET /v1/sessions/:id/memory-status` 的 `autoConsolidationArmed`)是可读的,而这一段仍然剥 ——
|
|
618
|
+
* 「一个事实一张脸」,两处同源、唯一读点。裁定正文在那一处,这里不抄第二份。
|
|
619
|
+
* · `readDeny`(`{ builtinTiers: readonly string[] }`;core 7.22.0 [ref],S-417 提货件)—— **裁:operator
|
|
620
|
+
* 受众,租户面剥**,所以它**不在本构造器里**,在 {@link wiringManifestOperatorEventData}。四条判据逐字在
|
|
621
|
+
* `core-keyset-guard.ts` ⑥b 的 `WiringSectionExcluded` 那一行(与 `governance` / `configFingerprint` /
|
|
622
|
+
* `autoConsolidation` 同表同处,免得同一条裁定有两个写者)。一句话:它答的是**这台部署选了哪几档内置
|
|
623
|
+
* 敏感路径 deny 表**——一条部署治理面的**选择**,与 `writeProtection` 那种「这条腿手里有什么设施」的
|
|
624
|
+
* 自述不同族;而且它是本仓 env `READ_DENY_BUILTIN_TIERS` 的生效读出口,名表面归 operator(S-138)。
|
|
625
|
+
* ⚠️ **缺席 = 没有内置行判这条腿**(core present-iff:实质激活至少一行才铸),**不是**「没有读被 deny
|
|
626
|
+
* 判」—— 部署自己的 `readDenyPatterns` 刻意不上报(模式文本是部署材料,不是枚举)。**空数组永不发出**
|
|
627
|
+
* (「关」与「开到什么都不判」是同一个事实)⇒ 收到空数组按**坏 producer** 处置并留痕,见那里。
|
|
616
628
|
* 未来 core 加一段而它没有 audience 标签时,**先在这里补一条裁定再决定挑不挑键** —— 段级完备性钉
|
|
617
629
|
* (test/wiring-manifest-projection.test.ts)会在那一刻先把人拦下来 —— ⚠️ **但那条钉是夹具驱动的**:它只看
|
|
618
630
|
* `deriveWiringManifest(EFFECTIVE_FACTS)` 真铸出来的段,facts 不带的可选段(147③ 的 `autoMode` 就是)它一格
|
package/dist/trace/project.js
CHANGED
|
@@ -554,6 +554,19 @@ export function wiringManifestOperatorEventData(ev) {
|
|
|
554
554
|
...(bool(g.memoryAdmission) !== undefined ? { memoryAdmission: g.memoryAdmission } : {}),
|
|
555
555
|
...(bool(g.retention) !== undefined ? { retention: g.retention } : {}),
|
|
556
556
|
};
|
|
557
|
+
const readDeny = (() => {
|
|
558
|
+
if (m.readDeny === undefined)
|
|
559
|
+
return undefined;
|
|
560
|
+
const seg = sec(m.readDeny);
|
|
561
|
+
if (seg === undefined)
|
|
562
|
+
return dropProjectedFace("wiring_manifest.readDeny is not an object");
|
|
563
|
+
const tiers = seg.builtinTiers;
|
|
564
|
+
if (!Array.isArray(tiers) || tiers.length === 0)
|
|
565
|
+
return dropProjectedFace("wiring_manifest.readDeny.builtinTiers is not a non-empty array (core never emits an empty one)");
|
|
566
|
+
if (!tiers.every((t) => str(t) !== undefined))
|
|
567
|
+
return dropProjectedFace("wiring_manifest.readDeny.builtinTiers has a non-string / empty member");
|
|
568
|
+
return { builtinTiers: [...tiers] };
|
|
569
|
+
})();
|
|
557
570
|
const autoConsolidation = (() => {
|
|
558
571
|
if (m.autoConsolidation === undefined)
|
|
559
572
|
return undefined;
|
|
@@ -567,6 +580,7 @@ export function wiringManifestOperatorEventData(ev) {
|
|
|
567
580
|
return {
|
|
568
581
|
...tenant,
|
|
569
582
|
...(Object.keys(governance).length > 0 ? { governance } : {}),
|
|
583
|
+
...(readDeny !== undefined ? { readDeny } : {}),
|
|
570
584
|
...(autoConsolidation !== undefined ? { autoConsolidation } : {}),
|
|
571
585
|
...(str(m.configFingerprint) !== undefined ? { configFingerprint: m.configFingerprint } : {}),
|
|
572
586
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.86.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sema-agent/core": "7.
|
|
57
|
+
"@sema-agent/core": "7.22.0",
|
|
58
58
|
"@sema-agent/settings-schema": "2.1.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|