@sema-agent/server 7.64.0 → 7.65.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 +18 -5
- package/dist/boot/resolve-spec.d.ts +0 -5
- package/dist/boot/resolve-spec.js +3 -4
- package/dist/capabilities/hands-lane.d.ts +52 -12
- package/dist/capabilities/hands-lane.js +19 -8
- package/dist/config-catalog.js +1 -1
- package/dist/config-center/read-face.js +10 -8
- package/dist/config-types.d.ts +12 -3
- package/dist/config.js +7 -4
- package/dist/http/routes/diagnostics.d.ts +0 -11
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/main.js +2 -4
- package/dist/read-face-posture.d.ts +52 -0
- package/dist/read-face-posture.js +20 -0
- package/package.json +1 -1
package/USAGE.md
CHANGED
|
@@ -594,10 +594,11 @@ WRITE_PROTECTED_TABLE_REPLACE='[{"name":".envrc","kind":"basename"}]' # 整表
|
|
|
594
594
|
- **leader 车道同席**:与其余部署姿态席同一个取值点(`buildDeploymentPostureSeats`),主 runner / subRunner /
|
|
595
595
|
run-local / leader 五只 Runner 同表。
|
|
596
596
|
|
|
597
|
-
**可选 — READ 面姿态(readFace,7.18.0
|
|
597
|
+
**可选 — READ 面姿态(readFace,7.18.0 起;缺席:单机 ⇒ `open`,多租户 ⇒ 引擎当家 —— 7.65.0 起,见下)**
|
|
598
598
|
|
|
599
599
|
```bash
|
|
600
|
-
READ_FACE=open # 词表 open|roots
|
|
600
|
+
READ_FACE=open # 词表 open|roots;坏词启动期响亮拒
|
|
601
|
+
# 不设:单用户 turnkey(REQUIRE_PRINCIPAL 未设)⇒ open;多租户 ⇒ 不传给引擎(引擎默认 roots)
|
|
601
602
|
READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitive":true}]'
|
|
602
603
|
```
|
|
603
604
|
- **`READ_FACE`**:READ 工具面(读文件/遍历/搜索)的部署级姿态。引擎默认 **`roots`** = 读被夹在任务的
|
|
@@ -609,8 +610,19 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
609
610
|
退出。这些条目**叠在** core 内建 deny 集之上(内建先、追加后,重复折叠),不是替换。
|
|
610
611
|
- **坏形拒启,不静默折默认**:坏 JSON / 非数组 / 缺 `pattern` / 编不过 core 的 `compileReadDeny`
|
|
611
612
|
一律启动期点名拒——一个手滑的 deny 集比没有 deny 集更糟(以为挡了其实没挡)。
|
|
612
|
-
-
|
|
613
|
-
|
|
613
|
+
- **缺席的语义按 posture 分两形(7.65.0 起,S-167)**:
|
|
614
|
+
· **单用户 turnkey**(`REQUIRE_PRINCIPAL` 未设 —— 一台机器一个可信主人,CC 的信任模型):`READ_FACE`
|
|
615
|
+
缺席 ⇒ 铸 **`open`**,工作区外的读默认放行。**这是 7.65.0 的行为变更**:同一台机器升级前后
|
|
616
|
+
「读工作区外的文件」从被拒变成放行。**要钉回收敛姿态:显式 `READ_FACE=roots`。**
|
|
617
|
+
· **多租户**(`REQUIRE_PRINCIPAL=true`):缺席仍**不铸**、不传给引擎(引擎默认 `roots`)——**一个字节不变**。
|
|
618
|
+
· `READ_DENY_PATTERNS` / `READ_DENY_BUILTIN_TIERS` / `READ_DENY_BUILTIN_EXCLUDE` 三键的「缺席不铸」
|
|
619
|
+
**不变**;**内建读拒表照旧生效** —— `open` 放开的是「工作区外」这道围栏,凭证 / shell 历史 / 浏览器 /
|
|
620
|
+
钱包那几档 deny 在 `open` 面上照样拦(两者在引擎里是并列机制)。
|
|
621
|
+
· 「这一档到底是谁定的」由 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace` 段答:
|
|
622
|
+
`{face, source, note}`,`source` ∈ `env`(本机 env 显式)/ `center`(组织下发)/ `posture`(单机派生)/
|
|
623
|
+
`engine-default`(没钉,引擎当家),`note` 是指路句。
|
|
624
|
+
- **显式值恒赢**:优先序 `env` > `center` > `posture` > 引擎默认;引擎抬默认/扩内建集那天,没钉过的
|
|
625
|
+
多租户部署自动跟上(与重试上限同款「引擎当家」纪律)。
|
|
614
626
|
- **leader 车道同席(7.54 起)**:`LEADER_ENABLED` 的 leader 编排(planner / worker / repair / conflict 四类
|
|
615
627
|
任务)与主车道吃**同一份**四键(连同 `MEMORY_DELEGATION_EVIDENCE` / `MEMORY_PROVENANCE` / `MEMORY_CAPTURE_POLICY`
|
|
616
628
|
与委派入口 caps;取值点唯一 = `buildDeploymentPostureSeats`,leader 不另读 env)。此前(≤7.53)那条腿一席都不带:
|
|
@@ -625,7 +637,8 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
625
637
|
保留 env 前值 + `sema_registry_read_face_invalid` warn(半应用一份 READ 姿态比不应用更危险);
|
|
626
638
|
③ **restart-to-apply**——四键在 boot 期被引擎装配捕获,下发改动经 `/health` 的 `restart.reasons`
|
|
627
639
|
带 `read-face` 通告,由编排器重启兑现。当前生效档可从 `GET /v1/capabilities` 的 `readFace` 位读
|
|
628
|
-
(`"open"`/`"roots"`/`null`=本部署未钉,引擎默认当家;deny 表内容属策略内容,**不上**能力面)
|
|
640
|
+
(`"open"`/`"roots"`/`null`=本部署未钉,引擎默认当家;deny 表内容属策略内容,**不上**能力面);
|
|
641
|
+
**来源**(env / center / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
|
|
629
642
|
⚠️ 本地 `config.d/` 车道今天**接不上**这一域(registry-core 的可移植域表里没有它),只有
|
|
630
643
|
`CONFIG_PROVIDER=remote` 的部署走得通。
|
|
631
644
|
|
|
@@ -3,7 +3,6 @@ import type { PromptsDomainFaces } from "../prompts-domain-validate.js";
|
|
|
3
3
|
import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
|
|
4
4
|
import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
|
|
5
5
|
import { selectScenario } from "../capabilities/scenarios.js";
|
|
6
|
-
import { type HandsLaneRegistry } from "../capabilities/hands-lane.js";
|
|
7
6
|
import type { ServiceConfig } from "../config.js";
|
|
8
7
|
import { type LiveQuestionFace } from "../deployment-governance.js";
|
|
9
8
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
@@ -23,10 +22,6 @@ type PrincipalCaps = ReturnType<typeof createPrincipalEntitlementsClient>;
|
|
|
23
22
|
/** `resolveSpec` 原先从 `main()` 闭包里拿到的全部 boot 局部量。 */
|
|
24
23
|
export interface ResolveSpecCtx {
|
|
25
24
|
config: ServiceConfig;
|
|
26
|
-
/** [ref]:hands lane 登记簿。resolveSpec 在产出 TaskSpec 时按场景判别位登记本请求的表态,HTTP 执行点
|
|
27
|
-
* 凭同一只登记簿选 Runner —— **必须与 main.ts 传给 `runnerFor` 的是同一实例**(异实例 = 每个请求都
|
|
28
|
-
* 落回 full,收窄静默失效)。 */
|
|
29
|
-
handsLanes: HandsLaneRegistry;
|
|
30
25
|
logger: Logger;
|
|
31
26
|
metrics: Metrics;
|
|
32
27
|
localRoot: string;
|
|
@@ -6,7 +6,7 @@ import { centerPromptProvider, centerIdentityAssembled } from "../capabilities/c
|
|
|
6
6
|
import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
|
|
7
7
|
import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
|
|
8
8
|
import { gateScenarioRequest, mergeUserSkills, selectScenario } from "../capabilities/scenarios.js";
|
|
9
|
-
import { toolPolicyForHands } from "../capabilities/hands-lane.js";
|
|
9
|
+
import { stampHandsLane, toolPolicyForHands } from "../capabilities/hands-lane.js";
|
|
10
10
|
import { shouldDiscloseNoPersistentMemory, withNoPersistentMemoryNotice } from "../capabilities/memory-notice.js";
|
|
11
11
|
import { applyLongtailDefer } from "../capabilities/tool-defer.js";
|
|
12
12
|
import { assertGuardPatternsUsable, buildOnlySensitiveBaselineWarning, createApprovalBaselinePolicy, createDeploymentGovernanceInputs } from "../deployment-governance.js";
|
|
@@ -36,7 +36,7 @@ import { redactSecrets } from "../trace/redact.js";
|
|
|
36
36
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
37
37
|
import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
|
|
38
38
|
export function createResolveSpec(ctx) {
|
|
39
|
-
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver,
|
|
39
|
+
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, noteSessionShellGate, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
|
|
40
40
|
assertGuardPatternsUsable(config);
|
|
41
41
|
const onlySensitiveBaseline = buildOnlySensitiveBaselineWarning(config, { durableEnabled, singleUserAutoAcceptBaseline });
|
|
42
42
|
if (onlySensitiveBaseline)
|
|
@@ -62,8 +62,7 @@ export function createResolveSpec(ctx) {
|
|
|
62
62
|
scratchpadDir: folded.scratchpadDir,
|
|
63
63
|
hostSemanticsLane: folded.hostSemanticsLane,
|
|
64
64
|
});
|
|
65
|
-
|
|
66
|
-
return final;
|
|
65
|
+
return stampHandsLane(final, gated.hands);
|
|
67
66
|
};
|
|
68
67
|
const gateScenarioAndAppend = async (body, auth, opts) => {
|
|
69
68
|
const requested = gateScenarioRequest(await principalCaps?.scenarioRuling(auth?.principal), body.scenario, config.defaultScenario, Object.keys(scenarios), logger);
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* 不是静默继承 full(词表闭集纪律)。
|
|
13
13
|
* ② {@link HANDS_EXCLUSIVE_TOOL_NAMES} + {@link handsDenyPolicy} —— 纵深:hands=none 的任务即便被误接到
|
|
14
14
|
* 有手 Runner 上,手独占名单逐一 deny。**不是**主保证(主保证是根本不挂 factory ⇒ 工具面不可见)。
|
|
15
|
-
* ③ {@link
|
|
15
|
+
* ③ {@link stampHandsLane} / {@link handsLaneOf} —— 车道作为 TaskSpec 上的**显式字段**(S-149):
|
|
16
|
+
* resolveSpec 出口铸一次,执行点读一次,**缺席即拒**(不是回落最宽车道)。
|
|
16
17
|
*/
|
|
17
18
|
import { type Runner, type TaskSpec, type ToolPolicy } from "@sema-agent/core";
|
|
18
19
|
/** 场景对手带工具面的表态(闭集)。`full` = core 手带 band 照常 mount;`none` = 不挂 executionEnvFactory 的 Runner。 */
|
|
@@ -82,19 +83,58 @@ export declare function tightenWithHandsDeny(base: ToolPolicy | undefined): Tool
|
|
|
82
83
|
/** 装配点的取用形:`full` 原样放行基线,`none` 叠 deny。**穷举 switch**,无缺省臂。 */
|
|
83
84
|
export declare function toolPolicyForHands(hands: ScenarioHands, base: ToolPolicy | undefined): ToolPolicy | undefined;
|
|
84
85
|
/**
|
|
85
|
-
*
|
|
86
|
+
* 执行车道在 TaskSpec 上的**显式字段键**(S-149;修前是一张以 spec **对象身份**为键的 WeakMap 旁表)。
|
|
86
87
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
88
|
+
* 为什么是 symbol 而不是字符串键:`TaskSpec` 是**上游 core 的类型面**,一个字符串键会进
|
|
89
|
+
* `Object.keys` / `JSON.stringify` / core 自己的键审计,等于 server 在别人的契约上私自加一个键;
|
|
90
|
+
* symbol 键随 spread(`{...spec}`)与 rest 解构(`const {objective, ...taskConfig} = spec`)结构性
|
|
91
|
+
* 地跟着走,却对上游的键面**不可见**(结构化克隆与 JSON 同样跳过 symbol 键)。于是「车道是 spec 上
|
|
92
|
+
* 的一个字段、复制 spec 不丢车道」与「不污染上游契约」两件事同时成立。
|
|
91
93
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
+
* 修前的旁表要求「从 resolveSpec 出来的那一只对象必须一路原样活到执行点」——一条**隐形**的对象身份
|
|
95
|
+
* 契约:任何一次 `{...spec}` 都会静默把车道降级成最宽的 `full`(实测:登记 `none` 的 spec 复制后
|
|
96
|
+
* `laneOf` 答 `full`)。字段化把那条隐形要求整条删掉。
|
|
94
97
|
*/
|
|
95
|
-
export
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
export declare const HANDS_LANE_FIELD: unique symbol;
|
|
99
|
+
/** 已铸车道的 TaskSpec。铸点唯一(`boot/resolve-spec.ts` 的 resolveSpec 出口),读点唯一({@link handsLaneOf})。 */
|
|
100
|
+
export type LanedTaskSpec = TaskSpec & {
|
|
101
|
+
readonly [HANDS_LANE_FIELD]: ScenarioHands;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* 铸车道(**单一属主**:只允许 resolveSpec 的出口调用)。
|
|
105
|
+
*
|
|
106
|
+
* 字段是 `enumerable`(所以复制得走)、`writable:false` + `configurable:false`(所以同一只 spec 被
|
|
107
|
+
* 第二次铸车道会当场 TypeError —— 两个写者是漂移的成因,响亮好过后到者悄悄赢)。
|
|
108
|
+
*/
|
|
109
|
+
export declare function stampHandsLane<T extends TaskSpec>(spec: T, hands: ScenarioHands): T & LanedTaskSpec;
|
|
110
|
+
/**
|
|
111
|
+
* 车道字段缺席的拒因 —— 具名类,便于调用点/测试按类型判,而不是嗅文案。
|
|
112
|
+
*
|
|
113
|
+
* 🔴 **刻意不是 `HttpError`、刻意不铸新的 wire 错误码**:它落到 `http/server.ts` 的兜底 catch,
|
|
114
|
+
* wire 上是 500 `internal.error`(内部文本不回显,只进 `request_error` 日志行)。理由两条:
|
|
115
|
+
* ① 对**消费端**它与任何一次服务端内部故障无差别可操作性(重试一次,仍复现即报运维)—— 附录 A 多一个
|
|
116
|
+
* 码却给不出不同的动作,是没有价值的契约面;
|
|
117
|
+
* ② 本仓的中继码登记表(`test/error-code-catalog-live.test.ts` 的 `RELAYED`,上限 24 条)明写「超了说明
|
|
118
|
+
* 『码不写字面量』正在变成默认写法,该反过来改代码」—— 一个只能靠登记才看得见的新码正是它拦的东西。
|
|
119
|
+
* 需要分辨的是**运维**,而运维读的是日志行(本 message 逐字在 `request_error` 里)。
|
|
120
|
+
*/
|
|
121
|
+
export declare class HandsLaneUnstampedError extends Error {
|
|
122
|
+
constructor();
|
|
98
123
|
}
|
|
99
|
-
|
|
124
|
+
/**
|
|
125
|
+
* 读车道 —— **缺席即拒**([ref] P 类:车道选择是保护型量,fail-closed;`observability/fail-open.ts`
|
|
126
|
+
* 头注把「车道选择」逐字列在「正解是 fail-closed,不是留痕后继续放行」那一族里)。
|
|
127
|
+
*
|
|
128
|
+
* 修前此处是 `?? "full"`:一只没经过本仓 resolver 的 spec 会**静默拿到最宽的车道**(全量可写手带)。
|
|
129
|
+
* 「唯一可达形是测试桩」这个论证不是防线 —— 它是一句关于今天调用图的断言,而防线要防的正是明天多出
|
|
130
|
+
* 来的第二个执行点。缺席现在整只拒(500 + 日志留原句),而不是把一个未经裁定的任务放进有手车道。
|
|
131
|
+
*/
|
|
132
|
+
export declare function handsLaneOf(spec: TaskSpec): ScenarioHands;
|
|
133
|
+
/**
|
|
134
|
+
* 装配点的选路席:`{full, handsless}` 两只 Runner + 车道字段 ⇒ 本任务跑哪一只。
|
|
135
|
+
*
|
|
136
|
+
* 具名工厂而不是在 main.ts 手搓闭包:选路规则(读字段 → 穷举 switch)于是只有**一份实现**,
|
|
137
|
+
* 测试与生产装配拿的是同一只(手搓等价闭包 = 第二真源,正是本件要删的那一类)。
|
|
138
|
+
*/
|
|
139
|
+
export declare function createHandsRunnerSelector(pair: HandsRunnerPair): (spec: TaskSpec) => Runner;
|
|
100
140
|
//# sourceMappingURL=hands-lane.d.ts.map
|
|
@@ -36,13 +36,24 @@ export function toolPolicyForHands(hands, base) {
|
|
|
36
36
|
return tightenWithHandsDeny(base);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
export const HANDS_LANE_FIELD = Symbol("sema-server.handsLane");
|
|
40
|
+
export function stampHandsLane(spec, hands) {
|
|
41
|
+
Object.defineProperty(spec, HANDS_LANE_FIELD, { value: hands, enumerable: true, writable: false, configurable: false });
|
|
42
|
+
return spec;
|
|
43
|
+
}
|
|
44
|
+
export class HandsLaneUnstampedError extends Error {
|
|
45
|
+
constructor() {
|
|
46
|
+
super("execution lane is not stamped on this task spec — refusing to run it rather than falling back to the widest (full-hands) lane");
|
|
47
|
+
this.name = "HandsLaneUnstampedError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function handsLaneOf(spec) {
|
|
51
|
+
const lane = spec[HANDS_LANE_FIELD];
|
|
52
|
+
if (lane === undefined)
|
|
53
|
+
throw new HandsLaneUnstampedError();
|
|
54
|
+
return lane;
|
|
55
|
+
}
|
|
56
|
+
export function createHandsRunnerSelector(pair) {
|
|
57
|
+
return (spec) => pickHandsRunner(handsLaneOf(spec), pair);
|
|
47
58
|
}
|
|
48
59
|
//# sourceMappingURL=hands-lane.js.map
|
package/dist/config-catalog.js
CHANGED
|
@@ -321,7 +321,7 @@ export const CONFIG_CATALOG = [
|
|
|
321
321
|
r("READ_DENY_BUILTIN_EXCLUDE", "orchestration", "csv", "按行名删内建 deny 条目(档位之后生效;绝不削任务显式 deny)", { danger: SEC, configKey: "readDenyBuiltinExclude", center: cRead("denyBuiltinExclude", "readDenyBuiltinExclude") }),
|
|
322
322
|
r("READ_DENY_BUILTIN_TIERS", "orchestration", "csv", "内建 deny 表档位选择(闭集四档;none=显式空姿态)", { danger: SEC, configKey: "readDenyBuiltinTiers", center: cRead("denyBuiltinTiers", "readDenyBuiltinTiers") }),
|
|
323
323
|
r("READ_DENY_PATTERNS", "orchestration", "csv", "READ 面额外 deny pattern 表", { danger: SEC, configKey: "readDenyPatterns", center: cRead("denyPatterns", "readDenyPatterns") }),
|
|
324
|
-
r("READ_FACE", "orchestration", "enum", "READ 容纳面档位(env 逐键恒赢 center —— 部署主权表达)", { enumValues: ["open", "roots"], danger: SEC, derivedDefaultNote: "缺席 ⇒
|
|
324
|
+
r("READ_FACE", "orchestration", "enum", "READ 容纳面档位(env 逐键恒赢 center —— 部署主权表达)", { enumValues: ["open", "roots"], danger: SEC, derivedDefaultNote: "缺席 ⇒ 按 posture 派生(S-167):单用户 turnkey(REQUIRE_PRINCIPAL 未设)铸 open;多租户不铸键、core 默认 roots。生效来源见 GET /v1/diagnostics/wiring 的 readFace.source 四词", configKey: "readFace", center: cRead("face", "readFace") }),
|
|
325
325
|
r("REAP_INTERVAL_SEC", "limitsHttp", "number", "reaper 周期(秒)", { staticDefault: "60", configKey: "reapIntervalSec" }),
|
|
326
326
|
r("REAP_RUN_STALE_SEC", "limitsHttp", "number", "run 活性判陈旧阈(秒)", { staticDefault: "120", configKey: "runStaleSec", center: cLimits("runStaleSec") }),
|
|
327
327
|
r("REMOTE_EXEC", "orchestration", "enum", "执行车道选择(点名而配不齐/认不出 ⇒ 拒启,绝不静默降级到 host/stub)", { enumValues: ["e2b", "k8s", "ssh", "adb", "host", "local-docker", "device"], danger: SEC, derivedDefaultNote: "缺席:CONFIG_PROVIDER=local ⇒ host;其余 ⇒ 进程内 stub", configKey: "remoteExec", resolve: (c) => c.remoteExec?.provider ?? null }),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { compileReadDeny, resolveReadDenyBuiltins } from "@sema-agent/core";
|
|
2
2
|
const KEY_MAP = [
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
{ centerKey: "face", cfgKey: "readFace", envHolds: (c) => c.readFaceSource === "env" },
|
|
4
|
+
{ centerKey: "denyPatterns", cfgKey: "readDenyPatterns", envHolds: (c) => c.readDenyPatterns !== undefined },
|
|
5
|
+
{ centerKey: "denyBuiltinTiers", cfgKey: "readDenyBuiltinTiers", envHolds: (c) => c.readDenyBuiltinTiers !== undefined },
|
|
6
|
+
{ centerKey: "denyBuiltinExclude", cfgKey: "readDenyBuiltinExclude", envHolds: (c) => c.readDenyBuiltinExclude !== undefined },
|
|
7
7
|
];
|
|
8
8
|
class ReadFaceDomainError extends Error {
|
|
9
9
|
}
|
|
@@ -43,7 +43,7 @@ function normalizeNameList(field, raw) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
export function applyCenterReadFace(config, eff, logger) {
|
|
46
|
-
const envHeldKeys = KEY_MAP.filter((
|
|
46
|
+
const envHeldKeys = KEY_MAP.filter((r) => r.envHolds(config)).map((r) => r.centerKey);
|
|
47
47
|
const domain = eff.readFace;
|
|
48
48
|
if (domain === undefined || domain === null)
|
|
49
49
|
return envHeldKeys;
|
|
@@ -55,7 +55,7 @@ export function applyCenterReadFace(config, eff, logger) {
|
|
|
55
55
|
return envHeldKeys;
|
|
56
56
|
}
|
|
57
57
|
const d = domain;
|
|
58
|
-
const envHeld = KEY_MAP.filter((
|
|
58
|
+
const envHeld = KEY_MAP.filter((r) => d[r.centerKey] !== undefined && r.envHolds(config)).map((r) => `${r.cfgKey} (readFace.${r.centerKey})`);
|
|
59
59
|
let staged;
|
|
60
60
|
try {
|
|
61
61
|
const normalized = {
|
|
@@ -69,7 +69,7 @@ export function applyCenterReadFace(config, eff, logger) {
|
|
|
69
69
|
if (normalized.readDenyBuiltinTiers !== undefined || normalized.readDenyBuiltinExclude !== undefined) {
|
|
70
70
|
resolveReadDenyBuiltins(builtinsArg(normalized.readDenyBuiltinTiers, normalized.readDenyBuiltinExclude));
|
|
71
71
|
}
|
|
72
|
-
staged = Object.fromEntries(KEY_MAP.filter((
|
|
72
|
+
staged = Object.fromEntries(KEY_MAP.filter((r) => !r.envHolds(config)).map((r) => [r.cfgKey, normalized[r.cfgKey]]).filter(([, v]) => v !== undefined));
|
|
73
73
|
const finalPatterns = staged.readDenyPatterns ?? config.readDenyPatterns;
|
|
74
74
|
const finalTiers = staged.readDenyBuiltinTiers ?? config.readDenyBuiltinTiers;
|
|
75
75
|
const finalExclude = staged.readDenyBuiltinExclude ?? config.readDenyBuiltinExclude;
|
|
@@ -85,8 +85,10 @@ export function applyCenterReadFace(config, eff, logger) {
|
|
|
85
85
|
});
|
|
86
86
|
return envHeldKeys;
|
|
87
87
|
}
|
|
88
|
-
if (staged.readFace !== undefined)
|
|
88
|
+
if (staged.readFace !== undefined) {
|
|
89
89
|
config.readFace = staged.readFace;
|
|
90
|
+
config.readFaceSource = "center";
|
|
91
|
+
}
|
|
90
92
|
if (staged.readDenyPatterns !== undefined)
|
|
91
93
|
config.readDenyPatterns = staged.readDenyPatterns;
|
|
92
94
|
if (staged.readDenyBuiltinTiers !== undefined)
|
package/dist/config-types.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* in config.ts, which re-exports these names so existing `from "./config.js"` importers keep
|
|
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
|
+
import type { ReadFaceSource } from "./read-face-posture.js";
|
|
8
9
|
import type { SealedKeyPoison } from "./sealed-key.js";
|
|
9
10
|
import type { A2aServerSpec, CompliancePosture, CrossSessionDialogExpiry, CrossSessionInboundSetting, FileHistoryRetentionPolicy, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy, WriteProtectedEntry } from "@sema-agent/core";
|
|
10
11
|
import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
|
|
@@ -1539,9 +1540,17 @@ export interface ServiceConfigFlat {
|
|
|
1539
1540
|
/** Task Trace API (S1): persist the agent's reasoning/thinking into the durable run event log (for the CC-style
|
|
1540
1541
|
* trace UI). Default ON; `TRACE_THINKING=false` disables capture for sensitive deployments. Redacted on persist. */
|
|
1541
1542
|
traceThinking: boolean;
|
|
1542
|
-
/** [ref](core 5.30.0 readFace 部署座席):READ
|
|
1543
|
-
*
|
|
1543
|
+
/** [ref](core 5.30.0 readFace 部署座席):READ 容纳面。显式坏词启动期响亮拒;只影响读面,写面永不
|
|
1544
|
+
* 受它管(core 契约)。
|
|
1545
|
+
* 🔴 **S-167(7.65.0)起「缺席」按 posture 分两形**(clay 裁定 2):单用户 turnkey(`REQUIRE_PRINCIPAL`
|
|
1546
|
+
* 未设)⇒ 铸 `open`(工作区外读默认放行);多租户 ⇒ 仍**不铸**(core 引擎默认 roots,server 不写死
|
|
1547
|
+
* 上游默认)。谁定的这一档由 {@link ServiceConfigFlat.readFaceSource} 答 —— 值与来源**同一处写**。 */
|
|
1544
1548
|
readFace?: "open" | "roots";
|
|
1549
|
+
/** S-167:上面那一档**是谁定的**(闭集四词,见 `read-face-posture.ts`)。不变量:
|
|
1550
|
+
* `readFace === undefined` ⟺ `readFaceSource === "engine-default"`。
|
|
1551
|
+
* 🔴 承重面**不是**装饰:`config-center/read-face.ts` 的「env 恒赢」逐键判据读的就是它(修前读的是
|
|
1552
|
+
* 「键在不在场」这个代理,posture 会铸键 ⇒ 代理当场失真,center 的 face 会被从没设过 env 的机器挡掉)。 */
|
|
1553
|
+
readFaceSource: ReadFaceSource;
|
|
1545
1554
|
/** [ref]:部署级 READ deny 追加表(additive-only,core 内建表永不可缩)。env=JSON 数组
|
|
1546
1555
|
* [{pattern, caseSensitive?}],boot 时用 core `compileReadDeny` 预验——坏形拒启。缺席=不铸。 */
|
|
1547
1556
|
readDenyPatterns?: {
|
|
@@ -1674,7 +1683,7 @@ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled"
|
|
|
1674
1683
|
* config 这一层它只是一句声明。 */
|
|
1675
1684
|
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">;
|
|
1676
1685
|
/** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
|
|
1677
|
-
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" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
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">;
|
|
1678
1687
|
/** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
|
|
1679
1688
|
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">;
|
|
1680
1689
|
/** 组:observability(可观测)。 */
|
package/dist/config.js
CHANGED
|
@@ -12,6 +12,7 @@ import { GIT_API_KINDS, isGitApiKind } from "./git-api-kind.js";
|
|
|
12
12
|
import { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config-types.js";
|
|
13
13
|
import { buildWriteProtectionPosture } from "./write-protection.js";
|
|
14
14
|
import { normalizeSshFingerprint } from "./ssh-host-key.js";
|
|
15
|
+
import { readFaceForPosture } from "./read-face-posture.js";
|
|
15
16
|
import { CONTROL_AND_BIDI_STRIP_RE } from "./text-bidi.js";
|
|
16
17
|
export { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config-types.js";
|
|
17
18
|
function csv(name) {
|
|
@@ -1458,7 +1459,7 @@ function parseAuthDomain(ctx) {
|
|
|
1458
1459
|
};
|
|
1459
1460
|
}
|
|
1460
1461
|
function parseOrchestrationDomain(ctx) {
|
|
1461
|
-
const { postureOn } = ctx;
|
|
1462
|
+
const { postureOn, singleUserTurnkey } = ctx;
|
|
1462
1463
|
const workflowSizeGuidelineRaw = process.env.WORKFLOW_SIZE_GUIDELINE;
|
|
1463
1464
|
if (workflowSizeGuidelineRaw !== undefined && !["small", "medium", "large", "unrestricted"].includes(workflowSizeGuidelineRaw)) {
|
|
1464
1465
|
throw new Error(`WORKFLOW_SIZE_GUIDELINE must be small|medium|large|unrestricted, got "${workflowSizeGuidelineRaw}"`);
|
|
@@ -1501,7 +1502,9 @@ function parseOrchestrationDomain(ctx) {
|
|
|
1501
1502
|
}
|
|
1502
1503
|
}
|
|
1503
1504
|
return {
|
|
1504
|
-
...(process.env.READ_FACE !== undefined
|
|
1505
|
+
...(process.env.READ_FACE !== undefined
|
|
1506
|
+
? { readFace: enumEnv("READ_FACE", "roots", ["open", "roots"]), readFaceSource: "env" }
|
|
1507
|
+
: readFaceForPosture(singleUserTurnkey)),
|
|
1505
1508
|
...(process.env.READ_DENY_PATTERNS !== undefined ? { readDenyPatterns: parseReadDenyPatternsEnv(process.env.READ_DENY_PATTERNS) } : {}),
|
|
1506
1509
|
...(() => {
|
|
1507
1510
|
const tiers = process.env.READ_DENY_BUILTIN_TIERS !== undefined ? parseNameListEnv("READ_DENY_BUILTIN_TIERS", process.env.READ_DENY_BUILTIN_TIERS, { emptySentinel: "none" }) : undefined;
|
|
@@ -1905,7 +1908,7 @@ const ORCHESTRATION_GROUP_KEYS = [
|
|
|
1905
1908
|
"workflowSizeGuideline", "backgroundAgentRetentionMs", "backgroundAgentStaleRunningMs",
|
|
1906
1909
|
"backgroundAgentParkClaimStaleMs", "delegationEntryCaps", "rosterRetentionMs", "leaderRunStaleMs", "scratchpadSweepTtlMs", "sandboxPkgSource",
|
|
1907
1910
|
"sessionAutoTitle", "selectEnvironmentTool", "envFactsEnabled", "toolDeferLongtail", "lspEnabled", "lspHostEnabled",
|
|
1908
|
-
"imageBakes", "readFace", "readDenyPatterns", "readDenyBuiltinTiers", "readDenyBuiltinExclude",
|
|
1911
|
+
"imageBakes", "readFace", "readFaceSource", "readDenyPatterns", "readDenyBuiltinTiers", "readDenyBuiltinExclude",
|
|
1909
1912
|
];
|
|
1910
1913
|
const LIMITS_HTTP_GROUP_KEYS = [
|
|
1911
1914
|
"port", "attachmentOrphanGraceMs", "workspaceFileMaxBytes", "attachmentMaxBytes", "attachmentMimeAllowlist",
|
|
@@ -1973,7 +1976,7 @@ export function loadConfig() {
|
|
|
1973
1976
|
const approval = parseApprovalDomain({ postureOn });
|
|
1974
1977
|
const memory = parseMemoryDomain({ requirePrincipal });
|
|
1975
1978
|
const auth = parseAuthDomain({ requirePrincipal });
|
|
1976
|
-
const orchestration = parseOrchestrationDomain({ postureOn });
|
|
1979
|
+
const orchestration = parseOrchestrationDomain({ postureOn, singleUserTurnkey });
|
|
1977
1980
|
const limitsHttp = parseLimitsHttpDomain();
|
|
1978
1981
|
const observability = parseObservabilityDomain();
|
|
1979
1982
|
const integrations = parseIntegrationsDomain();
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [ref] 件二+件三 —— **装配自证**域:启动期静态自检(拒启判据)与 operator 诊断读面
|
|
3
|
-
* (`GET /v1/diagnostics/wiring`)。
|
|
4
|
-
*
|
|
5
|
-
* 🔴 两件为什么同住一个模块:它们回答的是同一个问题(「这台 worker 到底接了什么线」),只是一个在 boot
|
|
6
|
-
* 用它拒绝启动、一个在运行期把它读给 operator。分成两处 = 拒启用的判据与诊断报的读数会各自演化,而
|
|
7
|
-
* 「拒启说没矛盾、诊断页显示矛盾」正是本设计要消灭的那类装配谎言。故 boot 半场是本文件导出的**纯函数**
|
|
8
|
-
* (可单测、无 I/O),路由半场只是它的读口。
|
|
9
|
-
*
|
|
10
|
-
* 分层:本模块不值 import `server.ts`(那条边会闭合运行时装载环),对它只有 `import type`。
|
|
11
|
-
*/
|
|
12
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
13
2
|
import type { WiringManifest } from "@sema-agent/core";
|
|
14
3
|
import { type StreamApprovalGate, type StreamApprovalGateInput } from "../../tool-approval.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildReadFaceWiring } from "../../read-face-posture.js";
|
|
1
2
|
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
2
3
|
import { sendJson, sendError } from "../send.js";
|
|
3
4
|
import { gatedPrincipal, explicitOperatorOk, isLoopbackAddress, verifiedIdentitySource } from "../principal-gate.js";
|
|
@@ -90,6 +91,7 @@ async function handleDiagnosticsBody(req, res, url, ctx, miss) {
|
|
|
90
91
|
memoryPosture: deps.memoryPosture ?? null,
|
|
91
92
|
sqlEngine: deps.sqlEngineFacts?.() ?? null,
|
|
92
93
|
writeProtection: deps.writeProtection ?? null,
|
|
94
|
+
readFace: buildReadFaceWiring(deps.config),
|
|
93
95
|
configApply: deps.configApplyState?.() ?? null,
|
|
94
96
|
mcpRevocations: deps.mcpRevocationState?.() ?? null,
|
|
95
97
|
serverGates: buildServerWiringGates({
|
package/dist/main.js
CHANGED
|
@@ -20,7 +20,7 @@ import { assertGateIntentServiceable, buildForceDurableGateInertNotice, hasOpera
|
|
|
20
20
|
import { loadSkills } from "./capabilities/skills.js";
|
|
21
21
|
import { createRepoClient } from "./capabilities/repo-tools.js";
|
|
22
22
|
import { buildScenarios, builtinScenarioDetails } from "./capabilities/scenarios.js";
|
|
23
|
-
import {
|
|
23
|
+
import { createHandsRunnerSelector, withoutExecutionEnv } from "./capabilities/hands-lane.js";
|
|
24
24
|
import { createLogger } from "./observability/logger.js";
|
|
25
25
|
import { createToolTracer, createPermissionDeniedMeter } from "./observability/tool-trace.js";
|
|
26
26
|
import { createRegistryJwtVerifier } from "./auth-bridge.js";
|
|
@@ -730,9 +730,7 @@ async function main() {
|
|
|
730
730
|
: undefined,
|
|
731
731
|
sessionEventsMaxConnections: sessionWatchRegistry ? posIntEnv(process.env.SESSION_EVENTS_MAX_CONNS, 256, 100_000) : undefined,
|
|
732
732
|
};
|
|
733
|
-
const handsLanes = createHandsLaneRegistry();
|
|
734
733
|
const resolveSpec = createResolveSpec({
|
|
735
|
-
handsLanes,
|
|
736
734
|
config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver,
|
|
737
735
|
getCenterPrompts: () => configCenter.getCenterPrompts(),
|
|
738
736
|
getKeyResolver: () => configCenter.getKeyResolver(),
|
|
@@ -746,7 +744,7 @@ async function main() {
|
|
|
746
744
|
liveQuestionFace: question,
|
|
747
745
|
approverSeat: createRunnerDepsOnAsk(toolApproval),
|
|
748
746
|
});
|
|
749
|
-
const runnerFor = (
|
|
747
|
+
const runnerFor = createHandsRunnerSelector({ full: runner, handsless: handslessRunner });
|
|
750
748
|
const sideQuery = createSideQueryLane({ brain, config, getKeyResolver: () => configCenter.getKeyResolver() });
|
|
751
749
|
const server = createHttpServer({ runner, runnerFor, sideQuery, config, resolveSpec, stores, coordinators, seams, observability, governance, deployment, knobs });
|
|
752
750
|
runDenySweep = server.denyExpiredApprovals;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-167(clay 裁定 2,[ref] 转达 / [ref] ②)—— **READ 容纳面档位的来源**(单一属主)。
|
|
3
|
+
*
|
|
4
|
+
* ## 它解决的问题
|
|
5
|
+
*
|
|
6
|
+
* `READ_FACE` 此前只有「显式设了就铸、缺席就不铸」两态,而**缺席**这一态里其实塞了两件完全不同的事:
|
|
7
|
+
* · 单机 turnkey(`REQUIRE_PRINCIPAL` 未设,CC 的信任模型:一台机器一个可信主人)—— 运维没表态是因为
|
|
8
|
+
* 他压根不认为工作区外的读需要围栏;引擎默认的 `roots` 于是成了一道**没人要求过**的墙(clay 裁:
|
|
9
|
+
* 「工作区外读默认放行(单机形)」)。
|
|
10
|
+
* · 多租户 —— 缺席就是缺席,引擎默认 `roots` 正是想要的收敛姿态。
|
|
11
|
+
*
|
|
12
|
+
* 于是本模块把「档位」与「档位是谁定的」拆成两个字段:值仍是 `open | roots | 缺席`,来源是本文件的
|
|
13
|
+
* **四词闭集**。来源不是装饰:
|
|
14
|
+
* ① `config-center/read-face.ts` 的 **env 恒赢**判据此前用的是「`config.readFace` 键在不在场」这个
|
|
15
|
+
* **代理**;posture 会铸键,代理当场失真(center 的 face 会被一台从没设过 env 的机器挡掉)。
|
|
16
|
+
* 换成读来源之后判据说的就是它真正想说的那件事:**这台机器的 env 显式占住了这个键吗**。
|
|
17
|
+
* ② 运维面:`GET /v1/diagnostics/wiring` 报 `{face, source, note}`,「为什么这台机器是 open」有答案
|
|
18
|
+
* (裁定的补偿项:升级即放开,得让运维看得见并知道怎么钉回)。
|
|
19
|
+
*
|
|
20
|
+
* ## 优先序(三态,与 `config.ts` 的 `postureOn` 同形)
|
|
21
|
+
* `env 显式` > `center 发布` > `posture 派生` > `引擎默认`。env 恒赢的论证逐字见 `config-center/read-face.ts`
|
|
22
|
+
* 头注①(READ 容纳面描述的是**这台机器的磁盘上有什么**,组织的下发面不知道)。
|
|
23
|
+
*
|
|
24
|
+
* ## 定界
|
|
25
|
+
* 本模块**只**管 `face` 一档。内建读拒表(`readDenyPatterns` / `readDenyBuiltinTiers` /
|
|
26
|
+
* `readDenyBuiltinExclude`)一个字节不动 —— 放开容纳面不等于放开凭证/shell 历史/钱包那几档 deny,
|
|
27
|
+
* 两者在 core 里是并列机制(deny 表在 open 面上照样拦)。core 的 `config.read_face_deployment_clamped`
|
|
28
|
+
* (只读挂载钳制)同样不受本件影响。
|
|
29
|
+
*/
|
|
30
|
+
/** 档位来源的闭集词表(顺序 = 优先序:env > center > posture > 引擎默认)。 */
|
|
31
|
+
export declare const READ_FACE_SOURCE_WORDS: readonly ["env", "center", "posture", "engine-default"];
|
|
32
|
+
/** 档位的来源(闭集四词)。加词 ⇒ {@link readFaceSourceNote} 的穷举 switch 编译红。 */
|
|
33
|
+
export type ReadFaceSource = (typeof READ_FACE_SOURCE_WORDS)[number];
|
|
34
|
+
/** 一台 worker 的 READ 面档位事实(值 + 来源)。`ServiceConfigFlat` 结构上满足本形。 */
|
|
35
|
+
export interface ReadFacePostureFacts {
|
|
36
|
+
readonly readFace?: "open" | "roots" | undefined;
|
|
37
|
+
readonly readFaceSource: ReadFaceSource;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 缺席 `READ_FACE` 时的 posture 派生(`config.ts` 的铸点调用;显式值不经此口)。
|
|
41
|
+
* 单用户 turnkey ⇒ `open`;多租户 ⇒ 不铸(引擎默认 `roots`,server 不复制上游默认)。
|
|
42
|
+
*/
|
|
43
|
+
export declare function readFaceForPosture(singleUserTurnkey: boolean): ReadFacePostureFacts;
|
|
44
|
+
/** 运维指路句 —— **穷举 switch,无缺省臂**:四词闭集加一个成员这里就编译红。 */
|
|
45
|
+
export declare function readFaceSourceNote(source: ReadFaceSource): string;
|
|
46
|
+
/** operator 诊断面的投影(`GET /v1/diagnostics/wiring`)。`face:null` = 本部署没钉档,引擎默认接管。 */
|
|
47
|
+
export declare function buildReadFaceWiring(facts: ReadFacePostureFacts): {
|
|
48
|
+
face: "open" | "roots" | null;
|
|
49
|
+
source: ReadFaceSource;
|
|
50
|
+
note: string;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=read-face-posture.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const READ_FACE_SOURCE_WORDS = ["env", "center", "posture", "engine-default"];
|
|
2
|
+
export function readFaceForPosture(singleUserTurnkey) {
|
|
3
|
+
return singleUserTurnkey ? { readFace: "open", readFaceSource: "posture" } : { readFaceSource: "engine-default" };
|
|
4
|
+
}
|
|
5
|
+
export function readFaceSourceNote(source) {
|
|
6
|
+
switch (source) {
|
|
7
|
+
case "env":
|
|
8
|
+
return "pinned by this machine's READ_FACE env var (deployment sovereignty — it wins over any published readFace domain)";
|
|
9
|
+
case "center":
|
|
10
|
+
return "applied from the config-center readFace domain (restart-to-apply); set READ_FACE on this machine to take the key back";
|
|
11
|
+
case "posture":
|
|
12
|
+
return "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set): reads outside the workspace roots are allowed by default — set READ_FACE=roots to pin the containing posture back";
|
|
13
|
+
case "engine-default":
|
|
14
|
+
return "not pinned by this deployment — the engine's own default (roots) is in force";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function buildReadFaceWiring(facts) {
|
|
18
|
+
return { face: facts.readFace ?? null, source: facts.readFaceSource, note: readFaceSourceNote(facts.readFaceSource) };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=read-face-posture.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.65.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|