@sema-agent/server 7.56.0 → 7.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +101 -9
- package/dist/approval-ask-audit-store.d.ts +100 -1
- package/dist/approval-ask-audit-store.js +103 -2
- package/dist/approval-card.d.ts +148 -13
- package/dist/approval-card.js +82 -13
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +18 -0
- package/dist/auto-mode-face.d.ts +111 -0
- package/dist/auto-mode-face.js +99 -0
- package/dist/bench/s1/arms.js +5 -5
- package/dist/bench/s1/live-deps.js +11 -11
- package/dist/bench/s1/run-firm.js +3 -0
- package/dist/bench/s1/runner-ctx.d.ts +4 -0
- package/dist/bench/s1/runner-ctx.js +7 -0
- package/dist/boot/config-center.js +23 -1
- package/dist/boot/coordinators.js +13 -1
- package/dist/boot/parked-revive-gate.d.ts +7 -2
- package/dist/boot/parked-revive-gate.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +3 -1
- package/dist/boot/runner-deps.d.ts +12 -0
- package/dist/boot/runner-deps.js +28 -3
- package/dist/boot/runtime-caps.d.ts +19 -9
- package/dist/boot/runtime-caps.js +49 -21
- package/dist/boot/session-shell-gate-registry.d.ts +39 -0
- package/dist/boot/session-shell-gate-registry.js +21 -0
- package/dist/config-catalog.js +14 -5
- package/dist/config-types.d.ts +41 -10
- package/dist/config.d.ts +21 -0
- package/dist/config.js +40 -5
- package/dist/http/route-ctx.d.ts +75 -0
- package/dist/http/routes/a2a-serve.js +5 -3
- package/dist/http/routes/approvals-assistant.js +19 -6
- package/dist/http/routes/capabilities.js +21 -4
- package/dist/http/routes/memory-origin.d.ts +2 -2
- package/dist/http/routes/rules.js +3 -2
- package/dist/http/routes/runs.d.ts +0 -14
- package/dist/http/routes/runs.js +20 -8
- package/dist/http/routes/tasks.js +34 -8
- package/dist/http/routes/workflows.js +19 -4
- package/dist/http/server.d.ts +11 -1
- package/dist/http/server.js +215 -43
- package/dist/http/wire-types.d.ts +9 -4
- package/dist/leader/diffout.js +2 -1
- package/dist/leader/endpoint.js +45 -12
- package/dist/leader/fanout.js +6 -5
- package/dist/leader/leader.js +17 -14
- package/dist/leader/merge.js +17 -12
- package/dist/leader/planner.js +3 -2
- package/dist/leader/repair-oracle.js +6 -4
- package/dist/leader/repair-wire.js +5 -3
- package/dist/leader/wire.d.ts +30 -1
- package/dist/leader/wire.js +36 -19
- package/dist/main.js +43 -7
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +10 -0
- package/dist/observability/fail-open.d.ts +40 -0
- package/dist/observability/fail-open.js +19 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.d.ts +120 -0
- package/dist/observability/run-terminal-log.js +360 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
- package/dist/orchestration/workflow-completion-inbox.js +8 -0
- package/dist/permission-rule-vocab.d.ts +40 -0
- package/dist/permission-rule-vocab.js +17 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
- package/dist/plugins/checkpoint-store-sql.js +19 -3
- package/dist/plugins/file-run-store.d.ts +3 -34
- package/dist/plugins/file-run-store.js +19 -0
- package/dist/plugins/local-checkpoint-store.d.ts +10 -0
- package/dist/plugins/local-checkpoint-store.js +3 -0
- package/dist/plugins/memory-run-store.d.ts +3 -15
- package/dist/plugins/memory-run-store.js +19 -0
- package/dist/plugins/permission-rule-store-file.js +8 -3
- package/dist/plugins/permission-rule-store-sql.js +16 -10
- package/dist/plugins/remote-scratchpad.js +3 -2
- package/dist/plugins/run-store-sql.d.ts +3 -42
- package/dist/plugins/run-store-sql.js +30 -1
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +66 -1
- package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
- package/dist/plugins/workflow-run-store-sql.js +10 -2
- package/dist/rules-consent.js +20 -13
- package/dist/run-cancel-context.d.ts +13 -0
- package/dist/run-cancel-context.js +15 -0
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +4 -1
- package/dist/runs.js +41 -10
- package/dist/runtime-caps-resolver.d.ts +133 -17
- package/dist/runtime-caps-resolver.js +41 -3
- package/dist/task-settings.d.ts +57 -3
- package/dist/task-settings.js +78 -5
- package/dist/task-workflow.d.ts +32 -2
- package/dist/task-workflow.js +8 -3
- package/dist/tool-approval.d.ts +26 -3
- package/dist/tool-approval.js +96 -13
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +13 -4
- package/dist/trace/ledger-sink.js +14 -6
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +10 -1
- package/dist/trace/redact.d.ts +22 -11
- package/dist/trace/redact.js +655 -20
- package/dist/trace/sema-provenance.d.ts +26 -0
- package/dist/trace/sema-provenance.js +11 -0
- package/dist/turn-activity.d.ts +32 -2
- package/dist/turn-activity.js +29 -4
- package/package.json +2 -2
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { resolveTaskModel } from "@sema-agent/core";
|
|
6
|
+
export function classifierRouteFor(deps) {
|
|
7
|
+
try {
|
|
8
|
+
return { id: resolveTaskModel({ modelRole: "classifier" }, deps).model.id };
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
return { id: undefined, fault: err instanceof Error ? err.message : String(err) };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const AUTO_MODE_UNARMED_REASONS = ["mode_not_auto", "deployment_incapable", "org_denied", "local_denied", "settings_denied", "resolver_fault"];
|
|
15
|
+
export const AUTO_MODE_UNARMED_REASON_SEMANTICS = {
|
|
16
|
+
mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\";座缺席)——只属意图武装式,旧式 core 不读模式",
|
|
17
|
+
deployment_incapable: "装配面没挂 RunnerDeps.autoMode 分类器席位(本部署/本进程形不具备分类器);旧式 core 下另含「无 center 源 = 零授予路径」",
|
|
18
|
+
org_denied: "per-principal caps.autoMode === false 且本地 deny 未设:center 显式 deny / center 硬失败 fail-closed 拒项;旧式 core 下另含「center 在场但未授予」",
|
|
19
|
+
local_denied: "本地 catalog 键 PERMISSIONS_DISABLE_AUTO_MODE=true(center/settings 键名 permissions.disableAutoMode)把 caps.autoMode 折成 false",
|
|
20
|
+
settings_denied: "settings 层 permissions.disableAutoMode 取 disable 或 true(两套已发布拼写同义;用户/managed 层 kill-switch)把本 run 折成非 auto —— capabilities 查询串不带 settings,本词由 run 级面产([6128] PM 裁定第六员)",
|
|
21
|
+
resolver_fault: "per-principal caps resolver 抛错/答非记录(core 同形全拒:autoMode:false + onError phase config)",
|
|
22
|
+
};
|
|
23
|
+
function armingProbeFailure(what) {
|
|
24
|
+
return new Error(`auto-mode arming probe cannot decide (${what}). This probe answers ONE question — does the INSTALLED @sema-agent/core arm ` +
|
|
25
|
+
`auto mode from the task's INTENT seat (core #521: spec.autoModeRequested ∧ seat ∧ caps !== false), or from the older ` +
|
|
26
|
+
`"org grants it" polarity (runtimeCaps.autoMode === true ∧ seat)? The two polarities are OPPOSITE on the permission axis, ` +
|
|
27
|
+
`so guessing would silently put this deployment on the wrong one — either arming runs the org never granted, or reporting ` +
|
|
28
|
+
`"not armed" at /v1/capabilities while the engine really is arming (#157: no silent fail-open on the permission axis). ` +
|
|
29
|
+
`Refusing to start instead. Fix: reinstall the engine (rm -rf node_modules && npm ci) so the package ships its full dist, ` +
|
|
30
|
+
`or — if core moved/renamed the arming site — update the probe in src/auto-mode-face.ts to read the new one.`);
|
|
31
|
+
}
|
|
32
|
+
export function coreArmsAutoOnIntentProbe(distDir) {
|
|
33
|
+
const dist = distDir ?? dirname(createRequire(import.meta.url).resolve("@sema-agent/core"));
|
|
34
|
+
const armingSite = join(dist, "core", "runner", "prepare-task.js");
|
|
35
|
+
let armingSrc;
|
|
36
|
+
try {
|
|
37
|
+
armingSrc = readFileSync(armingSite, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw armingProbeFailure(`the engine's arming site is unreadable at ${armingSite}: ${err instanceof Error ? err.message : String(err)}`);
|
|
41
|
+
}
|
|
42
|
+
if (armingSrc.includes(CORE_AUTO_MODE_INTENT_SEAT))
|
|
43
|
+
return true;
|
|
44
|
+
const snapPath = join(dist, "..", "test", "export-surface.snapshot.json");
|
|
45
|
+
let snap;
|
|
46
|
+
try {
|
|
47
|
+
const parsed = ExportSurfaceSnapshotSchema.safeParse(JSON.parse(readFileSync(snapPath, "utf8")));
|
|
48
|
+
if (!parsed.success)
|
|
49
|
+
throw new Error(`export-surface snapshot shape mismatch: ${parsed.error.issues.map((i) => i.path.join(".") + ":" + i.message).join("; ")}`);
|
|
50
|
+
snap = parsed.data;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
throw armingProbeFailure(`the arming seat is absent from ${armingSite} and the corroborating export-surface snapshot is unreadable at ${snapPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
54
|
+
}
|
|
55
|
+
if (!snap.exports || typeof snap.exports !== "object")
|
|
56
|
+
throw armingProbeFailure(`core export-surface snapshot malformed at ${snapPath}`);
|
|
57
|
+
if (typeof snap.exports.mailboxCrossProcessMountVerdict === "string") {
|
|
58
|
+
throw armingProbeFailure(`contradiction — the installed core's export surface is at/after the batch that shipped #521, but its arming site ` +
|
|
59
|
+
`(${armingSite}) never names \`${CORE_AUTO_MODE_INTENT_SEAT}\`, so the engine either moved the arming site or renamed the seat`);
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const CORE_AUTO_MODE_INTENT_SEAT = "autoModeRequested";
|
|
64
|
+
let armsOnIntentMemo;
|
|
65
|
+
const ExportSurfaceSnapshotSchema = z.object({ exports: z.record(z.string(), z.string()).optional() }).passthrough();
|
|
66
|
+
export function coreArmsAutoOnIntent() {
|
|
67
|
+
if (armsOnIntentMemo === undefined)
|
|
68
|
+
armsOnIntentMemo = coreArmsAutoOnIntentProbe();
|
|
69
|
+
return armsOnIntentMemo;
|
|
70
|
+
}
|
|
71
|
+
export async function judgeAutoModeArming(face, input) {
|
|
72
|
+
const route = face?.seatMounted ? face.classifierModel?.() : undefined;
|
|
73
|
+
const model = route !== undefined ? { model: route } : {};
|
|
74
|
+
if (face?.engineArmsOnIntent && input.requestedMode !== undefined && input.requestedMode !== "auto")
|
|
75
|
+
return { armed: false, reason: "mode_not_auto", ...model };
|
|
76
|
+
if (face === undefined || !face.seatMounted)
|
|
77
|
+
return { armed: false, reason: "deployment_incapable" };
|
|
78
|
+
let caps;
|
|
79
|
+
try {
|
|
80
|
+
caps = face.resolveCaps !== undefined ? await face.resolveCaps(input.principal) : undefined;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return { armed: false, reason: "resolver_fault", ...model, fault: err instanceof Error ? err.message : String(err) };
|
|
84
|
+
}
|
|
85
|
+
const deniedWord = input.localDeny ? "local_denied" : "org_denied";
|
|
86
|
+
if (face.engineArmsOnIntent) {
|
|
87
|
+
if (caps?.autoMode === false)
|
|
88
|
+
return { armed: false, reason: deniedWord, ...model };
|
|
89
|
+
return { armed: true, ...model };
|
|
90
|
+
}
|
|
91
|
+
if (caps?.autoMode === true)
|
|
92
|
+
return { armed: true, ...model };
|
|
93
|
+
if (caps?.autoMode === false)
|
|
94
|
+
return { armed: false, reason: deniedWord, ...model };
|
|
95
|
+
if (!input.entitlementSource)
|
|
96
|
+
return { armed: false, reason: "deployment_incapable", ...model };
|
|
97
|
+
return { armed: false, reason: "org_denied", ...model };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=auto-mode-face.js.map
|
package/dist/bench/s1/arms.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { leafBudgetFields } from "./runner-ctx.js";
|
|
1
|
+
import { leafBudgetFields, s1DebugGit } from "./runner-ctx.js";
|
|
2
2
|
import {} from "./tasks.js";
|
|
3
3
|
import { decideApproval, decidePlan } from "./reviewer.js";
|
|
4
4
|
import { oracleTrulyCorrect, pickOracleVerdicts, armTag, S1_SCHEMA_VERSION, S1_CORE_VERSION, } from "./row.js";
|
|
@@ -164,7 +164,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
164
164
|
vr = await deps.runWithVerification(implSpec);
|
|
165
165
|
}
|
|
166
166
|
catch (e) {
|
|
167
|
-
if (
|
|
167
|
+
if (s1DebugGit())
|
|
168
168
|
console.error("[S1 SOLO infra throw]", e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
169
169
|
return assembleRow({
|
|
170
170
|
arm, trap, seed, ctx,
|
|
@@ -176,7 +176,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
176
176
|
finishedAt: ctx.clock.now(),
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
|
-
if (
|
|
179
|
+
if (s1DebugGit())
|
|
180
180
|
console.error("[S1 SOLO vr.status]", vr.status, "| stats:", JSON.stringify(vr.stats)?.slice(0, 200));
|
|
181
181
|
const oracle = pickOracleVerdicts(await deps.runOracle());
|
|
182
182
|
return assembleRow({
|
|
@@ -210,7 +210,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
catch (e) {
|
|
213
|
-
if (
|
|
213
|
+
if (s1DebugGit())
|
|
214
214
|
console.error(`[S1 SUP infra throw driver=${trap.supDriver}]`, e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
215
215
|
return assembleRow({
|
|
216
216
|
arm, trap, seed, ctx,
|
|
@@ -222,7 +222,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
|
|
|
222
222
|
finishedAt: ctx.clock.now(),
|
|
223
223
|
});
|
|
224
224
|
}
|
|
225
|
-
if (
|
|
225
|
+
if (s1DebugGit())
|
|
226
226
|
console.error(`[S1 SUP vr.status driver=${trap.supDriver}]`, vr.status, "| repairTerminal:", repairTerminal, "| verdict:", vr.verification?.verdict, "| unverifiedReason:", vr.verification?.unverifiedReason, "| checkpointGate:", JSON.stringify(vr.checkpointGate), "| error:", vr.error, "| stats:", JSON.stringify(vr.stats)?.slice(0, 180));
|
|
227
227
|
const oracle = pickOracleVerdicts(await deps.runOracle());
|
|
228
228
|
const runStatus = classifyRunStatus({ status: vr.status, repairTerminal });
|
|
@@ -7,7 +7,7 @@ import { createDurableAskPolicy } from "../../approval.js";
|
|
|
7
7
|
import { createLeaderRunner } from "../../leader/wire.js";
|
|
8
8
|
import { runOracle as runStandaloneOracle } from "./oracle.js";
|
|
9
9
|
import { makeRepairOracle } from "./repair-oracle-adapter.js";
|
|
10
|
-
import { assertDistinctEnvs } from "./runner-ctx.js";
|
|
10
|
+
import { assertDistinctEnvs, s1DebugGit } from "./runner-ctx.js";
|
|
11
11
|
const W = "/home/user";
|
|
12
12
|
const REPO = `${W}/repo`;
|
|
13
13
|
const BASE_REF = "s1-base";
|
|
@@ -112,7 +112,7 @@ async function transferWorkerTreeToGrader(worker, grader) {
|
|
|
112
112
|
const ext = await sh(grader)(`set -e; test -s /tmp/s1-wt.tar; rm -rf ${REPO}; mkdir -p ${REPO}; tar -xf /tmp/s1-wt.tar -C ${REPO}; echo "EXTRACTED=$(ls -A ${REPO} | wc -l)"`);
|
|
113
113
|
if (ext.code !== 0 || !ext.out.includes("EXTRACTED="))
|
|
114
114
|
throw new Error(`s1 grader tar extract failed: ${ext.out.slice(-400)}`);
|
|
115
|
-
if (
|
|
115
|
+
if (s1DebugGit())
|
|
116
116
|
console.error(`[S1_DEBUG_TAR] head=${head.slice(0, 8)} base=${base.slice(0, 8)} tarBytes=${buf.length} ${ext.out.slice(-60)}`);
|
|
117
117
|
return { head, base };
|
|
118
118
|
}
|
|
@@ -141,7 +141,7 @@ async function importBundleBinaryToGrader(grader, tarBuffer, shas) {
|
|
|
141
141
|
const ext = await sh(grader)(`set -e; test -s /tmp/s1-wt.tar; rm -rf ${REPO}; mkdir -p ${REPO}; tar -xf /tmp/s1-wt.tar -C ${REPO}; echo "EXTRACTED=$(ls -A ${REPO} | wc -l)"`);
|
|
142
142
|
if (ext.code !== 0 || !ext.out.includes("EXTRACTED="))
|
|
143
143
|
throw new Error(`s1 grader tar extract failed: ${ext.out.slice(-400)}`);
|
|
144
|
-
if (
|
|
144
|
+
if (s1DebugGit())
|
|
145
145
|
console.error(`[S1_DEBUG_TAR team] head=${shas.head.slice(0, 8)} base=${shas.base.slice(0, 8)} ${ext.out.slice(-60)}`);
|
|
146
146
|
return shas;
|
|
147
147
|
}
|
|
@@ -196,7 +196,7 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
|
|
|
196
196
|
state.shas = await transferWorkerTreeToGrader(worker, grader);
|
|
197
197
|
const task = { ...oracleTaskFromTrap(trap), ...(state.shas ? { deliveredShas: state.shas } : {}) };
|
|
198
198
|
const v = await runStandaloneOracle(grader, graderTransport(grader), worker, task);
|
|
199
|
-
if (
|
|
199
|
+
if (s1DebugGit())
|
|
200
200
|
console.error(`[S1_DEBUG_ORACLE ${trap.id}] delivered=${v.delivered} tests=${v.hiddenTestsGreen} build=${v.buildPassed} inv=${v.invariantsOk} | raw=`, JSON.stringify(v.raw)?.slice(0, 700));
|
|
201
201
|
return v;
|
|
202
202
|
};
|
|
@@ -250,27 +250,27 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
|
|
|
250
250
|
execFileSync("git", ["clone", "-q", "--branch", branch, remoteUrl, cloneDir], { stdio: ["ignore", "ignore", "pipe"] });
|
|
251
251
|
}
|
|
252
252
|
catch (e1) {
|
|
253
|
-
if (
|
|
253
|
+
if (s1DebugGit())
|
|
254
254
|
console.error(`[S1_DEBUG_IMPORT] cp clone --branch ${branch} failed: ${e1.stderr?.toString().slice(-200)}; retrying default`);
|
|
255
255
|
execFileSync("git", ["clone", "-q", remoteUrl, cloneDir], { stdio: ["ignore", "ignore", "pipe"] });
|
|
256
256
|
}
|
|
257
257
|
const baseHash = execFileSync("git", ["-C", cloneDir, "rev-list", "--max-parents=0", "HEAD"]).toString().trim().split("\n").pop().trim();
|
|
258
258
|
const head = execFileSync("git", ["-C", cloneDir, "rev-parse", "HEAD"]).toString().trim();
|
|
259
259
|
if (!/^[0-9a-f]{40}$/.test(baseHash) || head === baseHash) {
|
|
260
|
-
if (
|
|
260
|
+
if (s1DebugGit())
|
|
261
261
|
console.error(`[S1_DEBUG_IMPORT] cp integrated tree not delivered: head=${head} base=${baseHash}`);
|
|
262
262
|
return false;
|
|
263
263
|
}
|
|
264
264
|
const bundlePath = join(tmp, "team.bundle");
|
|
265
265
|
execFileSync("git", ["-C", cloneDir, "archive", "--format=tar", "-o", bundlePath, "HEAD"], { stdio: ["ignore", "ignore", "pipe"] });
|
|
266
266
|
const buffer = readFileSync(bundlePath);
|
|
267
|
-
if (
|
|
267
|
+
if (s1DebugGit())
|
|
268
268
|
console.error(`[S1_DEBUG_IMPORT] cp integrated head=${head} base=${baseHash} tar bytes=${buffer.length}`);
|
|
269
269
|
state.teamBundle = { buffer, head, base: baseHash };
|
|
270
270
|
return true;
|
|
271
271
|
}
|
|
272
272
|
catch (e) {
|
|
273
|
-
if (
|
|
273
|
+
if (s1DebugGit())
|
|
274
274
|
console.error(`[S1_DEBUG_IMPORT] control-plane THREW:`, e instanceof Error ? e.message : String(e));
|
|
275
275
|
return false;
|
|
276
276
|
}
|
|
@@ -361,7 +361,7 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
|
|
|
361
361
|
process.env.LEADER_CONFLICT_ROUNDS = process.env.LEADER_CONFLICT_ROUNDS ?? "2";
|
|
362
362
|
const run = createLeaderRunner({
|
|
363
363
|
brain, models, roles, pricing, e2bApiKey: rt.e2bApiKey, workspace: W,
|
|
364
|
-
...(
|
|
364
|
+
...(s1DebugGit() ? { logger: { warn: (m, x) => console.error(`[S1_DEBUG_LEADER warn] ${m}`, x ? JSON.stringify(x).slice(0, 300) : ""), info: (m, x) => console.error(`[S1_DEBUG_LEADER info] ${m}`, x ? JSON.stringify(x).slice(0, 200) : "") } } : {}),
|
|
365
365
|
});
|
|
366
366
|
const teamSeedWrite = (trap.seedFiles ?? [])
|
|
367
367
|
.map((f) => {
|
|
@@ -415,13 +415,13 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
|
|
|
415
415
|
});
|
|
416
416
|
}
|
|
417
417
|
catch (e) {
|
|
418
|
-
if (
|
|
418
|
+
if (s1DebugGit())
|
|
419
419
|
console.error(`[S1_DEBUG_LEADER ${trap.id}] run() THREW:`, e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
420
420
|
killDaemon();
|
|
421
421
|
rmSync(baseDir, { recursive: true, force: true });
|
|
422
422
|
return { ok: false, reports: [], infraFailed: true };
|
|
423
423
|
}
|
|
424
|
-
if (
|
|
424
|
+
if (s1DebugGit())
|
|
425
425
|
console.error(`[S1_DEBUG_LEADER ${trap.id}] ok=${res.ok} error=${JSON.stringify(res.error)} cancelled=${res.cancelled} merge=${JSON.stringify(res.merge)} repairTerminal=${res.repairTerminal} reports=${(res.reports ?? []).length} reportsDetail=${JSON.stringify((res.reports ?? []).map((r) => ({ w: r.workerId, ok: r.ok, err: r.error })))?.slice(0, 400)}`);
|
|
426
426
|
let imported = false;
|
|
427
427
|
const merged = res.merge?.ok === true;
|
|
@@ -7,6 +7,7 @@ import { makeBenchClock } from "./runner-ctx.js";
|
|
|
7
7
|
import { buildS1Report, S1_SCHEMA_VERSION, S1_CORE_VERSION } from "./row.js";
|
|
8
8
|
import { armTag } from "./row.js";
|
|
9
9
|
import { buildLiveDeps, liveRuntimeConfigFromEnv } from "./live-deps.js";
|
|
10
|
+
import { setS1DebugGit } from "./runner-ctx.js";
|
|
10
11
|
export function enumerateCells(seeds, filter = {}) {
|
|
11
12
|
const cells = [];
|
|
12
13
|
const source = filter.only ? ALL_TRAPS : FIRM_TRAPS;
|
|
@@ -184,6 +185,7 @@ function parseArgs(argv) {
|
|
|
184
185
|
const cellTimeoutMs = positiveIntArg(get("cell-timeout-ms"), 15 * 60_000, "--cell-timeout-ms");
|
|
185
186
|
return {
|
|
186
187
|
dryRun: argv.includes("--dry-run"),
|
|
188
|
+
debugGit: argv.includes("--debug-git"),
|
|
187
189
|
seeds,
|
|
188
190
|
cellTimeoutMs,
|
|
189
191
|
ledgerPath: get("ledger") ?? path.resolve(`./s1-out/${runId}.ledger.jsonl`),
|
|
@@ -234,6 +236,7 @@ function dryRunMockFactory() {
|
|
|
234
236
|
}
|
|
235
237
|
export async function main(argv = process.argv.slice(2)) {
|
|
236
238
|
const args = parseArgs(argv);
|
|
239
|
+
setS1DebugGit(args.debugGit);
|
|
237
240
|
const liveRuntime = liveRuntimeConfigFromEnv();
|
|
238
241
|
if (!args.dryRun && !liveRuntime) {
|
|
239
242
|
console.error("[s1 run-firm] LIVE path refused: E2B_API_KEY + DEEPSEEK_API_KEY are required (the brain gateway). " +
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
* + the clock; a deterministic shape test exercises it with a MOCK runner (no real agents, no keys).
|
|
18
18
|
*/
|
|
19
19
|
import type { ExecutionEnv } from "@sema-agent/core";
|
|
20
|
+
/** 由 bench 入口(`run-firm.ts` 的 `--debug-git`)翻开;不调 = 关。 */
|
|
21
|
+
export declare function setS1DebugGit(on: boolean): void;
|
|
22
|
+
/** tar/import/oracle/leader 四族 stderr 追踪开着没有(读侧单点)。 */
|
|
23
|
+
export declare function s1DebugGit(): boolean;
|
|
20
24
|
/**
|
|
21
25
|
* The budget block the budget-match guard compares. NOT a s1.v1 RawRow field (the contract carries no budget) —
|
|
22
26
|
* it is a PRODUCER-side fairness descriptor (one per emitted cell). `teamWorkerBudgetSumUsd` is the bigger-pie
|
|
@@ -14,6 +14,7 @@ import { configLkgEnabled } from "../config.js";
|
|
|
14
14
|
import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg } from "../config-lkg.js";
|
|
15
15
|
import { centerManagedConfigKeys } from "../config-center/apply-effective.js";
|
|
16
16
|
import { createConfigProvider, raceBootFetch, BOOT_FETCH_DEFERRED } from "../config-provider.js";
|
|
17
|
+
import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
|
|
17
18
|
import { createKeyResolver } from "../key-resolver.js";
|
|
18
19
|
import { ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
19
20
|
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, planeHasActiveTiers, fetchPromptArtifact } from "../config-center/facade.js";
|
|
@@ -366,12 +367,32 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
366
367
|
try {
|
|
367
368
|
const fileRx = await loadRemoteExec(localRoot);
|
|
368
369
|
if (fileRx) {
|
|
370
|
+
const preemptedLane = config.remoteExec?.provider ?? "in-process";
|
|
371
|
+
const isolatedLane = (lane) => {
|
|
372
|
+
const word = executionLaneOf(lane === "in-process" ? undefined : lane);
|
|
373
|
+
return word !== undefined && executionLaneCaps(word).isolation === "supported";
|
|
374
|
+
};
|
|
375
|
+
const disclosePreemption = (winner) => {
|
|
376
|
+
if (winner === preemptedLane)
|
|
377
|
+
return;
|
|
378
|
+
const downgrade = isolatedLane(preemptedLane) && !isolatedLane(winner);
|
|
379
|
+
logger.warn("remote_exec_lane_preempted_by_file", {
|
|
380
|
+
preemptedLane,
|
|
381
|
+
effectiveLane: winner,
|
|
382
|
+
root: localRoot,
|
|
383
|
+
isolationDowngrade: downgrade,
|
|
384
|
+
note: `config.d/remote-exec.json (CONFIG_PROVIDER=local, file-wins) replaced the env-derived execution lane ` +
|
|
385
|
+
`"${preemptedLane}" with "${winner}" — any REMOTE_EXEC/lane coordinate env for "${preemptedLane}" is now INERT` +
|
|
386
|
+
(downgrade ? `. This is an ISOLATION DOWNGRADE: tasks run on a non-isolated target. Delete/fix the file, or unset the lane env if the file is the intended source.` : `. Delete/fix the file, or unset the lane env if the file is the intended source.`),
|
|
387
|
+
});
|
|
388
|
+
};
|
|
369
389
|
if (fileRx.provider === "host") {
|
|
370
390
|
config.remoteExec = {
|
|
371
391
|
provider: "host",
|
|
372
392
|
...(fileRx.workdir ? { workspaceBase: fileRx.workdir } : {}),
|
|
373
393
|
...(fileRx.commandTimeoutMs != null ? { commandTimeoutMs: fileRx.commandTimeoutMs } : {}),
|
|
374
394
|
};
|
|
395
|
+
disclosePreemption("host");
|
|
375
396
|
logger.info("remote_exec_from_file", { provider: "host", root: localRoot });
|
|
376
397
|
}
|
|
377
398
|
else if (fileRx.provider === "local-docker") {
|
|
@@ -383,10 +404,11 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
383
404
|
...(fileRx.cpus != null ? { cpus: fileRx.cpus } : {}),
|
|
384
405
|
...(fileRx.network ? { network: fileRx.network } : {}),
|
|
385
406
|
};
|
|
407
|
+
disclosePreemption("local-docker");
|
|
386
408
|
logger.info("remote_exec_from_file", { provider: "local-docker", root: localRoot, image: fileRx.image });
|
|
387
409
|
}
|
|
388
410
|
else {
|
|
389
|
-
logger.warn("remote_exec_file_isolated_lane_unwired", { provider: fileRx.provider, note: "e2b/k8s/ssh/adb are sourced from REMOTE_EXEC env until file-source NAME→value resolution lands (center field map)" });
|
|
411
|
+
logger.warn("remote_exec_file_isolated_lane_unwired", { provider: fileRx.provider, effectiveLane: preemptedLane, note: "e2b/k8s/ssh/adb are sourced from REMOTE_EXEC env until file-source NAME→value resolution lands (center field map) — the env-derived lane above stays in force" });
|
|
390
412
|
}
|
|
391
413
|
}
|
|
392
414
|
}
|
|
@@ -26,6 +26,17 @@ export function createLiveCoordinators(ctx) {
|
|
|
26
26
|
checkpointStore: backend?.checkpoint !== undefined,
|
|
27
27
|
durableApproval: config.durableApproval === true,
|
|
28
28
|
toolApproval: config.toolApprovalEnabled === true,
|
|
29
|
+
askWindowMs: config.streamApproval.windowMs,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (config.toolApprovalEnabled === true && config.streamApproval.windowMs === 0) {
|
|
33
|
+
logger.warn("stream_ask_window_zero", {
|
|
34
|
+
unattendedPolicy: config.unattendedApprovalPolicy,
|
|
35
|
+
parkFacility: durableEnabled,
|
|
36
|
+
streamApproval: streamApprovalGate.active ? "active" : streamApprovalGate.reason,
|
|
37
|
+
note: durableEnabled && config.unattendedApprovalPolicy === "park"
|
|
38
|
+
? "STREAM_ASK_WINDOW_MS=0 — no approval card is emitted on ANY leg; every gated ask parks immediately (answer it via POST /v1/approvals/:sessionId/decide). Set a positive window (ms) to get live cards back."
|
|
39
|
+
: "STREAM_ASK_WINDOW_MS=0 — no approval card is emitted on ANY leg, and this deployment has no park destination (UNATTENDED_APPROVAL_POLICY=deny and/or no checkpoint store), so every gated tool call is refused fail-closed. Set a positive window (ms), or wire DURABLE_APPROVAL + a checkpoint store.",
|
|
29
40
|
});
|
|
30
41
|
}
|
|
31
42
|
const approvalAskAudit = config.toolApprovalEnabled && backend?.kind === "local" && config.localDataRoot !== undefined
|
|
@@ -45,8 +56,9 @@ export function createLiveCoordinators(ctx) {
|
|
|
45
56
|
}
|
|
46
57
|
const toolApproval = config.toolApprovalEnabled
|
|
47
58
|
? new ToolApprovalCoordinator({
|
|
59
|
+
ttlMs: config.streamApproval.windowMs,
|
|
48
60
|
...(streamApprovalGate.active
|
|
49
|
-
? { askStore: streamApprovalGate.askStore,
|
|
61
|
+
? { askStore: streamApprovalGate.askStore, unreachedTtlMs: config.streamApproval.unreachedTtlMs }
|
|
50
62
|
: {}),
|
|
51
63
|
windowMarginMs: config.streamAskWindowMarginMs,
|
|
52
64
|
admitMaxPerTask: config.streamApproval.admitMaxPerTask,
|
|
@@ -57,9 +57,14 @@ export interface ParkedReviveGateDeps {
|
|
|
57
57
|
* 手抄一个等价签名会让 core 改这条 seam 的那天变成静默漂移而不是编译期事件)。
|
|
58
58
|
*/
|
|
59
59
|
readonly resolveRuntimeCaps?: RunnerDeps["runtimeCapsResolver"];
|
|
60
|
-
/** `RunnerDeps.autoMode` 分类器面(信任门半场)在不在场 —— core 的武装条件是
|
|
61
|
-
* `runtimeCaps.autoMode === true` **∧**
|
|
60
|
+
/** `RunnerDeps.autoMode` 分类器面(信任门半场)在不在场 —— core 7.2.0 的武装条件是
|
|
61
|
+
* `runtimeCaps.autoMode === true` **∧** 这一面在场(prepare-task 原式)。⚠️ [ref] / core [ref] 起武装式换成
|
|
62
|
+
* `spec.autoModeRequested === true ∧ 席位 ∧ runtimeCaps?.autoMode !== false`——本文件的镜像
|
|
63
|
+
* {@link autoModePostureOf} **未随改**(赎回行无父 run 的 auto 意图可读),见该函数头注的未闭环登记。 */
|
|
62
64
|
readonly autoModeSeatMounted?: boolean;
|
|
65
|
+
/** [ref]:父 run 的 auto 意图(`TaskSpec.autoModeRequested` / 链位)——意图武装式下重建 decider 的第三项。⚠️ 今日
|
|
66
|
+
* **无持久源**(core [ref] 链位 live-only;RunRecord 无 body),生产装配不递 ⇒ 恒缺席;座留给 core 持久化到货。 */
|
|
67
|
+
readonly parentAutoModeIntent?: ((row: BackgroundAgentRecord) => boolean) | undefined;
|
|
63
68
|
readonly logger: {
|
|
64
69
|
info(event: string, fields?: Record<string, unknown>): void;
|
|
65
70
|
warn(event: string, fields?: Record<string, unknown>): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { coreArmsAutoOnIntent } from "../auto-mode-face.js";
|
|
1
2
|
import { join } from "node:path";
|
|
2
3
|
import { createAutoModeDecider, NodeExecutionEnv } from "@sema-agent/core";
|
|
3
4
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
@@ -66,8 +67,18 @@ async function resolveCapsFor(deps, identity) {
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
function autoModePostureOf(deps, row, caps) {
|
|
69
|
-
if (deps.autoModeSeatMounted !== true
|
|
70
|
+
if (deps.autoModeSeatMounted !== true)
|
|
70
71
|
return {};
|
|
72
|
+
if (!coreArmsAutoOnIntent()) {
|
|
73
|
+
if (caps?.autoMode !== true)
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
if (caps?.autoMode === false)
|
|
78
|
+
return {};
|
|
79
|
+
if (deps.parentAutoModeIntent?.(row) !== true)
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
71
82
|
return { autoMode: { decider: createUnreachableAncestorClassifier(deps, row) } };
|
|
72
83
|
}
|
|
73
84
|
function createUnreachableAncestorClassifier(deps, row) {
|
|
@@ -50,6 +50,9 @@ export interface ResolveSpecCtx {
|
|
|
50
50
|
hookWakeBus: {
|
|
51
51
|
deliver?: (sessionId: string, text: string) => Promise<boolean>;
|
|
52
52
|
};
|
|
53
|
+
/** [ref] 件⑤:per-session「显式 bypassPermissions」登记簿的写腿(`boot/session-shell-gate-registry.ts`);
|
|
54
|
+
* 阶段④ 算出 effMode 的那一行登记,runner-deps 的 config 忠告汇点按 sessionId 回查定级。缺席(桩)= 不登记。 */
|
|
55
|
+
noteSessionShellGate?: ((sessionId: string | undefined, explicitOff: boolean) => void) | undefined;
|
|
53
56
|
resumeAnchorStore: ReturnType<StoreBackend["resumeAnchor"]> | undefined;
|
|
54
57
|
ownerAware: OwnerAwareSessionStore;
|
|
55
58
|
taskLimitCaps: {
|
|
@@ -36,7 +36,7 @@ import { redactSecrets } from "../trace/redact.js";
|
|
|
36
36
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
37
37
|
import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
|
|
38
38
|
export function createResolveSpec(ctx) {
|
|
39
|
-
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
|
|
39
|
+
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, noteSessionShellGate, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
|
|
40
40
|
assertGuardPatternsUsable(config);
|
|
41
41
|
const onlySensitiveBaseline = buildOnlySensitiveBaselineWarning(config, { durableEnabled, singleUserAutoAcceptBaseline });
|
|
42
42
|
if (onlySensitiveBaseline)
|
|
@@ -52,6 +52,7 @@ export function createResolveSpec(ctx) {
|
|
|
52
52
|
assertRequestA2aUnlocked(body.a2aPeers, lockedKeys);
|
|
53
53
|
const gated = await gateScenarioAndAppend(body, auth, opts);
|
|
54
54
|
const effMode = effectivePermissionMode(body, gated.parsedSettings.settings);
|
|
55
|
+
noteSessionShellGate?.(auth?.sessionId, effMode === "bypassPermissions");
|
|
55
56
|
const lane = bindSettingsCwdEnvAndModel(body, auth, gated, effMode, opts);
|
|
56
57
|
const anchors = await resolveHistoryAnchors(body, auth);
|
|
57
58
|
const spec = assembleSpecLiteral(body, auth, req, opts, { ...gated, ...lane, ...anchors }, effMode);
|
|
@@ -278,6 +279,7 @@ export function createResolveSpec(ctx) {
|
|
|
278
279
|
additionalReadDirectories,
|
|
279
280
|
enablePlanMode: config.planModeEnabled ? true : undefined,
|
|
280
281
|
...(effMode ? { shellGate: shellGateForMode(effMode) } : {}),
|
|
282
|
+
...(effMode === "auto" ? { autoModeRequested: true } : {}),
|
|
281
283
|
selfOrchestration: selfOrchestrationFromBody({ selfOrchestration: body.selfOrchestration === true || parsedSettings.settings?.ultracode === true }, config, Boolean(centerRuntimeCapsResolver)),
|
|
282
284
|
forwardSubagentEvents: body.forwardSubagentEvents === true ? true : undefined,
|
|
283
285
|
retainSubagentSessions: normalizeRetainSubagentSessions(body.retainSubagentSessions),
|
|
@@ -52,6 +52,14 @@ import type { MemorySyncRunner } from "../memory-sync-client.js";
|
|
|
52
52
|
*/
|
|
53
53
|
export type RunnerDepsOnAsk = (req: Parameters<ToolApprovalCoordinator["ask"]>[0], signal?: AbortSignal) => Promise<AskOutcome>;
|
|
54
54
|
export declare function createRunnerDepsOnAsk(toolApproval: ToolApprovalCoordinator | undefined): RunnerDepsOnAsk | undefined;
|
|
55
|
+
/** [ref] 件⑤:core `onError(phase:"config")` 的 `classification` 已知词表。⚠️ core d.ts(`RunnerDeps.onError` 注)只登记了
|
|
56
|
+
* prompt-cache 相的五词,config 相的词**只在 dist js 出现**(7.2.0 亲扫 dist 下全部 js 的 `phase: "config"` 发射点:五词——
|
|
57
|
+
* `interaction-posture-refused`(prepare-config-doors)/ `memory-tools-not-mounted` / `sandbox-admission-unavailable` /
|
|
58
|
+
* `shared-memory-not-mounted` / `shell-gate-off`(prepare-hands-readface));[ref] 去幻觉轮真抓:首版只写了 shell-gate-off
|
|
59
|
+
* 一词,其余四词会被误标 unknown。所以这是「按实现亲读」的开集快照,不是 core 类型闭集——词表外的值原样进日志并标
|
|
60
|
+
* `classificationKnown:false`(指标标签折 `unknown`),不吞不编;与安装包的锁步由 test/boot-runner-deps-shared-base.test.ts
|
|
61
|
+
* 的 dist 扫描格钉住(core 加词/收进 d.ts 那天先红)。 */
|
|
62
|
+
export declare const CONFIG_ADVISORY_CLASSIFICATIONS: ReadonlySet<string>;
|
|
55
63
|
export interface RunnerDepsCtx {
|
|
56
64
|
config: ServiceConfig;
|
|
57
65
|
logger: Logger;
|
|
@@ -113,6 +121,10 @@ export interface RunnerDepsCtx {
|
|
|
113
121
|
* 来源=configCenter.takeMcpRevocations()(取走即声明接线,restart 豁免与承接绑同一动作)。
|
|
114
122
|
* 缺席=undefined=pre-338 语义(纯 env 部署无中心撤销面)。 */
|
|
115
123
|
mcpRevocations: RunnerDeps["mcpRevocations"];
|
|
124
|
+
/** [ref] 件⑤([ref] server ③):per-session「显式 bypassPermissions」登记簿的读腿 —— `onError(phase:"config",
|
|
125
|
+
* classification:"shell-gate-off")` 在本 session 显式声明 bypass 时降级 info(正常态不打 warn)。缺席(桩 /
|
|
126
|
+
* run-local)= 照 warn(退化方向=多一条告警)。 */
|
|
127
|
+
sessionShellGateExplicitlyOff?: ((sessionId: string) => boolean) | undefined;
|
|
116
128
|
/**
|
|
117
129
|
* 交接件⑤ —— commit 尾注的署名座(`RunnerDeps.hands.commitCoAuthor`)。
|
|
118
130
|
*
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -15,6 +15,13 @@ export function createRunnerDepsOnAsk(toolApproval) {
|
|
|
15
15
|
return undefined;
|
|
16
16
|
return (req, signal) => toolApproval.ask(req, signal);
|
|
17
17
|
}
|
|
18
|
+
export const CONFIG_ADVISORY_CLASSIFICATIONS = new Set([
|
|
19
|
+
"interaction-posture-refused",
|
|
20
|
+
"memory-tools-not-mounted",
|
|
21
|
+
"sandbox-admission-unavailable",
|
|
22
|
+
"shared-memory-not-mounted",
|
|
23
|
+
"shell-gate-off",
|
|
24
|
+
]);
|
|
18
25
|
export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter) {
|
|
19
26
|
return (notice) => {
|
|
20
27
|
logger.warn("engine_notice", { code: notice.code, message: notice.message, ...(notice.detail !== undefined ? { detail: notice.detail } : {}) });
|
|
@@ -71,7 +78,7 @@ export function createSharedRunnerDeps(ctx) {
|
|
|
71
78
|
return shared;
|
|
72
79
|
}
|
|
73
80
|
export function createRunnerDeps(ctx) {
|
|
74
|
-
const { config, logger, metrics, localRoot, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine, sessionCaptureRecordStore, memorySyncRunner, runtimeCapsResolver, fileHistoryStore, legacyRewindBoundaryProbe, permissionRuleStore, fleetBus, workflowRunStore, workflowJournalStore, workflowAgentRegistry, workflowNotifyGate, workflowCompletionInbox, deliverWorkflowCompletion, getRunStore, } = ctx;
|
|
81
|
+
const { config, logger, metrics, localRoot, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine, sessionCaptureRecordStore, memorySyncRunner, runtimeCapsResolver, fileHistoryStore, legacyRewindBoundaryProbe, permissionRuleStore, fleetBus, workflowRunStore, workflowJournalStore, workflowAgentRegistry, workflowNotifyGate, workflowCompletionInbox, deliverWorkflowCompletion, getRunStore, sessionShellGateExplicitlyOff, } = ctx;
|
|
75
82
|
const sharedRunnerDeps = createSharedRunnerDeps(ctx);
|
|
76
83
|
const workflowModelAllowlist = workflowModelAllowlistFor(config);
|
|
77
84
|
const selfOrchestrationDeps = config.selfOrchestrationEnabled
|
|
@@ -216,8 +223,26 @@ export function createRunnerDeps(ctx) {
|
|
|
216
223
|
return;
|
|
217
224
|
}
|
|
218
225
|
if (ctx.phase === "config") {
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
const code = typeof err?.code === "string" ? err.code : undefined;
|
|
227
|
+
const classification = ctx.classification;
|
|
228
|
+
const classificationKnown = classification === undefined ? undefined : CONFIG_ADVISORY_CLASSIFICATIONS.has(classification);
|
|
229
|
+
const expectedShellGateOff = classification === "shell-gate-off" && sessionShellGateExplicitlyOff?.(ctx.sessionId) === true;
|
|
230
|
+
const fields = {
|
|
231
|
+
sessionId: ctx.sessionId,
|
|
232
|
+
...(code !== undefined ? { code } : {}),
|
|
233
|
+
...(classification !== undefined ? { classification, classificationKnown } : {}),
|
|
234
|
+
...(expectedShellGateOff ? { expected: "bypassPermissions declared by the caller for this session — shell gate off is the requested posture, not a misconfiguration" } : {}),
|
|
235
|
+
info: String(err),
|
|
236
|
+
};
|
|
237
|
+
if (expectedShellGateOff)
|
|
238
|
+
logger.info("runner_config_advisory", fields);
|
|
239
|
+
else
|
|
240
|
+
logger.warn("runner_config_advisory", fields);
|
|
241
|
+
metrics.inc("runner_config_advisory_total", {
|
|
242
|
+
...(code !== undefined ? { code } : {}),
|
|
243
|
+
...(classification !== undefined ? { classification: classificationKnown ? classification : "unknown" } : {}),
|
|
244
|
+
level: expectedShellGateOff ? "info" : "warn",
|
|
245
|
+
});
|
|
221
246
|
return;
|
|
222
247
|
}
|
|
223
248
|
logger.error("runner_error", { phase: ctx.phase, sessionId: ctx.sessionId, err: String(err) });
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ServiceConfig } from "../config.js";
|
|
11
11
|
import type { Logger } from "../observability/logger.js";
|
|
12
|
-
import { type MemoryOptOutVerdictSource } from "../runtime-caps-resolver.js";
|
|
12
|
+
import { type EntitlementsResolverFn, type MemoryOptOutVerdictSource } from "../runtime-caps-resolver.js";
|
|
13
13
|
export interface RuntimeCapsCtx {
|
|
14
14
|
config: ServiceConfig;
|
|
15
15
|
logger: Logger;
|
|
@@ -22,24 +22,34 @@ export interface RuntimeCapsCtx {
|
|
|
22
22
|
* D1 / `boot/retention-lane.ts` 同姿态:安全/合规控件不得半开)。
|
|
23
23
|
*
|
|
24
24
|
* governed 的语义是「per-principal verdict **必答**」:core 对 verdict 缺席一律**拒跑**(fail-closed,部署显式
|
|
25
|
-
* 选的方向)
|
|
26
|
-
* 恒答显式 boolean)
|
|
25
|
+
* 选的方向)。verdict 源**两条**(析取,[ref] 起):①SQL 后端上的 `memory_optout_grant` 表(三层折叠后
|
|
26
|
+
* 恒答显式 boolean),②非 dry-run 的 config center 发 `EntitlementRuntimeCaps.allowMemoryOptOut`。
|
|
27
|
+
* governed 而两源皆无 ⇒ 每一个 `capture:"off"` 声明都被 core 以
|
|
27
28
|
* `memory.capture_optout_denied` 拒掉,而运维看到的只是一台「启动成功、opt-out 全拒」的机器 —— 正是
|
|
28
|
-
* 「阀门开着而配不出来 ⇒
|
|
29
|
-
* ensureSchema 建)
|
|
30
|
-
*
|
|
29
|
+
* 「阀门开着而配不出来 ⇒ 悄悄读成别的」那一形。所以在**启动期**点名:接 SQL 后端(表随中央
|
|
30
|
+
* ensureSchema 建)、接非 dry-run center,或改姿态。`open`/`capture-required`/缺席 不依赖源(前者故障放行、
|
|
31
|
+
* 后者全拒,都不问 verdict)。
|
|
31
32
|
*
|
|
32
33
|
* ⚠️ center 键 `EntitlementRuntimeCaps.allowMemoryOptOut` 是 settings-schema 的独立小件([ref] §四 开放问题①),
|
|
33
|
-
* 已到货(1.4.0,[ref])
|
|
34
|
+
* 已到货(1.4.0,[ref])——析取臂已加,dry-run 不算源(不 APPLY 即不 ENFORCE 同律)。
|
|
35
|
+
*
|
|
36
|
+
* 🔴 **[ref] —— 第三臂的辖域**:它证的是「center 这个**源**在场」,**不是**「center 会为每个 principal
|
|
37
|
+
* **发** `allowMemoryOptOut`」。该键在 settings-schema 里是**可选**的(`z.ZodOptional<z.ZodBoolean>`),
|
|
38
|
+
* `toCoreRuntimeCaps` 对缺席不铸键 ⇒ 一台 center 从不发该键的 governed worker 照样起动、然后每一个
|
|
39
|
+
* `memory.capture:"off"` 恒拒(core: verdict absent)。这条**不能**在启动期证死(部署级布尔对 per-principal
|
|
40
|
+
* 的授权说不了话,且 boot 期去问 center 会把拒启挂在一次网络上),所以取「拒启臂放行 + 两处响亮」:
|
|
41
|
+
* 启动期一行 `memory_capture_governed_center_only_source`(这一形有风险)+ 运行期首次真缺席一行
|
|
42
|
+
* (见 `applyGovernedVerdictAbsenceWarning`,一次性、不改判)。
|
|
34
43
|
*/
|
|
35
44
|
export declare function assertMemoryCapturePolicyWirable(input: {
|
|
36
45
|
policy: ServiceConfig["memoryCapturePolicy"];
|
|
37
46
|
grantSourceWired: boolean;
|
|
38
|
-
|
|
47
|
+
centerEntitlementSourceWired?: boolean;
|
|
39
48
|
}): void;
|
|
40
49
|
export declare function createRuntimeCaps(ctx: RuntimeCapsCtx): {
|
|
41
50
|
principalCaps: import("../runtime-caps-resolver.js").PrincipalEntitlementsClient | undefined;
|
|
42
51
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
43
|
-
runtimeCapsResolver:
|
|
52
|
+
runtimeCapsResolver: EntitlementsResolverFn | undefined;
|
|
53
|
+
runtimeCapsPeekResolver: EntitlementsResolverFn | undefined;
|
|
44
54
|
};
|
|
45
55
|
//# sourceMappingURL=runtime-caps.d.ts.map
|