@sema-agent/server 7.138.0 → 7.139.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +276 -7
- package/README.md +7 -4
- package/README.zh-CN.md +6 -4
- package/USAGE.md +73 -29
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +20 -20
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +16 -24
- package/dist/boot/execution-lane.d.ts +1 -1
- package/dist/boot/governance-seams.d.ts +3 -2
- package/dist/boot/governance-seams.js +9 -11
- package/dist/boot/memory-consolidation.js +2 -2
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +8 -9
- package/dist/boot/retention-lane.d.ts +5 -5
- package/dist/boot/retention-lane.js +0 -6
- package/dist/boot/runner-deps.d.ts +3 -1
- package/dist/boot/runner-deps.js +2 -1
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.js +0 -1
- package/dist/boot/stage-09-leader.js +1 -2
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/boot/stores.js +4 -4
- package/dist/boot/web-admin.js +2 -2
- package/dist/capabilities/mcp-probe.js +1 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +15 -7
- package/dist/capability-facts.js +8 -3
- package/dist/config-catalog.js +7 -7
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +19 -24
- package/dist/config-provider.js +27 -12
- package/dist/config-types.d.ts +10 -5
- package/dist/config.d.ts +33 -0
- package/dist/config.js +44 -15
- package/dist/delegated-session-owner.d.ts +2 -2
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +19 -0
- package/dist/execution-lane-caps.js +10 -0
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/durable-run-leg.js +9 -5
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/resume-legs.js +50 -20
- package/dist/http/route-ctx.d.ts +14 -8
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +115 -11
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +4 -5
- package/dist/http/routes/tasks.js +37 -31
- package/dist/http/server.d.ts +35 -19
- package/dist/http/server.js +11 -0
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +8 -12
- package/dist/index.d.ts +1 -0
- package/dist/memory-sync-client.d.ts +53 -14
- package/dist/memory-sync-client.js +196 -116
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.js +0 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +34 -9
- package/dist/parked-decide.js +12 -19
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +13 -7
- package/dist/peer-seat.js +37 -3
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +30 -12
- package/dist/plugins/caching-session-store.d.ts +6 -2
- package/dist/plugins/caching-session-store.js +9 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
- package/dist/plugins/checkpoint-store-sql.js +5 -7
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +49 -103
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +7 -6
- package/dist/plugins/file-history-store-sql.js +5 -5
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/local-session-store.d.ts +149 -50
- package/dist/plugins/local-session-store.js +382 -107
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/one-time-migrations.d.ts +111 -3
- package/dist/plugins/one-time-migrations.js +380 -10
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
- package/dist/plugins/permission-rule-store-sql.js +13 -16
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +31 -20
- package/dist/plugins/pg-session-storage.js +120 -52
- package/dist/plugins/remote-env-adb.d.ts +20 -4
- package/dist/plugins/remote-env-adb.js +70 -62
- package/dist/plugins/remote-env-device.d.ts +30 -12
- package/dist/plugins/remote-env-device.js +18 -17
- package/dist/plugins/remote-env-e2b.d.ts +18 -4
- package/dist/plugins/remote-env-e2b.js +41 -42
- package/dist/plugins/remote-env-file-error.d.ts +2 -0
- package/dist/plugins/remote-env-file-error.js +1 -1
- package/dist/plugins/remote-env-host.d.ts +21 -8
- package/dist/plugins/remote-env-host.js +66 -70
- package/dist/plugins/remote-env-k8s.d.ts +11 -16
- package/dist/plugins/remote-env-k8s.js +61 -54
- package/dist/plugins/remote-env-local-docker.d.ts +29 -6
- package/dist/plugins/remote-env-local-docker.js +46 -59
- package/dist/plugins/remote-env-ssh.d.ts +16 -4
- package/dist/plugins/remote-env-ssh.js +72 -56
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/retention-store-sql.d.ts +9 -6
- package/dist/plugins/retention-store-sql.js +7 -1
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +5 -0
- package/dist/plugins/schema-consistency.d.ts +15 -5
- package/dist/plugins/schema-consistency.js +14 -8
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/session-fork.d.ts +54 -0
- package/dist/plugins/session-fork.js +38 -1
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-backend.js +19 -7
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +9 -3
- package/dist/plugins/tidb-session-store.d.ts +57 -26
- package/dist/plugins/tidb-session-store.js +118 -50
- package/dist/plugins/tool-result-store-sql.d.ts +3 -2
- package/dist/plugins/tool-result-store-sql.js +18 -10
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +24 -20
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +3 -1
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +9 -5
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +19 -9
- package/dist/session-incarnation.js +4 -2
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +3 -3
- package/dist/tool-approval.d.ts +6 -0
- package/dist/tool-approval.js +8 -1
- package/dist/trace/project.d.ts +1 -0
- package/dist/trace/project.js +1 -0
- package/dist/trace/self-minted-wire.d.ts +111 -2
- package/dist/trace/self-minted-wire.js +38 -1
- package/dist/url-refusal.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/skills-mcp.js +3 -2
- package/dist/web-admin-client/types.d.ts +6 -0
- package/package.json +3 -3
- package/wire-changes/server@7.139.0.json +348 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
package/dist/bench/s1/arms.js
CHANGED
|
@@ -24,7 +24,7 @@ export function bindingFromCheckpoint(cp) {
|
|
|
24
24
|
}
|
|
25
25
|
const ORACLE_UNDELIVERED = { delivered: false, hiddenTestsGreen: false, buildPassed: false, invariantsOk: false };
|
|
26
26
|
function emptyCostBreakdown() {
|
|
27
|
-
return { llmRootMicroUsd: 0, nestedSubagentMicroUsd: 0,
|
|
27
|
+
return { llmRootMicroUsd: 0, nestedSubagentMicroUsd: 0, compactionMicroUsd: 0 };
|
|
28
28
|
}
|
|
29
29
|
export function classifyRunStatus(input) {
|
|
30
30
|
if (input.status === "failed")
|
|
@@ -40,7 +40,6 @@ export function captureStats(stats) {
|
|
|
40
40
|
costBreakdown: {
|
|
41
41
|
llmRootMicroUsd: bd.llmRootMicroUsd,
|
|
42
42
|
nestedSubagentMicroUsd: bd.nestedSubagentMicroUsd,
|
|
43
|
-
memoryConsolidationMicroUsd: bd.memoryConsolidationMicroUsd,
|
|
44
43
|
compactionMicroUsd: bd.compactionMicroUsd,
|
|
45
44
|
},
|
|
46
45
|
humanReview: { count: hr.count, totalWaitMs: hr.totalWaitMs, gates: hr.gates },
|
|
@@ -55,7 +54,6 @@ export async function driveSupSuspendResume(initial, deps, implSpec, trap, advan
|
|
|
55
54
|
return;
|
|
56
55
|
c1Acc.llmRootMicroUsd += b.llmRootMicroUsd;
|
|
57
56
|
c1Acc.nestedSubagentMicroUsd += b.nestedSubagentMicroUsd;
|
|
58
|
-
c1Acc.memoryConsolidationMicroUsd += b.memoryConsolidationMicroUsd;
|
|
59
57
|
c1Acc.compactionMicroUsd += b.compactionMicroUsd;
|
|
60
58
|
};
|
|
61
59
|
let exitedViaBreak = false;
|
|
@@ -109,7 +109,7 @@ function infraFailedRow(cell, ctx) {
|
|
|
109
109
|
oracle: { delivered: false, hiddenTestsGreen: false, buildPassed: false, invariantsOk: false },
|
|
110
110
|
delivery: { withheld: false },
|
|
111
111
|
stats: {
|
|
112
|
-
costBreakdown: { llmRootMicroUsd: 0, nestedSubagentMicroUsd: 0,
|
|
112
|
+
costBreakdown: { llmRootMicroUsd: 0, nestedSubagentMicroUsd: 0, compactionMicroUsd: 0 },
|
|
113
113
|
humanReview: { count: 0, totalWaitMs: 0, gates: [] },
|
|
114
114
|
},
|
|
115
115
|
wallTimeSec: 0,
|
|
@@ -221,7 +221,7 @@ export function positiveIntArg(raw, def, flag) {
|
|
|
221
221
|
function dryRunMockFactory() {
|
|
222
222
|
return (trap, seed) => {
|
|
223
223
|
const stats = {
|
|
224
|
-
costBreakdown: { llmRootMicroUsd: 50_000, nestedSubagentMicroUsd: 0,
|
|
224
|
+
costBreakdown: { llmRootMicroUsd: 50_000, nestedSubagentMicroUsd: 0, compactionMicroUsd: 0 },
|
|
225
225
|
humanReview: { count: 0, totalWaitMs: 0, gates: [] },
|
|
226
226
|
};
|
|
227
227
|
const green = trap.id !== "C2-honest-bound";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* [ref] A10 分段 —— **device lane 的装配件**(device-executor-lane-v2 §4.6/§4.7/§5,车A-4)。
|
|
3
3
|
*
|
|
4
|
-
* 一处建齐三件活对象:**店**(
|
|
4
|
+
* 一处建齐三件活对象:**店**(三表双库的 `DeviceStore`;绑定写在根会话行的执行格上)→ **准入链**(`DeviceEnrollment`,读会话店的执行席,含跨副本
|
|
5
5
|
* 限速座)→ **WS 汇聚端**(`DeviceWsHub`)。三件的依赖是单向的(hub 吃 store+enrollment),所以它们
|
|
6
6
|
* 必须在同一处按序建 —— 拆到三处装配点会让「hub 拿到的是不是同一只店」变成一个要读三个文件才答得出
|
|
7
7
|
* 的问题(而答错的后果是 presence 与租约各写各的)。
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { type DeviceEnrollment } from "../device-enrollment.js";
|
|
17
17
|
import type { DeviceStore } from "../device-store.js";
|
|
18
|
+
import type { ExecutionPlacementSeat } from "../execution-placement.js";
|
|
18
19
|
import { type DeviceWsHub } from "../device-ws-hub.js";
|
|
19
20
|
import type { ServiceConfig } from "../config.js";
|
|
20
21
|
import type { Logger } from "../observability/logger.js";
|
|
@@ -25,6 +26,8 @@ export interface DeviceLaneCtx {
|
|
|
25
26
|
logger: Logger;
|
|
26
27
|
metrics: Metrics;
|
|
27
28
|
backend: StoreBackend | undefined;
|
|
29
|
+
/** 会话店的执行席(`sessionStore.executionPlacement`):根会话 ↔ 设备绑定的唯一载体与唯一读。device 车道只在 SQL 后端 ⇒ 席恒在;缺 = 装配缝 ⇒ 本函数响亮抛。 */
|
|
30
|
+
seat: ExecutionPlacementSeat | undefined;
|
|
28
31
|
/** 本副本 id(写进连接租约行 —— §4.6 的跨副本排障面)。 */
|
|
29
32
|
replicaId: string;
|
|
30
33
|
}
|
|
@@ -32,6 +35,8 @@ export interface DeviceLane {
|
|
|
32
35
|
store: DeviceStore;
|
|
33
36
|
enrollment: DeviceEnrollment;
|
|
34
37
|
hub: DeviceWsHub;
|
|
38
|
+
/** 会话店的执行席(准入链、工厂、换绑门读的同一只)。 */
|
|
39
|
+
seat: ExecutionPlacementSeat;
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* 注册口(`POST /v1/devices/enroll`,pre-auth)的限速策略 —— **本车道自己的地板,不挂任何运维旋钮**。
|
|
@@ -49,7 +54,7 @@ export declare const DEVICE_ENROLL_RATE_POLICY: {
|
|
|
49
54
|
readonly windowMs: 60000;
|
|
50
55
|
};
|
|
51
56
|
/**
|
|
52
|
-
* 建齐 device lane 的三件。返回 `undefined` = **建不起来**(没有 SQL 后端 ⇒
|
|
57
|
+
* 建齐 device lane 的三件。返回 `undefined` = **建不起来**(没有 SQL 后端 ⇒ 没有设备店)。
|
|
53
58
|
*
|
|
54
59
|
* 只在调用方判定本部署是 device 车道时调用;非 device 车道调用它是纯浪费(会白建一只 WS server)。
|
|
55
60
|
*/
|
|
@@ -61,7 +66,7 @@ export declare function createDeviceLane(ctx: DeviceLaneCtx): DeviceLane | undef
|
|
|
61
66
|
* config 层的门①判的是「运维配没配 SQL 坐标」——那句话回答的是**意图**。它回答不了「这次启动**真的
|
|
62
67
|
* 拿到**了店吗」:`openStoreBackendWithFallback` 曾在 `SESSION_BACKEND=auto` 下,SQL 连不上 /
|
|
63
68
|
* 建表失败 ⇒ `backend=undefined` + 一条 warn 就继续启动(那条降级臂已退役:配了 DSN 的库窗内重试、到窗拒启,见 `plugins/store-open-wait.ts`;
|
|
64
|
-
* 仍走得到本门的形:`DB_BACKEND=mysql|pg` 声明了 SQL 而 `SESSION_BACKEND=memory`,刻意不持久 ⇒ 不开店)。那次启动里 device lane
|
|
69
|
+
* 仍走得到本门的形:`DB_BACKEND=mysql|pg` 声明了 SQL 而 `SESSION_BACKEND=memory`,刻意不持久 ⇒ 不开店)。那次启动里 device lane 的表整个不在,
|
|
65
70
|
* 而 `boot/execution-env.ts` 的第七臂拿不到三件 ⇒ `executionEnvFactory` 落 `undefined` ⇒ core 拿
|
|
66
71
|
* `StubExecutionEnv`:**一台自称在员工设备上执行的 worker 实际在自己进程里跑,而且回执同形**。
|
|
67
72
|
* 那正是 [ref]「执行车道禁静默降级」点名的样板病,也正是 A-1 那条临时门当初要拦的东西 —— 换执法点,
|
package/dist/boot/device-lane.js
CHANGED
|
@@ -2,13 +2,18 @@ import { createDeviceEnrollment, createFixedWindowRateLimiter } from "../device-
|
|
|
2
2
|
import { createDeviceWsHub } from "../device-ws-hub.js";
|
|
3
3
|
export const DEVICE_ENROLL_RATE_POLICY = { limit: 10, windowMs: 60_000 };
|
|
4
4
|
export function createDeviceLane(ctx) {
|
|
5
|
-
const { config, logger, metrics, backend, replicaId } = ctx;
|
|
5
|
+
const { config, logger, metrics, backend, replicaId, seat } = ctx;
|
|
6
6
|
const store = backend?.device?.();
|
|
7
7
|
if (!store)
|
|
8
8
|
return undefined;
|
|
9
|
+
if (seat === undefined) {
|
|
10
|
+
throw new Error("device lane: the device store is wired but the session store carries no execution placement seat — the session↔device binding lives on the " +
|
|
11
|
+
"root session row and is read only through that seat; refusing to start a device lane that could neither bind nor read a binding (assembly bug)");
|
|
12
|
+
}
|
|
9
13
|
const knobs = config.remoteExec?.provider === "device" ? config.remoteExec : undefined;
|
|
10
14
|
const enrollment = createDeviceEnrollment({
|
|
11
15
|
store,
|
|
16
|
+
seat,
|
|
12
17
|
rateLimiter: createFixedWindowRateLimiter(DEVICE_ENROLL_RATE_POLICY),
|
|
13
18
|
});
|
|
14
19
|
const hub = createDeviceWsHub({
|
|
@@ -33,7 +38,7 @@ export function createDeviceLane(ctx) {
|
|
|
33
38
|
maxPreAuthPerRemote: knobs?.maxPreAuthPerRemote ?? 8,
|
|
34
39
|
enrollRateLimit: `${DEVICE_ENROLL_RATE_POLICY.limit}/${DEVICE_ENROLL_RATE_POLICY.windowMs / 1000}s per caller identity (per replica; independent of RATE_LIMIT_RPM)`,
|
|
35
40
|
});
|
|
36
|
-
return { store, enrollment, hub };
|
|
41
|
+
return { store, enrollment, hub, seat };
|
|
37
42
|
}
|
|
38
43
|
export function assertDeviceLaneWired(isDeviceLane, lane, hasFactory) {
|
|
39
44
|
if (!isDeviceLane)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { TaskEnvRegistry } from "../capabilities/sandbox-file-send.js";
|
|
2
2
|
import { SessionEnvironmentSelection } from "../capabilities/select-environment-tool.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ServiceConfig } from "../config.js";
|
|
4
4
|
import type { Logger } from "../observability/logger.js";
|
|
5
5
|
import type { Metrics } from "../observability/metrics.js";
|
|
6
6
|
import { PerTaskImageRegistry } from "../per-task-image.js";
|
|
7
7
|
import type { DeviceEnrollment } from "../device-enrollment.js";
|
|
8
8
|
import type { DeviceStore } from "../device-store.js";
|
|
9
|
+
import type { ExecutionPlacementSeat } from "../execution-placement.js";
|
|
9
10
|
import type { DeviceWsHub } from "../device-ws-hub.js";
|
|
10
11
|
import { k8sExecutionEnvFactory, type K8sEnvConfig, type K8sEnvDeps, type SandboxSecretSeat } from "../plugins/remote-env-k8s.js";
|
|
11
12
|
import { type SandboxGitPlane, type SandboxGitRunRows } from "../sandbox-git-plane.js";
|
|
@@ -117,11 +118,12 @@ export declare function assertWorkspaceSpaceReadable(space: ReturnType<typeof fa
|
|
|
117
118
|
readonly headOf: HeadRead | undefined;
|
|
118
119
|
readonly spaceCommits: (() => SpaceCommitRows | undefined) | undefined;
|
|
119
120
|
}, cache: ReturnType<typeof facts.workspaceCache>, sessionStoreLabel: string): void;
|
|
120
|
-
/** device lane
|
|
121
|
+
/** device lane 的活对象(店 / 汇聚端 / 准入链 + 会话店的执行席 —— 绑定的唯一载体与唯一读,工厂与准入链读同一只)。缺席 = 本部署没有 device 车道。 */
|
|
121
122
|
export interface DeviceLaneWiring {
|
|
122
123
|
store: DeviceStore;
|
|
123
124
|
hub: DeviceWsHub;
|
|
124
125
|
enrollment: DeviceEnrollment;
|
|
126
|
+
seat: ExecutionPlacementSeat;
|
|
125
127
|
}
|
|
126
128
|
export interface ExecutionEnvCtx {
|
|
127
129
|
config: ServiceConfig;
|
|
@@ -4,6 +4,7 @@ import { createHostLspManager } from "../host-lsp-manager.js";
|
|
|
4
4
|
import { TaskEnvRegistry } from "../capabilities/sandbox-file-send.js";
|
|
5
5
|
import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
|
|
6
6
|
import { SessionEnvironmentSelection } from "../capabilities/select-environment-tool.js";
|
|
7
|
+
import { sandboxEnvNamesInForce } from "../config.js";
|
|
7
8
|
import { createE2bLspManager } from "../lsp/e2b-manager.js";
|
|
8
9
|
import { evictLspOnDestroy } from "../lsp-evict.js";
|
|
9
10
|
import { PerTaskImageRegistry } from "../per-task-image.js";
|
|
@@ -11,7 +12,7 @@ import { adbExecutionEnvFactory } from "../plugins/remote-env-adb.js";
|
|
|
11
12
|
import { deviceExecutionEnvFactory } from "../plugins/remote-env-device.js";
|
|
12
13
|
import { e2bExecutionEnvFactory } from "../plugins/remote-env-e2b.js";
|
|
13
14
|
import { hostExecutionEnvFactory, RemoteHostExecutionEnv } from "../plugins/remote-env-host.js";
|
|
14
|
-
import { boundedStoreCall, checkSandboxEgressPolicy, createSandboxPodApi, createSpaceReclaimer,
|
|
15
|
+
import { boundedStoreCall, checkSandboxEgressPolicy, createSandboxPodApi, createSpaceReclaimer, k8sExecutionEnvFactory, readSandboxSecretSeat, rpcTimeoutMsOf } from "../plugins/remote-env-k8s.js";
|
|
15
16
|
import { podSpecPatchBrokerRefusal, SANDBOX_SECRET_SEAT_VERBS } from "../plugins/k8s-exec-protocol.js";
|
|
16
17
|
import { judgeSandboxGitPlane, runIdentitySourceOf, sandboxBrokerSeatOf, sandboxGitPlaneAsksSecretSeat, sandboxGitPlanePremisesOf } from "../sandbox-git-plane.js";
|
|
17
18
|
import { createSandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
|
|
@@ -43,24 +44,6 @@ function boundedSessionSetter(map, maxEntries) {
|
|
|
43
44
|
map.delete(map.keys().next().value);
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
|
-
function forwardedSandboxEnvKeys(remoteExec) {
|
|
47
|
-
switch (remoteExec.provider) {
|
|
48
|
-
case "e2b":
|
|
49
|
-
case "k8s":
|
|
50
|
-
return Object.keys(remoteExec.sandboxEnv ?? {}).sort();
|
|
51
|
-
case "local-docker":
|
|
52
|
-
return Object.keys(remoteExec.env ?? {}).sort();
|
|
53
|
-
case "ssh":
|
|
54
|
-
case "adb":
|
|
55
|
-
case "host":
|
|
56
|
-
case "device":
|
|
57
|
-
return [];
|
|
58
|
-
default: {
|
|
59
|
-
const unreachable = remoteExec;
|
|
60
|
-
throw new Error(`unhandled remote-exec provider: ${String(unreachable.provider)}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
47
|
function buildSandboxLaneEnv(config, explicit) {
|
|
65
48
|
const secretEnv = { ...(explicit ?? {}) };
|
|
66
49
|
const env = Object.fromEntries(Object.entries(derivePkgSourceEnv(config.sandboxPkgSource, config.sandboxPkgSourceCustom)).filter(([name]) => !Object.hasOwn(secretEnv, name)));
|
|
@@ -173,7 +156,7 @@ export async function ensureSandboxSecretSeat(k8sCfg, logger, deps, read = () =>
|
|
|
173
156
|
const names = Object.keys(k8sCfg.podSecretEnv ?? {});
|
|
174
157
|
if (names.length === 0)
|
|
175
158
|
return;
|
|
176
|
-
const namespace = k8sCfg.namespace
|
|
159
|
+
const namespace = k8sCfg.namespace;
|
|
177
160
|
const seat = await read();
|
|
178
161
|
switch (seat.state) {
|
|
179
162
|
case "granted":
|
|
@@ -235,6 +218,12 @@ function buildRealSystemNote(remoteExec) {
|
|
|
235
218
|
"and the Bash read boundary is a text-level friction floor, not a safety boundary; the safety boundary is a sandbox lane: " +
|
|
236
219
|
`set REMOTE_EXEC=${sandboxLanes} (see docs/DEPLOY-PREREQS.md, section "S-281")`);
|
|
237
220
|
}
|
|
221
|
+
function hostWorktreeUnwrappedRefusal() {
|
|
222
|
+
return Object.assign(new Error('isolation "worktree" was requested but cannot be honored: this host lane has no worktree wrapper active ' +
|
|
223
|
+
"(WORKTREE_ISOLATION_ENABLED=true together with WORKTREE_REPO_ROOT=<a git repository> turns it on; a repo root that does not exist or has no `.git` leaves it off — see the boot log), " +
|
|
224
|
+
"and the base host environment would place the sub-agent in an empty temporary directory instead of an isolated checkout. " +
|
|
225
|
+
"The sub-agent was not started (fail-closed — no silent fallback to the shared working tree)."), { code: "isolation.unavailable" });
|
|
226
|
+
}
|
|
238
227
|
export function createExecutionEnv(ctx) {
|
|
239
228
|
const { config, logger, metrics, taskAttachmentStore } = ctx;
|
|
240
229
|
const laneHomeDir = executionLaneHomeDir(config);
|
|
@@ -275,7 +264,7 @@ export function createExecutionEnv(ctx) {
|
|
|
275
264
|
...(config.remoteExec.token ? { token: config.remoteExec.token } : {}),
|
|
276
265
|
...(config.remoteExec.caCert ? { caCert: config.remoteExec.caCert } : {}),
|
|
277
266
|
...(config.remoteExec.insecureTls ? { insecureTls: true } : {}),
|
|
278
|
-
|
|
267
|
+
namespace: config.remoteExec.namespace,
|
|
279
268
|
...(config.remoteExec.runtimeClass != null ? { runtimeClass: config.remoteExec.runtimeClass } : {}),
|
|
280
269
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
281
270
|
...(config.remoteExec.timeoutMs != null ? { timeoutMs: config.remoteExec.timeoutMs } : {}),
|
|
@@ -312,7 +301,7 @@ export function createExecutionEnv(ctx) {
|
|
|
312
301
|
};
|
|
313
302
|
sandboxPodReaper = createSandboxPodReaper({ api: createSandboxPodApi(k8sCfg), logger, metrics });
|
|
314
303
|
void checkSandboxEgressPolicy(k8sCfg)
|
|
315
|
-
.then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace
|
|
304
|
+
.then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace))
|
|
316
305
|
.catch((e) => logger.error("sandbox_egress_policy_check_crashed", { error: e instanceof Error ? e.message : String(e), effect: "the sandbox egress self-check did not complete this boot — the lane runs; the egress policy was not read" }));
|
|
317
306
|
}
|
|
318
307
|
else if (config.remoteExec?.provider === "ssh") {
|
|
@@ -332,6 +321,7 @@ export function createExecutionEnv(ctx) {
|
|
|
332
321
|
hub: ctx.deviceLane.hub,
|
|
333
322
|
store: ctx.deviceLane.store,
|
|
334
323
|
enrollment: ctx.deviceLane.enrollment,
|
|
324
|
+
seat: ctx.deviceLane.seat,
|
|
335
325
|
...homeDirSeat,
|
|
336
326
|
...(config.remoteExec.execTimeoutMs != null ? { execTimeoutMs: config.remoteExec.execTimeoutMs } : {}),
|
|
337
327
|
perSessionCwd,
|
|
@@ -375,6 +365,8 @@ export function createExecutionEnv(ctx) {
|
|
|
375
365
|
backgroundShell: config.requirePrincipal !== true,
|
|
376
366
|
};
|
|
377
367
|
executionEnvFactory = (ctx) => {
|
|
368
|
+
if (ctx.isolation === "worktree")
|
|
369
|
+
throw hostWorktreeUnwrappedRefusal();
|
|
378
370
|
const cwd = effectiveHostWorkspace(perSessionCwd.get(ctx.sessionId), ctx);
|
|
379
371
|
const shellEnv = perSessionShellEnv.get(ctx.sessionId);
|
|
380
372
|
const inheritEnv = perSessionInheritEnv.get(ctx.sessionId);
|
|
@@ -414,7 +406,7 @@ export function createExecutionEnv(ctx) {
|
|
|
414
406
|
logger.error("worktree_isolation_disabled_bad_repo_root", {
|
|
415
407
|
repoRoot: wtRoot,
|
|
416
408
|
reason: existsSync(wtRoot) ? "not a git repository (no .git)" : "path does not exist",
|
|
417
|
-
note: "WORKTREE_ISOLATION_ENABLED is set but WORKTREE_REPO_ROOT is not a usable git repo — isolation left OFF; isolation:'worktree' agents
|
|
409
|
+
note: "WORKTREE_ISOLATION_ENABLED is set but WORKTREE_REPO_ROOT is not a usable git repo — isolation left OFF; isolation:'worktree' workflow agents are refused (isolation.unavailable) until it is fixed. Fix the path or unset the flag.",
|
|
418
410
|
});
|
|
419
411
|
}
|
|
420
412
|
else if (config.remoteExec?.provider === "host") {
|
|
@@ -473,7 +465,7 @@ export function createExecutionEnv(ctx) {
|
|
|
473
465
|
if (executionEnvFactory) {
|
|
474
466
|
const isolated = laneIsolated(executionLaneOf(config.remoteExec.provider));
|
|
475
467
|
const suspendable = DEPLOYMENT_SHAPE_PREDICATES.suspendableHands.holds(config);
|
|
476
|
-
const sandboxEnvKeys =
|
|
468
|
+
const sandboxEnvKeys = [...sandboxEnvNamesInForce(config)].sort();
|
|
477
469
|
logger.info("remote_exec_enabled", { provider: config.remoteExec.provider, isolated, suspendable, ...(sandboxEnvKeys.length > 0 ? { sandboxEnvKeys } : {}) });
|
|
478
470
|
if (!isolated)
|
|
479
471
|
logger.warn("remote_exec_real_system", { provider: config.remoteExec.provider, note: buildRealSystemNote(config.remoteExec) });
|
|
@@ -5,7 +5,7 @@ import type { Logger } from "../observability/logger.js";
|
|
|
5
5
|
export declare const FILE_SOURCED_LANES: readonly ["host", "local-docker"];
|
|
6
6
|
export type FileSourcedLane = (typeof FILE_SOURCED_LANES)[number];
|
|
7
7
|
export interface ExecutionLaneComposeCtx {
|
|
8
|
-
/** 本入口的配置源形态(`
|
|
8
|
+
/** 本入口的配置源形态(`configSourceKind` 的答案 —— 与它真建的 provider 同一只判据)。
|
|
9
9
|
* 只有 `local` 才读文件;`undefined` = 纯 env 部署。 */
|
|
10
10
|
readonly configSource: "local" | "remote" | undefined;
|
|
11
11
|
/** 配置根(与配置源同一个根,不分裂)。 */
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
* `retentionSweepRuns`):开 ⇒ `managed`,关(缺省)⇒ `none`;local 形的店恒声明 `none`。删除的执行面是扫车道(`boot/retention-lane.ts`)与 SQL 店上的
|
|
46
46
|
* 执行器,不在本装配点。所以本装配点对件D 只做两件事:①把策略与执行器递给 core 的启动期校验
|
|
47
47
|
* (`assertRetentionCapability` —— 配对由引擎一个判官判:店声明 `managed` 而部署不跑执行器、执行器不是执行器,锁不锁都拒;锁着 + 店不能删 ⇒ 拒启,
|
|
48
|
-
* 「策略锁着、数据永存」不许发生)
|
|
48
|
+
* 「策略锁着、数据永存」不许发生);②策略在场而扫车道关着(节律缺或为 0)时打一条响亮 warn,句说这个真因。**不**假装有执行面。
|
|
49
|
+
* 车道关着而策略在场只可能在 `audit-only` 档:`enforce` 而车道不齐在 `loadConfig` 就拒了(配置不变量 `retention-lane-complete`)。
|
|
49
50
|
*/
|
|
50
51
|
import { type LockedKey, type ManagedRetentionCapability, type RunnerDeps } from "@sema-agent/core";
|
|
51
52
|
import type { ServiceConfig } from "../config.js";
|
|
@@ -61,7 +62,7 @@ export interface GovernanceSeams {
|
|
|
61
62
|
}
|
|
62
63
|
/** 依赖收窄到真实消费面(接口隔离 —— 测试用真形字面量,零铸形)。 */
|
|
63
64
|
export interface GovernanceSeamsCtx {
|
|
64
|
-
config: Pick<ServiceConfig, "compliancePosture" | "lockedConfigKeys" | "retentionPolicy" | "mcpServers" | "requirePrincipal">;
|
|
65
|
+
config: Pick<ServiceConfig, "compliancePosture" | "lockedConfigKeys" | "retentionPolicy" | "retentionSweep" | "mcpServers" | "requirePrincipal">;
|
|
65
66
|
logger: Pick<Logger, "info" | "warn">;
|
|
66
67
|
/**
|
|
67
68
|
* core 留存能力校验的对象集(名字进拒启文案)。喂的是**装配现场的真店**,不是「我以为装了什么」——
|
|
@@ -50,17 +50,15 @@ export function createGovernanceSeams(ctx) {
|
|
|
50
50
|
const retentionPolicy = config.retentionPolicy;
|
|
51
51
|
const locked = lockedKeys.has("retentionPolicy");
|
|
52
52
|
assertRetentionCapability({ policy: retentionPolicy, locked, stores: retentionStores, executor: ctx.retentionExecutor });
|
|
53
|
-
if (retentionPolicy !== undefined) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
}
|
|
53
|
+
if (retentionPolicy !== undefined && config.retentionSweep.intervalSec === 0) {
|
|
54
|
+
logger.warn("retention_policy_not_executed", {
|
|
55
|
+
maxAgeDays: retentionPolicy.maxAgeDays,
|
|
56
|
+
mode: config.retentionSweep.mode,
|
|
57
|
+
retentionSweepIntervalSec: config.retentionSweep.intervalSec,
|
|
58
|
+
note: "RETENTION_MAX_AGE_DAYS is set but RETENTION_SWEEP_INTERVAL_SEC is unset or 0, so the retention sweep lane is off: nothing deletes data — " +
|
|
59
|
+
"or records what it would delete — on this policy's account; the policy is a declaration relayed to the engine's startup capability check. " +
|
|
60
|
+
"To run the lane set RETENTION_SWEEP_INTERVAL_SEC above 0 on a SQL backend (RETENTION_MODE=audit-only records what would be deleted; enforce deletes it).",
|
|
61
|
+
});
|
|
64
62
|
}
|
|
65
63
|
return { compliancePostureResolver, lockedConfig, retentionPolicy, lockedKeys };
|
|
66
64
|
}
|
|
@@ -5,7 +5,7 @@ import { memoryMountRootOf } from "../memory-scope.js";
|
|
|
5
5
|
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
6
6
|
import { DEFAULT_MODEL_REF, resolveMainModel } from "../model-select.js";
|
|
7
7
|
import { backendOwnsControlPlane } from "../memory-operator-faces.js";
|
|
8
|
-
import { createConsolidationWriteJudge, deliverWriteRefusal, expandTiers, MemoryEngine, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
|
|
8
|
+
import { createConsolidationWriteJudge, deliverWriteRefusal, expandTiers, MemoryEngine, modelOriginOf, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
|
|
9
9
|
export function resolveConsolidationDriverSeat(deps) {
|
|
10
10
|
let options;
|
|
11
11
|
try {
|
|
@@ -151,7 +151,7 @@ export function consolidationSeatBillingOf(plane) {
|
|
|
151
151
|
catch (err) {
|
|
152
152
|
return { kind: "unresolvable", fault: `the main model (${DEFAULT_MODEL_REF}) does not resolve: ${err instanceof Error ? err.message : String(err)}` };
|
|
153
153
|
}
|
|
154
|
-
return seat.entry === main ? { kind: "main-model", mainModel: main.id } : { kind: "own-model", seatModel: seat.entry.id, mainModel: main.id };
|
|
154
|
+
return modelOriginOf(seat.entry) === modelOriginOf(main) ? { kind: "main-model", mainModel: main.id } : { kind: "own-model", seatModel: seat.entry.id, mainModel: main.id };
|
|
155
155
|
}
|
|
156
156
|
export function resolveConsolidationValve(input) {
|
|
157
157
|
const decided = decideConsolidationValve(input);
|
|
@@ -15,7 +15,7 @@ import type { Metrics } from "../observability/metrics.js";
|
|
|
15
15
|
import { PerTaskImageRegistry } from "../per-task-image.js";
|
|
16
16
|
import type { StoreBackend } from "../plugins/store-backend.js";
|
|
17
17
|
import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
|
|
18
|
-
import {
|
|
18
|
+
import type { createPrincipalEntitlementsClient } from "../runtime-caps-resolver.js";
|
|
19
19
|
import { type OwnerAwareSessionStore } from "../security.js";
|
|
20
20
|
import type { MemorySyncRunner } from "../memory-sync-client.js";
|
|
21
21
|
import type { PeerDesk } from "../peer-seat.js";
|
|
@@ -25,7 +25,6 @@ import { bindAttachmentsForTask } from "../plugins/task-attachment-store.js";
|
|
|
25
25
|
import { resourceSuspendOptIn } from "../resource-suspend.js";
|
|
26
26
|
import { routeServiceTask, supPostureOverrides } from "../router/route-orchestration.js";
|
|
27
27
|
import { tightenGoverned } from "../governance-overrides.js";
|
|
28
|
-
import { gateExecutionLane } from "../runtime-caps-resolver.js";
|
|
29
28
|
import { applyRuntimeGovernance } from "../runtime-governance.js";
|
|
30
29
|
import { HttpError, createCodedHttpError, encodeCheckpointScope } from "../security.js";
|
|
31
30
|
import { headReadOf, sessionFaceOfHead } from "../session-incarnation.js";
|
|
@@ -55,6 +54,10 @@ import { executionLaneHomeDir, executionLaneOf, laneWiresExecutionEnv } from "..
|
|
|
55
54
|
import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
|
|
56
55
|
import { withUnattendedLeg } from "../unattended-permission-mode.js";
|
|
57
56
|
import { fieldConflictFacts } from "../field-conflict.js";
|
|
57
|
+
function unrooted(spec) {
|
|
58
|
+
const { sessionId: _neverWritten, ...resolved } = spec;
|
|
59
|
+
return resolved;
|
|
60
|
+
}
|
|
58
61
|
export function createResolveSpec(ctx) {
|
|
59
62
|
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, memorySyncRunner, durableEnabled, approvalExemptionStore, checkpointStore, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, loadedSkills, peerDesk, } = ctx;
|
|
60
63
|
const deploymentSkillSources = new Map(loadedSkills.map((k) => [k.spec.name, k.source]));
|
|
@@ -179,9 +182,9 @@ export function createResolveSpec(ctx) {
|
|
|
179
182
|
};
|
|
180
183
|
const gateScenarioAndAppend = async (body, auth, opts) => {
|
|
181
184
|
const scenarioName = await scenarioGate(auth?.principal, body.scenario);
|
|
182
|
-
|
|
183
|
-
const sessionWitness =
|
|
184
|
-
const cap = selectScenario(scenarios, scenarioName)(body, auth?.principal,
|
|
185
|
+
const root = opts.execution.root;
|
|
186
|
+
const sessionWitness = { sessionId: root, incarnation: (sessionHeadOf === undefined ? undefined : sessionFaceOfHead(root, await sessionHeadOf(root)))?.incarnation ?? null };
|
|
187
|
+
const cap = selectScenario(scenarios, scenarioName)(body, auth?.principal, { sessionId: sessionWitness.sessionId, delegationRoot: sessionWitness });
|
|
185
188
|
const acceptedAppend = acceptAppendSystemPrompt(body.appendSystemPrompt, (detail) => logger.warn("append_system_prompt_dropped", { detail, sessionId: auth?.sessionId ?? null }));
|
|
186
189
|
const objective = typeof body.objective === "string" ? body.objective : "";
|
|
187
190
|
const parsedSettings = parseTaskSettings(body.settings, opts?.leg === "fresh" || !taskHooksHonored(config) ? {} : { storedHooksReread: storedHooksRereadM20(new Date().toISOString().slice(0, 10)) });
|
|
@@ -375,7 +378,6 @@ export function createResolveSpec(ctx) {
|
|
|
375
378
|
appendSystemPrompt: acceptedAppend,
|
|
376
379
|
...(body.outputStyle !== undefined ? { outputStyle: body.outputStyle } : {}),
|
|
377
380
|
...(body.projectMemory !== undefined ? { projectMemory: body.projectMemory } : {}),
|
|
378
|
-
sessionId: auth?.sessionId,
|
|
379
381
|
...(legProjectId !== undefined ? { projectId: legProjectId } : {}),
|
|
380
382
|
principal: auth?.principal,
|
|
381
383
|
images: body.images,
|
|
@@ -396,7 +398,6 @@ export function createResolveSpec(ctx) {
|
|
|
396
398
|
suggestNextPrompts: normalizeSuggestNextPrompts(body.suggestNextPrompts),
|
|
397
399
|
restoreFiles: body.restoreFiles === true ? true : undefined,
|
|
398
400
|
acceptPartialRestore: body.acceptPartialRestore === true ? true : undefined,
|
|
399
|
-
...(body.rewindFiles === true ? { rewindFiles: true } : {}),
|
|
400
401
|
rewindFilesTo: rewindFilesToEntryId,
|
|
401
402
|
...(body.fileHistory === "disabled" ? { fileHistory: "disabled" } : {}),
|
|
402
403
|
additionalDirectories,
|
|
@@ -670,9 +671,7 @@ export function createResolveSpec(ctx) {
|
|
|
670
671
|
};
|
|
671
672
|
const resolveSpecForLeg = async (body, req, auth, opts) => {
|
|
672
673
|
const spec = await resolveSpec(body, req, auth, opts);
|
|
673
|
-
|
|
674
|
-
return spec;
|
|
675
|
-
return withUnattendedLeg(spec);
|
|
674
|
+
return unrooted(opts.unattendedLeg !== true ? spec : withUnattendedLeg(spec));
|
|
676
675
|
};
|
|
677
676
|
return resolveSpecForLeg;
|
|
678
677
|
}
|
|
@@ -158,14 +158,15 @@ export interface RetentionSweepOutcome {
|
|
|
158
158
|
* · core 那道判**配对**,锁不锁都判(`executor` 必给):声明 `managed` 的店 × 没有执行器(`null`)⇒ 拒;执行器四个契约方法
|
|
159
159
|
* (`listRetentionDomains` / `expireCheckpoints` / `deleteExpiredSessions` / `deleteOrphanToolResults`)缺一 ⇒ 拒;
|
|
160
160
|
* **锁着**的策略盖在声明 `none` 的店上 ⇒ 拒。它不问 lane 开没开、策略在不在,也不拦没锁时「执行器在场 × 店声明 `none`」(部署可以持有执行器而不在某只店上跑);
|
|
161
|
-
* · **本道**问的是「lane
|
|
161
|
+
* · **本道**问的是「lane 开着的时候,它接得上能删的东西吗」——**不看 locked 状态**:执行器在场、lane 调的**五**个方法都在
|
|
162
162
|
* (多出的 `previewRetention` 是 audit-only 档的读数口,不在 core 的四方法契约里,引擎的检查覆盖不到它)、每只被 lane 消费的店都声明 `managed`。
|
|
163
163
|
* 「执行器在场」与「契约方法齐」两格和引擎的判官说的是同一件事(各自一句拒句),其余几格是引擎不问的。
|
|
164
164
|
*
|
|
165
|
+
* 「lane 开着而没有视界(天数)」与「`RETENTION_MODE=enforce` 而车道不齐」不在本道:那是配置事实,判在 `loadConfig` 的配置不变量段
|
|
166
|
+
* (`config-invariants.ts` 的 `retentionLaneRefusal`,一条规则)—— 到得了本道的部署,lane 开着就一定有视界。本道只判**接线**(配置看不见的那几格)。
|
|
167
|
+
*
|
|
165
168
|
* 为什么本道必须存在:引擎的门不拦
|
|
166
|
-
*
|
|
167
|
-
* 读数一路 0,看起来像"没有候选");
|
|
168
|
-
* ② 没锁时,执行器在场 × 一只 `"none"` 的店 —— 部署于是**广告了一个不执行的留存面**:能力位说
|
|
169
|
+
* · 没锁时,执行器在场 × 一只 `"none"` 的店 —— 部署于是**广告了一个不执行的留存面**:能力位说
|
|
169
170
|
* `{mode,maxAgeDays}`、审计表也在长,而那只店的行谁都没在删。
|
|
170
171
|
* 本仓的 SQL 后端按同一个事实(扫车道开不开,`plugins/retention-store-sql.ts` 的 `retentionSweepRuns`)给它铸的三只店声明词,所以 lane 开着时
|
|
171
172
|
* 这三只店在这一格上恒是 `managed`;这一格防的是声明与 lane 脱钩的店(别的后端、替身)。
|
|
@@ -176,7 +177,6 @@ export interface RetentionSweepOutcome {
|
|
|
176
177
|
*/
|
|
177
178
|
export declare function assertRetentionLaneWirable(input: {
|
|
178
179
|
intervalSec: number;
|
|
179
|
-
policy: RetentionPolicy | undefined;
|
|
180
180
|
executor: RetentionExecutor | undefined;
|
|
181
181
|
stores: ReadonlyArray<{
|
|
182
182
|
name: string;
|
|
@@ -7,12 +7,6 @@ export function assertRetentionLaneWirable(input) {
|
|
|
7
7
|
if (input.intervalSec <= 0)
|
|
8
8
|
return;
|
|
9
9
|
const lockNote = input.locked === true ? " (the retentionPolicy key is LOCKED, but this gate refuses either way)" : "";
|
|
10
|
-
if (input.policy === undefined) {
|
|
11
|
-
throw new Error(`RETENTION_SWEEP_INTERVAL_SEC=${input.intervalSec} arms the managed-retention sweep lane, but RETENTION_MAX_AGE_DAYS is not ` +
|
|
12
|
-
`configured — the lane would run with NO horizon to compute a cutoff from, enumerate domains every tick and delete nothing, ` +
|
|
13
|
-
`while the capability surface advertises a retention face that does not exist${lockNote}. Set RETENTION_MAX_AGE_DAYS, or set ` +
|
|
14
|
-
`RETENTION_SWEEP_INTERVAL_SEC=0 to leave the lane off.`);
|
|
15
|
-
}
|
|
16
10
|
if (input.executor === undefined) {
|
|
17
11
|
throw new Error(`RETENTION_SWEEP_INTERVAL_SEC=${input.intervalSec} arms the managed-retention sweep lane, but this deployment wires NO retention ` +
|
|
18
12
|
`executor — the destructive store methods live on the SQL backends only (DB_BACKEND=mysql|pg); the local/file backend honestly ` +
|
|
@@ -66,7 +66,9 @@ export declare function createRunnerDepsOnAsk(toolApproval: ToolApprovalCoordina
|
|
|
66
66
|
* [ref]:第五词 `shell-gate-off` 随 core 7.34.0 的 off 车道一起退役(引擎侧零发射点),同批删 —— 它唯一的本仓消费者
|
|
67
67
|
* (「本 session 显式 bypass ⇒ 降级 info」那条回查腿与 `boot/session-shell-gate-registry.ts` 登记簿)一并删干净。所以这是「按实现亲读」的开集快照,不是 core 类型闭集——词表外的值原样进日志并标
|
|
68
68
|
* `classificationKnown:false`(指标标签折 `unknown`),不吞不编;与安装包的锁步由 test/boot-runner-deps-shared-base.test.ts
|
|
69
|
-
* 的 dist 扫描格钉住(两种发射写法都读;读不出词的经变量发射点本身红;core 加词/收进 d.ts 那天先红)。
|
|
69
|
+
* 的 dist 扫描格钉住(两种发射写法都读;读不出词的经变量发射点本身红;core 加词/收进 d.ts 那天先红)。
|
|
70
|
+
* 第七词 `peer-seat-hold`(runtask 的 `holdDeclarations`,字面量):对端座的三员对象里 `hold` / `release` 抛了 ⇒ 引擎报一次、该引用
|
|
71
|
+
* 不被持有;座是本仓的 desk,所以这是部署自己的故障,与其余六词同走 warn + 计数。 */
|
|
70
72
|
export declare const CONFIG_ADVISORY_CLASSIFICATIONS: ReadonlySet<string>;
|
|
71
73
|
/** [ref]:core 的 [ref] 前缀缓存**断裂**探测器交给宿主的根因**闭集**,以及每个词的日志级别。
|
|
72
74
|
* 闭集**不抄词**:词源直接取 core 公开导出的 `CacheBreakFinding["cause"]`(`@sema-agent/core` 根导出,
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -32,6 +32,7 @@ export const CONFIG_ADVISORY_CLASSIFICATIONS = new Set([
|
|
|
32
32
|
"file-history-no-store",
|
|
33
33
|
"interaction-posture-refused",
|
|
34
34
|
"memory-tools-not-mounted",
|
|
35
|
+
"peer-seat-hold",
|
|
35
36
|
"sandbox-admission-unavailable",
|
|
36
37
|
"shared-memory-not-mounted",
|
|
37
38
|
]);
|
|
@@ -131,7 +132,7 @@ export function createSharedRunnerDeps(ctx) {
|
|
|
131
132
|
lockedConfig: ctx.governanceSeams.lockedConfig,
|
|
132
133
|
retentionPolicy: ctx.governanceSeams.retentionPolicy,
|
|
133
134
|
mcpRevocations: ctx.mcpRevocations ? ctx.mcpRevocations : undefined,
|
|
134
|
-
peerSeat: ctx.peerDesk.seat
|
|
135
|
+
peerSeat: ctx.peerDesk.seat,
|
|
135
136
|
};
|
|
136
137
|
return shared;
|
|
137
138
|
}
|
|
@@ -21,7 +21,6 @@ import type { createSessionStore } from "../plugins/session-store.js";
|
|
|
21
21
|
import type { TaskAttachmentStore } from "../plugins/task-attachment-store.js";
|
|
22
22
|
import type { FileHistoryErasureFace } from "../file-history-capture.js";
|
|
23
23
|
import type { OwnerAwareSessionStore } from "../security.js";
|
|
24
|
-
import type { DeviceStore } from "../device-store.js";
|
|
25
24
|
import { SessionWatchRegistry } from "../session-watch.js";
|
|
26
25
|
import type { TaskListLane } from "./task-list-lane.js";
|
|
27
26
|
import { type RunnerDeps } from "@sema-agent/core";
|
|
@@ -48,8 +47,6 @@ export interface SessionFacesCtx {
|
|
|
48
47
|
workflowCompletionInbox: WorkflowCompletionInbox | undefined;
|
|
49
48
|
/** [ref]:会话级任务清单车道(E21 级联的一条腿)。缺席 = 装配未接本车道 ⇒ 无清单可删。 */
|
|
50
49
|
taskListLane: TaskListLane | undefined;
|
|
51
|
-
/** device lane 的四表店(E21 级联的一条腿:`device_session` 绑定行随会话删)。缺席 = 非 SQL 后端。 */
|
|
52
|
-
deviceStore: DeviceStore | undefined;
|
|
53
50
|
/** per-session cwd/shellEnv 登记簿的清除口(E21 级联的一条腿,codex R1-F1)。缺席 = 装配未接。 */
|
|
54
51
|
dropSessionScoped: ((sid: string) => void) | undefined;
|
|
55
52
|
/** [ref]:会话盒(`session.<id>`)的信箱 + 它的收件人生命周期面(E21 级联的一条腿)。**成对**在场
|
|
@@ -6,11 +6,12 @@ import { setLeafAdvanceListener } from "../session-leaf-bus.js";
|
|
|
6
6
|
import { SessionWatchRegistry, posIntEnv } from "../session-watch.js";
|
|
7
7
|
import { defaultTaskRegistry, peerSessionBoxHandle } from "@sema-agent/core";
|
|
8
8
|
import { reapSessionBackgroundHard } from "../background-session-reap.js";
|
|
9
|
-
import { probeSessionChildren, retireSessionChildren, SessionChildrenStopIncompleteError } from "../session-children-retire.js";
|
|
9
|
+
import { probeSessionChildren, retireSessionChildren, sessionChildrenScope, SessionChildrenStopIncompleteError } from "../session-children-retire.js";
|
|
10
|
+
import { SessionActiveRunError } from "../execution-placement.js";
|
|
10
11
|
import { headReadOf, redactIncarnations } from "../session-incarnation.js";
|
|
11
12
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
12
13
|
export function createSessionFaces(ctx) {
|
|
13
|
-
const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane,
|
|
14
|
+
const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, userInputLedgerStore, spaceReclaimer, } = ctx;
|
|
14
15
|
const mysqlPool = backend?.mysqlPool();
|
|
15
16
|
const auditPgPool = backend?.pgPool();
|
|
16
17
|
const auditLocalRoot = backend?.kind === "local" ? (config.localDataRoot ?? localRoot) : undefined;
|
|
@@ -49,7 +50,7 @@ export function createSessionFaces(ctx) {
|
|
|
49
50
|
return { deleted: false };
|
|
50
51
|
spaceKey = spaceReclaimer !== undefined ? head.incarnation : undefined;
|
|
51
52
|
}
|
|
52
|
-
const childScope = owner
|
|
53
|
+
const childScope = sessionChildrenScope(owner);
|
|
53
54
|
const reach = await probeSessionChildren({ agentStore: backgroundAgentStore, checkpointStore, registry: defaultTaskRegistry }, sessionId, childScope);
|
|
54
55
|
if (!reach.reachable) {
|
|
55
56
|
logger.warn("session_delete_children_still_alive", { sessionId, remaining: reach.unreachableHandles.length, handles: reach.unreachableHandles });
|
|
@@ -60,8 +61,8 @@ export function createSessionFaces(ctx) {
|
|
|
60
61
|
return { active: runs.active };
|
|
61
62
|
{
|
|
62
63
|
const children = await retireSessionChildren({ agentStore: backgroundAgentStore, checkpointStore, registry: defaultTaskRegistry }, sessionId, childScope);
|
|
63
|
-
if (children.
|
|
64
|
-
logger.info("session_children_retired", { sessionId, found: children.
|
|
64
|
+
if (children.observed.length > 0)
|
|
65
|
+
logger.info("session_children_retired", { sessionId, found: children.observed.length, stopped: children.stopped, rounds: children.rounds });
|
|
65
66
|
if (children.stopped === "incomplete") {
|
|
66
67
|
logger.warn("session_delete_children_stop_incomplete", { sessionId, remaining: children.remainingHandles.length, handles: children.remainingHandles });
|
|
67
68
|
throw new SessionChildrenStopIncompleteError(sessionId, children.remainingHandles.length);
|
|
@@ -91,8 +92,6 @@ export function createSessionFaces(ctx) {
|
|
|
91
92
|
await userInputLedgerStore.deleteBySession(sessionId);
|
|
92
93
|
if (taskListLane)
|
|
93
94
|
await taskListLane.deleteBySession(sessionId);
|
|
94
|
-
if (deviceStore)
|
|
95
|
-
await deviceStore.deleteByRootSession(sessionId);
|
|
96
95
|
dropSessionScoped?.(sessionId);
|
|
97
96
|
if (fileHistoryErasure) {
|
|
98
97
|
if (typeof fileHistoryErasure.deleteBySession === "function")
|
|
@@ -122,7 +121,15 @@ export function createSessionFaces(ctx) {
|
|
|
122
121
|
}
|
|
123
122
|
if (peerBox !== undefined)
|
|
124
123
|
await peerBox.store.drop(peerBox.scope, peerBox.handle);
|
|
125
|
-
|
|
124
|
+
let historyDeleted;
|
|
125
|
+
try {
|
|
126
|
+
historyDeleted = await ownerAware.deleteSession(sessionId, owner);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err instanceof SessionActiveRunError)
|
|
130
|
+
return { active: err.activeTaskId };
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
126
133
|
sessionWatchRegistry?.dropSession(sessionId);
|
|
127
134
|
if (historyDeleted && spaceReclaimer !== undefined && spaceKey !== undefined) {
|
|
128
135
|
void spaceReclaimer.reclaim([{ sessionId, spaceKey }]).catch((err) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createUnpricedCeilingVerdict } from "./unpriced-ceiling-verdict.js";
|
|
2
2
|
import { createWebAdminRuntime } from "./web-admin.js";
|
|
3
3
|
import { composeExecutionLane, FILE_SOURCED_LANES } from "./execution-lane.js";
|
|
4
|
-
import {
|
|
4
|
+
import { configSourceKind } from "../config-provider.js";
|
|
5
5
|
import { logConfigDirMigration, migrateLocalConfigDirectory } from "../config-d-migration.js";
|
|
6
6
|
import { assertLocalConfigDirTrusted } from "../config-dir-trust.js";
|
|
7
7
|
import { defaultModelRefusal, refusedNameMintedRefusal } from "../model-entry-refusal.js";
|
|
@@ -10,7 +10,7 @@ import { judgeServedModelRoute } from "../model-route-endpoint.js";
|
|
|
10
10
|
import { servedModelTableLeg } from "../model-select.js";
|
|
11
11
|
export async function runStage02(ctx) {
|
|
12
12
|
const { config, localRoot, logger, metrics } = ctx;
|
|
13
|
-
const configSource =
|
|
13
|
+
const configSource = configSourceKind(config);
|
|
14
14
|
if (configSource === "local") {
|
|
15
15
|
await assertLocalConfigDirTrusted({ root: localRoot, declaredOwnerUid: config.configLocalOwnerUid });
|
|
16
16
|
logConfigDirMigration(logger, await migrateLocalConfigDirectory(localRoot));
|
|
@@ -10,7 +10,7 @@ import { SANDBOX_GIT_PLANE_OFF_TEXT } from "../sandbox-git-plane.js";
|
|
|
10
10
|
import { createPeerDesk } from "../peer-seat.js";
|
|
11
11
|
export async function runStage05(ctx) {
|
|
12
12
|
const { backend, config, instanceId, logger, metrics, sessionStore, taskAttachmentStore } = ctx;
|
|
13
|
-
const deviceLane = config.remoteExec?.provider === "device" ? createDeviceLane({ config, logger, metrics, backend, replicaId: instanceId }) : undefined;
|
|
13
|
+
const deviceLane = config.remoteExec?.provider === "device" ? createDeviceLane({ config, logger, metrics, backend, seat: sessionStore.executionPlacement, replicaId: instanceId }) : undefined;
|
|
14
14
|
const headOf = headReadOf(sessionStore);
|
|
15
15
|
const spaceCommits = backend !== undefined ? () => runStore : undefined;
|
|
16
16
|
const { perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv, fenceSessionOwner, dropSessionScoped, executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, readSandboxGitPlane, } = createExecutionEnv({ config, logger, metrics, taskAttachmentStore, ...(deviceLane ? { deviceLane } : {}), ...(spaceCommits !== undefined ? { spaceCommits } : {}), ...(headOf !== undefined ? { headOf } : {}), ...(backend !== undefined ? { runRows: () => runStore } : {}) });
|
|
@@ -29,7 +29,7 @@ export async function runStage05(ctx) {
|
|
|
29
29
|
const { registrations: deploymentMods, loaded: deploymentModsLoaded } = await assembleDeploymentMods({ metrics, ...(config.toolTrace ? { traceLogger: logger } : {}), brandIdentity: isBrandedDeployment(config), declaration: config.modsDeclaration, logger });
|
|
30
30
|
const peerDesk = createPeerDesk({ log: logger });
|
|
31
31
|
metrics.dynamicGauge("peer_registry_entries", "Peer desk entries (declared peer credentials registered for in-flight legs and the roster)", () => peerDesk.size().entries);
|
|
32
|
-
metrics.dynamicGauge("peer_registry_holders", "Peer desk host leases
|
|
32
|
+
metrics.dynamicGauge("peer_registry_holders", "Peer desk holders of entries (host leases and engine holds), summed across entries", () => peerDesk.size().holders);
|
|
33
33
|
return { ...ctx, deploymentMods, deploymentModsLoaded, deviceLane, peerDesk, dropSessionScoped, executionEnvFactory, fenceSessionOwner, lspManager, spaceReclaimer, sandboxPodReaper, sandboxGitPlane, perSessionCwd, perTaskImage, sendUserFileTaskEnvs, sessionEnvSelection, setSessionCwd, setSessionInheritEnv, setSessionShellEnv, worktreeReap };
|
|
34
34
|
}
|
|
35
35
|
//# sourceMappingURL=stage-05-execution-env.js.map
|