@sema-agent/server 7.104.0 → 7.106.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 +114 -0
- package/USAGE.md +193 -52
- 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 +5 -10
- 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 +30 -5
- package/dist/config-catalog.js +75 -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 +50 -2
- package/dist/config.js +111 -23
- 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/file-history-disclosure.d.ts +7 -31
- package/dist/file-history-disclosure.js +12 -0
- package/dist/fleet/fleet-bus.d.ts +5 -1
- 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.d.ts +1 -1
- package/dist/http/admission.js +14 -9
- package/dist/http/approval-sweeps.js +13 -1
- package/dist/http/dispatch.d.ts +18 -0
- package/dist/http/dispatch.js +18 -5
- 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.d.ts +1 -1
- package/dist/http/resume-legs.js +19 -11
- package/dist/http/route-ctx.d.ts +9 -0
- package/dist/http/route-table.d.ts +15 -4
- package/dist/http/route-table.js +6 -5
- package/dist/http/routes/approvals-assistant.js +0 -1
- package/dist/http/routes/capabilities.js +12 -11
- package/dist/http/routes/config-catalog.js +2 -1
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/http/routes/fleet.d.ts +3 -2
- package/dist/http/routes/images.js +0 -1
- package/dist/http/routes/runs.js +2 -8
- package/dist/http/routes/session-sync.js +6 -1
- package/dist/http/routes/sessions.js +5 -2
- package/dist/http/routes/side-query.js +8 -1
- package/dist/http/routes/tasks.js +4 -1
- package/dist/http/routes/trace-usage.d.ts +2 -0
- package/dist/http/routes/trace-usage.js +4 -9
- package/dist/http/server.d.ts +29 -8
- package/dist/http/server.js +4 -1
- package/dist/http/session-inherit-env.d.ts +37 -0
- package/dist/http/session-inherit-env.js +6 -0
- package/dist/http/sse-lifecycle.d.ts +1 -1
- package/dist/http/sse-log.d.ts +12 -9
- package/dist/http/sse-log.js +1 -8
- 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 +9 -1
- package/dist/observability/fail-open.js +9 -1
- package/dist/observability/metrics.js +3 -1
- 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 +82 -9
- package/dist/observability/run-terminal.js +112 -9
- 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/image-bake-store-sql.d.ts +0 -2
- package/dist/plugins/image-bake-store-sql.js +0 -5
- package/dist/plugins/retention-store-sql.js +1 -0
- package/dist/plugins/run-store-sql.d.ts +3 -2
- package/dist/plugins/schema-indexes.js +1 -0
- package/dist/plugins/session-policy-store-sql.d.ts +38 -7
- package/dist/plugins/session-policy-store-sql.js +73 -18
- package/dist/plugins/sql-driver.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +17 -6
- package/dist/plugins/store-backend.js +16 -5
- package/dist/plugins/tidb-pool.js +5 -4
- package/dist/read-face-posture.d.ts +25 -4
- package/dist/read-face-posture.js +9 -0
- 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/rules-consent.js +0 -11
- 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 +100 -12
- package/dist/session-policy-wire.js +71 -8
- 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 +34 -45
- package/dist/task-settings.js +46 -25
- 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 +13 -7
- package/dist/trace/project.js +54 -6
- package/dist/trace/redact.d.ts +7 -0
- package/dist/trace/redact.js +153 -61
- 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/plugins/memory-session-policy-store.d.ts +0 -46
- package/dist/plugins/memory-session-policy-store.js +0 -52
- package/dist/roster-bound-http-status.d.ts +0 -6
- package/dist/roster-bound-http-status.js +0 -6
|
@@ -150,12 +150,12 @@ export declare function autonomyOverrides(autonomy: Autonomy | undefined): Parti
|
|
|
150
150
|
*/
|
|
151
151
|
export declare function governanceMandatesShellGateAlways(governance: {
|
|
152
152
|
autonomy?: Autonomy;
|
|
153
|
-
manualModeShellGate?:
|
|
153
|
+
manualModeShellGate?: ShellGateDoctrine;
|
|
154
154
|
}): boolean;
|
|
155
155
|
/** [ref] 归因的部署侧入参(两个治理旋钮;判据属主见 {@link governanceMandatesShellGateAlways})。 */
|
|
156
156
|
export interface GovernancePosture {
|
|
157
157
|
autonomy?: Autonomy;
|
|
158
|
-
manualModeShellGate?:
|
|
158
|
+
manualModeShellGate?: ShellGateDoctrine;
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
161
|
* [ref]:推「这道门是运维治理层下的吗」。判据是**合取**,两半各管一件事:
|
|
@@ -172,8 +172,9 @@ export interface GovernancePosture {
|
|
|
172
172
|
* ⚠️ 残留(双向失真的窄在场向 + 根治路径)与「只收窄」的那一个裁决读者,都登记在
|
|
173
173
|
* {@link governanceMandatesShellGateAlways} 的顶注,别在这里复述。
|
|
174
174
|
*
|
|
175
|
-
* 🔴
|
|
176
|
-
*
|
|
175
|
+
* 🔴 **形状档的 ask 刻意不标**(S-877 起它不再是一个档位词:core 7.34.0 把形状档装在**每一种模式**的全权 shell 上,
|
|
176
|
+
* 行上的 `shellGateDoctrine` 只在运维地板 `"always"` 铸门时出现):形状档的一次 shell ask 出自引擎自己的判断,
|
|
177
|
+
* 也可能叠着 `APPROVAL_REQUIRE` 这类别的门,行上无从分辨 —— 与活卡腿同一条「宁缺毋假」纪律。
|
|
177
178
|
*
|
|
178
179
|
* 🔴 **单一属主,四个读者**(不在任何读面另写一份判据 —— 这一位恰恰是靠「行上取证 ∧ 部署姿态」的合取才不
|
|
179
180
|
* 谎报的,两份必漂):409 体的待批门材料(`http/active-run-conflict.ts`)、两条 durable 读面
|
|
@@ -196,10 +197,6 @@ export declare function governanceOriginOf(gate: {
|
|
|
196
197
|
shellGateDoctrine?: ShellGateDoctrine;
|
|
197
198
|
};
|
|
198
199
|
} | undefined, governance: GovernancePosture | undefined): true | undefined;
|
|
199
|
-
/** spec 序里的秩(0 起,越大越严)。入参收 `string`(fail-loud 边界:schema `trustRank` 同款姿势)——词表外 ⇒
|
|
200
|
-
* RangeError,绝不 undefined/NaN 让比较静默成 false。导出只为对表钉(test/safety-merge-spec-consumption.test.ts,
|
|
201
|
-
* [ref] 件4)——运行时消费仍走上表(键类型闭集)。 */
|
|
202
|
-
export declare function shellGateRank(word: string): number;
|
|
203
200
|
/**
|
|
204
201
|
* Apply the operator's runtime governance (autonomy + commandPolicy + manualModeShellGate) onto a base
|
|
205
202
|
* `TaskSpec`, TIGHTEN-ONLY, in a SINGLE {@link tightenTaskSpec} call: commandPolicy compiles to a `toolPolicy`
|
|
@@ -212,8 +209,8 @@ export declare function shellGateRank(word: string): number;
|
|
|
212
209
|
* (task-settings deriveSettingsPolicy 的 default/auto/acceptEdits 三臂)让一个**部署级 tighten-only 旋钮
|
|
213
210
|
* 的生死由客户端表态在场性决定**:body 无 settings/permissionMode(cli print/headless 腿按设计不 stamp)
|
|
214
211
|
* ⇒ 旋钮静默失效(clay 实机 Monitor 案「门根本没铸」);bypassPermissions 一句话掀掉部署闸;非 host lane
|
|
215
|
-
* fsWriteGate wiring 不建 ⇒ 沙箱 lane 全失效。governance 层与 autonomy 同拍施加后三者全闭。
|
|
216
|
-
*
|
|
212
|
+
* fsWriteGate wiring 不建 ⇒ 沙箱 lane 全失效。governance 层与 autonomy 同拍施加后三者全闭。S-877 起地板只有
|
|
213
|
+
* `"always"` 一个词,合成退化成在场性(两个来源同词),不再有秩比较。
|
|
217
214
|
*
|
|
218
215
|
* [ref]([ref] issue #29,**邻仓 test AI 的 46 格真机矩阵**实证——不是本仓的钉,别到本仓文件里数 46;
|
|
219
216
|
* 本仓的类级门是 test/operator-knob-client-posture-matrix.test.ts 的 旋钮×表态×lane 矩阵):
|
|
@@ -231,7 +228,7 @@ export declare function shellGateRank(word: string): number;
|
|
|
231
228
|
export declare function applyRuntimeGovernance(base: TaskSpec, governance: {
|
|
232
229
|
autonomy?: Autonomy;
|
|
233
230
|
commandPolicy?: CommandRule[];
|
|
234
|
-
manualModeShellGate?:
|
|
231
|
+
manualModeShellGate?: ShellGateDoctrine;
|
|
235
232
|
sensitivePathPolicy?: ToolPolicy;
|
|
236
233
|
/** [ref]/[ref] `governanceForced` 的标记表(缺省 = **当前 run 腿的 ALS 表**,见
|
|
237
234
|
* `governance-ask-marks.ts` 顶注「作用域」段)。注入口只为测试与将来的多实例形。 */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { combinePolicies, COARSE_SHELL_TOOLS as CORE_COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, DEFAULT_SUBAGENT_TOOL_NAME, parseLeadingCommandName, tightenTaskSpec, } from "@sema-agent/core";
|
|
2
|
-
import { GovernanceConfig
|
|
1
|
+
import { combinePolicies, COARSE_SHELL_TOOLS as CORE_COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, DEFAULT_SUBAGENT_TOOL_NAME, parseLeadingCommandName, tightenTaskSpec, wrapToolPolicy, } from "@sema-agent/core";
|
|
2
|
+
import { GovernanceConfig } from "@sema-agent/settings-schema";
|
|
3
3
|
import { currentGovernanceAskMarks } from "./governance-ask-marks.js";
|
|
4
|
-
import { operatorAsk } from "./operator-ask.js";
|
|
4
|
+
import { operatorAsk, withDeploymentAuthorship } from "./operator-ask.js";
|
|
5
5
|
const COMMAND_RULE_SHAPE = GovernanceConfig.shape.commandPolicy.unwrap().element.shape;
|
|
6
6
|
const COMMAND_DECISIONS = COMMAND_RULE_SHAPE.decision.options;
|
|
7
7
|
export const AUTONOMY_MODES = GovernanceConfig.shape.autonomy.unwrap().options;
|
|
@@ -124,14 +124,12 @@ export function governanceOriginOf(gate, governance) {
|
|
|
124
124
|
function createGovernanceAskMarkingPolicy(inner, marksOf) {
|
|
125
125
|
const note = (d, req) => {
|
|
126
126
|
marksOf()?.setMarked(req.toolCallId, d.action === "ask");
|
|
127
|
-
return d;
|
|
128
|
-
};
|
|
129
|
-
return {
|
|
130
|
-
check(req, signal) {
|
|
131
|
-
const decision = inner.check(req, signal);
|
|
132
|
-
return decision instanceof Promise ? decision.then((d) => note(d, req)) : note(decision, req);
|
|
133
|
-
},
|
|
127
|
+
return d.action === "ask" ? withDeploymentAuthorship(d) : d;
|
|
134
128
|
};
|
|
129
|
+
return wrapToolPolicy(inner, (req, signal) => {
|
|
130
|
+
const decision = inner.check(req, signal);
|
|
131
|
+
return decision instanceof Promise ? decision.then((d) => note(d, req)) : note(decision, req);
|
|
132
|
+
});
|
|
135
133
|
}
|
|
136
134
|
function createGovernanceShellGateMarkPolicy(marksOf, tools = COARSE_SHELL_TOOLS) {
|
|
137
135
|
const shellTools = new Set(tools);
|
|
@@ -143,28 +141,6 @@ function createGovernanceShellGateMarkPolicy(marksOf, tools = COARSE_SHELL_TOOLS
|
|
|
143
141
|
},
|
|
144
142
|
};
|
|
145
143
|
}
|
|
146
|
-
const SHELL_GATE_RANK = (() => {
|
|
147
|
-
const tighten = SAFETY_MERGE_SPEC.shellGate.tighten;
|
|
148
|
-
const rule = tighten.rule;
|
|
149
|
-
if (tighten.rule !== "enumRank")
|
|
150
|
-
throw new Error(`SAFETY_MERGE_SPEC.shellGate.tighten.rule=${rule} — 不是 enumRank,shellGate 秩无从派生`);
|
|
151
|
-
const table = { off: -1, classify: -1, always: -1 };
|
|
152
|
-
for (const [i, w] of tighten.order.entries()) {
|
|
153
|
-
if (!(w in table))
|
|
154
|
-
throw new Error(`SAFETY_MERGE_SPEC.shellGate 序里有本仓词表外的词:${w}`);
|
|
155
|
-
table[w] = i;
|
|
156
|
-
}
|
|
157
|
-
for (const [w, r] of Object.entries(table))
|
|
158
|
-
if (r < 0)
|
|
159
|
-
throw new Error(`SAFETY_MERGE_SPEC.shellGate 序缺词:${w}`);
|
|
160
|
-
return table;
|
|
161
|
-
})();
|
|
162
|
-
export function shellGateRank(word) {
|
|
163
|
-
const r = SHELL_GATE_RANK[word];
|
|
164
|
-
if (r === undefined)
|
|
165
|
-
throw new RangeError(`unknown shellGate word: ${word}`);
|
|
166
|
-
return r;
|
|
167
|
-
}
|
|
168
144
|
export function applyRuntimeGovernance(base, governance) {
|
|
169
145
|
const marksOf = () => governance.askMarks ?? currentGovernanceAskMarks();
|
|
170
146
|
const overrides = { ...autonomyOverrides(governance.autonomy) };
|
|
@@ -175,15 +151,8 @@ export function applyRuntimeGovernance(base, governance) {
|
|
|
175
151
|
: undefined;
|
|
176
152
|
if (governancePolicy !== undefined)
|
|
177
153
|
overrides.toolPolicy = governancePolicy;
|
|
178
|
-
if (governance.manualModeShellGate
|
|
179
|
-
|
|
180
|
-
? overrides.shellGate
|
|
181
|
-
: governance.manualModeShellGate;
|
|
182
|
-
if (SHELL_GATE_RANK[candidate] >= SHELL_GATE_RANK[base.shellGate ?? "off"])
|
|
183
|
-
overrides.shellGate = candidate;
|
|
184
|
-
else
|
|
185
|
-
delete overrides.shellGate;
|
|
186
|
-
}
|
|
154
|
+
if (governance.manualModeShellGate === "always")
|
|
155
|
+
overrides.shellGate = "always";
|
|
187
156
|
if (overrides.shellGate === "always") {
|
|
188
157
|
const shellObserver = createGovernanceShellGateMarkPolicy(marksOf);
|
|
189
158
|
overrides.toolPolicy = governancePolicy === undefined ? shellObserver : combinePolicies(governancePolicy, shellObserver);
|
package/dist/sealed-key.d.ts
CHANGED
|
@@ -50,46 +50,6 @@ export interface SealedKeyCapability {
|
|
|
50
50
|
* omits the key entirely (present-iff this process built its custody store — see the route for the two meanings
|
|
51
51
|
* of absence). */
|
|
52
52
|
export declare function projectSealedKeyCapability(face: SealedKeyPublicFace | undefined): SealedKeyCapability | undefined;
|
|
53
|
-
/** Why a model's sealed key could not be unsealed (the poison-pill diagnosis, see SealedKeyPoison). CUSTODY failures only:
|
|
54
|
-
* a sealedApiKey that is malformed (alg other than the pinned v1 SEALED_KEY_ALG, non-base64 ciphertext) makes the whole
|
|
55
|
-
* catalog entry unavailable instead (S-704 `config.model_entry_invalid`, settings-schema `SealedApiKey` is the shape
|
|
56
|
-
* owner) — the former `unsupported_alg` reason was retired with that move (it could no longer be reached). */
|
|
57
|
-
export type SealedKeyPoisonReason = "sealed_decrypt_failed" | "stale_ciphertext" | "no_key_store";
|
|
58
|
-
/**
|
|
59
|
-
* Poison-pill entry for the per-model key map (`config.modelApiKeys`): planted by applyEffective when
|
|
60
|
-
* a model CARRIES `sealedApiKey` but the ciphertext cannot be unsealed. We deliberately do NOT drop
|
|
61
|
-
* the model from the map — dropping made "sealed key broken" indistinguishable from "no per-model key
|
|
62
|
-
* configured", and when the deployment also has a gateway MODEL_API_KEY the model's tasks silently
|
|
63
|
-
* fell back onto the shared account (the exact hole the no-apiKeyEnv-fallback contract exists to
|
|
64
|
-
* close). createKeyResolver turns this marker into a task-level `SealedKeyPoisonedError` — never
|
|
65
|
-
* `undefined`, because undefined IS the "use the gateway key" semantic. Carries DIAGNOSIS only:
|
|
66
|
-
* never the ciphertext, never any plaintext.
|
|
67
|
-
*/
|
|
68
|
-
export interface SealedKeyPoison {
|
|
69
|
-
readonly sealedKeyPoison: true;
|
|
70
|
-
readonly publicKeyId: string;
|
|
71
|
-
readonly reason: SealedKeyPoisonReason;
|
|
72
|
-
/** Error CLASS of the unseal failure (e.g. "Error") — never the message (message text is logged
|
|
73
|
-
* at plant time; keeping the object minimal keeps every serialization face trivially secret-free). */
|
|
74
|
-
readonly errKind?: string;
|
|
75
|
-
/** ISO timestamp of the apply that detected the failure. */
|
|
76
|
-
readonly at: string;
|
|
77
|
-
}
|
|
78
|
-
export declare function sealedKeyPoison(publicKeyId: string, reason: SealedKeyPoisonReason, errKind?: string): SealedKeyPoison;
|
|
79
|
-
export declare function isSealedKeyPoison(v: unknown): v is SealedKeyPoison;
|
|
80
|
-
/**
|
|
81
|
-
* The task-level fail-loud face of a poison pill: thrown by the key resolver on EVERY brain call that
|
|
82
|
-
* would otherwise authenticate this model, so a broken sealed key fails the task visibly (same family
|
|
83
|
-
* as a rotation-leg auth failure) instead of silently burning the gateway account. Message carries the
|
|
84
|
-
* sealed-failure marker (reason + publicKeyId + detection time) plus the operator fix — and nothing
|
|
85
|
-
* secret (no ciphertext, no plaintext, by construction of SealedKeyPoison).
|
|
86
|
-
*/
|
|
87
|
-
export declare class SealedKeyPoisonedError extends Error {
|
|
88
|
-
readonly publicKeyId: string;
|
|
89
|
-
readonly reason: SealedKeyPoisonReason;
|
|
90
|
-
readonly at: string;
|
|
91
|
-
constructor(modelName: string, poison: SealedKeyPoison);
|
|
92
|
-
}
|
|
93
53
|
/** Default id = fingerprint of the RAW public-key bytes: `exec-<sha256(pk)[0..12) hex>`.
|
|
94
54
|
* Deterministic → regenerating the id from the same key file is stable across boots. */
|
|
95
55
|
export declare function deriveKeyId(publicKeyRaw: Uint8Array): string;
|
package/dist/sealed-key.js
CHANGED
|
@@ -17,26 +17,6 @@ export function projectSealedKeyCapability(face) {
|
|
|
17
17
|
return undefined;
|
|
18
18
|
return { alg: SEALED_KEY_ALG, publicKeyId: face.latest.publicKeyId, publicKey: face.latest.publicKey };
|
|
19
19
|
}
|
|
20
|
-
export function sealedKeyPoison(publicKeyId, reason, errKind) {
|
|
21
|
-
return { sealedKeyPoison: true, publicKeyId, reason, ...(errKind ? { errKind } : {}), at: new Date().toISOString() };
|
|
22
|
-
}
|
|
23
|
-
export function isSealedKeyPoison(v) {
|
|
24
|
-
return typeof v === "object" && v !== null && v.sealedKeyPoison === true;
|
|
25
|
-
}
|
|
26
|
-
export class SealedKeyPoisonedError extends Error {
|
|
27
|
-
publicKeyId;
|
|
28
|
-
reason;
|
|
29
|
-
at;
|
|
30
|
-
constructor(modelName, poison) {
|
|
31
|
-
super(`sealed api key for model "${modelName}" is unusable (${poison.reason}; publicKeyId "${poison.publicKeyId}"; detected ${poison.at}) — ` +
|
|
32
|
-
`refusing the gateway-key fallback. Fix custody: re-paste the key in the web UI (it seals against the newest registered public key), ` +
|
|
33
|
-
`or restore this host's private key for that id / complete the rotation so the ciphertext targets a held key.`);
|
|
34
|
-
this.name = "SealedKeyPoisonedError";
|
|
35
|
-
this.publicKeyId = poison.publicKeyId;
|
|
36
|
-
this.reason = poison.reason;
|
|
37
|
-
this.at = poison.at;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
20
|
export function deriveKeyId(publicKeyRaw) {
|
|
41
21
|
return `exec-${createHash("sha256").update(publicKeyRaw).digest("hex").slice(0, 12)}`;
|
|
42
22
|
}
|
|
@@ -69,9 +69,9 @@ export declare const CATALOG_SECRET_ENV_NAMES: ReadonlySet<string>;
|
|
|
69
69
|
* 目录臂的计数恰好就是 core 形状规则的缺口读数;名单型旋钮臂只记前两臂都漏下的(今天 = 不在服务目录里的 `BAKE_ENV_ALLOW`)。
|
|
70
70
|
* 各腿不直接调它 —— 经 {@link hostSubprocessEnv}(剥 + 放回 + 留痕一只口)。
|
|
71
71
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
72
|
+
* **只为宿主子进程组合**。本包跑在别的平台上的子进程(ssh / k8s / adb / e2b / docker 车道)的 env 不经本函数:那些车道不继承本进程 env,只交
|
|
73
|
+
* 调用方显式给的 env 与部署方点名的键(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`,经 {@link allowlistedEnv})。(core 7.33.6 #1159 的平台尾参随
|
|
74
|
+
* core 7.35.0 #1201 删除 —— git 的 env 配置族成了豁免族,不再有「按子进程平台读记录」这件事,S-877。)
|
|
75
75
|
*/
|
|
76
76
|
export declare function scrubServerSecrets(env: NodeJS.ProcessEnv, findings: ServerSecretEnvFinding[]): NodeJS.ProcessEnv;
|
|
77
77
|
/**
|
|
@@ -87,15 +87,14 @@ export declare function scrubServerSecrets(env: NodeJS.ProcessEnv, findings: Ser
|
|
|
87
87
|
* 返回 `NodeJS.ProcessEnv` 形(值可能是 `undefined` 的键本来就不会进子进程);调用方显式要交的键(`LC_ALL`、`cfg.env` …)叠在它之上。
|
|
88
88
|
* 调用时机 = 每次要交 env 时现算一次(`process.env` 在本仓是 boot 期输入,各次同值;计数因此是「每组合一次」)。
|
|
89
89
|
*
|
|
90
|
-
* 🔴 **git 的 env
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* 🔴 **披露谓词 = 「这个名字没有按原样交给子进程」**(输入值 vs
|
|
97
|
-
*
|
|
98
|
-
* 带凭据、VALUE 本就是空串)子进程拿到的正是原值 ⇒ 也不披露。一只谓词盖住「剥掉」「换占位」「点名放回」三形,不按形分臂。
|
|
90
|
+
* 🔴 **git 的 env 配置族与代理变量一族只有 core 一只写手**:core 7.35.0 #1201(S-877,CC 2.1.280 形)起它们是 core 的**豁免族** ——
|
|
91
|
+
* `GIT_CONFIG_COUNT` / `GIT_CONFIG_PARAMETERS` / `GIT_CONFIG_KEY_<数字>` / `GIT_CONFIG_VALUE_<数字>`(大写)与 `http(s)_proxy` / `all_proxy` / `no_proxy` /
|
|
92
|
+
* `npm_config_*proxy` …(任意大小写),名与值都不读、**原样交出,凭据在内**(7.33.2–7.33.8 的逐条扣 / 原位占位 `sema.withheld` / 整只扣全部退役;
|
|
93
|
+
* 部署面:凭据不放本进程 env —— `docs/DEPLOY-PREREQS.md`)。本层 7.103.0 的「整组扣」与 7.103.1 中间态的「按 pair 扣留 + 重编号」(D-7)更早退役,
|
|
94
|
+
* 本层不重判(同一语义面上不留第二只写手;在下游把 core 的豁免改回扣 = 源头修复宪法禁止的改写)。本仓按名两臂(目录 secret 名 / 名单型旋钮)不含
|
|
95
|
+
* `GIT_CONFIG_` 这一族的名字(钉:`test/secret-env-git-config-pair.test.ts`「一只写手」格)。
|
|
96
|
+
* 🔴 **披露谓词 = 「这个名字没有按原样交给子进程」**(输入值 vs 输出值)。core 对一个变量的动作恒为「整只删」(从不改写值,#1201),点名放回的名字
|
|
97
|
+
* 取回源值 ⇒ 与输入同值 ⇒ 不是剥除。一只谓词盖住「剥掉」「点名放回」两形,不按形分臂(7.33.6–7.33.8 还有第三形「换占位」,随 #1201 消失)。
|
|
99
98
|
*/
|
|
100
99
|
export declare function hostSubprocessEnv(o: {
|
|
101
100
|
site: SecretEnvScrubSite;
|
|
@@ -16,10 +16,16 @@
|
|
|
16
16
|
* 自陈「Malformed shape → 400(never a downstream TypeError)」,这一半没守);
|
|
17
17
|
* · `loosenReasons` 的每一臂都从 `prior` 出发,**prior 为空时恒回空集** ⇒ tighten-only 门对一条
|
|
18
18
|
* 全新的会话放行任何内容,坏形因此连「收紧门」这一层都碰不到。
|
|
19
|
-
* ⇒ 类修:一只读者,键集由 `keyof SessionPermissionRules` **派生**(下方 `
|
|
20
|
-
* `satisfies Record<keyof SessionPermissionRules,
|
|
19
|
+
* ⇒ 类修:一只读者,键集由 `keyof SessionPermissionRules` **派生**(下方 `RULE_FIELD_ROLE` 的
|
|
20
|
+
* `satisfies Record<keyof SessionPermissionRules, RuleFieldRole>`:core 加桶 ⇒ 缺键**编译红**;core 删桶 ⇒
|
|
21
21
|
* 多键**编译红**),两条腿同调。
|
|
22
22
|
*
|
|
23
|
+
* ── S-800(7.105.0):同一只读者多判一问 —— 写得进却跑不了的工具名 ─────────────────────────────────
|
|
24
|
+
* 两条写入腿是 server 往会话规则店写的**全部**写口,所以「写口 fail-closed」收成一只**写口读者**
|
|
25
|
+
* ({@link readWritableSessionPermissionRules} = 形读者 + {@link legacyToolNameOf}),两腿同调,将来第三条写入腿
|
|
26
|
+
* 调同一只也同得。判词多一种(`legacy-tool-name`),两腿各把它映射成 `400 rules.legacy_tool_name`(映射点仍在各自
|
|
27
|
+
* 站点:码位只放字面量)。形读者本身不变 —— SQL 店的读回也用它,读回不许判名字(理由见写口读者头注)。
|
|
28
|
+
*
|
|
23
29
|
* ── 文案也在这里(codex 一轮追加:两条腿各铸一份句子 = 第二份手抄)───────────────────────────────
|
|
24
30
|
* 初版把 wire 文案留在两个路由各自铸,理由是错误文案冻结门(`test/api-error-text-freeze.test.ts`)锚的是
|
|
25
31
|
* `src/http/` 桶里的 `sendError` 站点。codex 一轮指出那等于**把同一句话写了两遍**(`sessions.ts` 与
|
|
@@ -28,10 +34,54 @@
|
|
|
28
34
|
* 渲染器对每一种判词的输出字节,且对判词闭集穷尽(加一种判词 ⇒ 那一格编译红)。文案一个字节未改。
|
|
29
35
|
*/
|
|
30
36
|
import type { SessionPermissionRules, SessionPolicyError, SessionRulesRecord } from "@sema-agent/core";
|
|
37
|
+
declare const RULE_FIELD_ROLE: {
|
|
38
|
+
readonly toolAllow: "tool-name";
|
|
39
|
+
readonly toolDeny: "tool-name";
|
|
40
|
+
readonly allowDirs: "path";
|
|
41
|
+
readonly commandAllow: "command";
|
|
42
|
+
readonly commandDeny: "command";
|
|
43
|
+
};
|
|
31
44
|
/** core `SessionPermissionRules` 的桶名全集(派生,不是手抄)。 */
|
|
32
45
|
export declare const SESSION_PERMISSION_RULE_FIELDS: readonly (keyof SessionPermissionRules)[];
|
|
33
|
-
/**
|
|
34
|
-
export type
|
|
46
|
+
/** 装工具名的那几桶(由 {@link RULE_FIELD_ROLE} 派生的型)。 */
|
|
47
|
+
export type ToolNameRuleField = {
|
|
48
|
+
[K in keyof typeof RULE_FIELD_ROLE]: (typeof RULE_FIELD_ROLE)[K] extends "tool-name" ? K : never;
|
|
49
|
+
}[keyof typeof RULE_FIELD_ROLE];
|
|
50
|
+
/**
|
|
51
|
+
* S-800(client-core [ref] CC-226;server [ref];core [ref])—— 「写得进、跑不了、撤不掉」的工具名。
|
|
52
|
+
*
|
|
53
|
+
* 两臂,逐字对应 core 7.33.8 准备阶段名审计抛 `config.legacy_tool_name` 的那两臂(判据只用 core **导出**的
|
|
54
|
+
* 两件,零镜像表 —— 表随 core 钉版走):
|
|
55
|
+
* · `"retired"`:名字在 core 的 `RETIRED_TOOL_NAMES` 里;`guidance` = 那张表给它的一句现役指引(原样);
|
|
56
|
+
* · `"unprefixed-namespace"`:名字里有 `"__"` 分隔符,而 `protocolOf(name)` 认不出它属于哪个协议。
|
|
57
|
+
*
|
|
58
|
+
* 为什么在**写口**判:会话规则店只准非 operator 收紧,删掉一条 deny 算放宽 ⇒ 写进去的这种名字属主自己删不掉,
|
|
59
|
+
* 而准备阶段见它就整只拒启 ⇒ 这个会话之后的每一跑都起不来。写口是唯一还来得及说「不」的地方。
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ **比准备阶段严一格**(方向安全,与 client-core 0.83.6 的 KL-157 同向):准备阶段对**本 run 名册里挂着**的
|
|
62
|
+
* 同名工具放行(部署自己挂了一只叫 `Task` 的工具就不审计它),写口看不到将来哪一跑的名册 ⇒ 一律拒。
|
|
63
|
+
* 受伤的是「部署自带与退役名同名的工具、又要在会话规则里点它」的形;出路 = 部署级工具策略(不经会话规则店)。
|
|
64
|
+
* ⚠️ **过渡形**:core 已表态(#1156)把这两臂抽成一只导出纯函数并在会话规则店写口即拒 —— 到货后本函数
|
|
65
|
+
* 换成调 core 那只(拒因原样透传),三份判定收成一份。**S-877 提货复核(core 7.35.0)**:#1156 **未随版**(发车帖 [ref] 逐字
|
|
66
|
+
* 「照 #1156 未随本版」);同版新导出的 `rosterToolName` / `ruleNamesOf`(#1194)是**别名归一**(一个名字在名册上落到哪只行的线名 /
|
|
67
|
+
* 一次调用可被规则拼成的名字集),不回答「这个名字会不会让准备阶段拒启」,不能替本函数 ⇒ 本函数照留,等 #1156。
|
|
68
|
+
*
|
|
69
|
+
* 🔴 **为什么不复用 `config.ts` 的 `findUnmatchableToolNames`**(本仓另一只按名字判的单点;读过实现后裁):两只回答的
|
|
70
|
+
* 不是同一个问题。那一只问「部署名单里这条名字会不会**永远匹配不上**活工具」—— 它刻意豁免 `allow`(白名单匹配不上 =
|
|
71
|
+
* fail-closed 且当场可见),并且拒两段 MCP 名 `mcp__srv`(一名两义)与 core 文法编不出的串。本函数问的是「引擎见到这条
|
|
72
|
+
* 会话规则会不会**拒启**」:引擎准备阶段的名审计对 `allow` 桶照样抛(豁免它 = 放过一条砖化路径),对 `mcp__srv` 却
|
|
73
|
+
* **不**抛(会话层自 core 5.55 / 5.56 起把它当整对端覆盖形真命中)。判据只能逐字跟 core 那两臂走,不能借另一只的口径。
|
|
74
|
+
* 两只在「退役名」「无前缀 `__` 名」两臂上同源(都读 core 导出的同两件),#1156 到货的那一批一并换调 core 的导出函数。
|
|
75
|
+
*/
|
|
76
|
+
export type LegacyToolName = {
|
|
77
|
+
why: "retired";
|
|
78
|
+
guidance: string;
|
|
79
|
+
} | {
|
|
80
|
+
why: "unprefixed-namespace";
|
|
81
|
+
};
|
|
82
|
+
export declare function legacyToolNameOf(name: string): LegacyToolName | undefined;
|
|
83
|
+
/** 一条规则体的**形**问题。判词而非文案(见文件头注的「文案属主」一段)。 */
|
|
84
|
+
export type SessionPermissionRulesShapeProblem =
|
|
35
85
|
/** 整体不是一只普通对象(`null` / 数组 / 标量 / 缺席)。 */
|
|
36
86
|
{
|
|
37
87
|
kind: "not-an-object";
|
|
@@ -41,22 +91,45 @@ export type SessionPermissionRulesProblem =
|
|
|
41
91
|
kind: "field-not-string-array";
|
|
42
92
|
field: keyof SessionPermissionRules;
|
|
43
93
|
};
|
|
44
|
-
/** {@link
|
|
45
|
-
export type
|
|
46
|
-
|
|
94
|
+
/** 一条**要写进店**的规则体的问题 = 形问题 ∪ S-800 的名字问题(判据 {@link legacyToolNameOf})。 */
|
|
95
|
+
export type SessionPermissionRulesProblem = SessionPermissionRulesShapeProblem
|
|
96
|
+
/** S-800:形对,但工具名桶里有一条写进去就跑不了的名字。 */
|
|
97
|
+
| {
|
|
98
|
+
kind: "legacy-tool-name";
|
|
99
|
+
field: ToolNameRuleField;
|
|
100
|
+
name: string;
|
|
101
|
+
legacy: LegacyToolName;
|
|
102
|
+
};
|
|
103
|
+
/** 两只读者的读数:有问题给判词,没问题给**已收窄**的规则体(调用方零 cast)。`P` = 这只读者会给的判词集。 */
|
|
104
|
+
export type SessionPermissionRulesRead<P extends SessionPermissionRulesProblem = SessionPermissionRulesProblem> = {
|
|
105
|
+
problem: P;
|
|
47
106
|
rules?: undefined;
|
|
48
107
|
} | {
|
|
49
108
|
problem?: undefined;
|
|
50
109
|
rules: SessionPermissionRules;
|
|
51
110
|
};
|
|
52
111
|
/**
|
|
53
|
-
*
|
|
112
|
+
* **形读者**:读一只 `SessionPermissionRules`,形不对给判词,形对给**只含五桶**的规则体。三个消费者:两条写入腿
|
|
113
|
+
* (经 {@link readWritableSessionPermissionRules})与 SQL 店的**读回**(`session-policy-store-sql.ts` 的
|
|
114
|
+
* `readSessionRulesBody`:读不出形的行判 `corrupt`)。
|
|
54
115
|
*
|
|
55
116
|
* 认识面窄得刻意:**只判形**,不判内容(桶内字符串的 pattern 文法、路径是否可达、tighten-only 是否成立
|
|
56
|
-
* 都不在这里 —— 前两者归 core 的 rule 文法与 `normalizeRules`,后者归 `putRules`
|
|
57
|
-
*
|
|
117
|
+
* 都不在这里 —— 前两者归 core 的 rule 文法与 `normalizeRules`,后者归 `putRules` 的收紧门;S-800 的名字判官
|
|
118
|
+
* 只在**写口**那一只读者里,见下)。未知键**照旧忽略**(不是新的拒因):core 的 `normalizeRules` 本来就只搬这五桶,
|
|
119
|
+
* 本读者与它同口径。
|
|
58
120
|
*/
|
|
59
|
-
export declare function readSessionPermissionRules(raw: unknown): SessionPermissionRulesRead
|
|
121
|
+
export declare function readSessionPermissionRules(raw: unknown): SessionPermissionRulesRead<SessionPermissionRulesShapeProblem>;
|
|
122
|
+
/**
|
|
123
|
+
* **写口读者**(S-800)= 形读者 + 名字判官:两条写入腿(`PUT /v1/sessions/:id/policy`、sync/import 的 `policy`
|
|
124
|
+
* 面经 {@link readSessionRulesRecords})往会话规则店写之前的**唯一**判据。两问,按序:
|
|
125
|
+
* ① 形(同 {@link readSessionPermissionRules};一份坏形的体答形的 400);
|
|
126
|
+
* ② 工具名桶里的每一条过 {@link legacyToolNameOf} —— 写进去之后属主撤不掉、会话之后每一跑都起不来的名字,
|
|
127
|
+
* 写口是唯一来得及拒的地方。
|
|
128
|
+
* 🔴 **只在写口,不在读回**:SQL 店读回同一行时只判形(形读者)。一条早先写进去的退役名行必须**读得出来** ——
|
|
129
|
+
* 那是运营方清掉它的唯一路径(operator 的 PUT 先读写前快照),也是 core 准备阶段报 `config.legacy_tool_name`
|
|
130
|
+
* 而不是一只 500 `corrupt` 的前提。把判官放进形读者会让读回把它判坏(7.105.0 车QQ 双库套件实抓,修在同批)。
|
|
131
|
+
*/
|
|
132
|
+
export declare function readWritableSessionPermissionRules(raw: unknown): SessionPermissionRulesRead;
|
|
60
133
|
/** 一条 `SessionRulesRecord` 的形问题(`policy[]` 的元素级;桶级问题原样转载在 `rules` 位)。 */
|
|
61
134
|
export type SessionRulesRecordProblem =
|
|
62
135
|
/** `policy` 本身不是数组。 */
|
|
@@ -90,6 +163,8 @@ export type SessionRulesRecordsRead = {
|
|
|
90
163
|
};
|
|
91
164
|
/**
|
|
92
165
|
* 读 sync/import 的 `policy` 面(`SessionRulesRecord[]`)。好形返回**已收窄**的记录数组,坏形给判词。
|
|
166
|
+
* 它只服务那一条**写入**腿(导入的记录随后 `putRules` 进店),所以每条记录的规则体走**写口读者**
|
|
167
|
+
* {@link readWritableSessionPermissionRules}(与 PUT 腿同一只判官)。
|
|
93
168
|
*
|
|
94
169
|
* `rev`:记录里带的 `rev` 是**导出端的**乐观锁读数,`putRules` 侧一进门就 `stripRev` 掉(`sameRulesContent`
|
|
95
170
|
* 也比的是去 rev 的字节形),所以它在本读者里只是过境位 —— 非数字即当缺席(不是拒因:一个坏 rev 改变不了
|
|
@@ -98,6 +173,15 @@ export type SessionRulesRecordsRead = {
|
|
|
98
173
|
export declare function readSessionRulesRecords(raw: unknown): SessionRulesRecordsRead;
|
|
99
174
|
/** 桶级判词 → 人话。闭集穷尽:{@link SessionPermissionRulesProblem} 加一种判词 ⇒ 本 switch 编译红。 */
|
|
100
175
|
export declare function describeSessionPermissionRulesProblem(p: SessionPermissionRulesProblem): string;
|
|
176
|
+
/**
|
|
177
|
+
* S-800:一只 `policy[]` 判词**是不是**「写得进却跑不了的名字」那一类 —— 是就交回那一格(桶 + 名字),
|
|
178
|
+
* 调用点据此发 `rules.legacy_tool_name`,否则发形的码。闭集穷尽两层:记录级判词或桶级判词加一种 ⇒ 编译红,
|
|
179
|
+
* 新判词不会静默落进某一个码。
|
|
180
|
+
*/
|
|
181
|
+
export declare function legacyToolNameProblemOf(p: SessionRulesRecordProblem): {
|
|
182
|
+
field: ToolNameRuleField;
|
|
183
|
+
name: string;
|
|
184
|
+
} | undefined;
|
|
101
185
|
/** `policy[]` 判词 → 人话(sync/import 腿在它前面拼 `policy: `)。闭集穷尽同上。 */
|
|
102
186
|
export declare function describeSessionRulesRecordProblem(p: SessionRulesRecordProblem): string;
|
|
103
187
|
/**
|
|
@@ -115,7 +199,10 @@ export declare function describeSessionRulesRecordProblem(p: SessionRulesRecordP
|
|
|
115
199
|
*
|
|
116
200
|
* 逐词:
|
|
117
201
|
* · `conflict`(409)—— CAS 输了,重读 `rev` 再来。**码位与状态逐字不变**。
|
|
118
|
-
* · `loosen_forbidden`(403)—— 非 operator
|
|
202
|
+
* · `loosen_forbidden`(403)—— 非 operator 想放松。**码位与状态逐字不变**。⚠️ 这个词还有**第二个铸点**(S-546 B5,
|
|
203
|
+
* 如实登记、未改):PG 后端的 SQL 双生对含 NUL / 孤代理字节的规则拒收时借用了它
|
|
204
|
+
* (`plugins/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
|
|
205
|
+
* core 的闭集里没有「输入不可存」这个词,改码位要先裁定新码与 PUT / 2c 导入两条入口同判(DEBTS S-546 B5)。
|
|
119
206
|
* · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂)。
|
|
120
207
|
* 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(core 成文的修复路径是
|
|
121
208
|
* **人**去改回或删掉那一行,而删记号要交出它握着的那个号)。
|
|
@@ -124,4 +211,5 @@ export declare function sessionPolicyErrorHttp(code: SessionPolicyError["code"])
|
|
|
124
211
|
status: number;
|
|
125
212
|
errorCode: string;
|
|
126
213
|
};
|
|
214
|
+
export {};
|
|
127
215
|
//# sourceMappingURL=session-policy-wire.d.ts.map
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { PROTOCOL_TABLE, RETIRED_TOOL_NAMES, protocolOf } from "@sema-agent/core";
|
|
2
|
+
const RULE_FIELD_ROLE = {
|
|
3
|
+
toolAllow: "tool-name",
|
|
4
|
+
toolDeny: "tool-name",
|
|
5
|
+
allowDirs: "path",
|
|
6
|
+
commandAllow: "command",
|
|
7
|
+
commandDeny: "command",
|
|
7
8
|
};
|
|
8
|
-
export const SESSION_PERMISSION_RULE_FIELDS = Object.keys(
|
|
9
|
+
export const SESSION_PERMISSION_RULE_FIELDS = Object.keys(RULE_FIELD_ROLE);
|
|
10
|
+
const TOOL_NAME_RULE_FIELDS = SESSION_PERMISSION_RULE_FIELDS.filter((f) => RULE_FIELD_ROLE[f] === "tool-name");
|
|
11
|
+
export function legacyToolNameOf(name) {
|
|
12
|
+
const guidance = RETIRED_TOOL_NAMES.get(name);
|
|
13
|
+
if (guidance !== undefined)
|
|
14
|
+
return { why: "retired", guidance };
|
|
15
|
+
if (name.includes("__") && protocolOf(name) === undefined)
|
|
16
|
+
return { why: "unprefixed-namespace" };
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
function protocolToolNameShapes() {
|
|
20
|
+
return PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>__<tool>`).join(" or ");
|
|
21
|
+
}
|
|
9
22
|
export function readSessionPermissionRules(raw) {
|
|
10
23
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
11
24
|
return { problem: { kind: "not-an-object" } };
|
|
@@ -21,6 +34,19 @@ export function readSessionPermissionRules(raw) {
|
|
|
21
34
|
}
|
|
22
35
|
return { rules };
|
|
23
36
|
}
|
|
37
|
+
export function readWritableSessionPermissionRules(raw) {
|
|
38
|
+
const read = readSessionPermissionRules(raw);
|
|
39
|
+
if (read.problem !== undefined)
|
|
40
|
+
return read;
|
|
41
|
+
for (const field of TOOL_NAME_RULE_FIELDS) {
|
|
42
|
+
for (const name of read.rules[field] ?? []) {
|
|
43
|
+
const legacy = legacyToolNameOf(name);
|
|
44
|
+
if (legacy !== undefined)
|
|
45
|
+
return { problem: { kind: "legacy-tool-name", field, name, legacy } };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return read;
|
|
49
|
+
}
|
|
24
50
|
export function readSessionRulesRecords(raw) {
|
|
25
51
|
if (!Array.isArray(raw))
|
|
26
52
|
return { problem: { kind: "not-an-array" } };
|
|
@@ -32,7 +58,7 @@ export function readSessionRulesRecords(raw) {
|
|
|
32
58
|
const { principal, rules, rev } = e;
|
|
33
59
|
if (principal !== undefined && typeof principal !== "string")
|
|
34
60
|
return { problem: { kind: "principal-not-a-string", index } };
|
|
35
|
-
const read =
|
|
61
|
+
const read = readWritableSessionPermissionRules(rules);
|
|
36
62
|
if (read.problem !== undefined)
|
|
37
63
|
return { problem: { kind: "rules", index, problem: read.problem } };
|
|
38
64
|
const innerRev = rules?.rev;
|
|
@@ -45,12 +71,49 @@ export function describeSessionPermissionRulesProblem(p) {
|
|
|
45
71
|
switch (p.kind) {
|
|
46
72
|
case "not-an-object": return "rules must be a SessionPermissionRules object";
|
|
47
73
|
case "field-not-string-array": return `${p.field} must be an array of strings`;
|
|
74
|
+
case "legacy-tool-name": return describeLegacyToolName(p.field, p.name, p.legacy);
|
|
48
75
|
default: {
|
|
49
76
|
const unhandled = p;
|
|
50
77
|
throw new Error(`unhandled SessionPermissionRules problem: ${JSON.stringify(unhandled)}`);
|
|
51
78
|
}
|
|
52
79
|
}
|
|
53
80
|
}
|
|
81
|
+
function describeLegacyToolName(field, name, legacy) {
|
|
82
|
+
switch (legacy.why) {
|
|
83
|
+
case "retired":
|
|
84
|
+
return `${field} names "${name}", a retired tool name (${legacy.guidance}); with that rule in place this session could not start another run, so nothing was written — use the current tool name`;
|
|
85
|
+
case "unprefixed-namespace":
|
|
86
|
+
return `${field} names "${name}", which contains "__" but no protocol prefix; with that rule in place this session could not start another run, so nothing was written — a protocol tool is named ${protocolToolNameShapes()}`;
|
|
87
|
+
default: {
|
|
88
|
+
const unhandled = legacy;
|
|
89
|
+
throw new Error(`unhandled legacy tool name arm: ${JSON.stringify(unhandled)}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function legacyToolNameProblemOf(p) {
|
|
94
|
+
switch (p.kind) {
|
|
95
|
+
case "not-an-array":
|
|
96
|
+
case "record-not-an-object":
|
|
97
|
+
case "principal-not-a-string":
|
|
98
|
+
return undefined;
|
|
99
|
+
case "rules":
|
|
100
|
+
switch (p.problem.kind) {
|
|
101
|
+
case "not-an-object":
|
|
102
|
+
case "field-not-string-array":
|
|
103
|
+
return undefined;
|
|
104
|
+
case "legacy-tool-name":
|
|
105
|
+
return { field: p.problem.field, name: p.problem.name };
|
|
106
|
+
default: {
|
|
107
|
+
const unhandled = p.problem;
|
|
108
|
+
throw new Error(`unhandled SessionPermissionRules problem: ${JSON.stringify(unhandled)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
default: {
|
|
112
|
+
const unhandled = p;
|
|
113
|
+
throw new Error(`unhandled SessionRulesRecord problem: ${JSON.stringify(unhandled)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
54
117
|
export function describeSessionRulesRecordProblem(p) {
|
|
55
118
|
switch (p.kind) {
|
|
56
119
|
case "not-an-array": return "must be an array of {principal, rules} records";
|
package/dist/session-titler.d.ts
CHANGED
|
@@ -54,6 +54,17 @@ export interface SessionTitler {
|
|
|
54
54
|
export declare const TITLE_MAX_CHARS = 80;
|
|
55
55
|
/** Single line, control chars stripped, hard cap; surrounding quotes (a common LLM tic) trimmed. */
|
|
56
56
|
export declare function sanitizeTitle(raw: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* S-236 ②(7.106.0,cli L-687)—— objective 里**用户自己写的**那部分:剥掉全部 `<system-reminder>…</system-reminder>` 块(跨行、
|
|
59
|
+
* 多块、夹在正文中间都剥;**未闭合**的块剥到末尾 —— 被截断的附件同样不是用户写的)后 trim。
|
|
60
|
+
*
|
|
61
|
+
* 为什么:壳把附件(技能清单一类的 `<system-reminder>` 块)拼在正文**之前**提交,标题器取前 {@link OBJECTIVE_SNIPPET_CHARS} 字
|
|
62
|
+
* 去起名 ⇒ 标题成了技能清单。判据只认这一个**字面标签**(CC 的附件信封),不按内容猜「像不像用户写的」——
|
|
63
|
+
* 正文里提到这个词的普通文字不是块,原样保留。剥完是空串 ⇒ 调用方走「空 objective 不起名」的既有臂。
|
|
64
|
+
* ⚠️ 只治这一类:S-236 原件(fresh `/decide` 腿把指路文本当 objective 提交)是另一类非用户文本,没有信封可剥,本函数刻意不加
|
|
65
|
+
* 按内容猜的第二臂(要么 fresh 腿传席,要么另立一臂 —— 候裁,见车RA 收车档)。
|
|
66
|
+
*/
|
|
67
|
+
export declare function userAuthoredObjectiveText(objective: string): string;
|
|
57
68
|
export declare function buildTitlePrompt(objective: string): string;
|
|
58
69
|
export declare function createSessionTitler(deps: SessionTitlerDeps): SessionTitler;
|
|
59
70
|
//# sourceMappingURL=session-titler.d.ts.map
|
package/dist/session-titler.js
CHANGED
|
@@ -12,8 +12,12 @@ export function sanitizeTitle(raw) {
|
|
|
12
12
|
const unquoted = oneLine.replace(/^["'「『]+/, "").replace(/["'」』]+$/, "").trim();
|
|
13
13
|
return unquoted.length > TITLE_MAX_CHARS ? `${unquoted.slice(0, TITLE_MAX_CHARS - 1)}…` : unquoted;
|
|
14
14
|
}
|
|
15
|
+
export function userAuthoredObjectiveText(objective) {
|
|
16
|
+
return objective.replace(/<system-reminder>[\s\S]*?(?:<\/system-reminder>|$)/g, "").trim();
|
|
17
|
+
}
|
|
15
18
|
export function buildTitlePrompt(objective) {
|
|
16
|
-
const
|
|
19
|
+
const text = userAuthoredObjectiveText(objective);
|
|
20
|
+
const snippet = text.length > OBJECTIVE_SNIPPET_CHARS ? `${text.slice(0, OBJECTIVE_SNIPPET_CHARS)}…` : text;
|
|
17
21
|
const fenced = snippet.replace(/<(\/?)request>/gi, "<\u200b$1request>");
|
|
18
22
|
return ("Write a short title (at most 8 words) summarizing the user request below. " +
|
|
19
23
|
"Reply with ONLY the title on a single line — no quotes, no preamble. " +
|
|
@@ -34,7 +38,7 @@ export function createSessionTitler(deps) {
|
|
|
34
38
|
.catch(() => ({ state: continueWord, unreadable: true }));
|
|
35
39
|
return {
|
|
36
40
|
maybeTitle(sessionId, objective, model) {
|
|
37
|
-
if (!sessionId || !objective
|
|
41
|
+
if (!sessionId || !userAuthoredObjectiveText(objective))
|
|
38
42
|
return;
|
|
39
43
|
if (seen.has(sessionId))
|
|
40
44
|
return;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* `sql-driver.ts` 的连接初始化在**每条池连接**上钉死这两件事并**回读复核**(`@contract txn.read-semantics`)。
|
|
13
13
|
* 本模块把那份读数拿出来,一次推导两个读面:operator 面(`GET /v1/diagnostics/wiring` 的 `sqlEngine` 段,
|
|
14
|
-
* 全量)与消费端面(`GET /v1/capabilities` 的 `sql`
|
|
14
|
+
* 全量)与消费端面(`GET /v1/capabilities` 的 `store.sql` 位(7.106.0 起;此前顶层 `sql`,S-866 ①),窄投影)。两面同源 —— 「诊断页说悲观、能力位说
|
|
15
15
|
* 乐观」在结构上不可能。
|
|
16
16
|
*
|
|
17
17
|
* ## 两条诚实纪律(与 memory-posture 同源)
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import { type SqlEngineFacts } from "./plugins/sql-driver.js";
|
|
28
28
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
29
29
|
import type { Pool as PgPool } from "pg";
|
|
30
|
-
/** 消费端窄投影(`GET /v1/capabilities` 的 `sql`
|
|
30
|
+
/** 消费端窄投影(`GET /v1/capabilities` 的 `store.sql` 位;7.106.0 前是顶层 `sql`,S-866 ①)。`null` = 驱动此刻给不出事实 ⇒ `store.sql` 缺席。 */
|
|
31
31
|
export interface SqlEngineCapability {
|
|
32
32
|
engine: SqlEngineFacts["engine"];
|
|
33
33
|
isolation: string;
|