@sema-agent/server 7.65.0 → 7.67.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/USAGE.md +6 -3
- package/dist/approval-card.d.ts +8 -0
- package/dist/approval.d.ts +3 -0
- package/dist/approval.js +2 -1
- package/dist/boot/governance-seams.js +1 -1
- package/dist/boot/resolve-spec.js +12 -5
- package/dist/boot/stores.js +3 -3
- package/dist/capabilities/hands-lane.d.ts +3 -1
- package/dist/capabilities/hands-lane.js +3 -3
- package/dist/config-catalog.js +2 -2
- package/dist/config-center/apply-effective.js +1 -1
- package/dist/config-center/skills-mcp.d.ts +3 -3
- package/dist/config-center/skills-mcp.js +17 -4
- package/dist/config-center/types.d.ts +11 -0
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +27 -4
- package/dist/config.d.ts +25 -3
- package/dist/config.js +58 -30
- package/dist/deployment-governance.d.ts +8 -0
- package/dist/http/routes/approvals-assistant.js +18 -0
- package/dist/http/routes/diagnostics.d.ts +27 -2
- package/dist/http/routes/diagnostics.js +21 -1
- package/dist/http/routes/rules.js +15 -10
- package/dist/http/routes/tasks.js +9 -9
- package/dist/http/server.js +4 -1
- package/dist/mcp-tool-faces.d.ts +64 -0
- package/dist/mcp-tool-faces.js +93 -0
- package/dist/observability/fail-open.d.ts +8 -0
- package/dist/observability/fail-open.js +8 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/permission-rule-vocab.d.ts +13 -21
- package/dist/permission-rule-vocab.js +6 -5
- package/dist/plugins/checkpoint-store-sql.d.ts +68 -7
- package/dist/plugins/checkpoint-store-sql.js +90 -25
- package/dist/plugins/local-checkpoint-store.d.ts +19 -1
- package/dist/plugins/local-checkpoint-store.js +6 -1
- package/dist/plugins/permission-rule-store-file.d.ts +3 -5
- package/dist/plugins/permission-rule-store-file.js +2 -1
- package/dist/plugins/permission-rule-store-sql.d.ts +10 -10
- package/dist/plugins/permission-rule-store-sql.js +35 -20
- package/dist/plugins/pg-pool.js +5 -0
- package/dist/plugins/tidb-pool.js +11 -0
- package/dist/posture-source.d.ts +57 -0
- package/dist/posture-source.js +27 -0
- package/dist/read-face-posture.d.ts +9 -4
- package/dist/read-face-posture.js +7 -11
- package/dist/rules-consent.d.ts +15 -10
- package/dist/rules-consent.js +41 -21
- package/dist/session-titler.js +3 -12
- package/dist/task-mcp.d.ts +18 -4
- package/dist/task-mcp.js +23 -4
- package/dist/task-settings.d.ts +30 -0
- package/dist/task-settings.js +39 -18
- package/dist/tool-approval.d.ts +5 -4
- package/dist/tool-approval.js +3 -3
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/project.d.ts +22 -0
- package/dist/trace/project.js +21 -1
- package/package.json +4 -4
package/USAGE.md
CHANGED
|
@@ -307,8 +307,11 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
307
307
|
- 入口:`import "@sema-agent/server/main"`(exports 正门;此前宿主只能用 node_modules 路径字符串定位
|
|
308
308
|
`dist/main.js`,那是未承诺的内部路径)。语义承诺三条:**import 即 boot**(副作用模块形,无需调用);
|
|
309
309
|
**不读 `process.argv`**(宿主可用 argv 传自己的标记,`ps` args 判据不受干扰);`./package.json` 可读。
|
|
310
|
-
- `/health` 身份字段承诺:`pid`(= 引擎进程)
|
|
311
|
-
与 DB_BACKEND 无关)
|
|
310
|
+
- `/health` 身份字段承诺:`pid`(= 引擎进程)、`startedAt`(**7.67.0+**,= 引擎进程起点的 epoch ms,
|
|
311
|
+
一次铸)与 `dataRoot`(= 生效数据根,解析恒回退 `~/.ai-agent`,与 DB_BACKEND 无关)**恒在**——宿主用
|
|
312
|
+
它们验证「这个端口上的 /health 是不是我起的那个引擎」。`startedAt` 变了 = 这个端口后面换了一条命
|
|
313
|
+
(另一个宿主重启了共享引擎):壳据此把「引擎换代」当**一等事实**处理,不必等某个带鉴权的请求先撞一次
|
|
314
|
+
401 才发现(`/health` 无鉴权,心跳恒绿)。⚠️ 只给这一个数:service token 本体与代际计数**不上**这一面。
|
|
312
315
|
钉:`test/health-identity-contract.test.ts`。
|
|
313
316
|
- `/health` 活体 store 探测(5.14.0,SQL 后端专属 additive):后台探针(`STORE_PROBE_INTERVAL_MS`,
|
|
314
317
|
默认 15000;0=关;负/非数启动响亮拒)缓存一份 DB 真往返结果,`storeProbe:{live,ageMs,error?}` 在
|
|
@@ -961,7 +964,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
961
964
|
| `POST /v1/admin/drain` `{"reason":"…"}` | **停机因由显式声明**(7.35.0+;operator-only —— principal 须在 `OPERATOR_PRINCIPALS` 内,空名单 = 谁都不是 operator ⇒ 恒 403)。编排壳在发 SIGTERM **之前**调它;server 记进程内 `drainState.reason`,draining 期由 `/health` 的 `drainReason` 与 503 `draining` 体的 `reason` 透出(未声明 = 两面键缺席)。声明是**覆盖式**、无 TTL、随进程重启清零 ⇒ 每次停机流程内都要重新声明。`reason` 非空、trim 后 ≤256 字符,坏值 400。🔴 **写口是特权的,读口不是**:`/health` 免鉴权(见下一行)且缺省全网卡监听 —— 任何能连到这个端口的人都读得到你写的那句话。**别在 reason 里写工单号、内部主机名或任何内部标识**。 |
|
|
962
965
|
| `GET /v1/config/catalog` | **配置目录自描述**(operator-only —— `explicitOperatorOk`,空名单=谁都不是)。逐 env 键一行:`{name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, staticDefault?/derivedDefaultNote?, effectiveValue, valueClass, danger, center?{domain,path,precedence,timing,restartSlice?,domainExists,managedNow,effectiveNow}}`;顶层带 `restartSlices`(重启片闭集)与 `centerDomains`(settings-schema 现行域表)。**两轴不混**:`writeLanes`=谁能写,`effectiveLane`=现在谁在生效(env-wins 族 env 占位 ⇒ center 腿 `effectiveNow:false`)。secret 类值**永不回传**(只回在场位;opaque 回在场位+长度;URL 剥 userinfo)。完备性由仓内 AST 双向对账门执法(分母=全 `src/` 生产 env 读取点)。旋钮 `CONFIG_CATALOG_ENABLED=false`(合规部署)⇒ 路径 404 + 能力位 `configCatalog:false`。响应 `Cache-Control: no-store`。**7.57.0 起**:`enum` 行的 `effectiveValue` **恒落在该行自己的 `enumValues` 闭集内**(仓内机器门执法)——此前 `SESSION_BACKEND` 回的是**内部标签** `tidb`(自 5.0.0 起是**退役公名**,写回去 boot 拒),现归一成公名 `mysql`;`MODEL_DEFAULT_THINKING` 补上一直被解析器接受、却漏在目录闭集外的 `xhigh`,并改为回**实算值**(`off`/未设 ⇒ `null`)而不是 env 原文。 |
|
|
963
966
|
| `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:空 `OPERATOR_PRINCIPALS` ⇒ 恒 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion` 同源。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
|
|
964
|
-
| `GET /health` | 健康(无需鉴权)。7.37.0+ 配置管道在场时带世代账键:`configTargetVersion`(本副本**最后见到**的配置版本,身份标)、`configAppliedVersion`(最后一次真落地的版本)、`configTargetOrdinal`/`configAppliedOrdinal`(7.38.0-rc.2 起,**收货序数**——`CONFIG_PROVIDER=local` 下 version 是内容哈希不承诺序,「改了没生效」看序数对不对齐)、`configApplyStaleMs`(target≠applied **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
|
|
967
|
+
| `GET /health` | 健康(无需鉴权)。**7.67.0+** 恒带 `startedAt`(引擎进程起点 epoch ms,一次铸;与 `pid` 并列的换代锚——值变了就是换了一条命)。7.37.0+ 配置管道在场时带世代账键:`configTargetVersion`(本副本**最后见到**的配置版本,身份标)、`configAppliedVersion`(最后一次真落地的版本)、`configTargetOrdinal`/`configAppliedOrdinal`(7.38.0-rc.2 起,**收货序数**——`CONFIG_PROVIDER=local` 下 version 是内容哈希不承诺序,「改了没生效」看序数对不对齐)、`configApplyStaleMs`(target≠applied **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
|
|
965
968
|
| `GET /metrics` | Prometheus 指标(有 token 时需带) |
|
|
966
969
|
|
|
967
970
|
> **场景可用性(7.7.0 起)**:场景详情里 `enabled` 与 `available` 是**两件事**。`enabled` = 这条场景
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -81,6 +81,7 @@ export declare const RuleOfferSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
81
81
|
kind: z.ZodLiteral<"single">;
|
|
82
82
|
rule: z.ZodString;
|
|
83
83
|
match: z.ZodEnum<{
|
|
84
|
+
path: "path";
|
|
84
85
|
exact: "exact";
|
|
85
86
|
prefix: "prefix";
|
|
86
87
|
wildcard: "wildcard";
|
|
@@ -93,6 +94,7 @@ export declare const RuleOfferSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
93
94
|
kind: z.ZodLiteral<"command">;
|
|
94
95
|
rule: z.ZodString;
|
|
95
96
|
match: z.ZodEnum<{
|
|
97
|
+
path: "path";
|
|
96
98
|
exact: "exact";
|
|
97
99
|
prefix: "prefix";
|
|
98
100
|
wildcard: "wildcard";
|
|
@@ -156,6 +158,7 @@ export declare const RuleOfferRawSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
156
158
|
kind: z.ZodLiteral<"single">;
|
|
157
159
|
rule: z.ZodString;
|
|
158
160
|
match: z.ZodEnum<{
|
|
161
|
+
path: "path";
|
|
159
162
|
exact: "exact";
|
|
160
163
|
prefix: "prefix";
|
|
161
164
|
wildcard: "wildcard";
|
|
@@ -168,6 +171,7 @@ export declare const RuleOfferRawSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
168
171
|
kind: z.ZodLiteral<"command">;
|
|
169
172
|
rule: z.ZodString;
|
|
170
173
|
match: z.ZodEnum<{
|
|
174
|
+
path: "path";
|
|
171
175
|
exact: "exact";
|
|
172
176
|
prefix: "prefix";
|
|
173
177
|
wildcard: "wildcard";
|
|
@@ -367,6 +371,7 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
367
371
|
kind: z.ZodLiteral<"single">;
|
|
368
372
|
rule: z.ZodString;
|
|
369
373
|
match: z.ZodEnum<{
|
|
374
|
+
path: "path";
|
|
370
375
|
exact: "exact";
|
|
371
376
|
prefix: "prefix";
|
|
372
377
|
wildcard: "wildcard";
|
|
@@ -379,6 +384,7 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
379
384
|
kind: z.ZodLiteral<"command">;
|
|
380
385
|
rule: z.ZodString;
|
|
381
386
|
match: z.ZodEnum<{
|
|
387
|
+
path: "path";
|
|
382
388
|
exact: "exact";
|
|
383
389
|
prefix: "prefix";
|
|
384
390
|
wildcard: "wildcard";
|
|
@@ -475,6 +481,7 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
475
481
|
kind: z.ZodLiteral<"single">;
|
|
476
482
|
rule: z.ZodString;
|
|
477
483
|
match: z.ZodEnum<{
|
|
484
|
+
path: "path";
|
|
478
485
|
exact: "exact";
|
|
479
486
|
prefix: "prefix";
|
|
480
487
|
wildcard: "wildcard";
|
|
@@ -487,6 +494,7 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
487
494
|
kind: z.ZodLiteral<"command">;
|
|
488
495
|
rule: z.ZodString;
|
|
489
496
|
match: z.ZodEnum<{
|
|
497
|
+
path: "path";
|
|
490
498
|
exact: "exact";
|
|
491
499
|
prefix: "prefix";
|
|
492
500
|
wildcard: "wildcard";
|
package/dist/approval.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* suspend/resume,见 createDurableAskPolicy 与 /v1/approvals 的 checkpointStore 腿)。
|
|
5
5
|
*/
|
|
6
6
|
import { type ToolPolicy } from "@sema-agent/core";
|
|
7
|
+
import { type PostureSource } from "./posture-source.js";
|
|
7
8
|
/**
|
|
8
9
|
* 运维**是否表达了门意图** —— boot 的单用户 allow-all 基线只在"零门意图"时才允许铺开,
|
|
9
10
|
* 所以这个谓词漏一格 = 那一格的意图被 allow-all 静默吞掉。
|
|
@@ -23,6 +24,7 @@ export declare function hasOperatorGateIntent(config: {
|
|
|
23
24
|
approvalDeny: readonly string[];
|
|
24
25
|
approvalNeverAuto: readonly string[];
|
|
25
26
|
durableApproval: boolean;
|
|
27
|
+
durableApprovalSource: PostureSource;
|
|
26
28
|
}): boolean;
|
|
27
29
|
/**
|
|
28
30
|
* 门意图不可服务=boot 拒启(全窗复审 D3-F1,HIGH;与 env 墓碑族同谱系)。
|
|
@@ -35,6 +37,7 @@ export declare function assertGateIntentServiceable(config: {
|
|
|
35
37
|
approvalDeny: readonly string[];
|
|
36
38
|
approvalNeverAuto: readonly string[];
|
|
37
39
|
durableApproval: boolean;
|
|
40
|
+
durableApprovalSource: PostureSource;
|
|
38
41
|
}, checkpointStorePresent: boolean): void;
|
|
39
42
|
/**
|
|
40
43
|
* [ref] 件⑩ —— **center 的 `forceDurableGate` 在无 checkpoint 店的部署上会静默 inert**,补一条 boot 披露。
|
package/dist/approval.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "@sema-agent/core";
|
|
2
|
+
import { isExplicitPosture } from "./posture-source.js";
|
|
2
3
|
export function hasOperatorGateIntent(config) {
|
|
3
4
|
return (config.approvalRequire.length > 0 ||
|
|
4
5
|
config.approvalDeny.length > 0 ||
|
|
5
6
|
config.approvalNeverAuto.length > 0 ||
|
|
6
|
-
config.durableApproval);
|
|
7
|
+
(config.durableApproval && isExplicitPosture(config.durableApprovalSource)));
|
|
7
8
|
}
|
|
8
9
|
export function assertGateIntentServiceable(config, checkpointStorePresent) {
|
|
9
10
|
if (!hasOperatorGateIntent(config) || checkpointStorePresent)
|
|
@@ -36,7 +36,7 @@ export function createGovernanceSeams(ctx) {
|
|
|
36
36
|
assertGovernanceAssemblable({
|
|
37
37
|
lockedKeys,
|
|
38
38
|
complianceDenies,
|
|
39
|
-
deploymentMcpConfigured: (config.mcpServers?.length ?? 0) > 0,
|
|
39
|
+
deploymentMcpConfigured: (config.mcpServers?.servers.length ?? 0) > 0,
|
|
40
40
|
webFetchMounted: config.requirePrincipal !== true,
|
|
41
41
|
});
|
|
42
42
|
const lockedConfig = config.lockedConfigKeys !== undefined && config.lockedConfigKeys.length > 0 ? { keys: config.lockedConfigKeys } : undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { realpathSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { NodeExecutionEnv, QUESTION_AWAITS_RESUME, deriveAskEffective, expandTiers, resolveAskSeamForm, tightenTaskSpec } from "@sema-agent/core";
|
|
4
5
|
import { cappedCeiling } from "../budget.js";
|
|
5
6
|
import { centerPromptProvider, centerIdentityAssembled } from "../capabilities/center-prompts.js";
|
|
@@ -411,11 +412,15 @@ export function createResolveSpec(ctx) {
|
|
|
411
412
|
const pathAdjudication = hostSemanticsLane
|
|
412
413
|
? (() => {
|
|
413
414
|
const sessionCwd = auth?.sessionId ? effectiveHostWorkspace(perSessionCwd.get(auth.sessionId), {}) : undefined;
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
415
|
+
const realRoot = sessionCwd ?? (config.remoteExec === undefined ? process.cwd() : undefined);
|
|
416
|
+
const gateCwd = realRoot ?? join(config.localDataRoot ?? localRoot, "fs-write-gate-unrooted");
|
|
417
|
+
const realHome = config.remoteExec === undefined ? homedir() : undefined;
|
|
418
|
+
return {
|
|
419
|
+
env: new NodeExecutionEnv({ cwd: gateCwd }),
|
|
420
|
+
cwd: gateCwd,
|
|
421
|
+
...(realRoot !== undefined ? { taskRoot: realRoot } : {}),
|
|
422
|
+
...(realHome !== undefined ? { taskHome: realHome } : {}),
|
|
423
|
+
};
|
|
419
424
|
})()
|
|
420
425
|
: {
|
|
421
426
|
env: new DeferredSandboxPathEnv({
|
|
@@ -461,6 +466,8 @@ export function createResolveSpec(ctx) {
|
|
|
461
466
|
? {
|
|
462
467
|
env: pathAdjudication.env,
|
|
463
468
|
...(pathAdjudication.cwd !== undefined ? { cwd: pathAdjudication.cwd } : {}),
|
|
469
|
+
...(pathAdjudication.taskRoot !== undefined ? { taskRoot: pathAdjudication.taskRoot } : {}),
|
|
470
|
+
...(pathAdjudication.taskHome !== undefined ? { taskHome: pathAdjudication.taskHome } : {}),
|
|
464
471
|
...(scratchpadDir ? { scratchpadDir } : {}),
|
|
465
472
|
...gateCommon,
|
|
466
473
|
}
|
package/dist/boot/stores.js
CHANGED
|
@@ -24,7 +24,7 @@ import { buildMemoryRemoteLaneWarn, memoryEngineBackendFor, memoryEngineRemoteLa
|
|
|
24
24
|
import { assertToolResultProvenanceSchema } from "../plugins/tool-result-store-sql.js";
|
|
25
25
|
import { assertPermissionRuleApprovalSchema, assertPermissionRuleTicketSchema } from "../plugins/permission-rule-store-sql.js";
|
|
26
26
|
import { assertApprovalAskRuleMaterialSchema } from "../plugins/approval-ask-store-sql.js";
|
|
27
|
-
import {
|
|
27
|
+
import { assertCheckpointColumnSchema } from "../plugins/checkpoint-store-sql.js";
|
|
28
28
|
import { assertFileHistorySchema } from "../plugins/file-history-store-sql.js";
|
|
29
29
|
import { resolveStreamApprovalGate } from "../tool-approval.js";
|
|
30
30
|
import { buildMemoryWriteBoundaryAudit, effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
|
|
@@ -56,9 +56,9 @@ export async function openStores(ctx) {
|
|
|
56
56
|
else if (mysqlPool)
|
|
57
57
|
await assertToolResultProvenanceSchema(async (sql) => ({ rows: (await mysqlPool.query(sql))[0] }), "tidb");
|
|
58
58
|
if (pgPool)
|
|
59
|
-
await
|
|
59
|
+
await assertCheckpointColumnSchema(async (sql) => ({ rows: (await pgPool.query(sql)).rows }), "pg");
|
|
60
60
|
else if (mysqlPool)
|
|
61
|
-
await
|
|
61
|
+
await assertCheckpointColumnSchema(async (sql) => ({ rows: (await mysqlPool.query(sql))[0] }), "tidb");
|
|
62
62
|
if (config.permissionRulesEnabled) {
|
|
63
63
|
if (pgPool)
|
|
64
64
|
await assertPermissionRuleApprovalSchema(async (sql) => ({ rows: (await pgPool.query(sql)).rows }), "pg");
|
|
@@ -37,7 +37,9 @@ export declare function pickHandsRunner(hands: ScenarioHands, pair: HandsRunnerP
|
|
|
37
37
|
* core 手带 band 的工具名**全集** —— 「挂 executionEnvFactory 才出现、不挂就消失」的那一批。
|
|
38
38
|
*
|
|
39
39
|
* 两段来源,都不是拍脑袋:
|
|
40
|
-
* · `
|
|
40
|
+
* · `handBandEffects()`(core 7.8.0 B1 起的导出面;此前是手抄表常量 `HAND_TOOL_EFFECTS`)—— band 的
|
|
41
|
+
* effect 表,由**静态工具目录**按 `mountedBy.includes("hands")` 派生。名册单源之后这张表不再是一张
|
|
42
|
+
* 手抄名表,加/退工具由目录一处说了算。
|
|
41
43
|
* · 下方锚定补集 —— `createHandsToolkit` / 手带块还会铸但**不在** effect 表里的 5 名(实测差集,见
|
|
42
44
|
* test/scenario-hands-lane.test.ts 的对表格:它用真 Runner 跑「有手 roster ∖ 无手 roster」,与本常量逐名对账,
|
|
43
45
|
* core 加/改 band 成员时红,防漂移)。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { handBandEffects, RETIRED_TOOL_NAMES, combinePolicies, createAllowDenyPolicy } from "@sema-agent/core";
|
|
2
2
|
export function withoutExecutionEnv(deps) {
|
|
3
3
|
const { executionEnvFactory: _handsDropped, ...rest } = deps;
|
|
4
4
|
return rest;
|
|
@@ -12,13 +12,13 @@ export function pickHandsRunner(hands, pair) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
export const HANDS_BAND_TOOL_NAMES = Object.freeze([
|
|
15
|
-
...
|
|
15
|
+
...handBandEffects().keys(),
|
|
16
16
|
"SendMessage",
|
|
17
17
|
"AgentTranscript",
|
|
18
18
|
"Monitor",
|
|
19
19
|
"EnterWorktree",
|
|
20
20
|
"ExitWorktree",
|
|
21
|
-
].filter((n) => !RETIRED_TOOL_NAMES.has(n)));
|
|
21
|
+
].filter((n, i, all) => !RETIRED_TOOL_NAMES.has(n) && all.indexOf(n) === i));
|
|
22
22
|
export const HANDS_DUAL_USE_TOOL_NAMES = Object.freeze(["TaskOutput", "TaskStop", "SendMessage", "AgentTranscript"]);
|
|
23
23
|
export const HANDS_EXCLUSIVE_TOOL_NAMES = Object.freeze(HANDS_BAND_TOOL_NAMES.filter((n) => !HANDS_DUAL_USE_TOOL_NAMES.includes(n)));
|
|
24
24
|
export function handsDenyPolicy() {
|
package/dist/config-catalog.js
CHANGED
|
@@ -111,7 +111,7 @@ export const CONFIG_CATALOG = [
|
|
|
111
111
|
r("DOCKER_PIDS_LIMIT", "orchestration", "number", "local-docker fork-bomb 背栓(--pids-limit;非法值回 adapter 默认 512+warn)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ adapter 默认 512" }),
|
|
112
112
|
r("DOCKER_SANDBOX_ENV", "orchestration", "csv", "转发进容器的本进程 env **名字**表(值只在 worker env,不进 spec/center)", { danger: SEC }),
|
|
113
113
|
r("DRAIN_GRACE_MS", "limitsHttp", "number", "SIGTERM 优雅排空窗(ms;期间新计费提交 503+Retry-After)", { staticDefault: String(10 * 60 * 1000), configKey: "drainGraceMs", center: cLimits("drainGraceMs") }),
|
|
114
|
-
r("DURABLE_APPROVAL", "approval", "boolean", "durable suspend/resume HITL(需 SQL 店;决定 checkpointStore 与 /v1/approvals 面的存在性)", {
|
|
114
|
+
r("DURABLE_APPROVAL", "approval", "boolean", "durable suspend/resume HITL(需 SQL 店;决定 checkpointStore 与 /v1/approvals 面的存在性)", { danger: SEC, derivedDefaultNote: "posture 三态(S-178):显式词恒赢,缺席 = 单用户(REQUIRE_PRINCIPAL≠true)且有耐久店 ⇒ on / 多租户或无耐久店 ⇒ off;生效来源见 GET /v1/diagnostics/wiring 的 serverGates.durableApproval.source", configKey: "durableApproval" }),
|
|
115
115
|
r("E2B_ALLOW_NET", "orchestration", "boolean", "e2b 沙箱出网许可", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 不铸键(adapter 默认允许)" }),
|
|
116
116
|
r("E2B_API_KEY", "orchestration", "secret", "E2B 车道 API 凭据(REMOTE_EXEC=e2b 必需件)", { danger: [...SEC, ...BILL] }),
|
|
117
117
|
r("E2B_BG_DEFAULT_TIMEOUT_SEC", "orchestration", "number", "e2b 后台 shell 缺省超时(秒)", { staticDefault: "300" }),
|
|
@@ -403,7 +403,7 @@ export const CONFIG_CATALOG = [
|
|
|
403
403
|
r("STREAM_APPROVAL_RECONCILE_BATCH", "approval", "number", "对账收敛器每 tick 每段行数帽(整数)", { staticDefault: "200" }),
|
|
404
404
|
r("STREAM_APPROVAL_REPLAY_MAX", "approval", "number", "开流 preamble 重放帽", { staticDefault: "50" }),
|
|
405
405
|
r("STREAM_ASK_WINDOW_MARGIN_MS", "approval", "number", "审批窗长三元安全余量(ms)", { staticDefault: "10000", configKey: "streamAskWindowMarginMs" }),
|
|
406
|
-
r("STREAM_ASK_WINDOW_MS", "approval", "number", "活卡审批窗长(ms;所有腿生效,与流内协议上不上场无关;0=关窗⇒不发卡恒走无人值守终局;负值与超 Node 定时器上界 2147483647 的值拒启)", {
|
|
406
|
+
r("STREAM_ASK_WINDOW_MS", "approval", "number", "活卡审批窗长(ms;所有腿生效,与流内协议上不上场无关;0=关窗⇒不发卡恒走无人值守终局;负值与超 Node 定时器上界 2147483647 的值拒启)", { derivedDefaultNote: "posture 三态(S-178):显式值恒赢,缺席 = 单用户(REQUIRE_PRINCIPAL≠true)⇒ 86400000(24h;若与显式 STREAM_APPROVAL_ORPHAN_TTL_MS 撞跨旋钮不变量则让位回 300000 并 warn)/ 多租户 ⇒ 300000;生效来源见 GET /v1/diagnostics/wiring 的 serverGates.streamAskWindowMs.source" }),
|
|
407
407
|
r("SYNC_IMPORT_LEASE_STALE_SEC", "memory", "number", "session-sync staged import 租约陈旧阈(秒)", { staticDefault: "600", configKey: "syncImportLeaseStaleSec" }),
|
|
408
408
|
r("TASK_MAX_OUTPUT_TOKENS_MAX", "limitsHttp", "number", "调用方自报 limits.maxOutputTokens 的运营方封顶(Math.min 钳显式请求值;坏值=封顶不生效+响亮 warn)", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 不封顶" }),
|
|
409
409
|
r("TASK_MAX_TURNS_MAX", "limitsHttp", "number", "调用方自报 limits.maxTurns 的运营方封顶(同族三旋钮;只钳显式请求值,不是全队默认限额)", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 不封顶" }),
|
|
@@ -373,7 +373,7 @@ function stageApprovalRequire(raw, logger) {
|
|
|
373
373
|
`Refusing the domain instead of dropping them: dropping every entry would SILENTLY remove the approval gate.`, nonString.map((e) => `[${e.i}]`), KEPT_WOULD_REMOVE_GATE);
|
|
374
374
|
}
|
|
375
375
|
const names = raw.filter((n) => typeof n === "string").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
376
|
-
const bad = findUnmatchableToolNames(names);
|
|
376
|
+
const bad = findUnmatchableToolNames(names, { consumer: "tool-name-table", behavior: "deny" });
|
|
377
377
|
if (bad.length > 0) {
|
|
378
378
|
return refuse(formatUnmatchableToolNames("published runtime.approvalRequire", bad), bad.map((b) => b.name), KEPT_WOULD_MATCH_NOTHING);
|
|
379
379
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { A2aServerSpec, McpServerSpec } from "@sema-agent/core";
|
|
2
|
-
import type {
|
|
2
|
+
import type { CenterMcp, ScopedA2aPeer } from "../config-types.js";
|
|
3
3
|
import type { Logger } from "../observability/logger.js";
|
|
4
4
|
import type { LoadedSkill } from "../capabilities/skills.js";
|
|
5
5
|
import type { CenterA2aPeer, CenterMcpServer, CenterSkillManifest } from "./types.js";
|
|
@@ -26,10 +26,10 @@ diskCacheDir?: string): Promise<LoadedSkill[]>;
|
|
|
26
26
|
*/
|
|
27
27
|
export declare function resolveMcpServers(mcp: {
|
|
28
28
|
servers: CenterMcpServer[];
|
|
29
|
-
}, logger?: Logger):
|
|
29
|
+
}, logger?: Logger): CenterMcp;
|
|
30
30
|
/** The MCP servers applicable to a scenario: tagged with it, or untagged (global). undefined when none.
|
|
31
31
|
* [ref]:alias 窗撤销,单键命中(同 `skillsForScenario`)。 */
|
|
32
|
-
export declare function mcpForScenario(
|
|
32
|
+
export declare function mcpForScenario(center: CenterMcp | undefined, scenario: string): McpServerSpec[] | undefined;
|
|
33
33
|
/**
|
|
34
34
|
* DESIGN-269 §2.1 —— resolve center A2A peers (`/effective.a2a`) into core `A2aServerSpec`s + the scenarios
|
|
35
35
|
* each applies to. Exact sibling of {@link resolveMcpServers}, same three rules:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readMcpSourceLabel } from "../mcp-source-label.js";
|
|
2
|
+
import { readMcpToolFaces } from "../mcp-tool-faces.js";
|
|
2
3
|
import { skillContentHash } from "@sema-agent/settings-schema";
|
|
3
4
|
import { CONTENT_ORIGIN_WORDS, describeBadContentOrigin, isContentOriginWord } from "../mcp-content-origin.js";
|
|
4
5
|
import { fetchSkillContent } from "./http-client.js";
|
|
@@ -79,6 +80,7 @@ function resolveRefs(refs, server, kind, logger, event = "sema_registry_mcp_env_
|
|
|
79
80
|
}
|
|
80
81
|
export function resolveMcpServers(mcp, logger) {
|
|
81
82
|
const out = [];
|
|
83
|
+
const reservedNames = (mcp.servers ?? []).map((s) => s.name);
|
|
82
84
|
for (const s of mcp.servers ?? []) {
|
|
83
85
|
if (s.enabled === false)
|
|
84
86
|
continue;
|
|
@@ -97,13 +99,23 @@ export function resolveMcpServers(mcp, logger) {
|
|
|
97
99
|
});
|
|
98
100
|
}
|
|
99
101
|
const source = readMcpSourceLabel(s.name, s.source);
|
|
102
|
+
const facesRead = readMcpToolFaces(s.toolFaces);
|
|
103
|
+
if (!facesRead.ok) {
|
|
104
|
+
logger?.warn("sema_registry_mcp_tool_faces_invalid", {
|
|
105
|
+
server: s.name,
|
|
106
|
+
problem: facesRead.problem,
|
|
107
|
+
note: "server SKIPPED — a toolFaces overlay is a NARROWING declaration (its pathTarget is what the sensitive-path / write fences read); mounting the server without it would silently drop the protection you declared. Fix the entry and republish.",
|
|
108
|
+
});
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const toolFaces = facesRead.faces === undefined ? {} : { toolFaces: facesRead.faces };
|
|
100
112
|
if (s.transport.kind === "stdio") {
|
|
101
113
|
const env = resolveRefs(s.transport.envRefs, s.name, "env", logger);
|
|
102
114
|
if (env === null)
|
|
103
115
|
continue;
|
|
104
116
|
out.push({
|
|
105
117
|
scenarios: s.scenarios ?? [],
|
|
106
|
-
spec: { name: s.name, transport: { kind: "stdio", command: s.transport.command, args: s.transport.args, ...(Object.keys(env).length ? { env } : {}) }, ...allow, ...elicit, ...contentOrigin, ...source },
|
|
118
|
+
spec: { name: s.name, transport: { kind: "stdio", command: s.transport.command, args: s.transport.args, ...(Object.keys(env).length ? { env } : {}) }, ...allow, ...elicit, ...contentOrigin, ...source, ...toolFaces },
|
|
107
119
|
});
|
|
108
120
|
}
|
|
109
121
|
else {
|
|
@@ -112,15 +124,16 @@ export function resolveMcpServers(mcp, logger) {
|
|
|
112
124
|
continue;
|
|
113
125
|
out.push({
|
|
114
126
|
scenarios: s.scenarios ?? [],
|
|
115
|
-
spec: { name: s.name, transport: { kind: "http", url: s.transport.url, ...(Object.keys(headers).length ? { headers } : {}), ...(s.transport.principalHeader ? { principalHeader: s.transport.principalHeader } : {}) }, ...allow, ...elicit, ...contentOrigin, ...source },
|
|
127
|
+
spec: { name: s.name, transport: { kind: "http", url: s.transport.url, ...(Object.keys(headers).length ? { headers } : {}), ...(s.transport.principalHeader ? { principalHeader: s.transport.principalHeader } : {}) }, ...allow, ...elicit, ...contentOrigin, ...source, ...toolFaces },
|
|
116
128
|
});
|
|
117
129
|
}
|
|
118
130
|
}
|
|
119
131
|
if (out.length > 0)
|
|
120
132
|
logger?.info("sema_registry_mcp", { servers: out.map((s) => s.spec.name) });
|
|
121
|
-
return out;
|
|
133
|
+
return { servers: out, reservedNames };
|
|
122
134
|
}
|
|
123
|
-
export function mcpForScenario(
|
|
135
|
+
export function mcpForScenario(center, scenario) {
|
|
136
|
+
const servers = center?.servers;
|
|
124
137
|
if (!servers || servers.length === 0)
|
|
125
138
|
return undefined;
|
|
126
139
|
const hit = servers.filter((s) => s.scenarios.length === 0 || s.scenarios.includes(scenario)).map((s) => s.spec);
|
|
@@ -116,6 +116,17 @@ export interface CenterMcpServer {
|
|
|
116
116
|
/** B-036 / S-143:声明方自述的分组标签(开集,非空 ≤80;settings-schema 1.7.0 `McpServerSpec.source`)——上 core
|
|
117
117
|
* `McpServerSpec.source`,由 `wiring_manifest.mcp[].source` 逐字回显。显示用,不进信任轴。 */
|
|
118
118
|
source?: string;
|
|
119
|
+
/**
|
|
120
|
+
* 件8(core 7.8.0 [ref] §2.7;settings-schema 1.9.0 `McpServerSpec.toolFaces`)—— 这台 server 的
|
|
121
|
+
* **per-tool 面覆盖层**,键 = server 的**原始(未加命名空间)**工具名,值 = `{ family?, pathTarget?,
|
|
122
|
+
* renderHints?, ruleFace? }`。上 core `McpServerSpec.toolFaces`。
|
|
123
|
+
*
|
|
124
|
+
* 🔴 类型是 `unknown` 而不是那个结构,**刻意的**:本类型是两条 center 腿的**承载形**(本地 config.d 经
|
|
125
|
+
* settings-schema 的 `z.unknown()` 解析透明地过来、远端 /effective 是直读 JSON),判据与坏形处置的单一
|
|
126
|
+
* 属主是 `mcp-tool-faces.ts` 的 `readMcpToolFaces`(`resolveMcpServers` 在铸 spec 那一刻调它)。在承载
|
|
127
|
+
* 形上先写一个结构,等于把同一张判据表抄成两份(而其中一份没有执行力)。
|
|
128
|
+
*/
|
|
129
|
+
toolFaces?: unknown;
|
|
119
130
|
transport: {
|
|
120
131
|
kind: "stdio";
|
|
121
132
|
command: string;
|
package/dist/config-provider.js
CHANGED
|
@@ -144,6 +144,7 @@ export function mapToServiceEffective(eff, version) {
|
|
|
144
144
|
...(s.elicitation !== undefined ? { elicitation: s.elicitation } : {}),
|
|
145
145
|
...(s.contentOrigin !== undefined ? { contentOrigin: s.contentOrigin } : {}),
|
|
146
146
|
...readMcpSourceLabel(s.name, s.source),
|
|
147
|
+
...(s.toolFaces !== undefined ? { toolFaces: s.toolFaces } : {}),
|
|
147
148
|
};
|
|
148
149
|
if (s.transport.kind === "stdio") {
|
|
149
150
|
return {
|
package/dist/config-types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* compiling unchanged. All imports below are top-level `import type` (erased — never a load edge,
|
|
7
7
|
* see test/module-cycle-gate.test.ts header on verbatimModuleSyntax). */
|
|
8
8
|
import type { ReadFaceSource } from "./read-face-posture.js";
|
|
9
|
+
import type { PostureSource } from "./posture-source.js";
|
|
9
10
|
import type { SealedKeyPoison } from "./sealed-key.js";
|
|
10
11
|
import type { A2aServerSpec, CompliancePosture, CrossSessionDialogExpiry, CrossSessionInboundSetting, FileHistoryRetentionPolicy, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy, WriteProtectedEntry } from "@sema-agent/core";
|
|
11
12
|
import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
|
|
@@ -84,6 +85,15 @@ export interface ScopedMcpServer {
|
|
|
84
85
|
scenarios: string[];
|
|
85
86
|
spec: McpServerSpec;
|
|
86
87
|
}
|
|
88
|
+
/** The deployment's MCP declaration, resolved (`resolveMcpServers`). `servers` = what actually MOUNTED (env-NAME refs
|
|
89
|
+
* resolved, `enabled:false` / missing-ref / malformed-`toolFaces` entries skipped, each with its own loud warn);
|
|
90
|
+
* `reservedNames` = EVERY name the deployment declared, mounted or not. A name the deployment declared is CLOSED to
|
|
91
|
+
* request-side injection (`resolveRequestMcp`) whether or not it mounted — otherwise "skipped" would silently mean
|
|
92
|
+
* "free for the caller to refill without the operator's `toolFaces` / `allowTools`" (车CF codex r2 [high]). */
|
|
93
|
+
export interface CenterMcp {
|
|
94
|
+
servers: ScopedMcpServer[];
|
|
95
|
+
reservedNames: readonly string[];
|
|
96
|
+
}
|
|
87
97
|
/** DESIGN-269 §2.1 —— an A2A PEER resolved to a core spec (header-NAME refs already → real values) plus
|
|
88
98
|
* the scenarios it applies to (empty = all). Exact sibling of {@link ScopedMcpServer}: `resolveSpec`
|
|
89
99
|
* filters by scenario (`a2aForScenario`) and passes `spec` to core as `TaskSpec.a2a`.
|
|
@@ -687,8 +697,9 @@ export interface ServiceConfigFlat {
|
|
|
687
697
|
};
|
|
688
698
|
/** MCP servers from sema-registry (`/effective.mcp`), resolved at startup (env-NAME refs → values) and
|
|
689
699
|
* filtered per scenario in `resolveSpec`. Not env-derived — populated by the sema-registry adapter when
|
|
690
|
-
* present (restart-to-apply, like models/roles); undefined = no center MCP (scenarios use no MCP).
|
|
691
|
-
|
|
700
|
+
* present (restart-to-apply, like models/roles); undefined = no center MCP (scenarios use no MCP).
|
|
701
|
+
* Carries both the mounted list and the full declared-name set — see {@link CenterMcp}. */
|
|
702
|
+
mcpServers?: CenterMcp;
|
|
692
703
|
/** DESIGN-269 §2.1 —— A2A peers from the config center (`/effective.a2a`), resolved at startup
|
|
693
704
|
* (header-NAME refs → values) and filtered per scenario in `resolveSpec` (`a2aForScenario`). Same
|
|
694
705
|
* posture as {@link mcpServers}: not env-derived (no A2A_* env leg — a peer is a structured record,
|
|
@@ -952,6 +963,14 @@ export interface ServiceConfigFlat {
|
|
|
952
963
|
* operator resumes it on any replica. Default OFF([ref] A-5:轮询车道已退役,OFF+门意图 ⇒ core UNGATED
|
|
953
964
|
* 告警,不再有回退门形). `DURABLE_APPROVAL=true`. */
|
|
954
965
|
durableApproval: boolean;
|
|
966
|
+
/** S-178:上面那一位**是谁定的**(闭集四词,见 `posture-source.ts` —— 与 `readFaceSource` 同一张表)。
|
|
967
|
+
* 7.67.0 起本键进 posture 家族:单机 turnkey ∧ 有耐久店 ⇒ 默认 ON,多租户/内存底座仍 OFF,显式 env
|
|
968
|
+
* 两个方向恒赢。来源上 operator 读面(`GET /v1/diagnostics/wiring` 的 `serverGates`),因为「这台机器
|
|
969
|
+
* 为什么开着 park/resume」在升级后是运维必须看得见的一句话(与 S-167 的补偿项同族)。 */
|
|
970
|
+
durableApprovalSource: PostureSource;
|
|
971
|
+
/** S-178:`STREAM_ASK_WINDOW_MS` 的来源(同一张四词表)。值在 {@link StreamApprovalConfig.windowMs};
|
|
972
|
+
* 单机 turnkey 缺席 ⇒ 24h(posture),多租户缺席 ⇒ 5min(engine-default),显式 env ⇒ env。 */
|
|
973
|
+
streamAskWindowMsSource: PostureSource;
|
|
955
974
|
/** [ref] seam #2 (preemption) / [ref] ("失败≠没做完" third state): when true (+ durable mode), the
|
|
956
975
|
* /v1/runs durable path opts every task into core `resourceSuspend` — a budget/turns/walltime limit OR a
|
|
957
976
|
* scheduler preempt (`POST /v1/assistant/tasks/:id/preempt`) durably SUSPENDS the task (resumable via
|
|
@@ -1237,6 +1256,10 @@ export interface ServiceConfigFlat {
|
|
|
1237
1256
|
/** Auto-generate a session title at first submit (one cheap-model call per
|
|
1238
1257
|
* session, write-once; the user rename layer is BFF-side). Default ON; `SESSION_AUTO_TITLE=false` opts out. */
|
|
1239
1258
|
sessionAutoTitle: boolean;
|
|
1259
|
+
/** S-178:上面那一位的来源(同一张四词表)。本键**不进 posture**(缺省恒 true,两形同值)——
|
|
1260
|
+
* 报来源是为了让运维分辨「这台机器显式关了自动标题」与「它本来就开着」;壳此前注 `false` 的那条
|
|
1261
|
+
* 理由(gateway 不可达时的 warn 噪音)已在 `session-titler.ts` 侧修掉。 */
|
|
1262
|
+
sessionAutoTitleSource: PostureSource;
|
|
1240
1263
|
/** RFC A2 (clay 拍 A 切片先行): mount the model-facing `SelectEnvironment` tool (spec.tools, 判定②(a)) when
|
|
1241
1264
|
* the per-task image chain is live (k8s + image catalog). Default ON; `SELECT_ENVIRONMENT_TOOL=false` opts out. */
|
|
1242
1265
|
selectEnvironmentTool: boolean;
|
|
@@ -1672,7 +1695,7 @@ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "ses
|
|
|
1672
1695
|
export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
|
|
1673
1696
|
/** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
|
|
1674
1697
|
* 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
|
|
1675
|
-
export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "crossSessionInbound" | "crossSessionDialogExpiry">;
|
|
1698
|
+
export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "durableApprovalSource" | "streamAskWindowMsSource" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "crossSessionInbound" | "crossSessionDialogExpiry">;
|
|
1676
1699
|
/** 组:memory(记忆面 + TOC 同步腿)。 */
|
|
1677
1700
|
export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
|
|
1678
1701
|
/** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
|
|
@@ -1683,7 +1706,7 @@ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled"
|
|
|
1683
1706
|
* config 这一层它只是一句声明。 */
|
|
1684
1707
|
export type ServiceAuthConfig = Pick<ServiceConfigFlat, "authToken" | "authTokens" | "allowUnauthedWrites" | "corsOrigins" | "principalHeader" | "requirePrincipal" | "autonomy" | "commandPolicy" | "compliancePosture" | "lockedConfigKeys" | "configCatalogEnabled" | "retentionPolicy" | "retentionSweep" | "operatorPrincipals" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "principalJwtMaxTtlSec" | "bindHost" | "bindHostSource">;
|
|
1685
1708
|
/** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
|
|
1686
|
-
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "hooksTimeoutMs" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "delegationEntryCaps" | "rosterRetentionMs" | "leaderRunStaleMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes" | "readFace" | "readFaceSource" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1709
|
+
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "hooksTimeoutMs" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "delegationEntryCaps" | "rosterRetentionMs" | "leaderRunStaleMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "sessionAutoTitleSource" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes" | "readFace" | "readFaceSource" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1687
1710
|
/** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
|
|
1688
1711
|
export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachmentOrphanGraceMs" | "workspaceFileMaxBytes" | "attachmentMaxBytes" | "attachmentMimeAllowlist" | "attachmentUnboundTtlMs" | "infraCostRates" | "drainGraceMs" | "sighupIdleGraceMs" | "parentPid" | "engineAutoExit" | "engineLingerMs" | "rateLimitPerMin" | "maxTaskCostUsd" | "maxTaskTokens" | "maxPrincipalCostUsd" | "costQuotaWindowSec" | "reapIntervalSec" | "runStaleSec" | "toolResultTtlSec">;
|
|
1689
1712
|
/** 组:observability(可观测)。 */
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Model } from "@sema-agent/core";
|
|
1
|
+
import { type Model, type PathRuleBases, type RuleBehavior } from "@sema-agent/core";
|
|
2
2
|
import type { Autonomy } from "./runtime-governance.js";
|
|
3
3
|
import type { ServiceConfig, ServiceConfigFlat, ServiceConfigGroups } from "./config-types.js";
|
|
4
4
|
/** [ref] A1: the config TYPE face lives in the leaf module config-types.ts; re-exported here so
|
|
@@ -237,8 +237,30 @@ export interface UnmatchableToolName {
|
|
|
237
237
|
name: string;
|
|
238
238
|
guidance: string;
|
|
239
239
|
}
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
/**
|
|
241
|
+
* S-177(core 7.9.0 [ref])—— **这条名单条目喂给谁**,决定「永不匹配」这句话怎么判。两个消费者形态:
|
|
242
|
+
*
|
|
243
|
+
* · `"tool-name-table"` —— 消费者按**整串工具名**比对(精确 ∪ covering):env `APPROVAL_*` 三键与
|
|
244
|
+
* 中心下发 `runtime.approvalRequire`(消费者 `createDurableAskPolicy`),**以及**
|
|
245
|
+
* `settings.permissions.allow`(消费者 core 的 `createAllowDenyPolicy` 的 allow 位)。带括号的内容形
|
|
246
|
+
* 在这里永远匹配不上任何活工具名 —— 与引擎的规则文法认不认得它**无关**,是这条腿的比对方式决定的,
|
|
247
|
+
* 所以内容形照旧响亮拒 + 指路。
|
|
248
|
+
* · `"rule-policy"` —— 消费者是 core 的**规则编译器**(`createPermissionRulePolicy`):
|
|
249
|
+
* `settings.permissions.deny` 与 `.ask`(S-177 / core 7.9.0 起两张表都走编译器)。引擎自此**真的按内容
|
|
250
|
+
* 匹配**它们(`Bash(rm -r:*)` / `Edit(~/.sema/**)`),所以本仓不许再替它拒 —— 判据整条交回 core 的
|
|
251
|
+
* `validatePermissionRules`,按这张表自己的 behavior 问。
|
|
252
|
+
*
|
|
253
|
+
* 🔴 **不对称不是特判,是从「消费者是谁」自然导出的**:`allow` 之所以豁免名字形与文法两族(退役名 /
|
|
254
|
+
* 短名 / 反斜杠 / 超帽),是因为它是**白名单** —— 一个匹配不上的条目让白名单空掉 ⇒ 一切都不在白名单
|
|
255
|
+
* ⇒ fail-CLOSED 且当场可见,那是可接受的另一种失败形;而内容形不同,它是一个 CC 用户为了**少弹窗**
|
|
256
|
+
* 写下的拼法族错,静默代价是整只 Bash 不可用。
|
|
257
|
+
*/
|
|
258
|
+
export type RuleEntryConsumer = "tool-name-table" | "rule-policy";
|
|
259
|
+
export declare function findUnmatchableToolNames(names: readonly string[], opts: {
|
|
260
|
+
consumer: RuleEntryConsumer;
|
|
261
|
+
behavior: RuleBehavior;
|
|
262
|
+
pathBases?: PathRuleBases;
|
|
263
|
+
}): UnmatchableToolName[];
|
|
242
264
|
/** {@link findUnmatchableToolNames} 的成句形——三条腿的文案同源(只有前缀/出口不同)。 */
|
|
243
265
|
export declare function formatUnmatchableToolNames(source: string, bad: readonly UnmatchableToolName[]): string;
|
|
244
266
|
/**
|