@sema-agent/server 7.130.0 → 7.132.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 +396 -0
- package/README.md +13 -6
- package/README.zh-CN.md +9 -5
- package/USAGE.md +187 -50
- package/dist/approval-card.d.ts +3 -1
- package/dist/approval-card.js +2 -3
- package/dist/audit.d.ts +7 -10
- package/dist/audit.js +4 -1
- package/dist/auth-bridge.d.ts +4 -0
- package/dist/auth-bridge.js +10 -11
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/deployment-mods.d.ts +31 -0
- package/dist/boot/deployment-mods.js +26 -0
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/execution-env.js +19 -6
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
- package/dist/boot/read-deny-anchor-audit.js +36 -0
- package/dist/boot/resolve-spec.d.ts +1 -5
- package/dist/boot/resolve-spec.js +56 -61
- package/dist/boot/runner-deps.d.ts +19 -8
- package/dist/boot/runner-deps.js +14 -2
- package/dist/boot/session-faces.d.ts +1 -7
- package/dist/boot/stage-02-web-admin-pull.js +9 -1
- 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 +4 -4
- package/dist/boot/stage-05-execution-env.js +7 -10
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +5 -4
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +3 -3
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +3 -9
- package/dist/boot/stage-10-http-server.d.ts +3 -9
- package/dist/boot/stage-10-http-server.js +12 -10
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +5 -6
- package/dist/boot/web-admin.d.ts +0 -3
- package/dist/boot/web-admin.js +35 -35
- package/dist/brain.js +17 -22
- package/dist/budget.js +5 -4
- package/dist/caller-text-echo.d.ts +20 -0
- package/dist/caller-text-echo.js +24 -0
- package/dist/capabilities/brand-identity.d.ts +26 -0
- package/dist/capabilities/brand-identity.js +27 -0
- package/dist/capabilities/builtin-tools.js +2 -1
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +32 -59
- package/dist/capabilities/center-prompts.js +18 -32
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capabilities/engine-capabilities.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +3 -3
- package/dist/capabilities/host-tool-names.d.ts +17 -0
- package/dist/capabilities/host-tool-names.js +15 -0
- package/dist/capabilities/mcp-probe.d.ts +14 -1
- package/dist/capabilities/mcp-probe.js +22 -133
- package/dist/capabilities/repo-tools.js +4 -3
- package/dist/capabilities/scenarios.d.ts +42 -47
- package/dist/capabilities/scenarios.js +19 -35
- package/dist/capabilities/select-environment-tool.js +2 -1
- package/dist/capabilities/send-user-file-tool.d.ts +0 -1
- package/dist/capabilities/send-user-file-tool.js +2 -2
- package/dist/capabilities/skills.d.ts +19 -4
- package/dist/capabilities/skills.js +30 -10
- package/dist/capabilities/tool-defer.js +2 -1
- package/dist/config-catalog.js +22 -11
- package/dist/config-invariants.js +2 -2
- package/dist/config-lkg.d.ts +9 -9
- package/dist/config-lkg.js +20 -4
- package/dist/config-provider.d.ts +3 -1
- package/dist/config-provider.js +14 -6
- package/dist/config-types.d.ts +13 -9
- package/dist/config.d.ts +2 -2
- package/dist/config.js +34 -20
- package/dist/device-ws-protocol.d.ts +1 -1
- package/dist/device-ws-protocol.js +1 -0
- package/dist/elicitation.d.ts +7 -0
- package/dist/elicitation.js +20 -5
- package/dist/env-name-allowlist-knobs.d.ts +12 -2
- package/dist/env-name-allowlist-knobs.js +5 -4
- package/dist/exec-bounds.d.ts +15 -3
- package/dist/exec-bounds.js +4 -0
- package/dist/execution-lane-caps.d.ts +9 -2
- package/dist/execution-lane-caps.js +11 -0
- package/dist/hooks/classic-stations.d.ts +86 -0
- package/dist/hooks/classic-stations.js +27 -0
- package/dist/hooks/hook-llm.d.ts +8 -5
- package/dist/hooks/hook-llm.js +80 -37
- package/dist/hooks/hook-runner.d.ts +273 -75
- package/dist/hooks/hook-runner.js +293 -317
- package/dist/http/admission.d.ts +8 -2
- package/dist/http/admission.js +103 -59
- package/dist/http/approval-sweeps.js +59 -18
- package/dist/http/checkpoint-ctx.d.ts +36 -18
- package/dist/http/checkpoint-ctx.js +14 -10
- package/dist/http/claimed-run.d.ts +13 -0
- package/dist/http/claimed-run.js +15 -0
- package/dist/http/device-placement.js +5 -10
- package/dist/http/dispatch.d.ts +0 -4
- package/dist/http/dispatch.js +6 -10
- package/dist/http/durable-run-leg.d.ts +2 -1
- package/dist/http/durable-run-leg.js +5 -3
- package/dist/http/health-self-report.d.ts +7 -5
- package/dist/http/health-self-report.js +4 -18
- package/dist/http/principal-gate.d.ts +23 -6
- package/dist/http/principal-gate.js +24 -5
- package/dist/http/refusal-body.d.ts +21 -0
- package/dist/http/refusal-body.js +4 -0
- package/dist/http/request-guards.d.ts +27 -0
- package/dist/http/request-guards.js +24 -3
- package/dist/http/resume-legs.d.ts +25 -0
- package/dist/http/resume-legs.js +48 -26
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/capabilities.js +7 -17
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/rules.js +2 -2
- package/dist/http/routes/runs.js +13 -17
- package/dist/http/routes/session-sync.js +4 -2
- package/dist/http/routes/sessions.js +23 -11
- package/dist/http/routes/shared-memory.js +2 -13
- package/dist/http/routes/side-query.js +2 -3
- package/dist/http/routes/tasks.js +34 -19
- package/dist/http/scenario-read-face.d.ts +14 -0
- package/dist/http/scenario-read-face.js +5 -0
- package/dist/http/send.d.ts +13 -1
- package/dist/http/send.js +12 -1
- package/dist/http/server.d.ts +37 -23
- package/dist/http/server.js +30 -4
- package/dist/http/stored-body-replay.d.ts +10 -3
- package/dist/http/stored-body-replay.js +27 -6
- package/dist/http/submit-ctx.d.ts +25 -0
- package/dist/http/submit-ctx.js +39 -0
- package/dist/http/wire-types.d.ts +18 -5
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/lane-home-probe.d.ts +43 -0
- package/dist/lane-home-probe.js +35 -0
- package/dist/limit-refusal.d.ts +47 -0
- package/dist/limit-refusal.js +12 -0
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +3 -11
- package/dist/mcp-assertion-header.js +3 -9
- package/dist/mcp-tool-faces.d.ts +5 -0
- package/dist/mcp-tool-faces.js +5 -1
- package/dist/memory-scope.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +1 -7
- package/dist/model-provider.d.ts +39 -12
- package/dist/model-provider.js +19 -2
- package/dist/model-route-endpoint.d.ts +52 -12
- package/dist/model-route-endpoint.js +48 -26
- package/dist/model-route-gap.d.ts +5 -4
- package/dist/model-route-gap.js +1 -1
- package/dist/model-select.d.ts +58 -0
- package/dist/model-select.js +31 -0
- package/dist/namespaced-rule-name.d.ts +7 -4
- package/dist/namespaced-rule-name.js +4 -3
- package/dist/observability/cost-taxonomy.d.ts +7 -7
- package/dist/observability/fail-open.d.ts +53 -12
- package/dist/observability/fail-open.js +47 -7
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/prompt-manifest.d.ts +47 -82
- package/dist/observability/prompt-manifest.js +119 -17
- package/dist/observability/run-terminal.js +14 -6
- package/dist/observability/secret-env-scrub.d.ts +13 -1
- package/dist/observability/secret-env-scrub.js +13 -1
- package/dist/observability/tool-trace.d.ts +16 -21
- package/dist/observability/tool-trace.js +51 -42
- package/dist/per-task-image.d.ts +7 -1
- package/dist/per-task-image.js +3 -3
- package/dist/plain-string-record.d.ts +25 -0
- package/dist/plain-string-record.js +32 -0
- package/dist/plugins/blob-backend.d.ts +68 -13
- package/dist/plugins/blob-backend.js +53 -10
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
- package/dist/plugins/checkpoint-store-sql.js +39 -26
- package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
- package/dist/plugins/k8s-bg-scripts.js +22 -0
- package/dist/plugins/local-checkpoint-store.js +5 -2
- package/dist/plugins/local-session-store.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +147 -91
- package/dist/plugins/one-time-migrations.js +372 -208
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
- package/dist/plugins/permission-rule-store-sql.js +4 -18
- package/dist/plugins/pg-session-storage.d.ts +4 -5
- package/dist/plugins/pg-session-storage.js +7 -17
- package/dist/plugins/remote-env-adb.d.ts +6 -0
- package/dist/plugins/remote-env-adb.js +9 -4
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +1 -1
- package/dist/plugins/remote-env-e2b.js +21 -9
- package/dist/plugins/remote-env-host.d.ts +9 -9
- package/dist/plugins/remote-env-host.js +10 -32
- package/dist/plugins/remote-env-k8s.d.ts +95 -26
- package/dist/plugins/remote-env-k8s.js +265 -90
- package/dist/plugins/remote-env-local-docker.d.ts +20 -1
- package/dist/plugins/remote-env-local-docker.js +77 -9
- package/dist/plugins/remote-env-ssh.d.ts +15 -0
- package/dist/plugins/remote-env-ssh.js +109 -3
- package/dist/plugins/remote-shell.d.ts +0 -1
- package/dist/plugins/remote-shell.js +1 -2
- package/dist/plugins/retention-store-sql.js +6 -2
- package/dist/plugins/schema-consistency.d.ts +54 -0
- package/dist/plugins/schema-consistency.js +52 -0
- package/dist/plugins/session-epoch-anchor.d.ts +13 -0
- package/dist/plugins/session-epoch-anchor.js +23 -0
- package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
- package/dist/plugins/shared-memory-store-sql.js +5 -16
- package/dist/plugins/sql-driver.d.ts +141 -0
- package/dist/plugins/sql-driver.js +115 -1
- package/dist/plugins/sql-errors.d.ts +41 -0
- package/dist/plugins/sql-errors.js +36 -0
- package/dist/plugins/store-backend.js +2 -1
- package/dist/plugins/tidb-session-storage.d.ts +7 -11
- package/dist/plugins/tidb-session-storage.js +6 -16
- package/dist/plugins/tidb-session-store.js +1 -1
- package/dist/prompts-domain-validate.d.ts +17 -61
- package/dist/prompts-domain-validate.js +9 -119
- package/dist/question.d.ts +18 -9
- package/dist/question.js +21 -13
- package/dist/request-key-closure.d.ts +4 -4
- package/dist/request-key-closure.js +18 -13
- package/dist/rule-entry-list.d.ts +134 -24
- package/dist/rule-entry-list.js +162 -30
- package/dist/run-local.d.ts +3 -3
- package/dist/run-local.js +26 -25
- package/dist/runs.d.ts +48 -2
- package/dist/runs.js +61 -2
- package/dist/server-secret-env.d.ts +44 -18
- package/dist/server-secret-env.js +49 -8
- package/dist/session-incarnation.d.ts +15 -5
- package/dist/session-incarnation.js +16 -9
- package/dist/session-titler.js +12 -2
- package/dist/spec-fields.d.ts +34 -18
- package/dist/spec-fields.js +46 -53
- package/dist/sql-engine-posture.d.ts +29 -4
- package/dist/sql-engine-posture.js +36 -1
- package/dist/stored-request-hooks-m20.d.ts +23 -0
- package/dist/stored-request-hooks-m20.js +124 -0
- package/dist/stored-request-tool-axes-m19.d.ts +20 -5
- package/dist/stored-request-tool-axes-m19.js +30 -2
- package/dist/task-a2a.d.ts +9 -1
- package/dist/task-a2a.js +73 -47
- package/dist/task-mcp.d.ts +54 -102
- package/dist/task-mcp.js +194 -92
- package/dist/task-settings.d.ts +47 -25
- package/dist/task-settings.js +46 -61
- package/dist/text-bidi.d.ts +3 -46
- package/dist/text-bidi.js +3 -3
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +24 -4
- package/dist/trace/engine-notice-wire.d.ts +11 -3
- package/dist/trace/engine-notice-wire.js +8 -1
- package/dist/trace/ledger-events.d.ts +3 -1
- package/dist/trace/ledger-sink.d.ts +29 -0
- package/dist/trace/ledger-sink.js +15 -1
- package/dist/trace/model-segment.js +1 -1
- package/dist/trace/project.d.ts +25 -6
- package/dist/trace/project.js +68 -22
- package/dist/trace/wire-projection-faces.d.ts +80 -2
- package/dist/trace/wire-projection-faces.js +23 -1
- package/dist/user-authored-text.d.ts +7 -0
- package/dist/user-authored-text.js +3 -0
- package/dist/web-admin-client/apply-effective.js +10 -5
- package/dist/web-admin-client/candidate-gate.d.ts +6 -6
- package/dist/web-admin-client/candidate-gate.js +3 -3
- package/dist/web-admin-client/http-client.js +0 -6
- package/dist/web-admin-client/read-warnings.d.ts +13 -0
- package/dist/web-admin-client/read-warnings.js +10 -0
- package/dist/web-admin-client/skills-mcp.d.ts +3 -0
- package/dist/web-admin-client/skills-mcp.js +9 -6
- package/dist/web-admin-client/types.d.ts +3 -4
- package/dist/workspace-cache.d.ts +35 -0
- package/dist/workspace-cache.js +36 -0
- package/dist/workspace-space.d.ts +3 -2
- package/dist/workspace-space.js +2 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
- package/package.json +4 -4
- package/scripts/drain-check-local.mjs +37 -3
- package/scripts/drain-check.mysql.sql +30 -2
- package/scripts/drain-check.pg.sql +29 -2
- package/dist/capabilities/memory-notice.d.ts +0 -138
- package/dist/capabilities/memory-notice.js +0 -64
- package/dist/capabilities/prompt.d.ts +0 -17
- package/dist/capabilities/prompt.js +0 -6
- package/dist/mcp-content-origin.d.ts +0 -34
- package/dist/mcp-content-origin.js +0 -10
- package/dist/tool-axis-words.d.ts +0 -27
- package/dist/tool-axis-words.js +0 -9
- package/dist/wire-name-bound.d.ts +0 -14
- package/dist/wire-name-bound.js +0 -13
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ## 律(一句;core #1753 定,本仓只传事实、不判)
|
|
12
12
|
* 本包任何进程在宿主上 spawn 的子进程:env = 引擎 `inheritedHostEnv(policy, findings)` ∪ 部署方点名放回的键(只在运维真可能要传东西的腿上有旋钮),
|
|
13
|
-
* 剥掉的键经一个登记过的 `SecretEnvScrubSite`
|
|
13
|
+
* 剥掉的键经一个登记过的 `SecretEnvScrubSite` 留痕(现场按**腿**命名);git 腿再叠 git 自己的两件({@link gitSubprocessEnv})。射程 = **每一条**宿主腿:
|
|
14
|
+
* src 里每一只值引 `node:child_process` 的文件都是登记腿、其中每一次起子进程都显式交组合出的 env(普查门在 `test/secret-env-scrub-host-legs.test.ts`)。
|
|
15
|
+
* 腿的继承词是调用点的事实,按「子进程里跑的是谁选的代码」定:模型命令(host shell,交任务自己的词)/ 仓里的钩子与凭据助手(三条 git 腿)/ 执行仓代码的
|
|
16
|
+
* 语言服务器 / 配方脚本 ⇒ 形状剥 `"scrub"`;请求带来的 settings hooks 命令 ⇒ 严格名单(引擎的 `[names]` 臂);部署方装在宿主上的 CLI(`docker` / `adb`,命令本身
|
|
17
|
+
* 在容器 / 设备里跑、那边的 env 由 `-e` 显式给)⇒ `"all"` —— 只扣本服务自有凭据,CLI 与它的凭据助手 / 插件要读的生态变量照交(形状剥会剥
|
|
18
|
+
* `ADB_VENDOR_KEYS` / `AWS_SECRET_ACCESS_KEY` 一类,实测)。三个继承词只在引擎一处判:
|
|
14
19
|
* · `"all"` = 整份环境 **减** 本进程声明的自有凭据名;`"scrub"`(缺省)= 形状剥 ∪ 该名单;`[names]` = 严格白名单,点到名单中的名 ⇒ 引擎响亮拒
|
|
15
20
|
* (`inheritEnvPolicyProblem`:构造时与 spawn 时两格都拒,拒句带名;本仓不自判 withheld、不留第二读法)。
|
|
16
21
|
* · 名单 = 配置目录(`config-catalog.ts`)全部 `type:"secret"` 名 ∪ 名单型旋钮名(`env-name-allowlist-knobs.ts`,其原文可能夹着被错展开的令牌)∪ **生效配置
|
|
@@ -23,17 +28,21 @@
|
|
|
23
28
|
* 抛同路)。能力词 `inherit_env.own_credentials_withheld` 在本进程的事实 =
|
|
24
29
|
* {@link ownCredentialsWithheld}:最近一次成功声明的集合 ⊇ 当前生效配置应声明的集合(活配置经 {@link bindOwnCredentialsConfig} 在 `loadConfig()` 之后绑一次;
|
|
25
30
|
* 未绑 / 未声明 ⇒ false,事实读不到应声明集合就不答 true)。
|
|
26
|
-
* ·
|
|
27
|
-
*
|
|
28
|
-
* (
|
|
31
|
+
* · **值臂**(core 7.52.0 #1754):声明是一张表 `{ names, values }`。`values` = 本进程持有凭据的变量此刻的值(目录 secret 名 ∪ 模型条目 `apiKeyEnv`,{@link serverCredentialValueSources}),
|
|
32
|
+
* 逐个交给引擎的同一只判;引擎判「不是凭据值」的那一个不扣、名照扣、不拒启、点名一次。能力词 `inherit_env.own_credential_values_withheld` 的事实 = {@link ownCredentialValuesWithheld}(位真 ⇔ 值臂真扣)。
|
|
33
|
+
* · 调用方显式传入的 env(`cfg.env` / 单次命令的 `env` / 沙箱的点名放回)不是继承,照旧原样叠在组合结果之上 —— 那是适配器自己的凭据通道。
|
|
34
|
+
* 各腿只调 {@link hostSubprocessEnv} / {@link gitSubprocessEnv} —— 不许再有内联拼装、不许再有 `{ ...process.env }`、不许本仓再判任何一个继承词、不许起子进程不交 env
|
|
35
|
+
* (类级门与普查门在 `test/secret-env-scrub-host-legs.test.ts`)。
|
|
29
36
|
*
|
|
30
37
|
* ## 点名放行(S-699 / S-713,一只谓词各处用)
|
|
31
|
-
* {@link allowlistedEnv} 是本仓「一张名单点到哪些 env 键,就把它们从源 env
|
|
32
|
-
*
|
|
38
|
+
* {@link allowlistedEnv} 是本仓「一张名单点到哪些 env 键,就把它们从源 env **原样**交给沙箱」的唯一实现。调用:沙箱带外 env 的 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` /
|
|
39
|
+
* `K8S_SANDBOX_ENV`(`config.ts`)与名单解析器的「在不在」判。宿主子进程的剥后放回(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)不经它 —— 那是引擎的严格名单臂(上一段)。
|
|
33
40
|
* host shell 的 `inheritEnv:[names]`(hermetic 形)7.129.0 起由引擎读(与 core `NodeExecutionEnv` 同名选项同义,点到自有凭据名即拒),不经本谓词。
|
|
34
41
|
* 名单型旋钮的**名单**另由 `config.ts` 的同一只解析器 `parseEnvNameAllowlist` 判形(形错拒启 / 源里没有的名按序号 warn),本函数只管取值。
|
|
35
42
|
* 「点名」在各处同义:点到的键原样带值(空串值照带),密钥形也照过(点名 = 部署方显式承担);源里没有的名不产生条目。
|
|
36
|
-
*
|
|
43
|
+
* 剥后放回形(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)的放回本身也经引擎:{@link composeHostSubprocessEnv} 把名单交引擎的严格名单臂(`inheritedHostEnv([names])`)叠在
|
|
44
|
+
* 继承结果之上 ⇒ 点到本服务自有凭据名 = 引擎抛(spawn 时;起服时解析器 `parseEnvNameAllowlist` 按表列 `channel` 已用同一只判 `inheritEnvPolicyProblem` 拒过),
|
|
45
|
+
* 点到的变量值是本服务凭据的副本 = 引擎扣下。本仓不另判。
|
|
37
46
|
*/
|
|
38
47
|
import { type InheritEnvPolicy, type SecretEnvFinding } from "@sema-agent/core";
|
|
39
48
|
import type { ServiceConfig } from "./config-types.js";
|
|
@@ -48,27 +57,44 @@ export type ModelApiKeyEnvTable = Readonly<Record<string, string>>;
|
|
|
48
57
|
*/
|
|
49
58
|
export declare function serverWithheldEnvNames(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
50
59
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
* 本进程**持有凭据值**的变量名(值臂的来源;core 7.52.0 #1754):目录 secret 名 ∪ 模型条目 `apiKeyEnv` 的值 —— 名单与值臂两臂同一张表派生,只少名单型旋钮名(它们的值是
|
|
61
|
+
* 一张变量名单,不是凭据)。纯函数,排序只为读数稳定。
|
|
62
|
+
*/
|
|
63
|
+
export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* 🔴 **唯一声明点**:把本进程自有凭据向引擎声明(core 7.52.0 `declareWithheldEnv({ names, values })`;重声明即整表替换,从下一次 spawn 起生效)。`modelApiKeyEnv` = 这一次要并进的
|
|
66
|
+
* 模型条目表(起服第一步 = 空表;换代 = 候选表)。两臂都只随**落地**的世代增长(落地过的名 / 值来源不退出:本进程 env 是 boot 期输入;没落地的候选不留痕 ——
|
|
67
|
+
* 换装拒 ⇒ {@link withdrawServerWithheldEnv})。
|
|
68
|
+
* · **名臂**:此前声明过的 ∪ 这张表的 {@link serverWithheldEnvNames}。引擎对坏形名 / 运行时回填名(`PATH` / `HOME` …)响亮抛且声明保持原状 —— 本函数不接住:
|
|
69
|
+
* 起服那次抛 = 拒启;换代那次抛 = 候选整拒(`applyEffective` 抛,与 stage 抛同路)。第一次交表带着名臂与**此前已收下的值**(引擎收过、env 不变 ⇒ 同值照收),
|
|
70
|
+
* 所以这一次抛只可能是名的问题。
|
|
71
|
+
* · **值臂**:{@link serverCredentialValueSources} 的每一个变量此刻在 env 里的值,逐个交给引擎**同一只**判(不另写「什么算凭据值」的第二份判据):引擎收下 ⇒ 记进值臂;
|
|
72
|
+
* 引擎拒(不是凭据值:空白 / 占位词 / 一个字符重复 / 不足 8 字符;或它是每个子进程都会拿到的变量的值)⇒ 这一个值不扣、名照扣、**不拒启**(与引擎自己的收值口
|
|
73
|
+
* `holdOwnCredentialValues` 同一姿势:不收 + 说一次),`console.warn` 点名那个变量一次(引擎的拒句从不回显值;本句也不),能力位 `inherit_env.own_credential_values_withheld`
|
|
74
|
+
* 因此答 false(位真 ⇔ 值臂真扣)。引擎拒一个值时声明保持上一份,所以逐个交表的中间态都是上一份的超集。
|
|
75
|
+
* 返回声明的名单(只名、不值)。
|
|
55
76
|
*/
|
|
56
77
|
export declare function declareServerWithheldEnv(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
57
78
|
/**
|
|
58
|
-
* 席位拒 ⇒ 撤回最近一次声明(
|
|
59
|
-
*
|
|
79
|
+
* 席位拒 ⇒ 撤回最近一次声明(两臂一起回到它之前的那一份):`applyEffective` 在 Runner 换装抛时调,紧跟着那一次 {@link declareServerWithheldEnv} —— 没落地的候选不留痕。
|
|
80
|
+
* 退回去的那一份是引擎之前接受过的(名确定、值来源的值随 env 不变)⇒ 不抛;连撤两次是同一个结果(幂等)。
|
|
60
81
|
*/
|
|
61
82
|
export declare function withdrawServerWithheldEnv(): void;
|
|
62
83
|
/** 把活配置交给能力位读(stage-01 / run-local 在 `loadConfig()` 之后各一次;读的是 `config.modelApiKeyEnv` 这个**属性**,commit 整表重赋值之后看到的就是新表)。 */
|
|
63
84
|
export declare function bindOwnCredentialsConfig(config: Pick<ServiceConfig, "modelApiKeyEnv">): void;
|
|
64
85
|
/**
|
|
65
|
-
* 能力词 `inherit_env.own_credentials_withheld`
|
|
86
|
+
* 能力词 `inherit_env.own_credentials_withheld` 在本进程的事实:最近一次成功声明的名单 ⊇ 当前生效配置应声明的名单。是一次比较,不是「声明过」的旗 ——
|
|
66
87
|
* 活表若被谁绕过声明写了新名,这里立刻 false(位不替流程担保)。没绑活配置 / 没声明过 ⇒ false(fail-closed)。
|
|
67
88
|
*/
|
|
68
89
|
export declare function ownCredentialsWithheld(): boolean;
|
|
69
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* 能力词 `inherit_env.own_credential_values_withheld`(core 7.52.0)在本进程的事实:当前生效配置的每一个凭据值来源({@link serverCredentialValueSources}),只要它此刻在 env 里
|
|
92
|
+
* 有值,那个值都已交进值臂、被引擎收下。位真 ⇔ 值臂真扣:一个值被引擎判「扣不了」⇒ false;env 里没有值的来源没有东西可扣,不影响事实。没绑活配置 / 没声明过 ⇒ false。
|
|
93
|
+
*/
|
|
94
|
+
export declare function ownCredentialValuesWithheld(): boolean;
|
|
95
|
+
/** 测试缝:撤回声明(引擎侧重声明为空表)、忘掉两份声明镜像、点名记账与活配置;生产代码从不调它。 */
|
|
70
96
|
export declare function __resetServerWithheldEnvForTest(): void;
|
|
71
|
-
/** 一条宿主腿要什么:现场(留痕)、继承策略(缺省 `"scrub"
|
|
97
|
+
/** 一条宿主腿要什么:现场(留痕)、继承策略(缺省 `"scrub"`;按头注「腿的继承词」一条由调用点定)、部署方点名放回的键。 */
|
|
72
98
|
export interface HostSubprocessEnvRequest {
|
|
73
99
|
readonly site: SecretEnvScrubSite;
|
|
74
100
|
readonly policy?: InheritEnvPolicy | undefined;
|
|
@@ -78,8 +104,8 @@ export interface HostSubprocessEnvRequest {
|
|
|
78
104
|
* 🔴 **宿主子进程 env 的唯一组合**:{@link composeHostSubprocessEnv} + 留痕。**没有按原样交给子进程**的名字(剥掉的)经 `site` 留痕;点名放回的键取回源值,
|
|
79
105
|
* 不是剥除,不计数、不披露。源 env 恒为本进程 env(引擎只读 `process.env`,本仓没有第二个源)。
|
|
80
106
|
*
|
|
81
|
-
*
|
|
82
|
-
* 本地语言服务器、bake-runner
|
|
107
|
+
* 九条宿主腿都只经它(直接或经 {@link gitSubprocessEnv}):host shell(三个继承词原样交给 `policy`)、project-memory / center-plugins / leader 三条 git 腿、
|
|
108
|
+
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(严格名单)。`allow` 只在**有部署级出路**的现场非空(今天 = `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`,
|
|
83
109
|
* 它们的现场在 `SECRET_ENV_SCRUB_SITES` 里登记了 `remedy`,披露行因此是 warn + 放行名单 + 出路句);其余现场传 `[]`。
|
|
84
110
|
* 返回 `NodeJS.ProcessEnv` 形(值可能是 `undefined` 的键本来就不会进子进程);调用方显式要交的键(`LC_ALL`、`cfg.env` …)叠在它之上。
|
|
85
111
|
* 调用时机 = 每次要交 env 时现算一次(`process.env` 在本仓是 boot 期输入,各次同值;计数因此是「每组合一次」)。
|
|
@@ -7,18 +7,42 @@ const NO_MODEL_ENTRIES = Object.freeze({});
|
|
|
7
7
|
export function serverWithheldEnvNames(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
8
8
|
return [...new Set([...CATALOG_SECRET_ENV_NAMES, ...ENV_NAME_ALLOWLIST_KNOB_NAMES, ...Object.values(modelApiKeyEnv)])].sort();
|
|
9
9
|
}
|
|
10
|
+
export function serverCredentialValueSources(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
11
|
+
return [...new Set([...CATALOG_SECRET_ENV_NAMES, ...Object.values(modelApiKeyEnv)])].sort();
|
|
12
|
+
}
|
|
10
13
|
let declared;
|
|
11
14
|
let beforeLast;
|
|
15
|
+
const valueRefusalAnnounced = new Set();
|
|
12
16
|
let liveConfig;
|
|
17
|
+
const envValueOf = (name) => process.env[name];
|
|
18
|
+
const valuesOf = (sources) => [...sources].map(envValueOf).filter((v) => typeof v === "string" && v !== "");
|
|
13
19
|
export function declareServerWithheldEnv(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
14
|
-
const names = [...new Set([...(declared ?? []), ...serverWithheldEnvNames(modelApiKeyEnv)])].sort();
|
|
15
|
-
|
|
20
|
+
const names = [...new Set([...(declared?.names ?? []), ...serverWithheldEnvNames(modelApiKeyEnv)])].sort();
|
|
21
|
+
const kept = [...(declared?.valueSources ?? [])];
|
|
22
|
+
declareWithheldEnv({ names, values: valuesOf(kept) });
|
|
23
|
+
const valueSources = new Set(kept);
|
|
24
|
+
for (const source of serverCredentialValueSources(modelApiKeyEnv)) {
|
|
25
|
+
if (valueSources.has(source) || valuesOf([source]).length === 0)
|
|
26
|
+
continue;
|
|
27
|
+
try {
|
|
28
|
+
declareWithheldEnv({ names, values: valuesOf([...valueSources, source]) });
|
|
29
|
+
valueSources.add(source);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
if (!valueRefusalAnnounced.has(source)) {
|
|
33
|
+
valueRefusalAnnounced.add(source);
|
|
34
|
+
console.warn(`[sema-server] own credential ${source}: its value is not withheld by value from spawned children — the engine refused it (${e instanceof Error ? e.message : String(e)}); ` +
|
|
35
|
+
"the variable name is still withheld under every inheritEnv policy, but a variable holding the same string under another name is not, and GET /v1/capabilities answers inherit_env.own_credential_values_withheld: false. " +
|
|
36
|
+
"Give it a real credential value (at least 8 characters, not a placeholder) to withhold copies of it too.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
16
40
|
beforeLast = declared;
|
|
17
|
-
declared = new Set(names);
|
|
41
|
+
declared = { names: new Set(names), valueSources };
|
|
18
42
|
return names;
|
|
19
43
|
}
|
|
20
44
|
export function withdrawServerWithheldEnv() {
|
|
21
|
-
declareWithheldEnv([...(beforeLast ?? [])]);
|
|
45
|
+
declareWithheldEnv({ names: [...(beforeLast?.names ?? [])], values: valuesOf(beforeLast?.valueSources ?? []) });
|
|
22
46
|
declared = beforeLast;
|
|
23
47
|
}
|
|
24
48
|
export function bindOwnCredentialsConfig(config) {
|
|
@@ -27,23 +51,40 @@ export function bindOwnCredentialsConfig(config) {
|
|
|
27
51
|
export function ownCredentialsWithheld() {
|
|
28
52
|
if (declared === undefined || liveConfig === undefined)
|
|
29
53
|
return false;
|
|
30
|
-
const own = declared;
|
|
54
|
+
const own = declared.names;
|
|
31
55
|
return serverWithheldEnvNames(liveConfig.modelApiKeyEnv).every((name) => own.has(name));
|
|
32
56
|
}
|
|
57
|
+
export function ownCredentialValuesWithheld() {
|
|
58
|
+
if (declared === undefined || liveConfig === undefined)
|
|
59
|
+
return false;
|
|
60
|
+
const own = declared.valueSources;
|
|
61
|
+
return serverCredentialValueSources(liveConfig.modelApiKeyEnv).every((source) => own.has(source) || valuesOf([source]).length === 0);
|
|
62
|
+
}
|
|
33
63
|
export function __resetServerWithheldEnvForTest() {
|
|
34
|
-
declareWithheldEnv([]);
|
|
64
|
+
declareWithheldEnv({ names: [], values: [] });
|
|
35
65
|
declared = undefined;
|
|
36
66
|
beforeLast = undefined;
|
|
37
67
|
liveConfig = undefined;
|
|
68
|
+
valueRefusalAnnounced.clear();
|
|
38
69
|
}
|
|
39
70
|
export function hostSubprocessEnv(o) {
|
|
40
71
|
const findings = [];
|
|
41
72
|
const composed = composeHostSubprocessEnv(o, findings);
|
|
42
|
-
|
|
73
|
+
const disclosed = [];
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
for (const f of findings) {
|
|
76
|
+
const k = `${f.key}=${f.kind}`;
|
|
77
|
+
if (composed[f.key] === envValueOf(f.key) || seen.has(k))
|
|
78
|
+
continue;
|
|
79
|
+
seen.add(k);
|
|
80
|
+
disclosed.push(f);
|
|
81
|
+
}
|
|
82
|
+
recordSecretEnvScrub(o.site, disclosed, o.allow);
|
|
43
83
|
return composed;
|
|
44
84
|
}
|
|
45
85
|
export function composeHostSubprocessEnv(o, findings = []) {
|
|
46
|
-
|
|
86
|
+
const putBack = o.allow.length > 0 ? inheritedHostEnv([...o.allow], findings) : {};
|
|
87
|
+
return { ...inheritedHostEnv(o.policy ?? "scrub", findings), ...putBack };
|
|
47
88
|
}
|
|
48
89
|
export function gitSubprocessEnv(o) {
|
|
49
90
|
return {
|
|
@@ -61,10 +61,19 @@ export declare class SessionIncarnationMissingError extends Error {
|
|
|
61
61
|
constructor(sessionId: string);
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* 空化身那一格怎么读 —— {@link incarnationFromCell} 的参数(一只解码,两种**显式**读法;不是两只函数):
|
|
65
|
+
* · `refuse`(缺省,读口):一位活住客必须有令牌 ⇒ 空 / 非串 = {@link SessionIncarnationMissingError}(响亮拒,不当通配);
|
|
66
|
+
* · `absent`:调用方问的是「这一格里有没有一只令牌」,空 / 非串的答案是**没有**(`undefined`)—— 会话树删除时的空间键
|
|
67
|
+
* (没有令牌 = 没有空间,不回收缓存卷)与整段换装的沿用判据(没有令牌 = 铸一只新的)。
|
|
66
68
|
*/
|
|
67
|
-
export
|
|
69
|
+
export type EmptyIncarnationReading = "refuse" | "absent";
|
|
70
|
+
/**
|
|
71
|
+
* 行上化身那一格的**唯一解码**(三只店的 `sessionFaceOf`、SQL 两店 wake 带出的会话元数据、留存删除的空间键、整段换装的沿用判据都经它 ——
|
|
72
|
+
* 「是不是一只令牌」只判这一处)。非空字符串 ⇒ 那只令牌;否则按 `empty`({@link EmptyIncarnationReading}):缺省 `refuse` ⇒
|
|
73
|
+
* {@link SessionIncarnationMissingError},`absent` ⇒ `undefined`。
|
|
74
|
+
*/
|
|
75
|
+
export declare function incarnationFromCell(sessionId: string, cell: unknown, empty?: "refuse"): string;
|
|
76
|
+
export declare function incarnationFromCell(sessionId: string, cell: unknown, empty: "absent"): string | undefined;
|
|
68
77
|
/** 店把一行原始列读成 {@link SessionFace} 的唯一口(三只店共用;化身那一格经 {@link incarnationFromCell})。 */
|
|
69
78
|
export declare function sessionFaceFromRow(sessionId: string, owner: string | null, incarnation: unknown): SessionFace;
|
|
70
79
|
/** 按 id 读住客(core `SessionStore.incarnationOf` 的形):`undefined` = 没有这只会话;读抛 = 读故障,不是答案。 */
|
|
@@ -81,9 +90,10 @@ export declare function incarnationReadOf(store: Pick<SessionStore, "incarnation
|
|
|
81
90
|
export declare function sessionFaceReadOf(store: object | undefined): SessionFaceReader | undefined;
|
|
82
91
|
/**
|
|
83
92
|
* session-sync **整段换装**(replace)时化身的去留 —— 三只店共用的一句判据:换装删掉再插回的是**同一位住客**的那一行
|
|
84
|
-
* (删除带属主门)
|
|
93
|
+
* (删除带属主门),所以行在、属主相同、那一格里有一只令牌(唯一解码 {@link incarnationFromCell} 的 `absent` 读法)⇒ 沿用;
|
|
94
|
+
* 行不在(这是一次新建)或还是空的存量形 ⇒ 铸一只新的。
|
|
85
95
|
*/
|
|
86
|
-
export declare function carriedIncarnation(prior: {
|
|
96
|
+
export declare function carriedIncarnation(sessionId: string, prior: {
|
|
87
97
|
owner?: unknown;
|
|
88
98
|
incarnation?: unknown;
|
|
89
99
|
} | undefined, owner: string | null): string;
|
|
@@ -24,10 +24,19 @@ export class SessionIncarnationMissingError extends Error {
|
|
|
24
24
|
this.name = "SessionIncarnationMissingError";
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
export function incarnationFromCell(sessionId, cell) {
|
|
28
|
-
if (typeof cell
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export function incarnationFromCell(sessionId, cell, empty = "refuse") {
|
|
28
|
+
if (typeof cell === "string" && cell !== "")
|
|
29
|
+
return cell;
|
|
30
|
+
switch (empty) {
|
|
31
|
+
case "refuse":
|
|
32
|
+
throw new SessionIncarnationMissingError(sessionId);
|
|
33
|
+
case "absent":
|
|
34
|
+
return undefined;
|
|
35
|
+
default: {
|
|
36
|
+
const unknownReading = empty;
|
|
37
|
+
throw new Error(`incarnationFromCell: no reading ${JSON.stringify(unknownReading)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
31
40
|
}
|
|
32
41
|
export function sessionFaceFromRow(sessionId, owner, incarnation) {
|
|
33
42
|
return { owner, incarnation: incarnationFromCell(sessionId, incarnation) };
|
|
@@ -40,11 +49,9 @@ export function sessionFaceReadOf(store) {
|
|
|
40
49
|
const read = store?.sessionFaceOf;
|
|
41
50
|
return typeof read === "function" ? read.bind(store) : undefined;
|
|
42
51
|
}
|
|
43
|
-
export function carriedIncarnation(prior, owner) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
return mintSessionIncarnation();
|
|
52
|
+
export function carriedIncarnation(sessionId, prior, owner) {
|
|
53
|
+
const held = prior !== undefined && (prior.owner ?? null) === owner ? incarnationFromCell(sessionId, prior.incarnation, "absent") : undefined;
|
|
54
|
+
return held ?? mintSessionIncarnation();
|
|
48
55
|
}
|
|
49
56
|
export const SESSION_META_INCARNATION_COLUMN = {
|
|
50
57
|
name: "incarnation",
|
package/dist/session-titler.js
CHANGED
|
@@ -10,12 +10,22 @@ const FAIL_CAUSE_KEY_CHARS = 80;
|
|
|
10
10
|
const SEEN_CAP = 10_000;
|
|
11
11
|
function failOpenTagFor(code) {
|
|
12
12
|
switch (code) {
|
|
13
|
+
case "model_unresolved":
|
|
14
|
+
return "server.session-titler.model-unresolved";
|
|
15
|
+
case "route_refused":
|
|
16
|
+
return "server.session-titler.route-refused";
|
|
17
|
+
case "credential_custody_failed":
|
|
18
|
+
return "server.session-titler.credential-custody-failed";
|
|
13
19
|
case "redirect_refused":
|
|
14
20
|
return "server.session-titler.redirect-refused";
|
|
21
|
+
case "bad_response":
|
|
22
|
+
return "server.session-titler.bad-response";
|
|
15
23
|
case "no_content":
|
|
16
|
-
|
|
17
|
-
case
|
|
24
|
+
return "server.session-titler.no-content";
|
|
25
|
+
case "unreachable":
|
|
18
26
|
return "server.session-titler.gateway-unreachable";
|
|
27
|
+
case undefined:
|
|
28
|
+
return "server.session-titler.unclassified";
|
|
19
29
|
}
|
|
20
30
|
}
|
|
21
31
|
export function sanitizeTitle(raw) {
|
package/dist/spec-fields.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* body WITHOUT the HTTP-layer 400 validation, so a malformed value must fall back to a safe default, never throw.
|
|
5
5
|
*/
|
|
6
6
|
import { type AgentDefinition, type TaskSpec } from "@sema-agent/core";
|
|
7
|
+
import { type LimitFacts, type LimitMeasure } from "./limit-refusal.js";
|
|
7
8
|
/**
|
|
8
9
|
* E12 — normalize `body.suggestNextPrompts` → `TaskSpec.suggestNextPrompts`. `true` = on with core defaults
|
|
9
10
|
* (count 3, cheap role); an object tunes `count` (clamped 1..10) / `role` (a free string — core validates it against
|
|
@@ -51,35 +52,52 @@ export declare const MAX_TASK_AGENTS = 32;
|
|
|
51
52
|
* `ruleEntryListShapeOk`):引擎直接 `for (const entry of list)` —— 一个裸字符串 `"Bash"` 会被逐字符迭代成 `B` / `a` / `s` / `h` 四条
|
|
52
53
|
* (deny 侧 = 意图静默落空,安全轴上的放宽方向)。条目的**文法与名字**是引擎的读法:允侧读不出的条目(`*` / 写路径 / 域名 / 退役名 …)引擎在开跑时发
|
|
53
54
|
* `permission.allowed_tool_not_granted`(受众 = 用户);拒侧读成空的条目(文法或名字层)在 `resolveSpec` 拒(提交 400 / 续跑 409,`rule-entry-list.ts` 的
|
|
54
|
-
* `
|
|
55
|
-
*
|
|
56
|
-
*
|
|
55
|
+
* `inertToolListEntryOf`,判据 = 引擎的条目读法;同一只函数也拒请求的工具名选择器清单里的近似名 / 退役名)。
|
|
56
|
+
* **条数帽**是全部工具名清单的同一只判(`rule-entry-list.ts` 的 `skillToolListsOverCap`;句子与机读位 `field` 点体内坐标 `skills[1].allowedTools` /
|
|
57
|
+
* `agents[0].skills[2].disallowedTools`):顶层 `validateUserSkills` 与子代 {@link validateTaskAgents} 两道门都逐条先形、后帽(与 7.131.0 的拒点与判序同);
|
|
58
|
+
* 拒点 `resolveSpec` 两腿同经的 `inertToolListEntryOf` 先过同一只判;无长度帽([ref])。
|
|
57
59
|
*/
|
|
58
60
|
export declare const skillToolListOk: (v: unknown) => v is readonly string[];
|
|
59
61
|
/**
|
|
60
|
-
*
|
|
61
|
-
* (`
|
|
62
|
-
*
|
|
63
|
-
* 它是引擎判官的判词,随之退役)。
|
|
62
|
+
* 一条子代定义的裁决:`message` = 一句可 400 的英文(不带下标,调用方加);撞帽时另带 `limit`(`rel` = 相对这条定义的坐标 `allowTools[3]` / `name`),
|
|
63
|
+
* 句子是撞帽的头一句(`agent.allowTools[3] has 129 characters — at most 128 are accepted`:撞了哪一条帽、送来多少、帽多少),
|
|
64
|
+
* {@link validateTaskAgents} 把它铸成体内坐标的机读位(`field: "agents[0].allowTools[3]"`)。
|
|
64
65
|
*/
|
|
65
|
-
export
|
|
66
|
+
export interface TaskAgentIssue {
|
|
67
|
+
readonly message: string;
|
|
68
|
+
readonly limit?: {
|
|
69
|
+
readonly rel: string;
|
|
70
|
+
readonly measure: LimitMeasure;
|
|
71
|
+
readonly count: number;
|
|
72
|
+
readonly cap: number;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** 提交体 `agents` 的拒:句子(带下标)+ 撞帽时的机读位(`field` / `reason` / `count` / `cap`,`limit-refusal.ts` 一处铸)。 */
|
|
76
|
+
export interface TaskAgentsRefusal {
|
|
77
|
+
readonly message: string;
|
|
78
|
+
readonly facts?: LimitFacts;
|
|
79
|
+
}
|
|
66
80
|
/** [ref] 单条 agent 定义的形状裁决:null=合法;否则一句可 400 的英文错误(不带索引,调用方自己加)。
|
|
67
81
|
* 白名单外键 fail-loud —— 与 resilience/limits 的「容忍未知键」相反:agents 是能力定义,静默丢字段 =
|
|
68
82
|
* 定义被悄悄削(一个丢掉的 isolation 就是放宽),比 400 更危险,故整条拒。`allowTools` / `denyTools` 本门只判形与条数(`toolList`),
|
|
69
83
|
* **名字归引擎一只判**(core 7.49.0 #1623 / #1433 第二步起与根 run 的 `allowTools` / `excludeTools` 是同一只选择器,开跑前,三来源同判):core 7.51.0 #1736 起
|
|
70
|
-
* 没有任何工具答应的名字(无人挂的名、大小写孪生 / 退役名 `bash` / `BASH`)
|
|
71
|
-
*
|
|
84
|
+
* 没有任何工具答应的名字(无人挂的名、大小写孪生 / 退役名 `bash` / `BASH`)**引擎不拒 run** —— 条目原样保留、经 onError(config)告知一次(带该写的拼法);
|
|
85
|
+
* 其中**写错现名的**(`denyTools:["BASH"]` 想禁的照用 = 静默放行;`allowTools:["BASH"]` 想放的什么也没放 = 静默收窄)由 `resolveSpec` 前置拒:**限制侧**(`denyTools`)四类全拒(近似名 · 退役名 · 文法读不出 · `*`),
|
|
86
|
+
* **放行侧**(`allowTools`)只拒近似名 —— 放行侧的内容形 / 没有后继的退役名 / 文法读不出照 7.130.0 受理、交引擎既有通告(有后继的退役名〔`bash` · `Task`〕与文法读不出的放行条目引擎在子代 prepare 整 run 拒 `config.agent.unknown_tool`,根 `allowTools` 拒 `config.tool_face_control`,同步腿两码同为 400〔`runs.ts` `PREPARE_REFUSAL_HTTP_STATUS`〕;`rule-entry-list.ts` `inertToolListEntryOf`:近似名提交 400 `config.agent.unknown_tool` 点名现名、续跑按方向分流;
|
|
87
|
+
* 判官仍是引擎的条目读法,不是第二个判官);
|
|
88
|
+
* **真未知名**(没有近似也没有退役对应:懒挂载的合法形)照旧只告运维、不拒。
|
|
89
|
+
* 任何工具名都不可能有的拼法(`grammar`)与非字符串条目引擎照旧整 run 拒 `config.agent.unknown_tool` —— 主车道的委派工具带 extraTools 工厂,本仓在同一处
|
|
72
90
|
* 声明它的名册(core 7.46.0 `extraToolNames`;工厂与名册读同一批工具对象,`capabilities/builtin-tools.ts` `createSubagentExtraTools`),引擎因此列得出子代工具池。
|
|
73
91
|
* 两张表管子代可挂载的**全部**工具:委派池、hands 内建、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载 ——
|
|
74
92
|
* `denyTools:["Monitor"]` ⇒ 子代没有 `Monitor`;`allowTools` 只点了调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具。`denyTools` 的 `Tool(content)` ⇒ 整只工具。
|
|
75
|
-
*
|
|
93
|
+
* 本门不做名字对照(拒侧的那一判在 `resolveSpec`,见上;本门只判形与条数)。
|
|
76
94
|
* ⚠️ 键判断必须 Object.hasOwn:`in` 走原型链,JSON 里的 "toString"/"constructor"/"__proto__" 键会被
|
|
77
95
|
* 误判为白名单字段,随后 SHAPES[k].ok 不是函数 ⇒ TypeError(fresh 500 / resume 砖;cross-review 复现)。 */
|
|
78
|
-
export declare function taskAgentIssue(item: unknown):
|
|
96
|
+
export declare function taskAgentIssue(item: unknown): TaskAgentIssue | null;
|
|
79
97
|
/** [ref] HTTP 门(fresh submit,server.ts prepareSpec):body.agents 在场必须是非空数组、每条过
|
|
80
98
|
* {@link taskAgentIssue}、name 数组内唯一、条数 ≤ {@link MAX_TASK_AGENTS}。返回 400 错误串或 null。
|
|
81
99
|
* RESUME 重放不过这道门 —— {@link taskAgentsFromBody} defensive(按条 DROP+warn,不砖 resume)。 */
|
|
82
|
-
export declare function validateTaskAgents(v: unknown):
|
|
100
|
+
export declare function validateTaskAgents(v: unknown): TaskAgentsRefusal | null;
|
|
83
101
|
/** [ref] body.agents → `TaskSpec.agents`(resolveSpec 的唯一装配点;main.ts 只 spread)。两层门:
|
|
84
102
|
* - 🔒 TOB 租户门:多租户(requirePrincipal===true)暂不放行 —— [ref] 原文「TOB 面按租户 caps 门」,
|
|
85
103
|
* 当前没有 per-tenant caps 面可挂,先 fail-closed 关多租(warn `task_agents_ignored`,capabilities
|
|
@@ -131,8 +149,6 @@ export declare function retainBackgroundProcessesFromBody(raw: unknown, requireP
|
|
|
131
149
|
* DEFENSIVE(resume 重放不过 HTTP 400 门):非数组 ⇒ undefined;项非非空字符串 ⇒ 逐项 DROP;空数组 ⇒
|
|
132
150
|
* undefined(不挂键=core 缺省,byte-compat)。HTTP 门另有 fail-loud 400(提交面邻居姿势)。 */
|
|
133
151
|
export declare function toolNameListFromBody(raw: unknown): string[] | undefined;
|
|
134
|
-
/** 引擎从哪一版起认 `TaskSpec.allowTools`(core 7.49.0 #1623,[ref])。 */
|
|
135
|
-
export declare const ALLOW_TOOLS_MIN_ENGINE = "7.49.0";
|
|
136
152
|
/**
|
|
137
153
|
* S-1448(core 7.49.0 #1623 / [ref])—— body.allowTools → `TaskSpec.allowTools`:引擎的**可用性选择器**,原样透传。
|
|
138
154
|
*
|
|
@@ -141,10 +157,10 @@ export declare const ALLOW_TOOLS_MIN_ENGINE = "7.49.0";
|
|
|
141
157
|
* `config.tool_face_control`)、不与 allow 层 `allowedTools`(免问)混词。受理门只判 wire 形(与 `excludeTools` / `deferTools` 同一只门)。
|
|
142
158
|
* 续跑重放同经此处:存量体逐字再投,坏形交引擎拒(收窄键丢了 = 放宽,所以不 DROP)。
|
|
143
159
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
160
|
+
* **没有引擎版本臂**:core 精确钉在认这个座(7.49.0 起)的版本之上,「装着的引擎不认该座」在生产里不可达;「core 至少是哪一版」是依赖下限门
|
|
161
|
+
* 的事实(`test/core-dependency-floor.test.ts` + `test/helpers/core-floor.ts`),不是本函数运行期的比对(一个事实一只源)。
|
|
146
162
|
*/
|
|
147
|
-
export declare function allowToolsSpecFragment(raw: unknown
|
|
163
|
+
export declare function allowToolsSpecFragment(raw: unknown): {
|
|
148
164
|
allowTools?: string[];
|
|
149
165
|
};
|
|
150
166
|
/** [ref]/[ref] — body.promptProfile → `TaskSpec.promptProfile`(core 1.328 R2 主件,CC LT(model_id)
|
package/dist/spec-fields.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isThinkingLevel } from "@sema-agent/core";
|
|
2
2
|
import { taskWallClockSec } from "./task-workflow.js";
|
|
3
3
|
import { isAbsoluteHostPath } from "@sema-agent/settings-schema";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { ruleEntryListShapeOk, skillToolListsOverCap } from "./rule-entry-list.js";
|
|
5
|
+
import { describeLimit, limitFacts, limitRefusal } from "./limit-refusal.js";
|
|
6
|
+
import { quotedCallerText } from "./caller-text-echo.js";
|
|
7
7
|
export function normalizeSuggestNextPrompts(v) {
|
|
8
8
|
if (v === true)
|
|
9
9
|
return true;
|
|
@@ -67,20 +67,19 @@ const boundedString = (max) => (v) => typeof v === "string" && v.length <= max;
|
|
|
67
67
|
const toolList = (v) => Array.isArray(v) &&
|
|
68
68
|
v.length <= MAX_AGENT_TOOLS &&
|
|
69
69
|
v.every((x) => typeof x === "string" && x.length > 0 && x.length <= MAX_AGENT_TOOL_NAME_CHARS);
|
|
70
|
+
const charLimit = (max) => (v) => typeof v === "string" && v.length > max ? { sub: "", measure: "characters", count: v.length, cap: max } : undefined;
|
|
71
|
+
const toolListLimit = (v) => {
|
|
72
|
+
if (!Array.isArray(v))
|
|
73
|
+
return undefined;
|
|
74
|
+
if (v.length > MAX_AGENT_TOOLS)
|
|
75
|
+
return { sub: "", measure: "entries", count: v.length, cap: MAX_AGENT_TOOLS };
|
|
76
|
+
if (!v.every((x) => typeof x === "string" && x.length > 0))
|
|
77
|
+
return undefined;
|
|
78
|
+
const j = v.findIndex((x) => x.length > MAX_AGENT_TOOL_NAME_CHARS);
|
|
79
|
+
return j < 0 ? undefined : { sub: `[${j}]`, measure: "characters", count: v[j].length, cap: MAX_AGENT_TOOL_NAME_CHARS };
|
|
80
|
+
};
|
|
70
81
|
const plainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
71
82
|
export const skillToolListOk = ruleEntryListShapeOk;
|
|
72
|
-
const SKILL_TOOL_LISTS = ["allowedTools", "disallowedTools"];
|
|
73
|
-
export function skillToolListRefusal(skill) {
|
|
74
|
-
for (const key of SKILL_TOOL_LISTS) {
|
|
75
|
-
const list = skill[key];
|
|
76
|
-
if (!ruleEntryListShapeOk(list))
|
|
77
|
-
continue;
|
|
78
|
-
const over = allowLayerEntryCountIssue(`skill.${key}`, list);
|
|
79
|
-
if (over !== undefined)
|
|
80
|
-
return over.message;
|
|
81
|
-
}
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
83
|
const skillFilesOk = (v) => Array.isArray(v) &&
|
|
85
84
|
v.every((f) => plainObject(f) && typeof f.path === "string" && f.path.length > 0 && typeof f.content === "string");
|
|
86
85
|
const SKILL_FIELD_SHAPES = {
|
|
@@ -132,11 +131,12 @@ const TASK_AGENT_FIELD_SHAPES = {
|
|
|
132
131
|
name: {
|
|
133
132
|
desc: `a non-empty string of at most ${MAX_AGENT_NAME_CHARS} characters`,
|
|
134
133
|
ok: (v) => typeof v === "string" && v.length > 0 && v.length <= MAX_AGENT_NAME_CHARS,
|
|
134
|
+
limit: charLimit(MAX_AGENT_NAME_CHARS),
|
|
135
135
|
},
|
|
136
|
-
whenToUse: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
137
|
-
whenToUseLean: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
138
|
-
allowTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList },
|
|
139
|
-
denyTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList },
|
|
136
|
+
whenToUse: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS), limit: charLimit(MAX_AGENT_TEXT_CHARS) },
|
|
137
|
+
whenToUseLean: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS), limit: charLimit(MAX_AGENT_TEXT_CHARS) },
|
|
138
|
+
allowTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList, limit: toolListLimit },
|
|
139
|
+
denyTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList, limit: toolListLimit },
|
|
140
140
|
skills: {
|
|
141
141
|
desc: "an array of { name, description, content } skill objects (optional allowedTools/disallowedTools/files/baseDir; allowedTools/disallowedTools are arrays of strings; files entries need { path, content }; baseDir must be an absolute path with no \"..\" segment) — no count cap; the engine renders the listing within its own byte budget",
|
|
142
142
|
ok: (v) => Array.isArray(v) && v.every(skillOk),
|
|
@@ -149,57 +149,64 @@ const TASK_AGENT_FIELD_SHAPES = {
|
|
|
149
149
|
(plainObject(v) && typeof v.id === "string" && v.id.length > 0),
|
|
150
150
|
},
|
|
151
151
|
thinking: { desc: "a thinking level (off/minimal/low/medium/high/xhigh/max)", ok: (v) => isThinkingLevel(v) },
|
|
152
|
-
systemPrompt: { desc: `a string of at most ${MAX_AGENT_SYSTEM_PROMPT_CHARS} characters`, ok: boundedString(MAX_AGENT_SYSTEM_PROMPT_CHARS) },
|
|
152
|
+
systemPrompt: { desc: `a string of at most ${MAX_AGENT_SYSTEM_PROMPT_CHARS} characters`, ok: boundedString(MAX_AGENT_SYSTEM_PROMPT_CHARS), limit: charLimit(MAX_AGENT_SYSTEM_PROMPT_CHARS) },
|
|
153
153
|
maxTurns: {
|
|
154
154
|
desc: `a positive integer of at most ${MAX_AGENT_MAX_TURNS}`,
|
|
155
155
|
ok: (v) => typeof v === "number" && Number.isInteger(v) && v >= 1 && v <= MAX_AGENT_MAX_TURNS,
|
|
156
156
|
},
|
|
157
157
|
memory: { desc: "a memory spec object ({ scopes?, writeScope?, enabled?, scopeContract? })", ok: memoryOk },
|
|
158
|
-
observer: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
159
|
-
observerMessage: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS) },
|
|
158
|
+
observer: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS), limit: charLimit(MAX_AGENT_TEXT_CHARS) },
|
|
159
|
+
observerMessage: { desc: `a string of at most ${MAX_AGENT_TEXT_CHARS} characters`, ok: boundedString(MAX_AGENT_TEXT_CHARS), limit: charLimit(MAX_AGENT_TEXT_CHARS) },
|
|
160
160
|
};
|
|
161
161
|
export function taskAgentIssue(item) {
|
|
162
162
|
if (item === null || typeof item !== "object" || Array.isArray(item))
|
|
163
|
-
return "each agent must be an object";
|
|
163
|
+
return { message: "each agent must be an object" };
|
|
164
164
|
const rec = item;
|
|
165
165
|
for (const k of Object.keys(rec)) {
|
|
166
166
|
if (k === "permissionMode")
|
|
167
|
-
return 'agent "permissionMode" is not accepted (deliberately excluded from the per-task agent contract)';
|
|
167
|
+
return { message: 'agent "permissionMode" is not accepted (deliberately excluded from the per-task agent contract)' };
|
|
168
168
|
if (!Object.hasOwn(TASK_AGENT_FIELD_SHAPES, k))
|
|
169
|
-
return `unknown agent key
|
|
169
|
+
return { message: `unknown agent key ${quotedCallerText(k)}` };
|
|
170
170
|
}
|
|
171
171
|
if (rec.name === undefined)
|
|
172
|
-
return "agent.name is required";
|
|
172
|
+
return { message: "agent.name is required" };
|
|
173
173
|
for (const [k, v] of Object.entries(rec)) {
|
|
174
174
|
if (v === undefined)
|
|
175
175
|
continue;
|
|
176
176
|
const shape = TASK_AGENT_FIELD_SHAPES[k];
|
|
177
|
-
if (
|
|
178
|
-
|
|
177
|
+
if (shape.ok(v))
|
|
178
|
+
continue;
|
|
179
|
+
const hit = shape.limit?.(v);
|
|
180
|
+
if (hit === undefined)
|
|
181
|
+
return { message: `agent.${k} must be ${shape.desc}` };
|
|
182
|
+
const rel = `${k}${hit.sub}`;
|
|
183
|
+
return { message: describeLimit(`agent.${rel}`, hit.measure, hit.count, hit.cap), limit: { rel, measure: hit.measure, count: hit.count, cap: hit.cap } };
|
|
179
184
|
}
|
|
180
185
|
return null;
|
|
181
186
|
}
|
|
182
187
|
export function validateTaskAgents(v) {
|
|
183
188
|
if (!Array.isArray(v) || v.length === 0)
|
|
184
|
-
return "agents must be a non-empty array of agent definitions";
|
|
189
|
+
return { message: "agents must be a non-empty array of agent definitions" };
|
|
185
190
|
if (v.length > MAX_TASK_AGENTS)
|
|
186
|
-
return
|
|
191
|
+
return limitRefusal("agents", "entries", v.length, MAX_TASK_AGENTS);
|
|
187
192
|
const names = new Set();
|
|
188
193
|
for (let i = 0; i < v.length; i++) {
|
|
189
194
|
const issue = taskAgentIssue(v[i]);
|
|
190
|
-
if (issue)
|
|
191
|
-
|
|
195
|
+
if (issue) {
|
|
196
|
+
const message = `agents[${i}]: ${issue.message}`;
|
|
197
|
+
return issue.limit === undefined ? { message } : { message, facts: limitFacts(`agents[${i}].${issue.limit.rel}`, issue.limit.measure, issue.limit.count, issue.limit.cap) };
|
|
198
|
+
}
|
|
192
199
|
const skills = v[i].skills;
|
|
193
200
|
if (Array.isArray(skills)) {
|
|
194
201
|
for (let j = 0; j < skills.length; j++) {
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
197
|
-
return
|
|
202
|
+
const over = skillToolListsOverCap(skills[j], `agents[${i}].skills[${j}]`, "agent");
|
|
203
|
+
if (over !== undefined)
|
|
204
|
+
return { message: over.message, ...(over.extra !== undefined ? { facts: over.extra } : {}) };
|
|
198
205
|
}
|
|
199
206
|
}
|
|
200
207
|
const name = v[i].name;
|
|
201
208
|
if (names.has(name))
|
|
202
|
-
return `agents[${i}]: duplicate agent name
|
|
209
|
+
return { message: `agents[${i}]: duplicate agent name ${quotedCallerText(name)}` };
|
|
203
210
|
names.add(name);
|
|
204
211
|
}
|
|
205
212
|
return null;
|
|
@@ -224,7 +231,7 @@ export function taskAgentsFromBody(raw, requirePrincipal, warn) {
|
|
|
224
231
|
}
|
|
225
232
|
const issue = taskAgentIssue(raw[i]);
|
|
226
233
|
if (issue) {
|
|
227
|
-
warn("task_agent_dropped", { index: i, reason: issue });
|
|
234
|
+
warn("task_agent_dropped", { index: i, reason: issue.message });
|
|
228
235
|
continue;
|
|
229
236
|
}
|
|
230
237
|
const rec = raw[i];
|
|
@@ -272,22 +279,8 @@ export function toolNameListFromBody(raw) {
|
|
|
272
279
|
const names = raw.filter((v) => typeof v === "string" && v.length > 0);
|
|
273
280
|
return names.length > 0 ? names : undefined;
|
|
274
281
|
}
|
|
275
|
-
export
|
|
276
|
-
|
|
277
|
-
const parse = (v) => v.split(/[-+]/, 1)[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
278
|
-
const [a, b] = [parse(version), parse(floor)];
|
|
279
|
-
for (let i = 0; i < 3; i++)
|
|
280
|
-
if ((a[i] ?? 0) !== (b[i] ?? 0))
|
|
281
|
-
return (a[i] ?? 0) > (b[i] ?? 0);
|
|
282
|
-
return true;
|
|
283
|
-
}
|
|
284
|
-
export function allowToolsSpecFragment(raw, engineVersion = CORE_ENGINE_VERSION) {
|
|
285
|
-
if (raw === undefined)
|
|
286
|
-
return {};
|
|
287
|
-
if (!engineAtLeast(engineVersion, ALLOW_TOOLS_MIN_ENGINE)) {
|
|
288
|
-
throw createCodedHttpError(400, "request.field_invalid", `allowTools needs engine ${ALLOW_TOOLS_MIN_ENGINE} or later; this server runs engine ${engineVersion}, which would drop the list and leave every tool available — send the request without allowTools, or upgrade the server`);
|
|
289
|
-
}
|
|
290
|
-
return { allowTools: raw };
|
|
282
|
+
export function allowToolsSpecFragment(raw) {
|
|
283
|
+
return raw === undefined ? {} : { allowTools: raw };
|
|
291
284
|
}
|
|
292
285
|
export function promptProfileFromBody(raw) {
|
|
293
286
|
return raw === "simple" || raw === "classic" ? raw : undefined;
|