@sema-agent/server 7.120.0 → 7.122.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 +173 -0
- package/README.md +1 -1
- package/README.zh-CN.md +3 -1
- package/USAGE.md +97 -29
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -2
- package/deploy/sema-up/chart/templates/server.yaml +2 -2
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +1 -1
- package/dist/approval-reconciler.d.ts +8 -0
- package/dist/approval-reconciler.js +72 -52
- package/dist/auto-mode-face.d.ts +9 -2
- package/dist/auto-mode-face.js +5 -3
- package/dist/bench/s1/live-deps.js +1 -0
- package/dist/boot/config-center.d.ts +5 -7
- package/dist/boot/config-center.js +33 -9
- package/dist/boot/engine-lease.d.ts +1 -1
- package/dist/boot/leader.js +1 -0
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +2 -1
- package/dist/boot/resolve-spec.js +2 -4
- package/dist/boot/shutdown.d.ts +9 -10
- package/dist/boot/shutdown.js +59 -16
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -9
- package/dist/boot/stage-10-http-server.d.ts +1 -9
- package/dist/boot/stage-10-http-server.js +31 -8
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/memory-notice.d.ts +1 -1
- package/dist/config-catalog.js +12 -6
- package/dist/config-center/center-request.d.ts +13 -1
- package/dist/config-center/center-request.js +25 -10
- package/dist/config-types.d.ts +44 -5
- package/dist/config-types.js +6 -0
- package/dist/config.d.ts +35 -6
- package/dist/config.js +113 -33
- package/dist/fleet/drain-entry.d.ts +112 -0
- package/dist/fleet/drain-entry.js +38 -0
- package/dist/fleet/engine-signal.d.ts +144 -0
- package/dist/fleet/engine-signal.js +60 -0
- package/dist/fleet/serving-lease.d.ts +47 -0
- package/dist/fleet/serving-lease.js +233 -0
- package/dist/fleet-client.d.ts +35 -19
- package/dist/fleet-client.js +49 -25
- package/dist/governance-overrides.d.ts +7 -2
- package/dist/http/admission.js +7 -5
- package/dist/http/approval-sweeps.js +5 -7
- package/dist/http/checkpoint-ctx.d.ts +60 -11
- package/dist/http/checkpoint-ctx.js +23 -9
- package/dist/http/dispatch.js +33 -16
- package/dist/http/health-self-report.d.ts +60 -0
- package/dist/http/health-self-report.js +53 -0
- package/dist/http/principal-gate.d.ts +8 -1
- package/dist/http/principal-gate.js +1 -0
- package/dist/http/readiness.d.ts +25 -5
- package/dist/http/readiness.js +31 -6
- package/dist/http/resume-legs.js +9 -9
- package/dist/http/route-ctx.d.ts +25 -7
- package/dist/http/route-table.d.ts +6 -0
- package/dist/http/route-table.js +8 -1
- package/dist/http/routes/a2a-serve.d.ts +1 -1
- package/dist/http/routes/a2a-serve.js +5 -5
- package/dist/http/routes/admin-drain.d.ts +15 -31
- package/dist/http/routes/admin-drain.js +28 -10
- package/dist/http/routes/approvals-assistant.d.ts +4 -3
- package/dist/http/routes/approvals-assistant.js +9 -9
- package/dist/http/routes/capabilities.js +7 -5
- package/dist/http/routes/devices.js +3 -2
- package/dist/http/routes/fleet.d.ts +1 -0
- package/dist/http/routes/fleet.js +1 -1
- package/dist/http/routes/images.d.ts +1 -0
- package/dist/http/routes/images.js +1 -1
- package/dist/http/routes/leader.d.ts +1 -1
- package/dist/http/routes/leader.js +7 -1
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/notify-wake.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +1 -1
- package/dist/http/routes/rules.js +4 -3
- package/dist/http/routes/runs.d.ts +4 -2
- package/dist/http/routes/runs.js +7 -6
- package/dist/http/routes/sessions.d.ts +1 -0
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.d.ts +1 -1
- package/dist/http/routes/side-query.js +1 -1
- package/dist/http/routes/tasks.d.ts +2 -2
- package/dist/http/routes/tasks.js +3 -3
- package/dist/http/routes/trace-usage.d.ts +1 -0
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +1 -1
- package/dist/http/send.d.ts +13 -0
- package/dist/http/send.js +9 -0
- package/dist/http/server.d.ts +10 -21
- package/dist/http/server.js +20 -1
- package/dist/http/stored-body-replay.d.ts +27 -0
- package/dist/http/stored-body-replay.js +23 -0
- package/dist/http/wire-types.d.ts +7 -1
- package/dist/leader/leader.d.ts +22 -1
- package/dist/leader/leader.js +4 -1
- package/dist/leader/repair-wire.d.ts +1 -1
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/run-entry.d.ts +27 -0
- package/dist/leader/run-entry.js +29 -0
- package/dist/leader/wire.d.ts +10 -0
- package/dist/leader/wire.js +15 -10
- package/dist/model-reach-scope.d.ts +15 -3
- package/dist/model-reach-scope.js +3 -2
- package/dist/model-route-url.d.ts +13 -20
- package/dist/model-route-url.js +28 -24
- package/dist/model-unreachable-notice.d.ts +2 -1
- package/dist/model-unreachable-notice.js +1 -0
- package/dist/observability/fail-open.d.ts +35 -2
- package/dist/observability/fail-open.js +20 -0
- package/dist/plugins/approval-ask-store-file.d.ts +1 -0
- package/dist/plugins/approval-ask-store-file.js +3 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +1 -0
- package/dist/plugins/approval-ask-store-memory.js +6 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +12 -0
- package/dist/plugins/approval-ask-store-sql.js +4 -0
- package/dist/plugins/one-time-migrations.js +14 -1
- package/dist/plugins/park-commit-watch.d.ts +19 -5
- package/dist/plugins/park-commit-watch.js +24 -17
- package/dist/plugins/permission-rule-store-sql.d.ts +2 -1
- package/dist/plugins/remote-env-device.js +0 -1
- package/dist/request-key-closure.js +1 -1
- package/dist/router/route-orchestration.d.ts +4 -2
- package/dist/router/route-orchestration.js +2 -1
- package/dist/rules-consent.d.ts +6 -2
- package/dist/rules-consent.js +3 -0
- package/dist/run-local.js +3 -1
- package/dist/runs.js +1 -1
- package/dist/runtime-governance.d.ts +44 -32
- package/dist/runtime-governance.js +23 -9
- package/dist/sighup-idle.d.ts +7 -1
- package/dist/sighup-idle.js +4 -4
- package/dist/task-settings.d.ts +37 -14
- package/dist/task-settings.js +20 -20
- package/dist/tool-approval.d.ts +13 -0
- package/dist/tool-approval.js +17 -2
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/redact.d.ts +3 -3
- package/dist/trace/self-minted-wire.d.ts +64 -0
- package/dist/trace/self-minted-wire.js +30 -0
- package/dist/unattended-permission-mode.d.ts +17 -13
- package/dist/unattended-permission-mode.js +3 -1
- package/dist/url-shape.d.ts +31 -0
- package/dist/url-shape.js +41 -0
- package/package.json +3 -3
- package/scripts/drain-check-local.mjs +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { combinePolicies, COARSE_SHELL_TOOLS as CORE_COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy,
|
|
1
|
+
import { clampPermissionMode, combinePolicies, COARSE_SHELL_TOOLS as CORE_COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, modePolicyOf, parseLeadingCommandName, wrapToolPolicy, } from "@sema-agent/core";
|
|
2
2
|
import { GovernanceConfig } from "@sema-agent/settings-schema";
|
|
3
3
|
import { currentGovernanceAskMarks } from "./governance-ask-marks.js";
|
|
4
4
|
import { tightenGoverned } from "./governance-overrides.js";
|
|
@@ -7,13 +7,6 @@ import { heldRuleName } from "./tool-rule-names.js";
|
|
|
7
7
|
const COMMAND_RULE_SHAPE = GovernanceConfig.shape.commandPolicy.unwrap().element.shape;
|
|
8
8
|
const COMMAND_DECISIONS = COMMAND_RULE_SHAPE.decision.options;
|
|
9
9
|
export const AUTONOMY_MODES = GovernanceConfig.shape.autonomy.unwrap().options;
|
|
10
|
-
export function isDelegationToolName(name) {
|
|
11
|
-
return name === DEFAULT_SUBAGENT_TOOL_NAME;
|
|
12
|
-
}
|
|
13
|
-
export function stripDelegationTools(tools) {
|
|
14
|
-
const kept = tools.filter((t) => !isDelegationToolName(t.name));
|
|
15
|
-
return kept.length === tools.length ? tools : kept;
|
|
16
|
-
}
|
|
17
10
|
const COARSE_SHELL_TOOLS = CORE_COARSE_SHELL_TOOLS;
|
|
18
11
|
export const COARSE_SHELL_TOOLS_MIRROR = COARSE_SHELL_TOOLS;
|
|
19
12
|
function createCommandAskListPolicy(askCommands, tools = COARSE_SHELL_TOOLS) {
|
|
@@ -104,15 +97,36 @@ export function compileCommandPolicyLayers(rules) {
|
|
|
104
97
|
export function autonomyOverrides(autonomy) {
|
|
105
98
|
switch (autonomy) {
|
|
106
99
|
case "read-only":
|
|
107
|
-
case "plan":
|
|
108
100
|
return { handsReadOnly: true, writeFace: "roots" };
|
|
109
101
|
case "ask":
|
|
110
102
|
return { shellGate: "always" };
|
|
103
|
+
case "plan":
|
|
111
104
|
case "auto":
|
|
112
105
|
case undefined:
|
|
113
106
|
return {};
|
|
114
107
|
}
|
|
115
108
|
}
|
|
109
|
+
export function autonomyModeFloor(autonomy) {
|
|
110
|
+
switch (autonomy) {
|
|
111
|
+
case "plan":
|
|
112
|
+
return "plan";
|
|
113
|
+
case "read-only":
|
|
114
|
+
case "ask":
|
|
115
|
+
case "auto":
|
|
116
|
+
case undefined:
|
|
117
|
+
return undefined;
|
|
118
|
+
default: {
|
|
119
|
+
const unhandled = autonomy;
|
|
120
|
+
throw new Error(`autonomy "${String(unhandled)}" has no entry-floor arm`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function autonomyKeepsOpenWriteFace(autonomy) {
|
|
125
|
+
const floor = autonomyModeFloor(autonomy);
|
|
126
|
+
const entry = floor === undefined ? "bypassPermissions" : clampPermissionMode("bypassPermissions", floor).effective;
|
|
127
|
+
const folded = tightenGoverned({ objective: "", permissionMode: entry }, autonomyOverrides(autonomy));
|
|
128
|
+
return folded.writeFace !== "roots" && folded.permissionMode !== undefined && modePolicyOf(folded.permissionMode).writeFace === "open";
|
|
129
|
+
}
|
|
116
130
|
export function governanceMandatesShellGateAlways(governance) {
|
|
117
131
|
return autonomyOverrides(governance.autonomy).shellGate === "always" || governance.manualModeShellGate === "always";
|
|
118
132
|
}
|
package/dist/sighup-idle.d.ts
CHANGED
|
@@ -8,11 +8,17 @@
|
|
|
8
8
|
* 有界自灭=T11 保留;第二个 SIGHUP 升级 escalate(既有 drain 路径,操作员硬梯子)。
|
|
9
9
|
*
|
|
10
10
|
* 诚实边界:idle-but-live 的 peer(两 turn 之间静默超过窗口)会碰到引擎已退→壳 dead-respawn 1-2s 自愈
|
|
11
|
-
* (伤害面从 mid-turn 斩降级为冷启动)
|
|
11
|
+
* (伤害面从 mid-turn 斩降级为冷启动)。
|
|
12
|
+
* S-1281:附着(打开着的 SSE 流)是判据的第二格 `attached` —— 只读订阅自此不进 inflight(排空不该等一个看着的壳),而一个只挂着订阅流的
|
|
13
|
+
* peer 壳(cli 附着形:`GET /v1/approvals/stream` 常开)仍然是「有人在用」,靠这一格不被当成孤儿。修前这一形是订阅流被算进 inflight
|
|
14
|
+
* 时顺带挡住的;拆开之后两件事各有一格,规则不变:inflight = 0 ∧ attached = 0 ∧ 活性静默满窗才关。
|
|
12
15
|
*/
|
|
13
16
|
export interface SighupIdleDeps {
|
|
14
17
|
/** This replica's in-flight legs (drainState.inflight). */
|
|
15
18
|
inflight: () => number;
|
|
19
|
+
/** S-1281:打开着的 SSE 流条数(drainState.attachedStreams,附着租约同一只 getter)。> 0 ⇒ 有壳挂着,同 inflight 一样重置窗口。
|
|
20
|
+
* 缺席 ⇒ 视为 0(单测 / 嵌入形)。 */
|
|
21
|
+
attached?: () => number;
|
|
16
22
|
/** MONOTONIC-clock ms of the last non-probe HTTP request (drainState.lastActivityAt). Covers what inflight
|
|
17
23
|
* can't: admitted-but-not-yet-registered requests (auth/prepare/claim phases) and sub-tick short requests —
|
|
18
24
|
* and keeps an idle-but-live peer (still listing/browsing) alive. Absent ⇒ inflight-only semantics.
|
package/dist/sighup-idle.js
CHANGED
|
@@ -22,16 +22,16 @@ export function createSighupIdleHandler(deps) {
|
|
|
22
22
|
}
|
|
23
23
|
armedAt = now();
|
|
24
24
|
const inflight = deps.inflight();
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const attached = deps.attached?.() ?? 0;
|
|
26
|
+
idleSince = inflight === 0 && attached === 0 ? now() : null;
|
|
27
|
+
deps.log("sighup_idle_watch_armed", { inflight, attached, idleGraceMs: deps.idleGraceMs });
|
|
27
28
|
watch = setInterval(() => {
|
|
28
29
|
if (deps.isStopped()) {
|
|
29
30
|
clearInterval(watch);
|
|
30
31
|
watch = undefined;
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
if (n > 0) {
|
|
34
|
+
if (deps.inflight() > 0 || (deps.attached?.() ?? 0) > 0) {
|
|
35
35
|
idleSince = null;
|
|
36
36
|
return;
|
|
37
37
|
}
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
*
|
|
14
14
|
* 🔒 SECURITY INVARIANTS (SDK settings.ts §3.3) —— **scope: the `settings.permissions` channel this module projects, and only it**:
|
|
15
15
|
* - `deny` / `ask` are folded via `tightenTaskSpec` (deny-wins `combinePolicies`); it THROWS on any override that would
|
|
16
|
-
* LOOSEN a stricter base safety field. Through those two buckets the client can only ADD denies / asks
|
|
17
|
-
*
|
|
16
|
+
* LOOSEN a stricter base safety field. Through those two buckets the client can only ADD denies / asks on top of the
|
|
17
|
+
* deployment + operator baseline.
|
|
18
18
|
* - `allow` (server 7.117.0, core 7.42.0) is NOT a policy: the list rides WHOLE and verbatim into `TaskSpec.allowedTools`,
|
|
19
19
|
* the request member of the engine's ONE allow layer (beside a deployment's `RunnerDeps.allowedTools`, a person's stored
|
|
20
20
|
* rows, an active skill's `allowed-tools` and a PreToolUse hook's `allow`), read under one predicate. It IS an accepted
|
|
@@ -81,12 +81,13 @@ export declare function coercePermissionModeStrict(raw: unknown): PermissionMode
|
|
|
81
81
|
* S-433 —— 一份**被批准的计划**之后,这条会话继续用的 permission mode(`plan_review` 决裁体的
|
|
82
82
|
* `permissionModeAfter`)。core 的契约句是 `present-plan-tool.js`:「If the plan is approved, the run
|
|
83
83
|
* continues (and any read-only restriction is lifted)」—— 而 core 顶层 resume 把宿主的续跑配置**整体**
|
|
84
|
-
* 展开成新一段 spec
|
|
85
|
-
*
|
|
84
|
+
* 展开成新一段 spec、读的是那段 spec 上的模式词(`TaskSpec.permissionMode`),所以「松开」这件事的属主在
|
|
85
|
+
* **本仓装配续跑配置的那一处**(改写续跑体上的模式词)。批准者要说的正是「松到哪一档」。
|
|
86
86
|
*
|
|
87
87
|
* 🔴 **闭集为什么只有这两个词**:它是 CC `ExitPlanMode` 三选卡上那两张 Yes 卡(「Yes, and auto-accept
|
|
88
|
-
* edits」/「Yes, and manually approve edits」)的对位;`plan` 自己(= 不松开)由 `
|
|
89
|
-
*
|
|
88
|
+
* edits」/「Yes, and manually approve edits」)的对位;`plan` 自己(= 不松开)由 `reject` 表达,
|
|
89
|
+
* `bypassPermissions` / `auto` 不在这张卡上 —— 一个批准动作不该顺带把部署的 ask 面整个关掉。
|
|
90
|
+
* 收这一位的决定 = 把计划带出 plan 的决定(`approve` / `edit`,{@link planReviewDecisionExitsPlan})。
|
|
90
91
|
*
|
|
91
92
|
* 🔴 **不手抄词表**:`satisfies readonly PermissionMode[]` 让「本表的成员必须仍是 core 的模式词」成为**编译期**事实 ——
|
|
92
93
|
* core 某天改名/删词,这里就是红的,而不是静默留一个没人认的词。
|
|
@@ -97,16 +98,40 @@ export type PlanApprovePermissionModeAfter = (typeof PLAN_APPROVE_PERMISSION_MOD
|
|
|
97
98
|
/** S-433 值闭集判据(wire 门用)。**宽臂一个都没有**:词表外的值 400 响亮拒 —— 这一位选的是批准之后
|
|
98
99
|
* 模型有没有手,把一个拼错的词折成任何一档都是安全轴上的静默降级([ref]「无静默 fail-open」)。 */
|
|
99
100
|
export declare function isPlanApprovePermissionModeAfter(raw: unknown): raw is PlanApprovePermissionModeAfter;
|
|
101
|
+
/**
|
|
102
|
+
* `plan_review` 的这个决定把计划**带出 plan** 吗 —— 判据与引擎**同一句**:引擎在 plan_review 的续跑上出 plan 的条件是「决定不是 `reject`」
|
|
103
|
+
* (`approve` 按原计划开干、`edit` 按人改过的计划开干;`reject` 送回去重做计划,plan 保留)。两个读者读这一只:HTTP 门(`permissionModeAfter`
|
|
104
|
+
* 只配出 plan 的决定)与写回(`drivePlanReview` 把存储体的词写成 `permissionModeAfter ?? "default"`)。两处各写一句判据,就会出现
|
|
105
|
+
* 「写回按 approve / edit 写、门只在 approve 上收」那种分家。闭集穷尽 switch:决定词表加一词 ⇒ 编译红。
|
|
106
|
+
*/
|
|
107
|
+
export declare function planReviewDecisionExitsPlan(decision: "approve" | "edit" | "reject"): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* 运维 `AUTONOMY=plan` 的**入口词地板**盖进提交体 —— 此后一条 run 的模式词只有存储体顶层 `permissionMode` 一个源。
|
|
110
|
+
*
|
|
111
|
+
* 规则:入口词 = core `clampPermissionMode(体的生效词, 地板).effective`(体无词 ⇒ 地板;比地板松的词 ⇒ 钳成地板;秩是 core 的,本仓不另写)。
|
|
112
|
+
* 地板缺席 ⇒ 体**原物**交回(零字节变化)。盖章 = 新体 `{ ...body, permissionMode: 入口词 }`:调用方的对象不改;bundle 的
|
|
113
|
+
* `settings.permissions.defaultMode` 不动 —— 顶层赢过它({@link effectivePermissionMode} 的优先序),再改它就是同一语义的第二个写者。
|
|
114
|
+
*
|
|
115
|
+
* 调用点两个:提交入口(`http/admission.ts` 的 `prepareSpec`:值形门全部判完之后、`resolveSpec` 之前)与 leader run 的入口体(`leader/wire.ts`:
|
|
116
|
+
* leader 请求不带模式词,盖的是一份空体 ⇒ 入口词 = 地板本身;本车道的每一条 spec 与 worker 的续跑体都从它取词)。提交入口那一份体就是首跑装配读的体,也是 `buildCheckpointCtx` 持久化的体 ⇒ 一处盖章同时覆盖首跑与每一次续跑重建;
|
|
117
|
+
* 装配腿与续跑腿都不读地板(两处读 = 两个源)。run-local 自建 spec、收不到客户端表态,在它的出生点直接写地板本身(同一个入口词)。
|
|
118
|
+
* 另一个写者是 `plan_review` 写回(`http/resume-legs.ts` `drivePlanReview`,判据 {@link planReviewDecisionExitsPlan})。
|
|
119
|
+
* 热改 `AUTONOMY` 只影响之后提交的任务:在飞的 run 保持它出生时盖下的词。地板的属主是 `runtime-governance.ts` 的 `autonomyModeFloor`。
|
|
120
|
+
*/
|
|
121
|
+
export declare function stampEntryPermissionMode<B extends {
|
|
122
|
+
permissionMode?: unknown;
|
|
123
|
+
settings?: unknown;
|
|
124
|
+
}>(body: B, floor: PermissionMode | undefined): B;
|
|
100
125
|
/**
|
|
101
126
|
* S-433 —— **一条已持久化的提交体的生效 permission mode**,单点。
|
|
102
127
|
*
|
|
103
128
|
* 它只是把 {@link effectivePermissionMode} 与「拿哪一份 settings 去问」这**一对**绑在一起:后者是
|
|
104
129
|
* `parseTaskSettings(body.settings)`(resume 重放共用的宽容解析层)。两处各写一遍这一对,就会在
|
|
105
130
|
* 「某天改成用别的 settings 去算生效模式」的那一拍静默分家 —— 而分家在这条轴上的形态是「wire 门说
|
|
106
|
-
*
|
|
131
|
+
* 这条任务不是 plan 起步、装配腿却按 plan 起步重建」,两个 4xx/行为各说各话,tsc 一声不响。
|
|
107
132
|
*
|
|
108
|
-
* ⚠️
|
|
109
|
-
*
|
|
133
|
+
* ⚠️ 射程:**只管持久化体这一路**(含即将成为持久化体的那一份提交体 —— {@link stampEntryPermissionMode} 读它)。首跑那一路的
|
|
134
|
+
* 生效模式在 `boot/resolve-spec.ts` 单点算(它吃的是场景折叠后的 settings),本函数刻意不去冒充那一处的属主。
|
|
110
135
|
*/
|
|
111
136
|
export declare function storedBodyPermissionMode(body: {
|
|
112
137
|
permissionMode?: unknown;
|
|
@@ -430,14 +455,12 @@ export interface FsWriteGateWiring {
|
|
|
430
455
|
}
|
|
431
456
|
export declare function deriveSettingsPolicy(settings: ParsedTaskSettings, gate?: FsWriteGateWiring, workflowGate?: WorkflowGateWiring): {
|
|
432
457
|
toolPolicy?: ToolPolicy;
|
|
433
|
-
handsReadOnly?: boolean;
|
|
434
|
-
enablePlanMode?: boolean;
|
|
435
458
|
};
|
|
436
459
|
/**
|
|
437
460
|
* Apply a client's parsed per-request settings onto a base `TaskSpec`, TIGHTEN-ONLY, in a SINGLE {@link
|
|
438
|
-
* tightenTaskSpec} pass (so the permission override composes deny-wins onto the baseline — TRAP #1
|
|
439
|
-
*
|
|
440
|
-
*
|
|
461
|
+
* tightenTaskSpec} pass (so the permission override composes deny-wins onto the baseline — TRAP #1). `tightenTaskSpec`
|
|
462
|
+
* THROWS `TaskSpecTightenError` if any override would LOOSEN a stricter base — surfaced by the caller as a 4xx (a
|
|
463
|
+
* client settings stamp that tries to widen is a
|
|
441
464
|
* client error, never a silent weakening). `model`/`outputStyle` are applied directly (model is the caller's
|
|
442
465
|
* choice WITHIN the catalog — the caller gates it before calling; outputStyle appends to the system prompt).
|
|
443
466
|
*
|
package/dist/task-settings.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { combinePolicies, createFsWriteGatePolicy, parseRuleText, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, RUN_WORKFLOW_TOOL_NAME, PERMISSION_MODES, isPermissionMode, ruleToolGrammarOf, } from "@sema-agent/core";
|
|
1
|
+
import { clampPermissionMode, combinePolicies, createFsWriteGatePolicy, parseRuleText, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, RUN_WORKFLOW_TOOL_NAME, PERMISSION_MODES, isPermissionMode, ruleToolGrammarOf, } from "@sema-agent/core";
|
|
2
2
|
import { unmatchableToolNamesRefusal } from "./config.js";
|
|
3
3
|
import { MAX_PERMISSION_RULES_PER_LIST, createRuleListPolicy } from "./permission-rule-list.js";
|
|
4
4
|
import { allowLayerEntryCountIssue, describeAllowLayerOverCap, describeEmptyToolSegmentEntry, emptyToolSegmentEntryOf, ruleEntryListShapeOk } from "./rule-entry-list.js";
|
|
@@ -23,6 +23,20 @@ export const PLAN_APPROVE_PERMISSION_MODES_AFTER = ["default", "acceptEdits"];
|
|
|
23
23
|
export function isPlanApprovePermissionModeAfter(raw) {
|
|
24
24
|
return PLAN_APPROVE_PERMISSION_MODES_AFTER.includes(raw);
|
|
25
25
|
}
|
|
26
|
+
export function planReviewDecisionExitsPlan(decision) {
|
|
27
|
+
switch (decision) {
|
|
28
|
+
case "approve":
|
|
29
|
+
case "edit":
|
|
30
|
+
return true;
|
|
31
|
+
case "reject":
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function stampEntryPermissionMode(body, floor) {
|
|
36
|
+
if (floor === undefined)
|
|
37
|
+
return body;
|
|
38
|
+
return { ...body, permissionMode: clampPermissionMode(storedBodyPermissionMode(body), floor).effective };
|
|
39
|
+
}
|
|
26
40
|
export function storedBodyPermissionMode(body) {
|
|
27
41
|
return effectivePermissionMode(body, parseTaskSettings(body.settings).settings);
|
|
28
42
|
}
|
|
@@ -306,16 +320,6 @@ function createWorkflowAskPolicy(isExempt) {
|
|
|
306
320
|
nameSets: [{ ask: WORKFLOW_ASK_NAMES }],
|
|
307
321
|
};
|
|
308
322
|
}
|
|
309
|
-
function createWorkflowPlanDenyPolicy() {
|
|
310
|
-
return {
|
|
311
|
-
check(req) {
|
|
312
|
-
return heldRuleName(req, isWorkflowName) !== undefined
|
|
313
|
-
? { action: "deny", message: `Plan mode is active — "${req.toolName}" is a write/mutating tool and is read-only-blocked.`, decisionReason: "rule" }
|
|
314
|
-
: { action: "allow" };
|
|
315
|
-
},
|
|
316
|
-
nameSets: [{ deny: WORKFLOW_ASK_NAMES }],
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
323
|
function rejectUnmatchableSettingsNames(perms, pathBases) {
|
|
320
324
|
if (pathBases?.home === undefined) {
|
|
321
325
|
for (const [field, names, behavior] of [
|
|
@@ -380,15 +384,14 @@ export function deriveSettingsPolicy(settings, gate, workflowGate) {
|
|
|
380
384
|
};
|
|
381
385
|
const basePolicy = compose();
|
|
382
386
|
switch (defaultMode) {
|
|
383
|
-
case "plan":
|
|
384
|
-
return { toolPolicy: compose(createWorkflowPlanDenyPolicy()), handsReadOnly: true, enablePlanMode: true };
|
|
385
387
|
case "acceptEdits": {
|
|
386
388
|
const p = compose(fsWriteGate(true), createWorkflowAskPolicy(workflowGate?.isExempt));
|
|
387
389
|
return p ? { toolPolicy: p } : {};
|
|
388
390
|
}
|
|
389
391
|
case "default":
|
|
390
392
|
case "auto":
|
|
391
|
-
case "dontAsk":
|
|
393
|
+
case "dontAsk":
|
|
394
|
+
case "plan": {
|
|
392
395
|
const p = compose(createWorkflowAskPolicy(workflowGate?.isExempt));
|
|
393
396
|
return p ? { toolPolicy: p } : {};
|
|
394
397
|
}
|
|
@@ -398,13 +401,10 @@ export function deriveSettingsPolicy(settings, gate, workflowGate) {
|
|
|
398
401
|
}
|
|
399
402
|
}
|
|
400
403
|
export function applyTaskSettings(base, settings, gate, workflowGate) {
|
|
401
|
-
const { toolPolicy
|
|
404
|
+
const { toolPolicy } = deriveSettingsPolicy(settings, gate, workflowGate);
|
|
402
405
|
let next = base;
|
|
403
|
-
if (toolPolicy !== undefined
|
|
404
|
-
next = tightenGoverned(next, {
|
|
405
|
-
}
|
|
406
|
-
if (enablePlanMode)
|
|
407
|
-
next = { ...next, enablePlanMode: true };
|
|
406
|
+
if (toolPolicy !== undefined)
|
|
407
|
+
next = tightenGoverned(next, { toolPolicy });
|
|
408
408
|
const allow = settings.permissions?.allow;
|
|
409
409
|
if (allow !== undefined)
|
|
410
410
|
next = { ...next, allowedTools: allow };
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export type ApprovalGateKind = (typeof APPROVAL_GATE_KINDS)[number];
|
|
|
19
19
|
export declare function isApprovalGateKind(k: string | undefined): k is ApprovalGateKind;
|
|
20
20
|
/** 两方言同形的 SQL IN 片段(值为闭集常量字面,无注入面)。 */
|
|
21
21
|
export declare const APPROVAL_GATE_KINDS_SQL_IN: string;
|
|
22
|
+
/**
|
|
23
|
+
* 耐久回决口 `POST /v1/approvals/:sessionId/decide` 的路径串 —— S-1280:迟到回决口 PARKING 臂 202 体的机读指路 `decidePath`。
|
|
24
|
+
* 路由表那一行(`http/routes/approvals-assistant.ts` 的 `approval-decide`)按 pattern 匹配它;两者同形由往返钉守
|
|
25
|
+
* (`test/parked-late-decision.test.ts` G3-c:本函数的输出经 `lookupRoute` 落 `approval-decide`)。会话段按路由的 `safeDecode` 对称编码。
|
|
26
|
+
*/
|
|
27
|
+
export declare function approvalDecidePath(sessionId: string): string;
|
|
22
28
|
/**
|
|
23
29
|
* 🔴 [ref](案A)追记 F6 —— durable 回决口(`POST /v1/tasks/:taskId/asks/:askId/decision`)的 **200 体
|
|
24
30
|
* additive 判别位**:这次决议在**本副本**上有没有被一条活着的腿消费掉。
|
|
@@ -1341,6 +1347,13 @@ export declare class ToolApprovalCoordinator {
|
|
|
1341
1347
|
* 流没了,值相等即目标集;极端复用形下多转的那条也只是从流内卡变 durable 卡,方向保守。
|
|
1342
1348
|
* 返回触发条数(0 = 无流内未决 ask,调用方照旧 abort)。幂等:已结算条目跳过,重复调用无害。 */
|
|
1343
1349
|
parkOpenAsksForTask(wireTaskId: string): number;
|
|
1350
|
+
/**
|
|
1351
|
+
* 编排器片 2 件 2 —— 下面 `drainForShutdown` 的第 ① 步,提前到**排空进入**那一拍(`boot/shutdown.ts` 的唯一排空入口调):锁存
|
|
1352
|
+
* {@link shutdownDraining},本进程握着的每一张未决卡按断连同路改路耐久停驻(core `onAsk` 答 "unavailable" 的合同 —— 有 park 设施则停驻、
|
|
1353
|
+
* 没有则无头自动拒,fail-closed);之后**登记**的卡在登记那一拍同路(`askBroadcast` 读同一位)。幂等(再调只是再扫一遍未决表)。
|
|
1354
|
+
* 让出放在进入那一拍而不是硬停那一拍:活卡在排空窗里没有人能答到一只正在下线的副本上,早一拍停驻 = 早一拍能在替补上赎回。
|
|
1355
|
+
*/
|
|
1356
|
+
beginDrain(): void;
|
|
1344
1357
|
/**
|
|
1345
1358
|
* 🔴 **S-929 —— 有序关停的审批面收尾(一只;`boot/shutdown.ts` 的 `hardShutdown` 起点调,SIGTERM / SIGINT / SIGHUP 三条停止路同一处)。**
|
|
1346
1359
|
*
|
package/dist/tool-approval.js
CHANGED
|
@@ -39,6 +39,12 @@ const DEFAULT_PARK_TOMBSTONE_MAX = 2000;
|
|
|
39
39
|
const DEFAULT_PARK_TOMBSTONE_TTL_MS = 60 * 60_000;
|
|
40
40
|
const DEFAULT_PARK_TOMBSTONE_PER_PRINCIPAL_MAX = 128;
|
|
41
41
|
const PARKING_RETRY_AFTER_MS = 250;
|
|
42
|
+
export function approvalDecidePath(sessionId) {
|
|
43
|
+
return `/v1/approvals/${encodeURIComponent(sessionId)}/decide`;
|
|
44
|
+
}
|
|
45
|
+
function parkSessionOf(row) {
|
|
46
|
+
return row.sourceTaskId === row.sessionId && row.sessionId !== row.taskId ? row.sessionId : undefined;
|
|
47
|
+
}
|
|
42
48
|
function ruleMaterialCarriesCredentialShape(command) {
|
|
43
49
|
return redactSecretsWithArms(command, { observe: false }).arms.has("full");
|
|
44
50
|
}
|
|
@@ -707,11 +713,14 @@ export class ToolApprovalCoordinator {
|
|
|
707
713
|
}
|
|
708
714
|
return n;
|
|
709
715
|
}
|
|
710
|
-
|
|
716
|
+
beginDrain() {
|
|
711
717
|
this.shutdownDraining = true;
|
|
712
718
|
for (const e of [...this.pending.values()])
|
|
713
719
|
if (e.settledOutcome === undefined)
|
|
714
720
|
e.forceParkNow();
|
|
721
|
+
}
|
|
722
|
+
async drainForShutdown(deadlineMs) {
|
|
723
|
+
this.beginDrain();
|
|
715
724
|
while ((this.asksInFlight > 0 || this.pending.size > 0 || this.streams.size > 0 || this.parksAwaitingCommit.size > 0) && Date.now() < deadlineMs) {
|
|
716
725
|
await new Promise((r) => setTimeout(r, SHUTDOWN_DRAIN_POLL_MS));
|
|
717
726
|
}
|
|
@@ -1958,17 +1967,23 @@ export class ToolApprovalCoordinator {
|
|
|
1958
1967
|
return this.replayDecidedAsk(id, row, parsed);
|
|
1959
1968
|
case "PARKED":
|
|
1960
1969
|
return this.redeemParkedAsk(id, tomb, row, parsed, httpReq, principal);
|
|
1961
|
-
case "PARKING":
|
|
1970
|
+
case "PARKING": {
|
|
1971
|
+
const parkSession = parkSessionOf(row);
|
|
1962
1972
|
return {
|
|
1963
1973
|
status: 202,
|
|
1964
1974
|
body: {
|
|
1965
1975
|
approvalId: id,
|
|
1966
1976
|
askId: row.askId,
|
|
1967
1977
|
reason: "parking_in_progress",
|
|
1978
|
+
message: parkSession !== undefined
|
|
1979
|
+
? "parking in progress — not applied; retry after retryAfterMs or answer through decidePath"
|
|
1980
|
+
: "parking in progress — not applied; retry after retryAfterMs or answer through the parent session's durable decide",
|
|
1968
1981
|
retryAfterMs: PARKING_RETRY_AFTER_MS,
|
|
1982
|
+
...(parkSession !== undefined ? { decidePath: approvalDecidePath(parkSession) } : {}),
|
|
1969
1983
|
...this.lateRuleRefusal(parsed),
|
|
1970
1984
|
},
|
|
1971
1985
|
};
|
|
1986
|
+
}
|
|
1972
1987
|
case "DENIED":
|
|
1973
1988
|
return this.notPending("denied");
|
|
1974
1989
|
case "VOID":
|
|
@@ -148,8 +148,8 @@ type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCal
|
|
|
148
148
|
type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
|
|
149
149
|
type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
|
|
150
150
|
type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
|
|
151
|
-
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" | "approvalReach" | "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" | "userInvokedSkills" | "allowedTools";
|
|
152
|
-
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence"
|
|
151
|
+
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" | "approvalReach" | "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" | "userInvokedSkills" | "outputStyle" | "allowedTools";
|
|
152
|
+
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
|
|
153
153
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
154
154
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
155
155
|
type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel" | "compaction";
|
package/dist/trace/redact.d.ts
CHANGED
|
@@ -179,9 +179,9 @@ export declare function redactDeepWith(value: unknown, leaf: (s: string) => stri
|
|
|
179
179
|
*/
|
|
180
180
|
export declare function redactedPreview(value: unknown, cap?: number): string | null;
|
|
181
181
|
/**
|
|
182
|
-
* **这一位是不是一个层级式绝对 URL**(有 authority 的那一族)——
|
|
183
|
-
* (
|
|
184
|
-
*
|
|
182
|
+
* **这一位是不是一个层级式绝对 URL**(有 authority 的那一族)—— 本铸点与 `url-shape.ts` 的 http(s) 形判官
|
|
183
|
+
* (模型路由三键 / 中心基址三席 / 公告地址 / embedder / A2A 的拒启都经它问到这里)共用的**同一只**判据:两处曾各判各的,
|
|
184
|
+
* 而 `URL.canParse` 单独为真的**不透明** URL 恰好是「凭据位看不见」的那一形。
|
|
185
185
|
*
|
|
186
186
|
* ⚠️ **射程如实**(车JC 复审去幻觉轮:此处原文写的是「本仓唯一判据」,亲读证伪)—— 仓里**另有一处**仍用
|
|
187
187
|
* 裸 `URL.canParse` 回答同一个问题,且**刻意不在射程内**(改它是一次新的行为面拒启,按三问另开车):
|
|
@@ -169,6 +169,70 @@ export declare const SELF_MINTED_WIRE: {
|
|
|
169
169
|
};
|
|
170
170
|
};
|
|
171
171
|
};
|
|
172
|
+
/** 7.121.0:`GET /health` 自报位 `auth.kinds[]` 的成员(验过的身份来源闭集;这台部署的装配能回的那几个)。 */
|
|
173
|
+
readonly healthAuthKind: {
|
|
174
|
+
readonly at: "GET /health auth.kinds[]";
|
|
175
|
+
readonly members: {
|
|
176
|
+
service_token: {
|
|
177
|
+
attr: string;
|
|
178
|
+
since: string;
|
|
179
|
+
};
|
|
180
|
+
sso: {
|
|
181
|
+
attr: string;
|
|
182
|
+
since: string;
|
|
183
|
+
};
|
|
184
|
+
direct_door_jwt: {
|
|
185
|
+
attr: string;
|
|
186
|
+
since: string;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
/** 7.121.0:`GET /health` 自报位 `auth.identityAttach`。 */
|
|
191
|
+
readonly healthIdentityAttach: {
|
|
192
|
+
readonly at: "GET /health auth.identityAttach";
|
|
193
|
+
readonly members: {
|
|
194
|
+
available: {
|
|
195
|
+
attr: string;
|
|
196
|
+
since: string;
|
|
197
|
+
};
|
|
198
|
+
unavailable: {
|
|
199
|
+
attr: string;
|
|
200
|
+
since: string;
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/** 7.121.0:`GET /health` 自报位 `operator`。 */
|
|
205
|
+
readonly healthOperator: {
|
|
206
|
+
readonly at: "GET /health operator";
|
|
207
|
+
readonly members: {
|
|
208
|
+
present: {
|
|
209
|
+
attr: string;
|
|
210
|
+
since: string;
|
|
211
|
+
};
|
|
212
|
+
absent: {
|
|
213
|
+
attr: string;
|
|
214
|
+
since: string;
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
/** 7.121.0:`GET /health` 自报位 `center.sessionsVisible`(两个布尔 + 一个词;成员按 JSON 字面登记)。 */
|
|
219
|
+
readonly healthSessionsVisible: {
|
|
220
|
+
readonly at: "GET /health center.sessionsVisible";
|
|
221
|
+
readonly members: {
|
|
222
|
+
true: {
|
|
223
|
+
attr: string;
|
|
224
|
+
since: string;
|
|
225
|
+
};
|
|
226
|
+
false: {
|
|
227
|
+
attr: string;
|
|
228
|
+
since: string;
|
|
229
|
+
};
|
|
230
|
+
unknown: {
|
|
231
|
+
attr: string;
|
|
232
|
+
since: string;
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
};
|
|
172
236
|
/** 本仓在出口对 core 值做的变换(值变、键不变)。 */
|
|
173
237
|
readonly wireValueRule: {
|
|
174
238
|
readonly at: "出口值变换(<端点>#<键> → 变换)";
|
|
@@ -60,6 +60,36 @@ export const SELF_MINTED_WIRE = {
|
|
|
60
60
|
shutdown_before_park: { attr: "-", since: "7.116.0" },
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
|
+
healthAuthKind: {
|
|
64
|
+
at: "GET /health auth.kinds[]",
|
|
65
|
+
members: {
|
|
66
|
+
service_token: { attr: "-", since: "7.121.0" },
|
|
67
|
+
sso: { attr: "-", since: "7.121.0" },
|
|
68
|
+
direct_door_jwt: { attr: "-", since: "7.121.0" },
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
healthIdentityAttach: {
|
|
72
|
+
at: "GET /health auth.identityAttach",
|
|
73
|
+
members: {
|
|
74
|
+
available: { attr: "-", since: "7.121.0" },
|
|
75
|
+
unavailable: { attr: "-", since: "7.121.0" },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
healthOperator: {
|
|
79
|
+
at: "GET /health operator",
|
|
80
|
+
members: {
|
|
81
|
+
present: { attr: "-", since: "7.121.0" },
|
|
82
|
+
absent: { attr: "-", since: "7.121.0" },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
healthSessionsVisible: {
|
|
86
|
+
at: "GET /health center.sessionsVisible",
|
|
87
|
+
members: {
|
|
88
|
+
true: { attr: "-", since: "7.121.0" },
|
|
89
|
+
false: { attr: "-", since: "7.121.0" },
|
|
90
|
+
unknown: { attr: "-", since: "7.121.0" },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
63
93
|
wireValueRule: {
|
|
64
94
|
at: "出口值变换(<端点>#<键> → 变换)",
|
|
65
95
|
members: {
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* 本仓**自己铸** spec、**没有调用方**能在请求上表态模式的腿 —— leader worker(`src/leader/wire.ts` provisionWorker 两车道)、leader 的
|
|
6
6
|
* 修复腿(集成沙箱修复 `mkRepair`、模块门修复 `leader.ts` `runModuleGate`、单代理修复环 `repair-wire.ts` `attachRepairLoopDeps`)、run-local
|
|
7
7
|
* (`src/run-local.ts`,一次性 CLI)与这些腿的 durable 续跑重挂(`CheckpointCtx.unattendedLeg`)—— 经 {@link withUnattendedLeg} 这**一只**
|
|
8
|
-
* 合并口铸两键:`permissionMode
|
|
8
|
+
* 合并口铸两键:`permissionMode`(`"default"`,显式:续跑时盖过 checkpoint 记下的旧模式;**钳在 spec 自带的模式词之下** —— run-local 在
|
|
9
|
+
* 它自建 spec 的出生点盖下的运维 `AUTONOMY=plan` 地板、续跑重建读到的存储体的 `plan` 都是天花板,本腿的 `default` 落在它下面就是 `plan`,
|
|
10
|
+
* 不把它降回去)与 `onAsk` = {@link createUnattendedOnAsk}。
|
|
9
11
|
* 那只 onAsk 读 `AskRequest.classes` **整体**:集非空且每一类都在 {@link UNATTENDED_ANSWERED_CLASSES}(= `{ordinary}`,常量,不是旋钮)⇒ 答 yes
|
|
10
12
|
* —— **以策略的名义**(S-953 / core 7.39.0 #1265:`{allow:true, settledBy:"policy", approver:"unattended_class_answer"}` ⇒ core 结算 `policy_allowed` /
|
|
11
13
|
* `who.party:"policy"`;修前是裸 `true`,core 把机器的放行记成**人**批 `human_allowed` / `person`);
|
|
@@ -17,18 +19,17 @@
|
|
|
17
19
|
* 不答 ⇒ 整问不答。
|
|
18
20
|
* · **缺席 = 不答的类**:没盖 `origin` 的站(共享制品读确认,KL-1214-a)、部署自铸的请求、7.37.0 之前 park 的行续跑(KL-1214-b,续跑不补)
|
|
19
21
|
* 都不带类 ⇒ 不答。
|
|
20
|
-
* · **答集只 `ordinary
|
|
21
|
-
*
|
|
22
|
-
* org_rule / operator_forced / …,权威为人提的问)恒不答。function approver 的 yes 是「这一次调用的答」—— 它连 `requiresRealApproval`
|
|
22
|
+
* · **答集只 `ordinary`**:引擎可放行的类(core `releasableAskClassesFor(face)`,按问的面读 —— 哪些类、哪一面放得行以引擎的表为准,本注不抄名单)里除 `ordinary`
|
|
23
|
+
* 之外的保护类与 `mode`(plan 的写问)留给人;可放行类之外的类(权威为人提的问)恒不答。function approver 的 yes 是「这一次调用的答」—— 它连 `requiresRealApproval`
|
|
23
24
|
* 都清得掉(引擎分不出函数与人),所以带那一位的类(`transcript_write` / `rule_unreadable` / `artifact_shared_read` / `policy_rra`)不进答集
|
|
24
|
-
* (钉:test/unattended-permission-mode.test.ts「答集 ⊆
|
|
25
|
+
* (钉:test/unattended-permission-mode.test.ts「答集 ⊆ 每一面的 releasableAskClassesFor(face),且与 RRA_EXPLAINERS ∪ {policy_rra} 不交」)。
|
|
25
26
|
* · **`ordinary` 覆盖什么**(core 原句):`Read` 工具的根外读、shell 的根外**写**(`echo x > <outside>` —— 写守卫只判保护表)、模式写门对每一次
|
|
26
27
|
* covered write 的问、形状层对非只读 shell 的问;shell 的根外**读**(`cat <outside>`)是 `probe`,不答。
|
|
27
28
|
*
|
|
28
29
|
* ## 三问
|
|
29
30
|
* · **谁需要**:用 leader / run-local 的部署 —— 普通写与非只读命令照跑(7.105.0 行为;7.106.0–7.108.0 过渡形靠 `bypassPermissions` 宽放给出的同一结果)。
|
|
30
|
-
* · **谁受伤**:① 设了 `UNATTENDED_PERMISSION_MODE` 的部署 —— 起服拒启(`config.ts` 墓碑表,`removed` 族);② 靠过渡形缺省 bypass
|
|
31
|
-
* `
|
|
31
|
+
* · **谁受伤**:① 设了 `UNATTENDED_PERMISSION_MODE` 的部署 —— 起服拒启(`config.ts` 墓碑表,`removed` 族);② 靠过渡形缺省 bypass 放行答集之外的保护类
|
|
32
|
+
* (哪些类见 `releasableAskClassesFor` 与答集的不变量钉,本注不抄名单)的无人值守流程 —— 改成不答(有席交席、无席 park 或拒)。安全轴收紧。
|
|
32
33
|
* · **什么补偿**:有人的腿照问人(run-local 在终端里跑;leader 配 durable 审批块 ⇒ park 到 `/decide`);答集是 core 推荐形的常量 ——
|
|
33
34
|
* 要放更多类的部署先立案候裁,不开旋钮。
|
|
34
35
|
*
|
|
@@ -43,10 +44,11 @@
|
|
|
43
44
|
* · 续跑腿上的协调器席:`http/resume-legs.ts` 只在「run 行在场 ∧ 活卡协调器在场」时给 spec 另装活席(`deps.toolApproval && taskId`)——
|
|
44
45
|
* leader worker 的会话没有 run 行,那一装今天够不着本腿;哪天够着了,活席会盖掉本层(方向 = 多问人,fail-closed),见收车档 ⑤。
|
|
45
46
|
*/
|
|
46
|
-
import type
|
|
47
|
+
import { type AskClass, type AskRequest, type OnAsk, type PermissionMode, type TaskSpec } from "@sema-agent/core";
|
|
47
48
|
/**
|
|
48
49
|
* 按类作答的**答集** —— core §4.6e 推荐形:机器只答 `ordinary`。**常量,不是旋钮**([ref]):要放更多类是部署级的安全决定,先立案候裁。
|
|
49
|
-
* 不变量(测试钉):答集 ⊆ core
|
|
50
|
+
* 不变量(测试钉):答集 ⊆ core 每一面的可放行类 `releasableAskClassesFor(face)`(本层不读问的面,同一只答集答每一面的问),且与带 `requiresRealApproval` 的类
|
|
51
|
+
* (core `RRA_EXPLAINERS` ∪ `policy_rra`)不交;`mode` 不在其中。
|
|
50
52
|
*/
|
|
51
53
|
export declare const UNATTENDED_ANSWERED_CLASSES: ReadonlySet<AskClass>;
|
|
52
54
|
/**
|
|
@@ -66,13 +68,15 @@ export declare function unattendedAnswers(req: Pick<AskRequest, "classes">): boo
|
|
|
66
68
|
export declare function createUnattendedOnAsk(seat: UnattendedSeat | undefined): UnattendedSeat;
|
|
67
69
|
/** {@link withUnattendedLeg} 铸进 spec 的两键。 */
|
|
68
70
|
export type UnattendedLegSpec = {
|
|
69
|
-
readonly permissionMode:
|
|
71
|
+
readonly permissionMode: PermissionMode;
|
|
70
72
|
readonly onAsk: UnattendedSeat;
|
|
71
73
|
};
|
|
72
74
|
/**
|
|
73
|
-
* 无人值守腿并进一条 spec —— 各腿的**唯一**合并口:`permissionMode
|
|
74
|
-
*
|
|
75
|
-
*
|
|
75
|
+
* 无人值守腿并进一条 spec —— 各腿的**唯一**合并口:`permissionMode` = `"default"` 钳在 spec 已有的模式词之下(core `clampPermissionMode`,与
|
|
76
|
+
* `tightenTaskSpec` 对模式词的合并同一只;spec 无词 ⇒ `"default"`)+ `onAsk: createUnattendedOnAsk(seat)`。`seat` = 本腿的活审批席(run-local
|
|
77
|
+
* 递它的 `createLocalApprover`;leader 各腿与续跑重挂无席,不递)—— 席只经这个参数进来,从不从 spec 上读。
|
|
78
|
+
* 调用点必须在 spec 其余键(含治理折叠)**之后**调它 —— spec 自带的 `plan` 由此被读到、留住;本口不碰 `toolPolicy`(没有策略层要并:过渡形的
|
|
79
|
+
* 写保护拒层已退役)。
|
|
76
80
|
*/
|
|
77
81
|
export declare function withUnattendedLeg<S extends Partial<TaskSpec>>(spec: S, seat?: UnattendedSeat): S & UnattendedLegSpec;
|
|
78
82
|
//# sourceMappingURL=unattended-permission-mode.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clampPermissionMode } from "@sema-agent/core";
|
|
1
2
|
export const UNATTENDED_ANSWERED_CLASSES = new Set(["ordinary"]);
|
|
2
3
|
export const UNATTENDED_POLICY_APPROVER = "unattended_class_answer";
|
|
3
4
|
function unattendedPolicyYes() {
|
|
@@ -11,6 +12,7 @@ export function createUnattendedOnAsk(seat) {
|
|
|
11
12
|
return (req, signal) => (unattendedAnswers(req) ? unattendedPolicyYes() : seat !== undefined ? seat(req, signal) : "unavailable");
|
|
12
13
|
}
|
|
13
14
|
export function withUnattendedLeg(spec, seat) {
|
|
14
|
-
|
|
15
|
+
const permissionMode = spec.permissionMode === undefined ? "default" : clampPermissionMode("default", spec.permissionMode).effective;
|
|
16
|
+
return { ...spec, permissionMode, onAsk: createUnattendedOnAsk(seat) };
|
|
15
17
|
}
|
|
16
18
|
//# sourceMappingURL=unattended-permission-mode.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** 座位可以禁掉的部件(闭集;判序 = 本数组序)。 */
|
|
2
|
+
export declare const HTTP_URL_PARTS: readonly ["userinfo", "query", "fragment"];
|
|
3
|
+
export type HttpUrlPart = (typeof HTTP_URL_PARTS)[number];
|
|
4
|
+
/** 判官的拒因词(闭集):形不对,或一件被禁的部件在场。 */
|
|
5
|
+
export type HttpUrlFlaw = "not-http-url" | HttpUrlPart;
|
|
6
|
+
/** 「不是一个 http(s) URL」的规则词 —— 不透明形、不可解析、层级式非 http(s) 同一句;只点名规则,不回显 scheme。钉读这一只,不手抄。 */
|
|
7
|
+
export declare const HTTP_URL_SHAPE_RULE = "is not a parseable http(s) URL (scheme must be http or https; it must look like http(s)://host[:port][/path])";
|
|
8
|
+
/**
|
|
9
|
+
* 判:合格 ⇒ `[]`;否则按判序交回规则词。形不对且不透明 ⇒ 只有 `not-http-url`(部件在不透明形上无从判,
|
|
10
|
+
* 也不需要判 —— 那一形整只不收);层级式非 http(s) ⇒ `not-http-url` 之后照判部件。
|
|
11
|
+
* 返回类型按 `forbid` 收窄:座位没禁的部件不会出现在答案里(调用方的穷举 switch 因此不需要一条「不可达」臂)。
|
|
12
|
+
*/
|
|
13
|
+
export declare function httpUrlFlaws<P extends HttpUrlPart>(raw: string, forbid: readonly P[]): ReadonlyArray<"not-http-url" | P>;
|
|
14
|
+
/** 一个拒因词的规则句(穷举;新词不补 = 编译红)。句子只说规则,不含值。 */
|
|
15
|
+
export declare function httpUrlRule(flaw: HttpUrlFlaw): string;
|
|
16
|
+
/**
|
|
17
|
+
* 拒句里值的样子(纯函数):合格的 http(s) 形 ⇒ 只到 origin(`scheme://host[:port]`,过展示铸点),origin 之后还有东西(userinfo /
|
|
18
|
+
* 路径 / query / fragment)⇒ 接一枚 `/…`;其余(不透明 / 不可解析 / 层级式非 http(s))⇒ 展示铸点的占位 `«redacted:url»` ——
|
|
19
|
+
* 拒句里不出现非 http(s) 的 scheme 词(主会话增补裁:写成 scheme 的令牌 `<48hex>://host` 曾原样进 fatal 行)。
|
|
20
|
+
*
|
|
21
|
+
* 🔴 为什么只到 origin(车内 codex 对抗复审 r1 [high],亲跑复现):展示铸点的路径段**逐字**保留、只靠形状律认令牌形 ——
|
|
22
|
+
* 形状律认不出的令牌(48 位十六进制拉取令牌就是一例)放在路径上会原样进拒句。拒句落在 fatal 行、stderr、每一轮的请求失败日志,
|
|
23
|
+
* 这几处不赌形状律的覆盖面:座位名 + 规则词已经说清了哪一席错在哪,值只需要认得出是哪一台主机。
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildRefusalShownUrl(raw: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* 拒句的**唯一**铸点:`<subject> <规则句>[; <规则句>…]; got "<值的样子>"`(`build*` = 纯数据)。
|
|
28
|
+
* 值只经 {@link buildRefusalShownUrl} 出现;`flaws` 来自 {@link httpUrlFlaws}(调用方按首个拒因选自己的码与出路句)。
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildHttpUrlRefusal(subject: string, raw: string, flaws: readonly HttpUrlFlaw[]): string;
|
|
31
|
+
//# sourceMappingURL=url-shape.d.ts.map
|