@sema-agent/server 7.0.0 → 7.1.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.
|
@@ -812,7 +812,13 @@ export function createResolveSpec(ctx) {
|
|
|
812
812
|
// per-task across suspend is a larger, separate decision that must cover the approval baseline too — tracked
|
|
813
813
|
// for center (adversarial-review HIGH-1), deliberately NOT a governance-only partial freeze here (that would
|
|
814
814
|
// be an inconsistent false-safety: approval would still be live).
|
|
815
|
-
|
|
815
|
+
// #153([2713] 分单②):MANUAL_MODE_SHELL_GATE 在此无条件施加(与 autonomy 同拍)——部署旋钮不再
|
|
816
|
+
// 依赖客户端 settings 在场性/模式臂/host lane(三重条件曾让 print/headless 腿与沙箱 lane 全静默失效)。
|
|
817
|
+
const governedBase = applyRuntimeGovernance(spec, {
|
|
818
|
+
autonomy: config.autonomy,
|
|
819
|
+
commandPolicy: config.commandPolicy,
|
|
820
|
+
...(config.manualModeShellGate ? { manualModeShellGate: config.manualModeShellGate } : {}),
|
|
821
|
+
});
|
|
816
822
|
// Fold the client's per-request settings stamp onto the governed spec, TIGHTEN-ONLY (deny-wins) — AFTER
|
|
817
823
|
// the approval baseline + operator governance, so the order is deployment ⊇ operator ⊇ client (each can only
|
|
818
824
|
// narrow). tightenTaskSpec THROWS if the client settings would LOOSEN a stricter baseline → a 422 client error
|
|
@@ -890,10 +896,8 @@ export function createResolveSpec(ctx) {
|
|
|
890
896
|
// (deny 恒赢 —— 豁免/acceptDirs 越不过;集合取舍 core 成文,server 只透传 config 旋钮:
|
|
891
897
|
// 缺省 = core RECOMMENDED_SENSITIVE_PATTERNS,SENSITIVE_WRITE_PATTERNS 显式替换/off 关闭)。
|
|
892
898
|
...(config.sensitiveWritePatterns.length > 0 ? { sensitivePatterns: config.sensitiveWritePatterns } : {}),
|
|
893
|
-
// [1557]§四
|
|
894
|
-
//
|
|
895
|
-
// auto/acceptEdits 分支照旧不碰 TaskSpec.shellGate,零行为变化。
|
|
896
|
-
...(config.manualModeShellGate ? { shellGate: config.manualModeShellGate } : {}),
|
|
899
|
+
// [1557]§四 的 shellGate 旧家已随 #153 搬到 applyRuntimeGovernance(governance 层无条件施加,
|
|
900
|
+
// 见上方 governedBase)——wiring 不再承运该旋钮,FsWriteGateWiring.shellGate 字段一并拆除。
|
|
897
901
|
// [841]① / core 1.294 exemption seam:gate 产 ask 前查同一只 approval_exemption 店(与 decide
|
|
898
902
|
// remember="session" 的授予、ask 政策层探针同店同 canonical toolName 键空间)——「本会话不再询问」
|
|
899
903
|
// 对 fs-write 门同 turn 即时生效(父+继承子任务,ask 路径不再重入)。has() reject ⇒ core 按
|
|
@@ -86,15 +86,23 @@ export declare function compileCommandPolicy(rules: CommandRule[] | undefined):
|
|
|
86
86
|
*/
|
|
87
87
|
export declare function autonomyOverrides(autonomy: Autonomy | undefined): Partial<TaskSpec>;
|
|
88
88
|
/**
|
|
89
|
-
* Apply the operator's runtime governance (autonomy + commandPolicy) onto a base
|
|
90
|
-
* SINGLE {@link tightenTaskSpec} call: commandPolicy compiles to a `toolPolicy`
|
|
91
|
-
* baseline by `tightenTaskSpec` — TRAP #1), and autonomy expands to
|
|
92
|
-
* (TRAP #2). `tightenTaskSpec` THROWS `TaskSpecTightenError` if any
|
|
93
|
-
* field — surfacing a misconfiguration loudly instead of silently
|
|
94
|
-
* when there is nothing to apply
|
|
89
|
+
* Apply the operator's runtime governance (autonomy + commandPolicy + manualModeShellGate) onto a base
|
|
90
|
+
* `TaskSpec`, TIGHTEN-ONLY, in a SINGLE {@link tightenTaskSpec} call: commandPolicy compiles to a `toolPolicy`
|
|
91
|
+
* override (combined onto the baseline by `tightenTaskSpec` — TRAP #1), and autonomy expands to
|
|
92
|
+
* `handsReadOnly` / `shellGate` overrides (TRAP #2). `tightenTaskSpec` THROWS `TaskSpecTightenError` if any
|
|
93
|
+
* override would LOOSEN a stricter base safety field — surfacing a misconfiguration loudly instead of silently
|
|
94
|
+
* weakening the gate. Returns `base` untouched when there is nothing to apply.
|
|
95
|
+
*
|
|
96
|
+
* #153([2713] server 分单②):`MANUAL_MODE_SHELL_GATE` 从 settings 折叠搬到这里**无条件施加**。旧家
|
|
97
|
+
* (task-settings deriveSettingsPolicy 的 default/auto/acceptEdits 三臂)让一个**部署级 tighten-only 旋钮
|
|
98
|
+
* 的生死由客户端表态在场性决定**:body 无 settings/permissionMode(cli print/headless 腿按设计不 stamp)
|
|
99
|
+
* ⇒ 旋钮静默失效(clay 实机 Monitor 案「门根本没铸」);bypassPermissions 一句话掀掉部署闸;非 host lane
|
|
100
|
+
* fsWriteGate wiring 不建 ⇒ 沙箱 lane 全失效。governance 层与 autonomy 同拍施加后三者全闭。rank 合成:
|
|
101
|
+
* autonomy 派生值与旋钮取大(ask→"always" 压过 "classify"),再对 base 只升不降(防 tightenTaskSpec throw)。
|
|
95
102
|
*/
|
|
96
103
|
export declare function applyRuntimeGovernance(base: TaskSpec, governance: {
|
|
97
104
|
autonomy?: Autonomy;
|
|
98
105
|
commandPolicy?: CommandRule[];
|
|
106
|
+
manualModeShellGate?: "always" | "classify";
|
|
99
107
|
}): TaskSpec;
|
|
100
108
|
//# sourceMappingURL=runtime-governance.d.ts.map
|
|
@@ -204,19 +204,40 @@ export function autonomyOverrides(autonomy) {
|
|
|
204
204
|
return {};
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
+
/** core `tighten-task-spec` 的 SHELL_GATE_RANK 镜像(单一属主:#153 搬家后本文件是 server 侧唯一副本;
|
|
208
|
+
* task-settings 的旧副本随 settings 折叠臂一并拆除)。tightenTaskSpec 的契约是「override 自身必须收紧」
|
|
209
|
+
* 而非「取两者较严」——低于 base 的 override 直接 throw,所以施加前要按 rank 只升不降。 */
|
|
210
|
+
const SHELL_GATE_RANK = { off: 0, classify: 1, always: 2 };
|
|
207
211
|
/**
|
|
208
|
-
* Apply the operator's runtime governance (autonomy + commandPolicy) onto a base
|
|
209
|
-
* SINGLE {@link tightenTaskSpec} call: commandPolicy compiles to a `toolPolicy`
|
|
210
|
-
* baseline by `tightenTaskSpec` — TRAP #1), and autonomy expands to
|
|
211
|
-
* (TRAP #2). `tightenTaskSpec` THROWS `TaskSpecTightenError` if any
|
|
212
|
-
* field — surfacing a misconfiguration loudly instead of silently
|
|
213
|
-
* when there is nothing to apply
|
|
212
|
+
* Apply the operator's runtime governance (autonomy + commandPolicy + manualModeShellGate) onto a base
|
|
213
|
+
* `TaskSpec`, TIGHTEN-ONLY, in a SINGLE {@link tightenTaskSpec} call: commandPolicy compiles to a `toolPolicy`
|
|
214
|
+
* override (combined onto the baseline by `tightenTaskSpec` — TRAP #1), and autonomy expands to
|
|
215
|
+
* `handsReadOnly` / `shellGate` overrides (TRAP #2). `tightenTaskSpec` THROWS `TaskSpecTightenError` if any
|
|
216
|
+
* override would LOOSEN a stricter base safety field — surfacing a misconfiguration loudly instead of silently
|
|
217
|
+
* weakening the gate. Returns `base` untouched when there is nothing to apply.
|
|
218
|
+
*
|
|
219
|
+
* #153([2713] server 分单②):`MANUAL_MODE_SHELL_GATE` 从 settings 折叠搬到这里**无条件施加**。旧家
|
|
220
|
+
* (task-settings deriveSettingsPolicy 的 default/auto/acceptEdits 三臂)让一个**部署级 tighten-only 旋钮
|
|
221
|
+
* 的生死由客户端表态在场性决定**:body 无 settings/permissionMode(cli print/headless 腿按设计不 stamp)
|
|
222
|
+
* ⇒ 旋钮静默失效(clay 实机 Monitor 案「门根本没铸」);bypassPermissions 一句话掀掉部署闸;非 host lane
|
|
223
|
+
* fsWriteGate wiring 不建 ⇒ 沙箱 lane 全失效。governance 层与 autonomy 同拍施加后三者全闭。rank 合成:
|
|
224
|
+
* autonomy 派生值与旋钮取大(ask→"always" 压过 "classify"),再对 base 只升不降(防 tightenTaskSpec throw)。
|
|
214
225
|
*/
|
|
215
226
|
export function applyRuntimeGovernance(base, governance) {
|
|
216
227
|
const overrides = { ...autonomyOverrides(governance.autonomy) };
|
|
217
228
|
const coarse = compileCommandPolicy(governance.commandPolicy);
|
|
218
229
|
if (coarse)
|
|
219
230
|
overrides.toolPolicy = coarse;
|
|
231
|
+
if (governance.manualModeShellGate !== undefined) {
|
|
232
|
+
const candidate = overrides.shellGate !== undefined && SHELL_GATE_RANK[overrides.shellGate] >= SHELL_GATE_RANK[governance.manualModeShellGate]
|
|
233
|
+
? overrides.shellGate
|
|
234
|
+
: governance.manualModeShellGate;
|
|
235
|
+
// base 已更严(如 resume 重放时 base 带 always)⇒ 省略,让 base 原样保留(省略=行为等价,不 throw)。
|
|
236
|
+
if (SHELL_GATE_RANK[candidate] >= SHELL_GATE_RANK[base.shellGate ?? "off"])
|
|
237
|
+
overrides.shellGate = candidate;
|
|
238
|
+
else
|
|
239
|
+
delete overrides.shellGate;
|
|
240
|
+
}
|
|
220
241
|
if (overrides.toolPolicy === undefined && overrides.handsReadOnly === undefined && overrides.shellGate === undefined) {
|
|
221
242
|
return base; // nothing to govern → avoid a needless tightenTaskSpec pass
|
|
222
243
|
}
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -181,19 +181,21 @@ export interface FsWriteGateWiring {
|
|
|
181
181
|
* now closed). Source = config `SENSITIVE_WRITE_PATTERNS` (unset ⇒ core RECOMMENDED_SENSITIVE_PATTERNS; the
|
|
182
182
|
* set curation is core's — the server passes through). Absent/empty ⇒ no sensitive policy (pre-1.295 shape). */
|
|
183
183
|
sensitivePatterns?: string[];
|
|
184
|
-
/** [1557]§四 opt-in(cli[1555]②「echo -n … > file 绕写门」缺口的部署侧补丁,core[1556] 建议):
|
|
185
|
-
* CC manual 系模式(default/auto/acceptEdits — 见 {@link deriveSettingsPolicy})額外把
|
|
186
|
-
* `TaskSpec.shellGate` 收紧到这个值,让 Bash 走同一 fs-write 审批链(core `bashReversibilityProbe`
|
|
187
|
-
* 在 `"classify"` 下只拦构造性不可逆命令——含 shell 重定向 `>`/`>>` 等,纯读命令白名单自动放行)。
|
|
188
|
-
* **Absent(缺省)= 零行为变化**(config `MANUAL_MODE_SHELL_GATE` 未设置时不传本字段,pre-此批行为
|
|
189
|
-
* byte-for-byte;这是个 tighten-only opt-in,不是默认开启——运营方需要显式选择开启)。 */
|
|
190
|
-
shellGate?: "always" | "classify";
|
|
191
184
|
}
|
|
192
185
|
export declare function deriveSettingsPolicy(settings: ParsedTaskSettings, gate?: FsWriteGateWiring, workflowGate?: WorkflowGateWiring): {
|
|
193
186
|
toolPolicy?: ToolPolicy;
|
|
194
187
|
handsReadOnly?: boolean;
|
|
195
188
|
enablePlanMode?: boolean;
|
|
196
|
-
shellGate?: "always" | "classify";
|
|
197
189
|
};
|
|
190
|
+
/**
|
|
191
|
+
* Apply a client's parsed per-request settings onto a base `TaskSpec`, TIGHTEN-ONLY, in a SINGLE {@link
|
|
192
|
+
* tightenTaskSpec} pass (so the permission override composes deny-wins onto the baseline — TRAP #1 — and
|
|
193
|
+
* `handsReadOnly`/`onAsk` can only narrow). `tightenTaskSpec` THROWS `TaskSpecTightenError` if any override would
|
|
194
|
+
* LOOSEN a stricter base — surfaced by the caller as a 4xx (a client settings stamp that tries to widen is a
|
|
195
|
+
* client error, never a silent weakening). `model`/`outputStyle` are applied directly (model is the caller's
|
|
196
|
+
* choice WITHIN the catalog — the caller gates it before calling; outputStyle appends to the system prompt).
|
|
197
|
+
*
|
|
198
|
+
* Returns `base` untouched when the parsed settings project nothing onto the spec.
|
|
199
|
+
*/
|
|
198
200
|
export declare function applyTaskSettings(base: TaskSpec, settings: ParsedTaskSettings, gate?: FsWriteGateWiring, workflowGate?: WorkflowGateWiring): TaskSpec;
|
|
199
201
|
//# sourceMappingURL=task-settings.d.ts.map
|
package/dist/task-settings.js
CHANGED
|
@@ -426,18 +426,16 @@ export function deriveSettingsPolicy(settings, gate, workflowGate) {
|
|
|
426
426
|
return { toolPolicy: compose(createWorkflowPlanDenyPolicy()), handsReadOnly: true, enablePlanMode: true };
|
|
427
427
|
case "acceptEdits": {
|
|
428
428
|
const p = compose(fsWriteGate(true), createWorkflowAskPolicy(workflowGate?.isExempt));
|
|
429
|
-
return
|
|
429
|
+
return p ? { toolPolicy: p } : {};
|
|
430
430
|
}
|
|
431
431
|
case "default":
|
|
432
432
|
case "auto": {
|
|
433
433
|
// (historical note kept: the pre-[816] impl fell through to base-only here — with no gate to attach,
|
|
434
434
|
// acceptEdits-as-loosen could not be honored. The [820] factory inverts the frame: default now ADDS an ask
|
|
435
435
|
// gate and acceptEdits ADDS a narrower one, both tighten-safe under the deny-wins fold.)
|
|
436
|
-
// [1557]
|
|
437
|
-
// gate on the SAME "CC manual" modes — closes the "echo > file bypasses the write gate" hole (cli[1555]②)
|
|
438
|
-
// when the deployment explicitly opts in (config MANUAL_MODE_SHELL_GATE).
|
|
436
|
+
// ([1557]§四 的 shellGate 便车已随 #153 搬 governance 层——见 FsWriteGateWiring 尾注。)
|
|
439
437
|
const p = compose(fsWriteGate(false), createWorkflowAskPolicy(workflowGate?.isExempt));
|
|
440
|
-
return
|
|
438
|
+
return p ? { toolPolicy: p } : {};
|
|
441
439
|
}
|
|
442
440
|
case "bypassPermissions":
|
|
443
441
|
case undefined:
|
|
@@ -454,27 +452,17 @@ export function deriveSettingsPolicy(settings, gate, workflowGate) {
|
|
|
454
452
|
*
|
|
455
453
|
* Returns `base` untouched when the parsed settings project nothing onto the spec.
|
|
456
454
|
*/
|
|
457
|
-
// [1557]
|
|
458
|
-
//
|
|
459
|
-
// handsReadOnly, which ORs). Red-first repro: tightenTaskSpec({shellGate:"always"}, {shellGate:"classify"})
|
|
460
|
-
// throws — so a deployment that layers autonomy=ask (→"always") UNDER this opt-in (→"classify") would 422
|
|
461
|
-
// EVERY request on that mode. Mirrors core's own SHELL_GATE_RANK (tighten-task-spec.js) — only pass the
|
|
462
|
-
// override through when it would not be a downgrade; otherwise the baseline already covers it (tightenTaskSpec's
|
|
463
|
-
// base-only branch keeps `base.shellGate` unchanged either way — omitting is behaviorally identical, never a
|
|
464
|
-
// silent weakening, just avoids the throw).
|
|
465
|
-
const SHELL_GATE_RANK = { off: 0, classify: 1, always: 2 };
|
|
455
|
+
// ([1557]§四 的 SHELL_GATE_RANK 副本与 shellGateSafe 守卫已随 #153 搬 runtime-governance ——
|
|
456
|
+
// settings 折叠不再触碰 TaskSpec.shellGate,base 上 governance 层施加的值原样透传。)
|
|
466
457
|
export function applyTaskSettings(base, settings, gate, workflowGate) {
|
|
467
|
-
const { toolPolicy, handsReadOnly, enablePlanMode
|
|
468
|
-
const shellGateSafe = shellGate !== undefined && SHELL_GATE_RANK[shellGate] >= SHELL_GATE_RANK[base.shellGate ?? "off"] ? shellGate : undefined;
|
|
458
|
+
const { toolPolicy, handsReadOnly, enablePlanMode } = deriveSettingsPolicy(settings, gate, workflowGate);
|
|
469
459
|
let next = base;
|
|
470
|
-
if (toolPolicy !== undefined || handsReadOnly !== undefined
|
|
460
|
+
if (toolPolicy !== undefined || handsReadOnly !== undefined) {
|
|
471
461
|
const overrides = {};
|
|
472
462
|
if (toolPolicy !== undefined)
|
|
473
463
|
overrides.toolPolicy = toolPolicy;
|
|
474
464
|
if (handsReadOnly !== undefined)
|
|
475
465
|
overrides.handsReadOnly = handsReadOnly;
|
|
476
|
-
if (shellGateSafe !== undefined)
|
|
477
|
-
overrides.shellGate = shellGateSafe; // [1557]§四 opt-in; rank-guarded above
|
|
478
466
|
next = tightenTaskSpec(next, overrides); // deny-wins compose + THROWS on loosen (fail-loud)
|
|
479
467
|
}
|
|
480
468
|
// R4: enablePlanMode (mount present_plan) is ADDITIVE — it widens NOTHING (a read-only plan workflow), so it rides
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"sharp": "^0.35.3"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@sema-agent/sdk": "^6.
|
|
71
|
+
"@sema-agent/sdk": "^6.6.0",
|
|
72
72
|
"@types/libsodium-wrappers": "^0.7.14",
|
|
73
73
|
"@types/node": "22.10.2",
|
|
74
74
|
"@types/pg": "^8.20.0",
|