@sema-agent/server 7.105.0 → 7.107.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 +105 -0
- package/README.md +2 -2
- package/USAGE.md +135 -43
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -0
- package/deploy/sema-up/cluster-up.sh +15 -2
- package/deploy/sema-up/kube-up.sh +8 -3
- package/deploy/sema-up/lib/flagshape.sh +29 -0
- package/deploy/sema-up/lib/parse-machines.py +4 -1
- package/deploy/sema-up/sema-up.sh +11 -9
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +3 -1
- package/dist/approval-card.d.ts +1 -1
- package/dist/approval.d.ts +6 -1
- package/dist/approval.js +4 -4
- package/dist/auth-bridge.d.ts +14 -0
- package/dist/auth-bridge.js +7 -0
- package/dist/auto-mode-face.d.ts +49 -40
- package/dist/auto-mode-face.js +6 -68
- package/dist/bench/s1/live-deps.d.ts +9 -2
- package/dist/bench/s1/live-deps.js +13 -8
- package/dist/boot/budget-tracing.js +3 -2
- package/dist/boot/config-center.d.ts +4 -0
- package/dist/boot/config-center.js +95 -37
- package/dist/boot/coordinators.d.ts +1 -0
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/device-lane.d.ts +2 -1
- package/dist/boot/execution-env.d.ts +11 -0
- package/dist/boot/execution-env.js +30 -6
- package/dist/boot/execution-lane.js +2 -3
- package/dist/boot/leader.d.ts +2 -1
- package/dist/boot/leader.js +2 -0
- package/dist/boot/parked-revive-gate.d.ts +10 -7
- package/dist/boot/parked-revive-gate.js +4 -11
- package/dist/boot/resolve-spec.d.ts +1 -4
- package/dist/boot/resolve-spec.js +44 -21
- package/dist/boot/runner-deps.d.ts +5 -8
- package/dist/boot/runner-deps.js +3 -9
- package/dist/boot/side-query-lane.d.ts +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -1
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +3 -2
- package/dist/boot/stage-06-runners.js +2 -5
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +1 -2
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -2
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -2
- package/dist/boot/stage-10-http-server.js +11 -7
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +6 -4
- package/dist/brain.d.ts +24 -2
- package/dist/brain.js +29 -10
- package/dist/budget.js +3 -0
- package/dist/capabilities/center-wiring.d.ts +64 -0
- package/dist/capabilities/center-wiring.js +24 -0
- package/dist/capabilities/composite-members.d.ts +89 -0
- package/dist/capabilities/composite-members.js +20 -0
- package/dist/capabilities/execution-lane.d.ts +3 -0
- package/dist/capabilities/execution-lane.js +5 -2
- package/dist/config-catalog.d.ts +35 -5
- package/dist/config-catalog.js +92 -26
- package/dist/config-center/apply-effective.d.ts +1 -1
- package/dist/config-center/apply-effective.js +7 -8
- package/dist/config-center/apply-ledger.d.ts +17 -0
- package/dist/config-center/apply-ledger.js +5 -0
- package/dist/config-center/center-request.d.ts +17 -0
- package/dist/config-center/center-request.js +4 -0
- package/dist/config-center/pull-state.d.ts +29 -0
- package/dist/config-center/pull-state.js +2 -0
- package/dist/config-types.d.ts +41 -14
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.js +69 -16
- package/dist/cross-session-settings.d.ts +1 -1
- package/dist/deployment-governance.d.ts +12 -1
- package/dist/deployment-governance.js +2 -0
- package/dist/execution-lane-caps.d.ts +30 -2
- package/dist/execution-lane-caps.js +12 -0
- package/dist/fleet-client.d.ts +18 -13
- package/dist/fleet-client.js +32 -22
- package/dist/fleet-lease.d.ts +72 -47
- package/dist/fleet-lease.js +66 -14
- package/dist/governance-http-status.d.ts +0 -1
- package/dist/governance-http-status.js +0 -1
- package/dist/hosted-posture.d.ts +6 -17
- package/dist/hosted-posture.js +11 -6
- package/dist/http/admission.js +14 -9
- package/dist/http/approval-sweeps.js +13 -1
- package/dist/http/dispatch.js +1 -1
- package/dist/http/durable-run-leg.js +2 -0
- package/dist/http/request-guards.d.ts +25 -0
- package/dist/http/request-guards.js +25 -5
- package/dist/http/resume-legs.js +19 -11
- package/dist/http/route-ctx.d.ts +9 -0
- package/dist/http/routes/approvals-assistant.js +0 -1
- package/dist/http/routes/capabilities.js +11 -12
- package/dist/http/routes/config-catalog.js +2 -1
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/http/routes/tasks.js +4 -1
- package/dist/http/server.d.ts +20 -4
- package/dist/http/session-inherit-env.d.ts +37 -0
- package/dist/http/session-inherit-env.js +6 -0
- package/dist/http/wire-types.d.ts +19 -28
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/key-resolver.d.ts +74 -19
- package/dist/key-resolver.js +65 -7
- package/dist/leader/leader.d.ts +6 -0
- package/dist/leader/leader.js +2 -1
- package/dist/leader/repair-wire.js +2 -1
- package/dist/leader/wire.d.ts +9 -1
- package/dist/leader/wire.js +10 -6
- package/dist/observability/fail-open.d.ts +5 -1
- package/dist/observability/fail-open.js +5 -1
- package/dist/observability/metrics.js +2 -0
- package/dist/observability/permission-rule-events.d.ts +1 -1
- package/dist/observability/permission-rule-events.js +1 -1
- package/dist/observability/run-terminal.d.ts +40 -1
- package/dist/observability/run-terminal.js +2 -5
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/observability/tool-trace.d.ts +2 -0
- package/dist/operator-ask.d.ts +29 -1
- package/dist/operator-ask.js +6 -0
- package/dist/parked-decide.d.ts +14 -1
- package/dist/parked-decide.js +8 -1
- package/dist/plugins/session-policy-store-sql.d.ts +38 -7
- package/dist/plugins/session-policy-store-sql.js +64 -22
- package/dist/plugins/sql-driver.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +12 -3
- package/dist/plugins/store-backend.js +16 -5
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/read-face-posture.d.ts +4 -4
- package/dist/request-key-closure.js +2 -2
- package/dist/request-value-closure.d.ts +17 -5
- package/dist/request-value-closure.js +12 -0
- package/dist/router/route-orchestration.d.ts +11 -0
- package/dist/router/route-orchestration.js +7 -6
- package/dist/run-local.js +4 -2
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +4 -4
- package/dist/runtime-caps-resolver.d.ts +4 -10
- package/dist/runtime-governance.d.ts +8 -11
- package/dist/runtime-governance.js +10 -41
- package/dist/sealed-key.d.ts +0 -40
- package/dist/sealed-key.js +0 -20
- package/dist/server-secret-env.d.ts +11 -12
- package/dist/session-policy-wire.d.ts +5 -3
- package/dist/session-titler.d.ts +11 -0
- package/dist/session-titler.js +6 -2
- package/dist/sql-engine-posture.d.ts +2 -2
- package/dist/store-posture.d.ts +53 -0
- package/dist/store-posture.js +32 -0
- package/dist/task-settings.d.ts +64 -62
- package/dist/task-settings.js +57 -27
- package/dist/tool-approval.d.ts +51 -22
- package/dist/tool-approval.js +44 -8
- package/dist/tool-declaration-http-status.d.ts +12 -0
- package/dist/tool-declaration-http-status.js +12 -0
- package/dist/trace/core-keyset-guard.d.ts +7 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +5 -0
- package/dist/trace/project.d.ts +7 -5
- package/dist/trace/project.js +51 -3
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/unattended-permission-mode.d.ts +110 -0
- package/dist/unattended-permission-mode.js +65 -0
- package/package.json +3 -3
- package/dist/boot/session-shell-gate-registry.d.ts +0 -39
- package/dist/boot/session-shell-gate-registry.js +0 -21
- package/dist/roster-bound-http-status.d.ts +0 -6
- package/dist/roster-bound-http-status.js +0 -6
package/dist/task-settings.d.ts
CHANGED
|
@@ -35,25 +35,38 @@
|
|
|
35
35
|
* - (`hooks` NO LONGER deferred either — 轴B #2 注释保鲜:applied 形已接线(本文件 hooks 分片 +
|
|
36
36
|
* main.ts 单用户闸),仅 malformed 才报 deferred;旧「需要 remote hook-runner」是接线前拍照。)
|
|
37
37
|
*/
|
|
38
|
-
import { type ExecutionEnv, type TaskSpec, type ThinkingLevel, type ToolPolicy, type PromptProvider } from "@sema-agent/core";
|
|
38
|
+
import { type ExecutionEnv, type TaskSpec, type ThinkingLevel, type ToolPolicy, type PromptProvider, type PermissionMode } from "@sema-agent/core";
|
|
39
39
|
import type { HooksConfig } from "@sema-agent/settings-schema/hooks";
|
|
40
40
|
import { type KeyClosureIssue } from "./request-key-closure.js";
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
/**
|
|
42
|
+
* S-902(7.107.0,车QY 候裁 11 裁 A;[ref])—— core 的**每一个**权限模式词,本仓请求面收不收:`"request"` = 受理(顶层 `permissionMode` /
|
|
43
|
+
* `settings.permissions.defaultMode` / `?permissionMode=` 查询串三处同读),`"excluded"` = 显式排除(不上请求面)。
|
|
44
|
+
*
|
|
45
|
+
* 🔴 这张表是**表态**,不是词表的第二份:词与词序只从 core `PERMISSION_MODES` 来(下面 {@link SETTINGS_PERMISSION_MODES} 按它过滤);
|
|
46
|
+
* 表只对每个词说一句「收 / 不收」。`satisfies Record<PermissionMode, …>` 对 core 的词型**穷举**:core 加一个词 ⇒ 本表缺键 **编译红**
|
|
47
|
+
* (新词必须被表态一次,不会被静默收进请求面、也不会被静默漏掉);core 删 / 改名一个词 ⇒ 本表多键编译红(残条目不留)。
|
|
48
|
+
*
|
|
49
|
+
* 今天的表态(与 7.106.0 逐字同行为):五词受理;`dontAsk`(CC 的「自动拒」档,core 7.34.0 起第六词)**排除** —— 它上不上请求面是一件
|
|
50
|
+
* 新能力(未裁,本表不替它裁);不收它时提交面 400 `request.field_invalid`,续跑重放腿按 {@link coercePermissionMode} 的既有宽读折 `default`。
|
|
51
|
+
* 语义沿革(为什么是这五个、`bypassPermissions` / `auto` 何时进来):[ref] / [ref] / [ref] —— CC 自己的全集是六词,CC 托管 / 远端形收四词
|
|
52
|
+
* (= 本仓 − `bypassPermissions`),本仓 = {CC 远端四词} ∪ {`bypassPermissions`},登记过的偏离;生效模式原样过境 `TaskSpec.permissionMode`(S-877)。
|
|
53
|
+
*/
|
|
54
|
+
export declare const PERMISSION_MODE_REQUEST_FACE: {
|
|
55
|
+
readonly default: "request";
|
|
56
|
+
readonly acceptEdits: "request";
|
|
57
|
+
readonly plan: "request";
|
|
58
|
+
readonly auto: "request";
|
|
59
|
+
readonly bypassPermissions: "request";
|
|
60
|
+
readonly dontAsk: "excluded";
|
|
61
|
+
};
|
|
62
|
+
/** 请求面受理的模式词的型形 —— 从表态表派生(`"request"` 那几行的键)。 */
|
|
63
|
+
export type SettingsPermissionMode = {
|
|
64
|
+
[M in PermissionMode]: (typeof PERMISSION_MODE_REQUEST_FACE)[M] extends "request" ? M : never;
|
|
65
|
+
}[PermissionMode];
|
|
66
|
+
/** 请求面受理的模式词(core 序)= core `PERMISSION_MODES` 过滤表态 `"request"`。wire 门、coerce 两族、两句 400 文案同读这一份。 */
|
|
67
|
+
export declare const SETTINGS_PERMISSION_MODES: readonly SettingsPermissionMode[];
|
|
68
|
+
/** 显式排除集(core 序)= core `PERMISSION_MODES` 过滤表态 `"excluded"`;今天恰是 `["dontAsk"]`。只作披露 / 钉读,不参与判定。 */
|
|
69
|
+
export declare const SETTINGS_PERMISSION_MODES_EXCLUDED: readonly Exclude<PermissionMode, SettingsPermissionMode>[];
|
|
57
70
|
/** 五词表的 wire 文案形(`"a" | "b" | …`)—— 两条 400 的句子从这里取词,不再各抄一份。 */
|
|
58
71
|
export declare const SETTINGS_PERMISSION_MODE_WORDS: string;
|
|
59
72
|
/** 闭集成员判定(`unknown` 入口用)—— 词表的**唯一**运行期读者:wire 门与 coerce 两族同调。 */
|
|
@@ -124,6 +137,12 @@ export declare function effectiveThinking(reasoningEffort: unknown, ultracode: b
|
|
|
124
137
|
* field is the explicit per-turn intent → it WINS over a bundle defaultMode). Creates a minimal settings object when
|
|
125
138
|
* no `body.settings` bundle was sent. The result flows through {@link applyTaskSettings} (tighten-only). */
|
|
126
139
|
export declare function withPermissionMode(settings: ParsedTaskSettings | undefined, mode: SettingsPermissionMode): ParsedTaskSettings;
|
|
140
|
+
/**
|
|
141
|
+
* 7.106.0 合并复审 X8(opus O-6)—— 请求**没有表态**模式时引擎实际跑的那个词。core 7.34.0 [ref]「Absent = `default`」
|
|
142
|
+
* (装树 `dist/core/runner/prepare-task.js` 的 `requested ?? "default"`)。只给**观测面**用(hook 载荷的 `permission_mode`,CC 恒带这个键):
|
|
143
|
+
* spec 过境点照旧不写键(`effectivePermissionMode` 缺席 ⇒ 不替调用方选 —— 那是引擎的默认,不是本仓的表态)。
|
|
144
|
+
*/
|
|
145
|
+
export declare const ENGINE_ABSENT_PERMISSION_MODE: "default";
|
|
127
146
|
/**
|
|
128
147
|
* [ref] §6-1 —— 本请求的**生效** permission mode,单点。优先序逐字同 {@link withPermissionMode}:
|
|
129
148
|
* 顶层 `body.permissionMode`(本轮显式表态)赢过 bundle 的 `settings.permissions.defaultMode`;两处
|
|
@@ -137,51 +156,6 @@ export declare function withPermissionMode(settings: ParsedTaskSettings | undefi
|
|
|
137
156
|
export declare function effectivePermissionMode(body: {
|
|
138
157
|
permissionMode?: unknown;
|
|
139
158
|
}, settings: ParsedTaskSettings | undefined): SettingsPermissionMode | undefined;
|
|
140
|
-
/**
|
|
141
|
-
* [ref] §2 —— 显式 permission mode → `TaskSpec.shellGate` 档位的**翻译表(单一真源)**。
|
|
142
|
-
*
|
|
143
|
-
* · `bypassPermissions` ⇒ `"off"` —— CC `--dangerously-skip-permissions` 的对位裁定
|
|
144
|
-
* (与 fs-write 面的 bypass 臂两面归一,见 {@link deriveSettingsPolicy})。
|
|
145
|
-
* · `auto` ⇒ `"classify"` —— **[ref] §8 开口按施工期新披露亲裁改译**(原裁定字面是 off,
|
|
146
|
-
* §8 保留「core auto 分类器覆盖面有新披露时可独立再裁,表驱动一行」——新披露见下段):
|
|
147
|
-
*
|
|
148
|
-
* ⚠️ **`auto` 这一行有一条待裁的开口([ref] §8 明列「core auto 分类器覆盖面有新披露时可独立再裁,
|
|
149
|
-
* 表驱动一行」)。施工期交叉复审给出了那条新披露,已亲读安装包核实**:
|
|
150
|
-
* · core 只在**已经产生 `ask`** 之后才咨询分类器(`dist/core/hooks.js`:`if (input.autoMode &&
|
|
151
|
-
* decision.action === "ask" …)`);
|
|
152
|
-
* · 而 `shellGate:"off"` 下 core **不铸任何 shell 面的门**(`dist/core/runner/prepare-task.js` 的
|
|
153
|
-
* `shellGate === "off"` 臂反而发一条 `classification:"shell-gate-off"` 的 onError:「真可写 Bash 挂着
|
|
154
|
-
* 却没有 shell 安全轴折叠」)。
|
|
155
|
-
* 两条合起来:`auto` × off ⇒ Bash 既不产 ask、分类器也就永不被咨询 —— off 档下「筛选权交给分类器」
|
|
156
|
-
* 不成立。故 auto 归 classify 组:分类器坐在 classify 产的 ask 之上,语义才真是「交给分类器」
|
|
157
|
-
* (良性只读命令由分类器自动放行,其余 ask;这正是 auto 模式的本义)。发车帖向 [ref] 裁定链披露此
|
|
158
|
-
* 一行偏离;core auto 分类器若来日在 off 档下也有咨询点,可再裁回(表驱动一行)。
|
|
159
|
-
* · `default` / `acceptEdits` / `plan` ⇒ `"classify"` —— 这一档原先由壳无条件注入 `MANUAL_MODE_SHELL_GATE`
|
|
160
|
-
* 供给(车道缺省走了 operator 通道),现在归位到表态轴。`plan` 本身 handsReadOnly(core 明写此档下
|
|
161
|
-
* shellGate 无效),给它 classify 纯为一致性。
|
|
162
|
-
*
|
|
163
|
-
* 🔴 **入参非可选**:缺席(无表态)不是这张表的一行 —— 它的语义是「不写这个键」,只能在调用点判。
|
|
164
|
-
* 把它折进来会逼出一个 `undefined` 返回值,而那正是「写 off」与「不写」被混同的入口(core 的
|
|
165
|
-
* 缺席默认是 off,但**写**一个 off 会成为 governance 的 base,与缺席不是同一件事)。
|
|
166
|
-
*
|
|
167
|
-
* 🔴 **闭集穷举表,无 default 臂**:五个模式词是封闭词表,新增一个模式而不更新本表
|
|
168
|
-
* 是**编译错误**([ref] 的安全轴纪律:词表的未知项不许有静默兜底臂)。
|
|
169
|
-
*/
|
|
170
|
-
export declare function shellGateForMode(effMode: SettingsPermissionMode): "off" | "classify";
|
|
171
|
-
/**
|
|
172
|
-
* [ref] —— 生效模式 → `TaskSpec.writeFace`(**写**面的 roots 容纳判断)。`"open"` = 工作区根之外也能写;
|
|
173
|
-
* `undefined` = **不写这个键**(core 缺省 `"roots"`,与 7.77.0 及以前逐字节相同)。表逐字在
|
|
174
|
-
* {@link MODE_ENGINE_FACES},裁定与硬边界写在 `resolve-spec.ts` 的写点上。
|
|
175
|
-
*
|
|
176
|
-
* 🔴 与 {@link shellGateForMode} 同律:入参非可选 —— 「表态缺席」不是这张表的一行(那是调用点判的
|
|
177
|
-
* 「整个键都不写」),把它折进来就会逼出第二个 `undefined` 语义。
|
|
178
|
-
*/
|
|
179
|
-
export declare function writeFaceForMode(effMode: SettingsPermissionMode): "open" | undefined;
|
|
180
|
-
/**
|
|
181
|
-
* [ref] ① —— 生效模式 → `TaskSpec.autoModeRequested`(auto **意图**座,CC 250「用户开」半场)。
|
|
182
|
-
* `true` 只有 auto 一行;`undefined` = 不写键。表逐字在 {@link MODE_ENGINE_FACES}。
|
|
183
|
-
*/
|
|
184
|
-
export declare function autoModeRequestedForMode(effMode: SettingsPermissionMode): true | undefined;
|
|
185
159
|
/**
|
|
186
160
|
* S-346 —— [ref] 技能清单预算两旋钮的**值域谓词**(一对具名单铸点)。
|
|
187
161
|
*
|
|
@@ -262,6 +236,34 @@ export declare const MAX_SETTINGS_PERMISSION_RULES = 1000;
|
|
|
262
236
|
export declare const MAX_SETTINGS_ENV_VARS = 256;
|
|
263
237
|
export declare const MAX_SETTINGS_ENV_VALUE_CHARS = 32768;
|
|
264
238
|
export declare const MAX_SETTINGS_ENV_KEY_CHARS = 256;
|
|
239
|
+
/** 三桶(闭集;判据按此序逐桶判)。 */
|
|
240
|
+
export declare const SETTINGS_PERMISSION_BUCKETS: readonly ["allow", "deny", "ask"];
|
|
241
|
+
export type SettingsPermissionBucket = (typeof SETTINGS_PERMISSION_BUCKETS)[number];
|
|
242
|
+
/** 拒因(闭集;wire 字段 `reason` 的全部词)。 */
|
|
243
|
+
export declare const SETTINGS_PERMISSION_BUCKET_REFUSALS: readonly ["over_cap", "bad_entry"];
|
|
244
|
+
export type SettingsPermissionBucketRefusal = (typeof SETTINGS_PERMISSION_BUCKET_REFUSALS)[number];
|
|
245
|
+
/** 拒体的机读字段(按 `reason` 定形;原样进 wire 错误体 / `HttpError.extra`)。 */
|
|
246
|
+
export type SettingsPermissionBucketIssueFields = {
|
|
247
|
+
readonly field: `settings.permissions.${SettingsPermissionBucket}`;
|
|
248
|
+
readonly reason: "over_cap";
|
|
249
|
+
readonly count: number;
|
|
250
|
+
readonly cap: number;
|
|
251
|
+
} | {
|
|
252
|
+
readonly field: `settings.permissions.${SettingsPermissionBucket}`;
|
|
253
|
+
readonly reason: "bad_entry";
|
|
254
|
+
readonly index?: number;
|
|
255
|
+
};
|
|
256
|
+
/** 一次裁决的产物:wire 文案 + 机读字段。 */
|
|
257
|
+
export interface SettingsPermissionBucketIssue {
|
|
258
|
+
readonly message: string;
|
|
259
|
+
readonly fields: SettingsPermissionBucketIssueFields;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 三桶的**唯一**判据(纯函数)。`perms` 不是对象 ⇒ 不判(那一形归「settings.permissions must be an object」那道门);桶缺席 / `null`
|
|
263
|
+
* ⇒ 不判(与 `permissions: null` 的「容器缺席」同一约定)。逐桶按 {@link SETTINGS_PERMISSION_BUCKETS} 的序:不是数组 ⇒ `bad_entry`(无
|
|
264
|
+
* `index`);超帽 ⇒ `over_cap`(先判,拒体有界);首个非串 / 空串项 ⇒ `bad_entry` + 它的下标。全合法 ⇒ `null`。
|
|
265
|
+
*/
|
|
266
|
+
export declare function settingsPermissionBucketIssue(perms: unknown): SettingsPermissionBucketIssue | null;
|
|
265
267
|
/** 本 service 真正**消费**的 `settings` 顶层键 —— 闭集。每一个都有消费点:
|
|
266
268
|
* `env`/`hooks`/`model`/`outputStyle`/`permissions`/`ultracode` 与两个 `skillListing*` 在
|
|
267
269
|
* {@link parseTaskSettings};`webSearch` 在 `capabilities/scenarios.ts`(判决 `judgeRequestWebSearch`,单用户闸;形错在受理面 400,S-199)。
|
package/dist/task-settings.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import { combinePolicies, createAllowDenyPolicy, createFsWriteGatePolicy, createPermissionRulePolicy, parseRuleText, tightenTaskSpec, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, RUN_WORKFLOW_TOOL_NAME, ruleToolGrammarOf, } from "@sema-agent/core";
|
|
1
|
+
import { combinePolicies, createAllowDenyPolicy, createFsWriteGatePolicy, createPermissionRulePolicy, parseRuleText, tightenTaskSpec, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, RUN_WORKFLOW_TOOL_NAME, PERMISSION_MODES, ruleToolGrammarOf, } from "@sema-agent/core";
|
|
2
2
|
import { findUnmatchableToolNames, formatUnmatchableToolNames } from "./config.js";
|
|
3
3
|
import { CONTROL_AND_BIDI_STRIP_RE } from "./text-bidi.js";
|
|
4
4
|
import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
|
|
5
5
|
import { parseHooksConfig } from "./hooks/hook-runner.js";
|
|
6
|
-
export const
|
|
6
|
+
export const PERMISSION_MODE_REQUEST_FACE = {
|
|
7
|
+
default: "request",
|
|
8
|
+
acceptEdits: "request",
|
|
9
|
+
plan: "request",
|
|
10
|
+
auto: "request",
|
|
11
|
+
bypassPermissions: "request",
|
|
12
|
+
dontAsk: "excluded",
|
|
13
|
+
};
|
|
14
|
+
export const SETTINGS_PERMISSION_MODES = PERMISSION_MODES.filter((m) => PERMISSION_MODE_REQUEST_FACE[m] === "request");
|
|
15
|
+
export const SETTINGS_PERMISSION_MODES_EXCLUDED = PERMISSION_MODES.filter((m) => PERMISSION_MODE_REQUEST_FACE[m] === "excluded");
|
|
7
16
|
export const SETTINGS_PERMISSION_MODE_WORDS = SETTINGS_PERMISSION_MODES.map((m) => `"${m}"`).join(" | ");
|
|
8
17
|
export function isSettingsPermissionMode(raw) {
|
|
9
18
|
return typeof raw === "string" && SETTINGS_PERMISSION_MODES.includes(raw);
|
|
@@ -37,6 +46,7 @@ export function withPermissionMode(settings, mode) {
|
|
|
37
46
|
const base = settings ?? {};
|
|
38
47
|
return { ...base, permissions: { ...base.permissions, defaultMode: mode } };
|
|
39
48
|
}
|
|
49
|
+
export const ENGINE_ABSENT_PERMISSION_MODE = "default";
|
|
40
50
|
export function effectivePermissionMode(body, settings) {
|
|
41
51
|
const requested = coercePermissionMode(body.permissionMode) ?? settings?.permissions?.defaultMode;
|
|
42
52
|
const kill = settings?.permissions?.disableAutoMode;
|
|
@@ -44,22 +54,6 @@ export function effectivePermissionMode(body, settings) {
|
|
|
44
54
|
return "default";
|
|
45
55
|
return requested;
|
|
46
56
|
}
|
|
47
|
-
export function shellGateForMode(effMode) {
|
|
48
|
-
return MODE_ENGINE_FACES[effMode].shellGate;
|
|
49
|
-
}
|
|
50
|
-
const MODE_ENGINE_FACES = {
|
|
51
|
-
bypassPermissions: { shellGate: "off", writeFace: "open", autoModeRequested: undefined },
|
|
52
|
-
auto: { shellGate: "classify", writeFace: undefined, autoModeRequested: true },
|
|
53
|
-
default: { shellGate: "classify", writeFace: undefined, autoModeRequested: undefined },
|
|
54
|
-
acceptEdits: { shellGate: "classify", writeFace: undefined, autoModeRequested: undefined },
|
|
55
|
-
plan: { shellGate: "classify", writeFace: undefined, autoModeRequested: undefined },
|
|
56
|
-
};
|
|
57
|
-
export function writeFaceForMode(effMode) {
|
|
58
|
-
return MODE_ENGINE_FACES[effMode].writeFace;
|
|
59
|
-
}
|
|
60
|
-
export function autoModeRequestedForMode(effMode) {
|
|
61
|
-
return MODE_ENGINE_FACES[effMode].autoModeRequested;
|
|
62
|
-
}
|
|
63
57
|
export function skillListingBudgetFractionOk(v) {
|
|
64
58
|
return typeof v === "number" && Number.isFinite(v) && v > 0 && v <= 1;
|
|
65
59
|
}
|
|
@@ -88,11 +82,44 @@ function cleanEnvMap(v) {
|
|
|
88
82
|
}
|
|
89
83
|
return n > 0 ? out : undefined;
|
|
90
84
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
export const SETTINGS_PERMISSION_BUCKETS = ["allow", "deny", "ask"];
|
|
86
|
+
export const SETTINGS_PERMISSION_BUCKET_REFUSALS = ["over_cap", "bad_entry"];
|
|
87
|
+
function bucketIssueMessage(f) {
|
|
88
|
+
switch (f.reason) {
|
|
89
|
+
case "over_cap":
|
|
90
|
+
return (`${f.field} has ${f.count} entries — at most ${f.cap} are accepted. Refused rather than truncated: a truncated list silently drops the rules past the cap, ` +
|
|
91
|
+
`and a dropped deny / ask rule is a tightening that never happened. Merge rules into broader patterns or trim the list`);
|
|
92
|
+
case "bad_entry":
|
|
93
|
+
return f.index === undefined
|
|
94
|
+
? `${f.field} must be an array of non-empty rule strings — refused rather than ignored, because an ignored list reads as "in effect" to the caller`
|
|
95
|
+
: `${f.field}[${f.index}] is not a non-empty rule string — every entry must be one. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function settingsPermissionBucketIssue(perms) {
|
|
99
|
+
if (perms === null || typeof perms !== "object" || Array.isArray(perms))
|
|
100
|
+
return null;
|
|
101
|
+
for (const bucket of SETTINGS_PERMISSION_BUCKETS) {
|
|
102
|
+
const v = perms[bucket];
|
|
103
|
+
if (v === undefined || v === null)
|
|
104
|
+
continue;
|
|
105
|
+
const field = `settings.permissions.${bucket}`;
|
|
106
|
+
let fields;
|
|
107
|
+
if (!Array.isArray(v))
|
|
108
|
+
fields = { field, reason: "bad_entry" };
|
|
109
|
+
else if (v.length > MAX_SETTINGS_PERMISSION_RULES)
|
|
110
|
+
fields = { field, reason: "over_cap", count: v.length, cap: MAX_SETTINGS_PERMISSION_RULES };
|
|
111
|
+
else {
|
|
112
|
+
const index = v.findIndex((x) => typeof x !== "string" || x.length === 0);
|
|
113
|
+
if (index >= 0)
|
|
114
|
+
fields = { field, reason: "bad_entry", index };
|
|
115
|
+
}
|
|
116
|
+
if (fields !== undefined)
|
|
117
|
+
return { message: bucketIssueMessage(fields), fields };
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function acceptedRuleList(v) {
|
|
122
|
+
return Array.isArray(v) && v.length > 0 ? v.slice() : undefined;
|
|
96
123
|
}
|
|
97
124
|
export const TASK_SETTINGS_KEYS = ["env", "hooks", "model", "outputStyle", "permissions", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch"];
|
|
98
125
|
export const TASK_SETTINGS_PERMISSION_KEYS = ["allow", "ask", "defaultMode", "deny", "disableAutoMode"];
|
|
@@ -154,9 +181,12 @@ export function parseTaskSettings(raw) {
|
|
|
154
181
|
}
|
|
155
182
|
if (perms !== null && typeof perms === "object" && !Array.isArray(perms)) {
|
|
156
183
|
const p = perms;
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
184
|
+
const bucketsOk = settingsPermissionBucketIssue(p) === null;
|
|
185
|
+
if (!bucketsOk)
|
|
186
|
+
deferred.push("permissions");
|
|
187
|
+
const allow = bucketsOk ? acceptedRuleList(p.allow) : undefined;
|
|
188
|
+
const deny = bucketsOk ? acceptedRuleList(p.deny) : undefined;
|
|
189
|
+
const ask = bucketsOk ? acceptedRuleList(p.ask) : undefined;
|
|
160
190
|
const defaultMode = coercePermissionModeStrict(p.defaultMode);
|
|
161
191
|
const rawKill = p.disableAutoMode;
|
|
162
192
|
const disableAutoMode = rawKill === "disable" || rawKill === true || rawKill === false ? rawKill : undefined;
|
|
@@ -348,7 +378,7 @@ export function deriveSettingsPolicy(settings, gate, workflowGate) {
|
|
|
348
378
|
}
|
|
349
379
|
case "default":
|
|
350
380
|
case "auto": {
|
|
351
|
-
const p = compose(
|
|
381
|
+
const p = compose(createWorkflowAskPolicy(workflowGate?.isExempt));
|
|
352
382
|
return p ? { toolPolicy: p } : {};
|
|
353
383
|
}
|
|
354
384
|
case "bypassPermissions":
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -284,27 +284,6 @@ export interface ToolApprovalFrame {
|
|
|
284
284
|
* ask」,治理策略产的与普通策略产的在这一个词上同形 ⇒ 顶替不了(逐字论证见 `governance-ask-marks.ts` 顶注)。
|
|
285
285
|
*/
|
|
286
286
|
origin?: string;
|
|
287
|
-
/**
|
|
288
|
-
* **[ref] C3**(core 7.14.0;**ADDITIVE**,`"tool_approval"` only)—— `origin: "rule_store_unavailable"`
|
|
289
|
-
* 的**机制判别位**:持久规则车道这一次**读不下来的是哪一半**。core 闭二词
|
|
290
|
-
* (`RULE_STORE_UNREADABLE_KINDS`):`"store"` = 接上的规则店读不了(读失败 / 超时);`"call"` = 店读到了,
|
|
291
|
-
* 但**这条命令**按人的 deny/ask 行读不出来(收紧 lexer 的 `unreadable`:展开里含规则词、引号没闭、语法
|
|
292
|
-
* 错——具体那句在 `message` 里)。`AskRequest.ruleStoreUnreadable` 逐字透传。
|
|
293
|
-
*
|
|
294
|
-
* 🔴 **在场 ⇔ `origin === "rule_store_unavailable"`**(core 顶注逐字)。两词对审批人是两件事:`store`
|
|
295
|
-
* 指向「去看规则店 / 网络」,`call` 指向「这条命令的写法让规则判不了」——一个 origin 词对这两件事同形。
|
|
296
|
-
* 缺席 = 车道把店和这条命令都读通了,**或**本部署没接规则车道;消费端禁把缺席读成对规则店健康的断言。
|
|
297
|
-
*
|
|
298
|
-
* 🔴 **不枚举词表**(值形是 `string` 而不是本仓自铸的联合):词表单一属主在 core
|
|
299
|
-
* (`isRuleStoreUnreadable` 是它的成员判),抄一份会把 core 加的新词吞成缺席 —— 与 {@link origin} 逐字同规。
|
|
300
|
-
*
|
|
301
|
-
* 🔴 **echo-only,零消费**:策略自声明的值在 core 的**唯一**盖章点就被抹掉了,server 不重算、不再筛第二遍,
|
|
302
|
-
* 更不许据本键自铸第二张车道资格表 —— 同 `origin` / `denialLimitFallback` 两条顶注的同一条禁令。
|
|
303
|
-
*
|
|
304
|
-
* ⚰️ 本键的前任 `classifierUnavailable`(S-185 / [ref])随 core 7.14.0 [ref] **整族退役**:引擎那份事实改骑
|
|
305
|
-
* `GateDisposition.denied.cause`(`tool_end.gate` / `permissionDenied.gate`),ask 面自此没有这一位。
|
|
306
|
-
*/
|
|
307
|
-
ruleStoreUnreadable?: string;
|
|
308
287
|
/**
|
|
309
288
|
* **[ref]**(core 7.19.0;**ADDITIVE**,`"tool_approval"` only)—— 这只**根外读** ask 的**清障目录**:
|
|
310
289
|
* 把 `dir` 加进本会话的读目录(壳侧既有的 `/add-dir` 口),同一条调用就不会再问。
|
|
@@ -319,7 +298,7 @@ export interface ToolApprovalFrame {
|
|
|
319
298
|
*
|
|
320
299
|
* 🔴 **echo-only,零判读**:server 不从命令文本重推目录,也不复核它真不真能清掉这只 ask —— core 已经
|
|
321
300
|
* 把提议的根放回读边界又走了一遍才递出来,下游再判一遍就是同一事实的第二个判官(与 `origin` /
|
|
322
|
-
* `
|
|
301
|
+
* `denialLimitFallback` 两条顶注的同一条禁令)。
|
|
323
302
|
*
|
|
324
303
|
* 🔴 **只此一面**:core 的负控逐字写着「the durable park row has no twin of this seat in this version」——
|
|
325
304
|
* 一只 PARKED 的根外读 ask 渲的仍是单独那张确认卡。所以 `card_json` / 耐久面**没有**这一座,本仓也不
|
|
@@ -849,6 +828,10 @@ export type UnattendedPolicyRefusal = Extract<AskOutcome, {
|
|
|
849
828
|
* 铸:park 政策的路由答 `"unavailable"`,deny 政策的 {@link UnattendedPolicyRefusal}。没有布尔臂(S-689:裸 `false`
|
|
850
829
|
* 是人的拒绝,这个口从不替人说话)。 */
|
|
851
830
|
export type UnattendedAskOutcome = Extract<AskOutcome, "unavailable"> | UnattendedPolicyRefusal;
|
|
831
|
+
/** S-877 候裁 16:耐久会话授权店的读面(`src/plugins/approval-exemption-store.ts` 的 `has`,结构型 —— 协调器不值引店模块)。 */
|
|
832
|
+
export interface SessionGrantReader {
|
|
833
|
+
has(sessionId: string, canonicalToolName: string): Promise<boolean>;
|
|
834
|
+
}
|
|
852
835
|
/**
|
|
853
836
|
* [ref] 车3 刀 3b —— 「流内审批协议到底上不上场」的**单一谓词**(设计稿 §2.3 注入 / §2.4 能力面 /
|
|
854
837
|
* §8.4 park 设施自检,三处同一份判据)。
|
|
@@ -1140,6 +1123,9 @@ export declare class ToolApprovalCoordinator {
|
|
|
1140
1123
|
* 腿闭(`runWithContext` finally)。**纯观察面**:sink 自己吞写失败并 `recordFailOpen`,任何一次审计
|
|
1141
1124
|
* 写都不得改变审批终局(店头注成文)。SQL 车道恒缺席(那里有真 ask 店)。 */
|
|
1142
1125
|
private readonly askAudit;
|
|
1126
|
+
/** S-877 候裁 16 → A:耐久会话授权店的**读面**(`/decide` 的 `remember:"session"` 写、本席读)。在场 ⇔ 部署有 store backend
|
|
1127
|
+
* (装配点 `boot/coordinators.ts`,与 `/decide` 写的是**同一只**店)。读口唯一 = {@link standingGrantSettles}。 */
|
|
1128
|
+
private readonly sessionGrants;
|
|
1143
1129
|
constructor(opts?: {
|
|
1144
1130
|
ttlMs?: number;
|
|
1145
1131
|
/** [ref](案A):零活流铸造的 ask 的窗(见 {@link unreachedTtlMs})。装配点在协议**上场**时才传
|
|
@@ -1167,6 +1153,8 @@ export declare class ToolApprovalCoordinator {
|
|
|
1167
1153
|
parkTombstonePerPrincipalMax?: number;
|
|
1168
1154
|
/** [ref] (c):local 车道审计 sink(见 {@link ToolApprovalCoordinator.askAudit})。 */
|
|
1169
1155
|
askAudit?: ApprovalAskAuditSink;
|
|
1156
|
+
/** S-877 候裁 16:耐久会话授权店的读面(见 {@link ToolApprovalCoordinator.sessionGrants})。 */
|
|
1157
|
+
sessionGrants?: SessionGrantReader;
|
|
1170
1158
|
});
|
|
1171
1159
|
/**
|
|
1172
1160
|
* [ref] R-13 C —— 「**无人可答**」这一类终局的**唯一**成形口(park 路由 vs deny 政策)。
|
|
@@ -1350,6 +1338,47 @@ export declare class ToolApprovalCoordinator {
|
|
|
1350
1338
|
* 面)—— 把它混进本名单会让「同 askId 的重复本地注册」拿到裸 deny,而赢家拿的是 park 路由:同一条
|
|
1351
1339
|
* 持久行、两种 live 终局。那一支改走 {@link settleSameAskIdParkRoute}。 */
|
|
1352
1340
|
private settleVoidedSiblings;
|
|
1341
|
+
/** [ref]/[ref]:本只 ask 的治理来源标(一次 peek,非消费式)。**出处决定判定**:按 `origin` 算 broker key(与写侧同一把),
|
|
1342
|
+
* 投递到哪条连接不参与 —— 见 {@link AskOriginIdentity} 顶注。 */
|
|
1343
|
+
private governanceForcedOf;
|
|
1344
|
+
/**
|
|
1345
|
+
* 🔴 **S-877 候裁 16 → A —— 站着的答案(会话授权)的唯一兑现口**(主会话裁定 2026-09-29;core 7.34.0 wire 段逐字指定的兑现位:
|
|
1346
|
+
* 「a host approval seat that settles asks from a session grant reads it [`authoredBy`] to keep its hands off」)。
|
|
1347
|
+
*
|
|
1348
|
+
* **一条判据**:一只 ask 由会话授权代答 ⇔ ① 它是站着的答案退得掉的一问({@link standingAnswerMayRetire} —— 治理强制 /
|
|
1349
|
+
* `requiresRealApproval` / `mandated` 三位的既有排除一字不动;运维「恒问人」的卡(`APPROVAL_NEVER_AUTO` / 耐久预算耗尽)
|
|
1350
|
+
* 自 S-877 起盖部署出身 ⇒ core 盖 `mandated`,同落这一臂)∧ ② 有真会话 ∧ ③ 两只授权店任一命中:live `allow_session` 的
|
|
1351
|
+
* 进程内表(按能力桶,[ref]② 修2)或耐久 `/decide` `remember:"session"` 的豁免店(按 canonical 工具名,`/decide` 写的同一只店;
|
|
1352
|
+
* 读失败 ⇒ 不命中 = fail-closed,人多看一张卡而不是被静默代答)∧ ④ 行上没有与本地 peek 矛盾的出处({@link sessionGrantUncontestedByRow})。
|
|
1353
|
+
*
|
|
1354
|
+
* **两个读位、一只店、按问句的作者分**(收车档 ⑩ 16):本席兑现**引擎自装的问**(模式写门 / 形状层 —— core 7.34.0 起它们是引擎
|
|
1355
|
+
* 自己的层、不读任何豁免,折叠 ask 赢 ⇒ 策略层探针对它们恒无效,codex F2);**本仓自己的问**(durable 名单门、workflow 问句门、
|
|
1356
|
+
* acceptEdits 的 cwd 域写门)照旧在各自的层上读同一只店「不问」。为什么两处都要:子代经父约束链继承到的问到达祖先审批席时 core
|
|
1357
|
+
* 盖 `mandated`(`ruleClearanceOf` 的 inheritedUnresolved / ancestorResolved 门),本席依约不退役 —— 只有策略层的「不问」够得着继承链
|
|
1358
|
+
* (主会话裁定原想退役那三只探针,施工中被 test/parked-decide-leg.test.ts「[候裁② 修]remember 全链行为」证伪,收车档 ⑩ 16-⑤)。
|
|
1359
|
+
* 本席的两个入口(`askBroadcast` 开头、窗 0 / 无活流身份席 {@link boundUnattendedAsk})都只调这一只。
|
|
1360
|
+
*/
|
|
1361
|
+
private standingGrantSettles;
|
|
1362
|
+
/** 耐久豁免店的一次读(fail-closed:读抛 **或超时** ⇒ 不命中 + warn)。只由 {@link standingGrantSettles} 调。
|
|
1363
|
+
* 🔴 **有界**(codex 续车轮 F2):这一读排在呈卡 / 待决登记 / 审批计时器**之前**,一次黑洞化的店读会让整只 ask 连一个兜底都没有地
|
|
1364
|
+
* 永久悬挂(与 R2-5 抓过的 `ensureAsk` 同病)。期限取 {@link DURABLE_CONVERGE_READ_TIMEOUT_MS}(只读复核,不是做事的调用);超时 ⇒
|
|
1365
|
+
* 按未命中继续既有的呈卡 / 无人可答路径 —— 方向是人多看一张卡,不是被静默代答。迟到的真答案不追认(授权是「这一问要不要问」,
|
|
1366
|
+
* 问已经发出去了)。 */
|
|
1367
|
+
private durableGrantHas;
|
|
1368
|
+
/**
|
|
1369
|
+
* 🔴 **S-877 候裁 16 —— 窗 0 席**:本席**不呈卡**,但在报「无人可答」之前先问站着的答案。装配点 = 三条腿的 `windowZero` 席
|
|
1370
|
+
* (sync `http/routes/tasks.ts`、bg `runs.ts`、resume `http/resume-legs.ts`)。修前三处是装配层的就地闭包 `() => unattendedAskOutcome(...)`
|
|
1371
|
+
* —— 不读授权,引擎那一问(模式写门 / 形状层)在窗 0 部署上 remember 恒无效。改形后:授权命中 ⇒ `true`;否则逐字走
|
|
1372
|
+
* {@link unattendedAskOutcome} 这**一个**政策口(五臂一处施加,[ref] R-13 C 不变)。
|
|
1373
|
+
* parked 赎回腿的继承条目席**不**改:那条腿上到席的问全是继承问(core 盖 `mandated`),本席依约不退役,改了也兑现不了任何东西 ——
|
|
1374
|
+
* 赎回腿上那类问的授权照旧由重建链里 durable 名单门的策略层探针兑现(锚 = 行的 root 会话)。复活子代**自己的**引擎问(模式写门 /
|
|
1375
|
+
* 形状层)则经赎回腿铸的 execute ctx `onAsk` 落本席(身份 = 行的 root 会话,S-877 四续 ⑪,`parked-decide.ts` `approverSeatFor`)。
|
|
1376
|
+
*/
|
|
1377
|
+
boundUnattendedAsk: (identity: {
|
|
1378
|
+
owner: string | null;
|
|
1379
|
+
taskId: string;
|
|
1380
|
+
sessionId?: string;
|
|
1381
|
+
}) => ((req: AskRequest) => Promise<AskOutcome>);
|
|
1353
1382
|
/**
|
|
1354
1383
|
* 🔴 session grant 短路前的**持久出处复核**([ref] 的 codex R1-[high] 补丁;[ref] 换判据)。
|
|
1355
1384
|
*
|
package/dist/tool-approval.js
CHANGED
|
@@ -363,6 +363,7 @@ export class ToolApprovalCoordinator {
|
|
|
363
363
|
parkTombstonePerPrincipalMax;
|
|
364
364
|
parkedRedeem;
|
|
365
365
|
askAudit;
|
|
366
|
+
sessionGrants;
|
|
366
367
|
constructor(opts) {
|
|
367
368
|
this.ruleConsent = opts?.ruleConsent;
|
|
368
369
|
this.ruleScopeRootFor = opts?.ruleScopeRootFor;
|
|
@@ -379,6 +380,7 @@ export class ToolApprovalCoordinator {
|
|
|
379
380
|
this.parkTombstoneTtlMs = opts?.parkTombstoneTtlMs ?? DEFAULT_PARK_TOMBSTONE_TTL_MS;
|
|
380
381
|
this.parkTombstonePerPrincipalMax = opts?.parkTombstonePerPrincipalMax ?? DEFAULT_PARK_TOMBSTONE_PER_PRINCIPAL_MAX;
|
|
381
382
|
this.askAudit = opts?.askAudit;
|
|
383
|
+
this.sessionGrants = opts?.sessionGrants;
|
|
382
384
|
}
|
|
383
385
|
unattendedOutcome(site) {
|
|
384
386
|
let outcome;
|
|
@@ -696,6 +698,45 @@ export class ToolApprovalCoordinator {
|
|
|
696
698
|
entry.settle(false, "expired");
|
|
697
699
|
}
|
|
698
700
|
}
|
|
701
|
+
governanceForcedOf(origin, req) {
|
|
702
|
+
return (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(origin.owner, origin.sessionId, origin.taskId))).isMarked(req.toolCallId);
|
|
703
|
+
}
|
|
704
|
+
async standingGrantSettles(origin, req, governanceForced) {
|
|
705
|
+
if (!standingAnswerMayRetire({ governanceForced, requiresRealApproval: req.requiresRealApproval === true, mandated: req.mandated === true }))
|
|
706
|
+
return false;
|
|
707
|
+
const sessionId = origin.sessionId;
|
|
708
|
+
if (!sessionId)
|
|
709
|
+
return false;
|
|
710
|
+
const live = this.allowAllSessions.has(sessionAllowKey(origin.owner, sessionId, sessionAllowCategory(req.toolName)));
|
|
711
|
+
const durable = !live && (await this.durableGrantHas(sessionId, req.toolName));
|
|
712
|
+
if (!live && !durable)
|
|
713
|
+
return false;
|
|
714
|
+
if (!(await this.sessionGrantUncontestedByRow(origin, req)))
|
|
715
|
+
return false;
|
|
716
|
+
if (durable)
|
|
717
|
+
defaultLogger.info("approval_exempted", { sessionId, toolName: req.toolName, seat: "approval_seat" });
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
async durableGrantHas(sessionId, canonicalToolName) {
|
|
721
|
+
if (!this.sessionGrants)
|
|
722
|
+
return false;
|
|
723
|
+
try {
|
|
724
|
+
const hit = await this.withStoreDeadline(this.sessionGrants.has(sessionId, canonicalToolName), "sessionGrants.has(standing-grant)", DURABLE_CONVERGE_READ_TIMEOUT_MS);
|
|
725
|
+
return hit === true;
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
defaultLogger.warn("session_grant_read_failed", { sessionId, toolName: canonicalToolName, error: String(err).slice(0, 200) });
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
boundUnattendedAsk = (identity) => {
|
|
733
|
+
return async (req) => {
|
|
734
|
+
const origin = { owner: identity.owner, taskId: identity.taskId, ...(identity.sessionId !== undefined ? { sessionId: identity.sessionId } : {}), legKey: "" };
|
|
735
|
+
if (await this.standingGrantSettles(origin, req, this.governanceForcedOf(origin, req)))
|
|
736
|
+
return true;
|
|
737
|
+
return this.unattendedAskOutcome({ taskId: identity.taskId, owner: identity.owner, toolName: req.toolName });
|
|
738
|
+
};
|
|
739
|
+
};
|
|
699
740
|
async sessionGrantUncontestedByRow(origin, req) {
|
|
700
741
|
if (!this.askStore)
|
|
701
742
|
return true;
|
|
@@ -944,6 +985,9 @@ export class ToolApprovalCoordinator {
|
|
|
944
985
|
async askBroadcast(origin, ctxs, req, signal) {
|
|
945
986
|
if (signal?.aborted)
|
|
946
987
|
return false;
|
|
988
|
+
const governanceForced = this.governanceForcedOf(origin, req);
|
|
989
|
+
if (await this.standingGrantSettles(origin, req, governanceForced))
|
|
990
|
+
return true;
|
|
947
991
|
const aliveCtxs = ctxs.filter((c) => !c.abortSignal?.aborted);
|
|
948
992
|
const reachability = aliveCtxs.length === 0 ? "unreached" : "reachable";
|
|
949
993
|
if (reachability === "unreached") {
|
|
@@ -952,13 +996,6 @@ export class ToolApprovalCoordinator {
|
|
|
952
996
|
}
|
|
953
997
|
}
|
|
954
998
|
const primary = aliveCtxs[0];
|
|
955
|
-
const governanceForced = (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(origin.owner, origin.sessionId, origin.taskId))).isMarked(req.toolCallId);
|
|
956
|
-
if (standingAnswerMayRetire({ governanceForced, requiresRealApproval: req.requiresRealApproval === true, mandated: req.mandated === true }) &&
|
|
957
|
-
origin.sessionId &&
|
|
958
|
-
this.allowAllSessions.has(sessionAllowKey(origin.owner, origin.sessionId, sessionAllowCategory(req.toolName))) &&
|
|
959
|
-
(await this.sessionGrantUncontestedByRow(origin, req))) {
|
|
960
|
-
return true;
|
|
961
|
-
}
|
|
962
999
|
const releaseAdmission = this.admit(origin.taskId, origin.owner);
|
|
963
1000
|
if (!releaseAdmission) {
|
|
964
1001
|
defaultLogger.warn("approval admission gate: too many pending asks — routing to park", {
|
|
@@ -1009,7 +1046,6 @@ export class ToolApprovalCoordinator {
|
|
|
1009
1046
|
...(ruleLaneMaterial !== undefined ? { ruleOffers: ruleLaneMaterial.offers } : {}),
|
|
1010
1047
|
...((a) => (a !== undefined ? { ruleOffersAbsence: a } : {}))(readRuleOffersAbsence(req)),
|
|
1011
1048
|
...((f) => (f !== undefined ? { denialLimitFallback: f } : {}))(readDenialLimitFallback(req)),
|
|
1012
|
-
...(typeof req.ruleStoreUnreadable === "string" && req.ruleStoreUnreadable.length > 0 ? { ruleStoreUnreadable: req.ruleStoreUnreadable } : {}),
|
|
1013
1049
|
...((g) => (g !== undefined ? { readRootCandidate: g } : {}))(readReadRootCandidate(req)),
|
|
1014
1050
|
...((c) => (c !== undefined ? { probeCause: c } : {}))(readProbeCause(req)),
|
|
1015
1051
|
...((c) => (c !== undefined ? { ruleEvidence: c } : {}))(readRuleEvidence(req)),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const TOOL_DECLARATION_HTTP_STATUS: {
|
|
2
|
+
readonly "config.tool_mount_denied": 400;
|
|
3
|
+
readonly "config.tool_roster_bound": 503;
|
|
4
|
+
readonly "config.tool_name_too_long": 503;
|
|
5
|
+
readonly "config.tool_contract_too_long": 503;
|
|
6
|
+
readonly "config.tool_schema_bound": 503;
|
|
7
|
+
readonly "config.tool_face_invalid": null;
|
|
8
|
+
readonly "config.tool_name_invalid": null;
|
|
9
|
+
readonly "config.egress_requires_write_effect": null;
|
|
10
|
+
readonly "config.reserved_tool_name": null;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=tool-declaration-http-status.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const TOOL_DECLARATION_HTTP_STATUS = {
|
|
2
|
+
"config.tool_mount_denied": 400,
|
|
3
|
+
"config.tool_roster_bound": 503,
|
|
4
|
+
"config.tool_name_too_long": 503,
|
|
5
|
+
"config.tool_contract_too_long": 503,
|
|
6
|
+
"config.tool_schema_bound": 503,
|
|
7
|
+
"config.tool_face_invalid": null,
|
|
8
|
+
"config.tool_name_invalid": null,
|
|
9
|
+
"config.egress_requires_write_effect": null,
|
|
10
|
+
"config.reserved_tool_name": null,
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=tool-declaration-http-status.js.map
|
|
@@ -43,8 +43,8 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
|
|
|
43
43
|
type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
|
|
44
44
|
type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
|
|
45
45
|
type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
|
|
46
|
-
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "
|
|
47
|
-
type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "probeReason" | "hasBidiControls" | "previewWithheld" | "execCwd";
|
|
46
|
+
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate";
|
|
47
|
+
type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "probeReason" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy";
|
|
48
48
|
type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
|
|
49
49
|
type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
|
|
50
50
|
type AskFrameServerMinted = "type" | "approvalId" | "argsOmitted" | "outcome" | "governanceForced" | "expiresAtMs" | "expiresInMs" | "serverNowMs" | "parked" | "inputHasBidi";
|
|
@@ -133,19 +133,19 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
|
|
|
133
133
|
/** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
|
|
134
134
|
type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
|
|
135
135
|
type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
|
|
136
|
-
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "
|
|
137
|
-
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
|
|
136
|
+
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "mandated" | "mandate";
|
|
137
|
+
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld" | "authoredBy";
|
|
138
138
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
139
139
|
type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
|
|
140
140
|
type ParkArmKeys = keyof Extract<PendingAction, {
|
|
141
141
|
kind: "tool_approval";
|
|
142
142
|
}>;
|
|
143
143
|
type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
|
|
144
|
-
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "
|
|
144
|
+
type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy";
|
|
145
145
|
type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
|
|
146
146
|
type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
|
|
147
147
|
type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
|
|
148
|
-
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "
|
|
148
|
+
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory";
|
|
149
149
|
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
|
|
150
150
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
151
151
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
@@ -163,7 +163,7 @@ type GateOutcomeProjected = "disposition" | "settlement" | "origin";
|
|
|
163
163
|
type _GuardGateOutcome = AssertAllKeysHandled<Exclude<keyof GateOutcome, GateOutcomeProjected>>;
|
|
164
164
|
type _GuardGateOutcomeReverse = AssertAllKeysHandled<Exclude<GateOutcomeProjected, keyof GateOutcome>>;
|
|
165
165
|
type GateDispositionKeys = GateDisposition extends infer D ? (D extends object ? keyof D : never) : never;
|
|
166
|
-
type GateDispositionProjected = "kind" | "deniedBy" | "cause" | "classifier";
|
|
166
|
+
type GateDispositionProjected = "kind" | "deniedBy" | "cause" | "classifier" | "released" | "mode" | "askClass" | "classes";
|
|
167
167
|
type _GuardGateDisposition = AssertAllKeysHandled<Exclude<GateDispositionKeys, GateDispositionProjected>>;
|
|
168
168
|
type _GuardGateDispositionReverse = AssertAllKeysHandled<Exclude<GateDispositionProjected, GateDispositionKeys>>;
|
|
169
169
|
type SettlementProjected = "kind" | "who" | "when" | "note";
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
|
|
17
17
|
* 刚立,立完就在下一次加码时自己咬住了)。
|
|
18
18
|
*/
|
|
19
|
-
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped"];
|
|
19
|
+
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable"];
|
|
20
20
|
export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
|
|
21
21
|
/**
|
|
22
22
|
* S-690 —— **本仓自铸**的通告码与它们的受众(码 / 铸点 / 受众三者的属主都是本仓)。
|
|
@@ -27,6 +27,11 @@ export const ENGINE_NOTICE_WIRE_CODES = [
|
|
|
27
27
|
"approval.read_root_grant_rejected",
|
|
28
28
|
"instructions.source_changed",
|
|
29
29
|
"session.records_skipped",
|
|
30
|
+
"classifier.unavailable",
|
|
31
|
+
"config.permission_mode_unavailable",
|
|
32
|
+
"permission.ask_released_by_mode",
|
|
33
|
+
"permission.read_deny_unapplied",
|
|
34
|
+
"permission.rule_store_unavailable",
|
|
30
35
|
];
|
|
31
36
|
export const SERVER_NOTICE_AUDIENCE = {
|
|
32
37
|
"instructions.source_changed": "user",
|