@sema-agent/server 7.20.0 → 7.21.0-rc.1
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 +31 -3
- package/dist/adoption/runner.js +4 -5
- package/dist/approval-card.d.ts +52 -0
- package/dist/approval-card.js +108 -0
- package/dist/boot/governance-seams.d.ts +112 -0
- package/dist/boot/governance-seams.js +151 -0
- package/dist/boot/leader.d.ts +3 -0
- package/dist/boot/leader.js +7 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +10 -2
- package/dist/boot/runner-deps.d.ts +6 -2
- package/dist/boot/runner-deps.js +13 -0
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +24 -5
- package/dist/config-types.d.ts +50 -4
- package/dist/config.js +129 -3
- package/dist/fleet/fleet-bus.d.ts +23 -2
- package/dist/fleet/fleet-bus.js +41 -2
- package/dist/fleet/subagent-tail-bus.d.ts +59 -0
- package/dist/fleet/subagent-tail-bus.js +60 -0
- package/dist/http/routes/capabilities.js +27 -0
- package/dist/http/routes/diagnostics.js +6 -0
- package/dist/http/routes/runs.js +10 -1
- package/dist/http/routes/tasks.js +15 -4
- package/dist/http/server.d.ts +5 -0
- package/dist/http/server.js +110 -7
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +12 -5
- package/dist/main.js +33 -8
- package/dist/memory-posture.d.ts +80 -0
- package/dist/memory-posture.js +40 -0
- package/dist/observability/fail-open.d.ts +4 -0
- package/dist/observability/fail-open.js +4 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +2 -1
- package/dist/parked-decide.js +9 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +3 -1
- package/dist/plugins/approval-ask-store-sql.js +5 -6
- package/dist/plugins/background-agent-store-sql.js +4 -1
- package/dist/plugins/checkpoint-store-sql.d.ts +2 -0
- package/dist/plugins/checkpoint-store-sql.js +4 -5
- package/dist/plugins/file-run-store.d.ts +2 -1
- package/dist/plugins/image-bake-store-sql.js +5 -5
- package/dist/plugins/memory-embedder-fingerprint.d.ts +23 -0
- package/dist/plugins/memory-embedder-fingerprint.js +28 -1
- package/dist/plugins/memory-engine-tidb.js +3 -5
- package/dist/plugins/memory-engine-vector-util.d.ts +0 -5
- package/dist/plugins/memory-engine-vector-util.js +10 -4
- package/dist/plugins/memory-run-store.d.ts +2 -1
- package/dist/plugins/pg-session-storage.js +3 -3
- package/dist/plugins/run-store-sql.d.ts +12 -3
- package/dist/plugins/run-store-sql.js +4 -3
- package/dist/plugins/session-policy-store-sql.d.ts +1 -0
- package/dist/plugins/session-policy-store-sql.js +3 -3
- package/dist/plugins/shared-memory-store-sql.js +4 -10
- package/dist/plugins/sql-driver.d.ts +7 -5
- package/dist/plugins/sql-errors.d.ts +41 -0
- package/dist/plugins/sql-errors.js +25 -0
- package/dist/plugins/tidb-session-storage.js +3 -3
- package/dist/plugins/workflow-run-store-sql.js +4 -4
- package/dist/run-local.js +15 -0
- package/dist/runs.js +76 -38
- package/dist/task-mcp.d.ts +22 -1
- package/dist/task-mcp.js +71 -1
- package/dist/tool-approval.d.ts +35 -0
- package/dist/tool-approval.js +4 -1
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-events.d.ts +43 -0
- package/dist/trace/ledger-events.js +2 -0
- package/dist/trace/ledger-sink.d.ts +3 -2
- package/dist/trace/project.d.ts +4 -1
- package/dist/trace/project.js +15 -0
- package/package.json +2 -2
|
@@ -46,13 +46,13 @@ import { memorySpecForRequest } from "../memory-scope.js";
|
|
|
46
46
|
import { mcpForScenario } from "../config-center/facade.js";
|
|
47
47
|
import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptProfileFromBody, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolMaterializeStrategyFromBody, toolNameListFromBody } from "../spec-fields.js";
|
|
48
48
|
import { cwdHonored, effectiveHostWorkspace, inProcessSingleUserLane, isValidCwd, parseAdditionalDirectories, satisfiedByProcessCwd, shellEnvMismatchCount } from "../task-cwd.js";
|
|
49
|
-
import { resolveRequestMcp } from "../task-mcp.js";
|
|
49
|
+
import { assertRequestMcpUnlocked, resolveRequestMcp } from "../task-mcp.js";
|
|
50
50
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSettings, effectivePermissionMode, effectiveThinking, hasConstitutionAnchors, parseTaskSettings, providerDropsAppend, shellGateForMode, withPermissionMode } from "../task-settings.js";
|
|
51
51
|
import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
|
|
52
52
|
import { redactSecrets } from "../trace/redact.js";
|
|
53
53
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
54
54
|
export function createResolveSpec(ctx) {
|
|
55
|
-
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, } = ctx;
|
|
55
|
+
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, lockedKeys, } = ctx;
|
|
56
56
|
// ── #177 的两件 boot 期收口(把守卫集搬到 governance 拍之后才成立的两条)────────────────────────
|
|
57
57
|
//
|
|
58
58
|
// ① 守卫集的**编译**从此每个请求都发生(旧家只在 default/auto/acceptEdits 三个模式臂里发生)。
|
|
@@ -75,6 +75,12 @@ export function createResolveSpec(ctx) {
|
|
|
75
75
|
// design/158 A8:从 createHttpServer 实参里提出来(原地占该字面量 1,093 行中的 967 行)——
|
|
76
76
|
// 类型标注 `ServiceDeps["resolveSpec"]` 顶替了原来的上下文推导,签名不变。
|
|
77
77
|
const resolveSpec = async (body, _req, auth, opts) => {
|
|
78
|
+
// design/170 件C §4.3 —— **锁的拒排在最前**(codex 对抗复审 F3,验真后采纳)。
|
|
79
|
+
// 判据不是「在哪一阶段读到这个字段」而是「在**任何副作用**之前」:①阶段最先跑的
|
|
80
|
+
// `gateScenarioAndAppend` 会把上传的附件行**持久绑定**到 session,②阶段会写 per-session 的 cwd/env 表
|
|
81
|
+
// —— 一个最终 400 的请求若已经改掉了这两样,它就改变了**后续被接受的**任务会物化什么、在哪里执行。
|
|
82
|
+
// core 对同一把锁的裁定原话是「Runs BEFORE any side effect of task preparation」,server 侧同姿势。
|
|
83
|
+
assertRequestMcpUnlocked(body.mcpServers, lockedKeys);
|
|
78
84
|
// A10 留档件#2:六阶段流水线。顺序=原文顺序(每一阶段的体逐字搬运),阶段之间只传显式产出;
|
|
79
85
|
// 六个阶段函数定义在本 `return` 之后(自上而下先读骨架、再读各阶段)。
|
|
80
86
|
const gated = await gateScenarioAndAppend(body, auth, opts);
|
|
@@ -801,6 +807,8 @@ export function createResolveSpec(ctx) {
|
|
|
801
807
|
// worker; the stdio MCP runs on the worker, not the exec env, so the lane is irrelevant → seamless TOC↔cloud);
|
|
802
808
|
// a multi-tenant deployment returns the baseline unchanged (gate closed → ignored, advertised via
|
|
803
809
|
// capabilities.mcpInjection). Baseline wins on a name clash (a caller can ADD a server, never SHADOW a configured one).
|
|
810
|
+
// design/170 件C:锁形下本行不可达 —— `resolveSpec` 腿首已整拒(见那里的 F3 注)。`resolveRequestMcp`
|
|
811
|
+
// 收下的 config 带着 `lockedConfigKeys`,所以即使将来有人绕开腿首那道门,honored 谓词也会翻假。
|
|
804
812
|
mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger),
|
|
805
813
|
// center prompts 投影([1057]①f):center 下发的场景终形(overrides[s] ?? 基线)赢过内置 provider
|
|
806
814
|
// (center-wins,scenario overlay 同先例);无 pack/该场景空声明 ⇒ 内置照旧。声明数组逐字喂 typed
|
|
@@ -75,6 +75,10 @@ export interface RunnerDepsCtx {
|
|
|
75
75
|
* 基座**——主/sub 两 Runner 必须同源:委托子代的 prepare 同样跑准入(委托冻结的重判腿),漏挂
|
|
76
76
|
* subRunner=子代平面绕过目录判决。 */
|
|
77
77
|
orgMemoryAdmission: import("./org-memory.js").OrgMemoryAdmissionWiring;
|
|
78
|
+
/** design/170 件B/C/D(#252 件1):三个部署治理座席的装配产物(boot/governance-seams.ts)。进**共享
|
|
79
|
+
* 基座** —— 合规否决与锁定层必须同样管住委托子代那条腿(子代 prepare 走同一套 preflight;漏挂
|
|
80
|
+
* subRunner = 子代平面绕过档位与锁,与件A 同一个理由)。`retentionPolicy` 是纯声明,同源携带。 */
|
|
81
|
+
governanceSeams: import("./governance-seams.js").GovernanceSeams;
|
|
78
82
|
/** design/177 —— org 共享记忆库的供给面(core `RunnerDeps.sharedMemoryStores`)。在场即挂载
|
|
79
83
|
* `memory_list`/`memory_read` 两只工具(core 没有 TaskSpec 伴生旋钮:dep 的在场**就是**部署意图);
|
|
80
84
|
* 缺席 ⇒ 两只都不挂,连占位都没有。装配点按「SQL 供给面 ∧ org 折叠面」双在场才铸它,与 HTTP 面
|
|
@@ -116,7 +120,7 @@ export interface RunnerDepsCtx {
|
|
|
116
120
|
}
|
|
117
121
|
/** design/158 A10 留档发现②:main runner `RunnerDeps` 与 main.ts subRunner 字面量之间此前手工重复
|
|
118
122
|
* 的 ~15 个键,类型标注见 {@link createSharedRunnerDeps} 头注。 */
|
|
119
|
-
export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "onNotice">;
|
|
123
|
+
export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice">;
|
|
120
124
|
/**
|
|
121
125
|
* design/158 A10 留档发现②(review 2026-07-29,[1543]§三族A 同源修补的延续):main runner 的
|
|
122
126
|
* `RunnerDeps` 字面量(下方 `createRunnerDeps`)与 `main.ts` 里 subRunner 的 `new Runner({...})`
|
|
@@ -145,7 +149,7 @@ export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDeny
|
|
|
145
149
|
* 属性,比再抽一层共享基座更强的同源保证),不重复收纳进这里。
|
|
146
150
|
*/
|
|
147
151
|
/** 基座真实消费的窄面(Pick)——subRunner 调用点(main.ts)只需凑这 13 个字段,不必造全量 ctx。 */
|
|
148
|
-
export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger">;
|
|
152
|
+
export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams">;
|
|
149
153
|
/**
|
|
150
154
|
* core `EngineNotice` → 结构化 `engine_notice` 行的**唯一**转发体(#240②)。
|
|
151
155
|
*
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -88,6 +88,12 @@ export function createSharedRunnerDeps(ctx) {
|
|
|
88
88
|
// Runner 必须同源同席([1543] 纪律,与 onNotice 同理);缺席=undefined=core 引擎默认(roots+内建表)。
|
|
89
89
|
readFace: ctx.config.readFace ? ctx.config.readFace : undefined,
|
|
90
90
|
readDenyPatterns: ctx.config.readDenyPatterns ? ctx.config.readDenyPatterns : undefined,
|
|
91
|
+
// #253 件 G5(core 5.33.0 #245,clay 裁定件):**内建** deny 表的档位/删行两座席——与上面两键同席
|
|
92
|
+
// 同源(主/子 Runner 必须同姿态)。缺席=undefined=core 自默认档位(除 shell-history 外全开)。
|
|
93
|
+
// 🔴 `?? undefined` 而不是真值判:`readDenyBuiltinTiers: []` 是**显式的空姿态**(env 写 `none`),
|
|
94
|
+
// 真值判会把它折回「缺席=默认全开」——正好把运维显式关掉的档位偷偷开回去。
|
|
95
|
+
readDenyBuiltinTiers: ctx.config.readDenyBuiltinTiers ?? undefined,
|
|
96
|
+
readDenyBuiltinExclude: ctx.config.readDenyBuiltinExclude ?? undefined,
|
|
91
97
|
executionEnvFactory: ctx.executionEnvFactory ? ctx.executionEnvFactory : undefined,
|
|
92
98
|
lspManager: ctx.lspManager ? ctx.lspManager : undefined,
|
|
93
99
|
// core 1.364 durable bg agents 读半场(写半场=scenarioDeps.backgroundAgentStore 同实例,组装区注释)。
|
|
@@ -120,6 +126,13 @@ export function createSharedRunnerDeps(ctx) {
|
|
|
120
126
|
// 「主 runner 挂了、subRunner 漏挂」那个病族。授权按 per-request principal 折叠(子代同宿主身份),
|
|
121
127
|
// 所以共享同一个 provider 实例既正确又是唯一能让两只 Runner 看到同一份库集合的做法。
|
|
122
128
|
sharedMemoryStores: ctx.sharedMemoryStores ? ctx.sharedMemoryStores : undefined,
|
|
129
|
+
// design/170 件B/C/D(#252 件1)—— 三个部署治理座席,同样必须进**共享基座**:合规档位的判决沿委托
|
|
130
|
+
// 树继承(core 按 principal 冻结一次),而委托子代在本仓跑在**另一只 Runner** 上 —— 那只 Runner 不
|
|
131
|
+
// 挂 resolver 就等于子代平面没有档位;`lockedConfig` 同理(§4.2 明写「覆盖 resume / refresh / 子代
|
|
132
|
+
// 构造三条路径」)。三键的缺席形都是 undefined = core 契约的「无限制/无锁/无策略」,诚实缺席。
|
|
133
|
+
compliancePostureResolver: ctx.governanceSeams.compliancePostureResolver,
|
|
134
|
+
lockedConfig: ctx.governanceSeams.lockedConfig,
|
|
135
|
+
retentionPolicy: ctx.governanceSeams.retentionPolicy,
|
|
123
136
|
};
|
|
124
137
|
return shared;
|
|
125
138
|
}
|
package/dist/boot/stores.d.ts
CHANGED
|
@@ -29,5 +29,6 @@ export declare function openStores(ctx: OpenStoresCtx): Promise<{
|
|
|
29
29
|
sessionStore: import("@sema-agent/core").SessionStore;
|
|
30
30
|
breakerState: import("../plugins/breaker-state-sql.js").TiDBBreakerState | import("../plugins/breaker-state-sql.js").PgBreakerState | undefined;
|
|
31
31
|
usageWindowStore: import("@sema-agent/core").UsageWindowStore | undefined;
|
|
32
|
+
memoryPosture: import("../memory-posture.js").MemoryPosture;
|
|
32
33
|
}>;
|
|
33
34
|
//# sourceMappingURL=stores.d.ts.map
|
package/dist/boot/stores.js
CHANGED
|
@@ -33,6 +33,14 @@ import { assertCloudSnapshotBlobPosture, openStoreBackendWithFallback } from "..
|
|
|
33
33
|
import { buildMemoryRemoteLaneWarn, memoryEngineBackendFor, memoryEngineRemoteLanePosture } from "../memory-scope.js";
|
|
34
34
|
import { assertToolResultProvenanceSchema } from "../plugins/tool-result-store-sql.js";
|
|
35
35
|
import { buildMemoryWriteBoundaryAudit } from "./memory-boundary.js";
|
|
36
|
+
import { buildMemoryPosture } from "../memory-posture.js";
|
|
37
|
+
/** #252:dark 三词 → 启动日志的人话(词表是机器面的真源,散文是它的**投影**,不是第二个真源)。
|
|
38
|
+
* 穷举 `Record<MemoryDarkReason, …>`:词表加员 ⇒ 这里编译红,不会静默漏一句人话。 */
|
|
39
|
+
const DARK_REASON_PROSE = {
|
|
40
|
+
engine_off: () => "MEMORY_ENGINE=off",
|
|
41
|
+
multi_tenant_file_backend: () => "multi-tenant on the FILE backend (no tenant isolation; set MEMORY_ENGINE_BACKEND=pg|tidb to light up)",
|
|
42
|
+
remote_lane_split: (lane) => `remote exec lane "${lane}" (worker/sandbox file planes split; MEMORY_ENGINE_REMOTE_LANE=allow overrides)`,
|
|
43
|
+
};
|
|
36
44
|
export async function openStores(ctx) {
|
|
37
45
|
const { config, logger, metrics, localRoot } = ctx;
|
|
38
46
|
// One shared SQL store backend for L1 + L2 (TiDB/MySQL or PostgreSQL per DB_BACKEND). Owned here.
|
|
@@ -101,6 +109,8 @@ export async function openStores(ctx) {
|
|
|
101
109
|
// 142-S2.5:per-(scope,peer) 同步游标面(sync_cursors)——POST /v1/memory/sync/:scope 的持久化半场。
|
|
102
110
|
// 只在 DB memory plane 上点亮(file 形态路由 501,不接);与 entry plane 同池同方言。
|
|
103
111
|
let memorySyncCursors;
|
|
112
|
+
/** #252:两支 SQL 后端从实例 getter 读到的档位真值;file/off 腿留 undefined(该腿无此指示面)。 */
|
|
113
|
+
let vectorMode;
|
|
104
114
|
if (config.memoryEngineBackend !== "file" && config.memoryEngineEnabled) {
|
|
105
115
|
const dialect = config.memoryEngineBackend;
|
|
106
116
|
// 142-S5.1 production history sink: every applied patch appends one
|
|
@@ -121,7 +131,8 @@ export async function openStores(ctx) {
|
|
|
121
131
|
// #228:检索档位(lexical|portable|native)是「后端上限 × embedder 在场」的**推断结果**,不是旋钮
|
|
122
132
|
// ([3590] 裁①)。下面两支各自从**实例的 getter** 读真值——不在这里自己推断,推断出的档与实际跑的档
|
|
123
133
|
// 一旦分家,启动日志就成了另一句谎([3606]①:lexical 在跑必须运维可见)。
|
|
124
|
-
|
|
134
|
+
// ⚠️ #252:声明上移到函数作用域(启动日志之外,`buildMemoryPosture` 也要读这个真值)——两支的赋值
|
|
135
|
+
// 逐字不变,file/off 那一臂照旧留 `undefined`(file 引擎没有档位指示面,不折成 lexical)。
|
|
125
136
|
if (dialect === "pg") {
|
|
126
137
|
const pool = backend?.pgPool();
|
|
127
138
|
if (!pool)
|
|
@@ -404,15 +415,23 @@ export async function openStores(ctx) {
|
|
|
404
415
|
// (S5.1, opts.history — the write point lives in the handed-over backend code).
|
|
405
416
|
const memoryExportBackend = config.memoryEngineBackend !== "file" ? memoryEngine?.backend : undefined;
|
|
406
417
|
const memoryLane = memoryEngineRemoteLanePosture(config); // N0: worker/sandbox file-plane split posture
|
|
418
|
+
// #252 件2/件3:记忆面姿态 —— **单一推导点**(纯,memory-posture.ts),两个读面(operator 诊断 /
|
|
419
|
+
// 能力位)都投影它,启动日志的 dark 报因也走它的闭集词,于是「日志说暗、能力位说亮」不可能发生。
|
|
420
|
+
// 位置:两支引擎装配都结束之后(要 `memoryEngine` 与 `vectorMode` 两件装配结果)。
|
|
421
|
+
const memoryPosture = buildMemoryPosture({
|
|
422
|
+
config,
|
|
423
|
+
engineWired: memoryEngine !== undefined,
|
|
424
|
+
vectorMode,
|
|
425
|
+
remoteLaneDark: memoryLane?.posture === "dark",
|
|
426
|
+
});
|
|
407
427
|
if (config.memoryEngineBackend === "file" || !config.memoryEngineEnabled) {
|
|
408
428
|
logger.info("memory_engine_enabled", memoryEngine
|
|
409
429
|
? { enabled: true, backend: "file", dir: memoryEngine.root }
|
|
410
430
|
: {
|
|
411
431
|
enabled: false,
|
|
412
432
|
// 复审 F-11:按最终门结果报因,多租户 file=隔离判据,其余照旧。
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
: `remote exec lane "${memoryLane?.lane}" (worker/sandbox file planes split; MEMORY_ENGINE_REMOTE_LANE=allow overrides)`,
|
|
433
|
+
// #252:散文由**闭集词**派生(`memoryPosture.darkReason`)——机器面与人面从此不会各报各的因。
|
|
434
|
+
reason: DARK_REASON_PROSE[memoryPosture.darkReason ?? "engine_off"](memoryLane?.lane),
|
|
416
435
|
});
|
|
417
436
|
}
|
|
418
437
|
// N0 boot warn: loud in BOTH postures — "dark" so an upgrade that silently turns memory off is visible,
|
|
@@ -502,7 +521,7 @@ export async function openStores(ctx) {
|
|
|
502
521
|
return {
|
|
503
522
|
backend, storeBackendDegraded, memoryEngine, memorySyncCursors, rosterStore, backgroundAgentStore,
|
|
504
523
|
taskAttachmentStore, mailboxStore, memoryExportBackend, memorySyncRunner, sessionStore, breakerState,
|
|
505
|
-
usageWindowStore,
|
|
524
|
+
usageWindowStore, memoryPosture,
|
|
506
525
|
};
|
|
507
526
|
}
|
|
508
527
|
//# sourceMappingURL=stores.js.map
|
package/dist/config-types.d.ts
CHANGED
|
@@ -6,7 +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 { SealedKeyPoison } from "./sealed-key.js";
|
|
9
|
-
import type { McpServerSpec, Model, ModelRoles } from "@sema-agent/core";
|
|
9
|
+
import type { CompliancePosture, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy } from "@sema-agent/core";
|
|
10
10
|
import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
|
|
11
11
|
import type { ElicitationThrottle } from "./elicitation.js";
|
|
12
12
|
import type { QuestionThrottle } from "./question.js";
|
|
@@ -603,6 +603,38 @@ export interface ServiceConfigFlat {
|
|
|
603
603
|
* filter, NOT a sandbox (sh -c/$()/pipes bypass argv[0] → fail toward ask). Per-request HOT; sourced from
|
|
604
604
|
* sema-registry `EffectiveConfig.runtime` (structured config, not an env scalar). undefined = unmanaged. */
|
|
605
605
|
commandPolicy?: CommandRule[];
|
|
606
|
+
/**
|
|
607
|
+
* design/170 件B —— 本部署的**合规档位**(core `RunnerDeps.compliancePostureResolver` 的供数)。
|
|
608
|
+
* env `COMPLIANCE_PROFILE`(闭集 `hipaa|zdr`)+ `COMPLIANCE_ADDITIONAL_DENIES`(闭集能力名 csv,
|
|
609
|
+
* **只能收紧** profile 的内建 floor)。两个词表都是 core 的闭集,拼错一律 boot 拒 —— 一个拼错的
|
|
610
|
+
* 能力名等于「标着 hipaa 的部署根本没在执行那一条」(design/170 §3.2)。
|
|
611
|
+
*
|
|
612
|
+
* 🔴 **档位是部署级、不是 per-principal**:core 的座席按 principal 取值,但本仓能拿到的**唯一真源**
|
|
613
|
+
* 是部署配置面(design/170 §1 表:center 侧的档位真源标着「新建」= 尚不存在)。所以 resolver 对每个
|
|
614
|
+
* principal 返回同一份档位 —— 诚实的窄实现,而不是一条永远查不到值的 center 假腿。
|
|
615
|
+
* 缺席 = 无合规限制(core 契约),现存部署逐字零影响。
|
|
616
|
+
*/
|
|
617
|
+
compliancePosture?: CompliancePosture;
|
|
618
|
+
/**
|
|
619
|
+
* design/170 件C —— **管理员锁定层**的部署声明(core `RunnerDeps.lockedConfig.keys`)。
|
|
620
|
+
* env `LOCKED_CONFIG_KEYS`(csv;词表 = core 的 `LOCKED_KEY_REGISTRY` 闭集,未知键 boot 拒 =
|
|
621
|
+
* `config.locked_registry`「不解析的安全控件不得半开」)。
|
|
622
|
+
*
|
|
623
|
+
* ⚠️ 本仓的两条**装配相容性**判据在 boot/governance-seams.ts(`assertLockAssemblable`):
|
|
624
|
+
* `toolPolicy` 锁与本仓「每条腿自铸 spec.toolPolicy」的装配形结构性冲突,`mcp` 锁与「部署自带 MCP
|
|
625
|
+
* 基线」冲突 —— 两者都在启动期拒,而不是让每条任务腿在 prepare 期各撞一次 `config.locked_key`。
|
|
626
|
+
*/
|
|
627
|
+
lockedConfigKeys?: readonly LockedKey[];
|
|
628
|
+
/**
|
|
629
|
+
* design/170 件D —— **托管留存期**声明(core `RunnerDeps.retentionPolicy`)。env `RETENTION_MAX_AGE_DAYS`
|
|
630
|
+
* (有限非负数;坏值 boot 拒 —— 删数据的旋钮不许 NaN 流通)。
|
|
631
|
+
*
|
|
632
|
+
* 🔴 语义边界(成文,别当成"数据会被删"):core **只**拿它做启动期能力校验(锁着 + 店未声明
|
|
633
|
+
* `retention:"managed"` ⇒ 拒启),引擎自己在任务路径上从不删数据;真执行(调度/重试/副本协调/审计)
|
|
634
|
+
* 是部署侧的活,本仓**尚未**建(design/170 §5.2「server 半场四件皆新建」)。所以配了策略而没有任何
|
|
635
|
+
* managed 店时,启动会打一条 `retention_policy_not_executed` 的响亮 warn。
|
|
636
|
+
*/
|
|
637
|
+
retentionPolicy?: RetentionPolicy;
|
|
606
638
|
/** Tool names that require operator approval before running (F4). Empty = no approval gate. */
|
|
607
639
|
approvalRequire: string[];
|
|
608
640
|
/** Principals allowed to act as OPERATORS on the F4 approval queue (`/v1/approvals`): list ANY
|
|
@@ -1054,6 +1086,16 @@ export interface ServiceConfigFlat {
|
|
|
1054
1086
|
pattern: string;
|
|
1055
1087
|
caseSensitive: boolean;
|
|
1056
1088
|
}[];
|
|
1089
|
+
/** #253 件 G5(core 5.33.0 #245,clay 裁定件):**内建** deny 表的档位选择(闭集四档:credentials /
|
|
1090
|
+
* shell-history / browser / wallet)。`READ_DENY_BUILTIN_TIERS`=逗号分隔;`none`=显式空姿态(core 的
|
|
1091
|
+
* `[]`:一档内建都不要);坏档名 / 空串**启动期响亮拒**(校验用 core 自己的 `resolveReadDenyBuiltins`)。
|
|
1092
|
+
* 缺席=不铸 ⇒ core 自默认(除 shell-history 外全开)。**仅 deployment 席**:TaskSpec 上没有同名键,
|
|
1093
|
+
* 任务/受治脚本无法据此放宽自己的部署(core 侧的设计,与本仓「部署级旋钮无条件施加」同向)。 */
|
|
1094
|
+
readDenyBuiltinTiers?: readonly string[];
|
|
1095
|
+
/** #253 件 G5:按**行名**(内建表的 canonical pattern)删内建条目,在档位选择**之后**生效。
|
|
1096
|
+
* `READ_DENY_BUILTIN_EXCLUDE`=逗号分隔;不在表里的行名 / 空串**启动期响亮拒**。
|
|
1097
|
+
* 🔴 不变量(判据帖 G5b):删内建**绝不**削弱任务自己显式声明的 deny —— 那是另一层(additions)。 */
|
|
1098
|
+
readDenyBuiltinExclude?: readonly string[];
|
|
1057
1099
|
/** Structured log level. */
|
|
1058
1100
|
logLevel: "debug" | "info" | "warn" | "error";
|
|
1059
1101
|
/** Per-principal request cap per minute on task/run endpoints. 0 disables. */
|
|
@@ -1163,10 +1205,14 @@ export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" |
|
|
|
1163
1205
|
/** 组:memory(记忆面 + TOC 同步腿)。 */
|
|
1164
1206
|
export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
|
|
1165
1207
|
/** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
|
|
1166
|
-
* 函数不产出它,但它与 `autonomy` 是同一根治理棒的两半,归本组。
|
|
1167
|
-
|
|
1208
|
+
* 函数不产出它,但它与 `autonomy` 是同一根治理棒的两半,归本组。
|
|
1209
|
+
* design/170 的三件部署治理声明(`compliancePosture`/`lockedConfigKeys`/`retentionPolicy`)同归本组:
|
|
1210
|
+
* 三者都是 operator 在配置面写下的**治理声明**(与 autonomy/commandPolicy 同族),不是存储旋钮
|
|
1211
|
+
* ——`retentionPolicy` 尤其容易被误归 store 组:它的消费点是 core 的启动期能力校验与将来的调度面,
|
|
1212
|
+
* config 这一层它只是一句声明。 */
|
|
1213
|
+
export type ServiceAuthConfig = Pick<ServiceConfigFlat, "authToken" | "authTokens" | "allowUnauthedWrites" | "corsOrigins" | "principalHeader" | "requirePrincipal" | "autonomy" | "commandPolicy" | "compliancePosture" | "lockedConfigKeys" | "retentionPolicy" | "operatorPrincipals" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "principalJwtMaxTtlSec" | "bindHost" | "bindHostSource">;
|
|
1168
1214
|
/** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
|
|
1169
|
-
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "rosterRetentionMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes" | "readFace" | "readDenyPatterns">;
|
|
1215
|
+
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "rosterRetentionMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1170
1216
|
/** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
|
|
1171
1217
|
export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachmentOrphanGraceMs" | "workspaceFileMaxBytes" | "attachmentMaxBytes" | "attachmentMimeAllowlist" | "attachmentUnboundTtlMs" | "infraCostRates" | "drainGraceMs" | "sighupIdleGraceMs" | "parentPid" | "rateLimitPerMin" | "maxTaskCostUsd" | "maxTaskTokens" | "maxPrincipalCostUsd" | "costQuotaWindowSec" | "reapIntervalSec" | "runStaleSec" | "toolResultTtlSec">;
|
|
1172
1218
|
/** 组:observability(可观测)。 */
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { compileReadDeny, CODE_AGENT_PROMPT, formatUserScope, isThinkingLevel, PROTOCOL_TABLE, protocolOf, RECOMMENDED_SENSITIVE_PATTERNS, RETIRED_TOOL_NAMES } from "@sema-agent/core";
|
|
4
|
+
import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, formatUserScope, isThinkingLevel, PROTOCOL_TABLE, protocolOf, RECOMMENDED_SENSITIVE_PATTERNS, RETIRED_TOOL_NAMES, resolveLockedKeys } from "@sema-agent/core";
|
|
5
5
|
import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES } from "@sema-agent/registry-core";
|
|
6
6
|
import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js"; // design/158 A4: the parser leaf — NOT security.js (base config layer must not value-import the 55KiB auth module)
|
|
7
7
|
import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
|
|
@@ -505,6 +505,52 @@ function parseReadDenyPatternsEnv(raw) {
|
|
|
505
505
|
compileReadDeny(entries); // 引擎规则预验;抛错原样上抛=拒启
|
|
506
506
|
return entries;
|
|
507
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* #253 件 G5(core 5.33.0 #245,clay 裁定件)—— 内建 deny 表管理面两键的 env 形:**逗号分隔的名字表**。
|
|
510
|
+
*
|
|
511
|
+
* 形状选择的理由:两个值域都是**短的、闭集或表内的名字**(四个档名 / 内建表的行名),JSON 数组在这里
|
|
512
|
+
* 只会让运维多写一层引号;`READ_DENY_PATTERNS` 用 JSON 是因为它的元素是**带可选字段的对象**。
|
|
513
|
+
*
|
|
514
|
+
* 🔴 **空串拒启,不折成 `[]`**:core 把 `tiers: []` 定义成一个合法姿态(「一档内建都不要」),但一个
|
|
515
|
+
* **空 env**(`READ_DENY_BUILTIN_TIERS=` / 变量没展开)在运维现场几乎总是手滑,而它的后果是**整张内建
|
|
516
|
+
* 敏感路径表被关掉** —— 让手滑与显式意图长得一样,正是 #157「安全轴上禁静默 fail-open」要禁的形。
|
|
517
|
+
* 要那个姿态就显式写 `none`(本仓的哨兵词,不是 core 词表的一员 —— 它在解析层就被翻成 `[]`)。
|
|
518
|
+
* exclude 侧没有对应的哨兵:一个「什么都不删」的 exclude 就是**不设这个 env**。
|
|
519
|
+
*/
|
|
520
|
+
function parseNameListEnv(name, raw, opts) {
|
|
521
|
+
const parts = raw.split(",").map((s) => s.trim());
|
|
522
|
+
if (opts?.emptySentinel !== undefined && parts.length === 1 && parts[0] === opts.emptySentinel)
|
|
523
|
+
return [];
|
|
524
|
+
const names = parts.filter((s) => s.length > 0);
|
|
525
|
+
if (names.length !== parts.length || names.length === 0) {
|
|
526
|
+
throw new Error(`env ${name}="${raw}" must be a comma-separated list of non-empty names` +
|
|
527
|
+
(opts?.emptySentinel !== undefined ? ` (write "${opts.emptySentinel}" for the explicit empty posture — an EMPTY value is refused so a mis-expanded variable cannot silently switch the built-in table off)` : "") +
|
|
528
|
+
" — refusing to start rather than silently applying a half-parsed list (#157).");
|
|
529
|
+
}
|
|
530
|
+
return names;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* #253 件 G5 —— 两键的**引擎规则预验**:交给 core 自己的 `resolveReadDenyBuiltins`(闭集档名 + 表内行名
|
|
534
|
+
* 两类坏值它都响亮抛),宿主**不自造第二份判定语义**(与 `READ_DENY_PATTERNS` 走 `compileReadDeny` 的
|
|
535
|
+
* 姿势逐字同款)。抛出的文案前面缀上出问题的 env 名 —— core 的原文点名的是那个非法值,运维还需要知道
|
|
536
|
+
* 是哪一个变量写错了。
|
|
537
|
+
*/
|
|
538
|
+
function assertReadDenyBuiltinsResolvable(tiers, exclude) {
|
|
539
|
+
// 分两次校验(而不是一次喂两键):core 的文案点名的是**那个非法值**,而运维还需要知道是**哪个变量**
|
|
540
|
+
// 写错了 —— 一次喂两键就只能在文案里同时挂两个名字,把排查面又糊回去。
|
|
541
|
+
const check = (which, config) => {
|
|
542
|
+
try {
|
|
543
|
+
resolveReadDenyBuiltins(config);
|
|
544
|
+
}
|
|
545
|
+
catch (e) {
|
|
546
|
+
throw new Error(`env ${which}: ${e instanceof Error ? e.message : String(e)}`);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
if (tiers !== undefined)
|
|
550
|
+
check("READ_DENY_BUILTIN_TIERS", { tiers });
|
|
551
|
+
if (exclude !== undefined)
|
|
552
|
+
check("READ_DENY_BUILTIN_EXCLUDE", { ...(tiers !== undefined ? { tiers } : {}), exclude });
|
|
553
|
+
}
|
|
508
554
|
function enumEnv(name, fallback, allowed) {
|
|
509
555
|
const v = (process.env[name] ?? fallback);
|
|
510
556
|
if (!allowed.includes(v))
|
|
@@ -1553,6 +1599,73 @@ function parseMemoryDomain(ctx) {
|
|
|
1553
1599
|
syncImportLeaseStaleSec: clampEnvWithWarn("SYNC_IMPORT_LEASE_STALE_SEC", "600", 0), // #210 B 档
|
|
1554
1600
|
};
|
|
1555
1601
|
}
|
|
1602
|
+
/**
|
|
1603
|
+
* design/170 件B/C/D —— 三条**部署治理声明**的 env 解析(auth 域的一段,fail-loud 全在这里)。
|
|
1604
|
+
*
|
|
1605
|
+
* 三条的词表真源都在 core(`ComplianceProfile` / `ComplianceCapability` / `LOCKED_KEY_REGISTRY`),本函数
|
|
1606
|
+
* 只负责「env 文本 → 闭集值」这一步,并且**每一步坏值都拒启**:这三根旋钮全在安全/治理轴上,一个静默
|
|
1607
|
+
* 不生效的档位或一个拼错的锁键,是「以为在管、其实没管」——比没配更贵(#210 A 档 + design/170 §3.4)。
|
|
1608
|
+
*
|
|
1609
|
+
* 半配也拒:`COMPLIANCE_ADDITIONAL_DENIES` 没有 profile 可收紧时是一句无处安放的话,静默丢掉它等于
|
|
1610
|
+
* 让 operator 以为多禁了一条(与 `MEMORY_EMBEDDER_*` 的半配拒启同族)。
|
|
1611
|
+
*/
|
|
1612
|
+
function parseGovernanceDeclarations() {
|
|
1613
|
+
const out = {};
|
|
1614
|
+
/**
|
|
1615
|
+
* codex 对抗复审 F1(high,验真后采纳)——**「未定义」与「定义了但是空」必须分开**。
|
|
1616
|
+
*
|
|
1617
|
+
* 病:模板化部署(k8s manifest、compose 的 `env_file`、`KEY=$(VAR)` 替换)极易产出一个**定义了但值为
|
|
1618
|
+
* 空**的变量。若照本仓其它 env 的惯例把它折成「未设」,一个明明写下了档位/锁声明的部署会**静默**以
|
|
1619
|
+
* 零档位零锁启动 —— 治理面静默失效正是这一族旋钮存在的理由,它不能自己犯这个病。
|
|
1620
|
+
*
|
|
1621
|
+
* 判据因此是:**未定义 = 文档化的缺省姿态(零 warn,#210「缺席不在律内」);已定义但解析不出内容 =
|
|
1622
|
+
* 配错了 ⇒ 拒启 + 指路「要缺省就 unset,别留空」。** 迁移代价为零:三根都是本批新增的旋钮,现存部署
|
|
1623
|
+
* 一个都没设过,所以这条更严的判据不会打到任何在跑的部署。
|
|
1624
|
+
*/
|
|
1625
|
+
const requireNonBlank = (name, raw) => {
|
|
1626
|
+
if (raw === undefined)
|
|
1627
|
+
return undefined; // 未定义 = 缺省姿态
|
|
1628
|
+
const trimmed = raw.trim();
|
|
1629
|
+
if (trimmed === "" || trimmed.split(",").every((t) => t.trim() === "")) {
|
|
1630
|
+
throw new Error(`env ${name} is set but empty (or has no parseable value) — a governance declaration that arms nothing must not boot silently. ` +
|
|
1631
|
+
`UNSET ${name} to take the documented default, or give it a real value.`);
|
|
1632
|
+
}
|
|
1633
|
+
return trimmed;
|
|
1634
|
+
};
|
|
1635
|
+
// 件B:合规档位。profile 闭集由 core 的内建 deny 表的键集给出(词表单一真源,不在这里抄一份)。
|
|
1636
|
+
const profileRaw = requireNonBlank("COMPLIANCE_PROFILE", process.env.COMPLIANCE_PROFILE) ?? "";
|
|
1637
|
+
requireNonBlank("COMPLIANCE_ADDITIONAL_DENIES", process.env.COMPLIANCE_ADDITIONAL_DENIES);
|
|
1638
|
+
const denyRaw = csv("COMPLIANCE_ADDITIONAL_DENIES");
|
|
1639
|
+
if (profileRaw !== "") {
|
|
1640
|
+
const profiles = Object.keys(BUILTIN_COMPLIANCE_DENIES);
|
|
1641
|
+
if (!profiles.includes(profileRaw)) {
|
|
1642
|
+
throw new Error(`COMPLIANCE_PROFILE must be one of ${profiles.join("|")}, got "${profileRaw}" — a misspelled compliance profile would run as NO profile at all`);
|
|
1643
|
+
}
|
|
1644
|
+
for (const cap of denyRaw) {
|
|
1645
|
+
if (!COMPLIANCE_CAPABILITIES.includes(cap)) {
|
|
1646
|
+
throw new Error(`COMPLIANCE_ADDITIONAL_DENIES contains "${cap}", not one of ${COMPLIANCE_CAPABILITIES.join("|")} — a misspelled capability name means that deny is simply not enforced`);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
out.compliancePosture = { profile: profileRaw, ...(denyRaw.length > 0 ? { additionalDenies: denyRaw } : {}) };
|
|
1650
|
+
}
|
|
1651
|
+
else if (denyRaw.length > 0) {
|
|
1652
|
+
throw new Error("COMPLIANCE_ADDITIONAL_DENIES is set but COMPLIANCE_PROFILE is not — additional denies only ever TIGHTEN a profile's builtin floor, so half of this configuration cannot be applied");
|
|
1653
|
+
}
|
|
1654
|
+
// 件C:锁定层声明。校验走 core 的 `resolveLockedKeys`(注册表是它的,未知键抛 config.locked_registry)。
|
|
1655
|
+
requireNonBlank("LOCKED_CONFIG_KEYS", process.env.LOCKED_CONFIG_KEYS);
|
|
1656
|
+
const lockedRaw = csv("LOCKED_CONFIG_KEYS");
|
|
1657
|
+
if (lockedRaw.length > 0) {
|
|
1658
|
+
resolveLockedKeys({ keys: lockedRaw }); // fail-loud;顺序保留交给下一行
|
|
1659
|
+
out.lockedConfigKeys = lockedRaw;
|
|
1660
|
+
}
|
|
1661
|
+
// 件D:留存期。删数据的旋钮 ⇒ A 档拒启(`0` 合法:立即可删,是可评估的显式表达)。
|
|
1662
|
+
// ⚠️ 空串必须走上面那道门:`Number("")` 是 **0**(有限!)—— 折成「立即可删」比折成「未设」更坏。
|
|
1663
|
+
const retentionRaw = requireNonBlank("RETENTION_MAX_AGE_DAYS", process.env.RETENTION_MAX_AGE_DAYS);
|
|
1664
|
+
if (retentionRaw !== undefined) {
|
|
1665
|
+
out.retentionPolicy = { maxAgeDays: parseNumOrFailNonNegative("RETENTION_MAX_AGE_DAYS", retentionRaw) };
|
|
1666
|
+
}
|
|
1667
|
+
return out;
|
|
1668
|
+
}
|
|
1556
1669
|
/** 域:auth(身份/凭证/绑址)—— service token 面、CORS、principal 头与开关、JWT 锚、autonomy、绑址。 */
|
|
1557
1670
|
function parseAuthDomain(ctx) {
|
|
1558
1671
|
const { requirePrincipal } = ctx; // 装配层已解析(它是四个域的共同入参),本域只负责把它落到字段上
|
|
@@ -1589,6 +1702,9 @@ function parseAuthDomain(ctx) {
|
|
|
1589
1702
|
// Runtime governance second baton (center §10): AUTONOMY env for the env/local path; sema-registry overlays
|
|
1590
1703
|
// it (applyRuntimeHot). commandPolicy is structured config — sema-registry-only (no flat env scalar form).
|
|
1591
1704
|
autonomy: parseAutonomy(process.env.AUTONOMY),
|
|
1705
|
+
// design/170 件B/C/D:三条部署治理声明(解析与 fail-loud 全在 parseGovernanceDeclarations;
|
|
1706
|
+
// 三键都是可选的,未配 = 键不在 = 现存部署逐字零影响)。
|
|
1707
|
+
...parseGovernanceDeclarations(),
|
|
1592
1708
|
operatorPrincipals: csv("OPERATOR_PRINCIPALS"),
|
|
1593
1709
|
principalJwtPubkeys,
|
|
1594
1710
|
principalJwtIss,
|
|
@@ -1673,6 +1789,15 @@ function parseOrchestrationDomain(ctx) {
|
|
|
1673
1789
|
// 逃过该函数返回型的多余属性检查,分组闭集校验从此看不见这两键。
|
|
1674
1790
|
...(process.env.READ_FACE !== undefined ? { readFace: enumEnv("READ_FACE", "roots", ["open", "roots"]) } : {}),
|
|
1675
1791
|
...(process.env.READ_DENY_PATTERNS !== undefined ? { readDenyPatterns: parseReadDenyPatternsEnv(process.env.READ_DENY_PATTERNS) } : {}),
|
|
1792
|
+
// #253 件 G5(core 5.33.0 #245):内建 deny 表的部署管理面。铸造与校验分两步——先按名字表解析
|
|
1793
|
+
// (空串/半空拒启,见 `parseNameListEnv` 顶注),再交 core 的 `resolveReadDenyBuiltins` 判档名/行名
|
|
1794
|
+
// (引擎同一套规则,宿主不自造第二份)。两键各自可单独设:只设 exclude 时 core 用默认档位。
|
|
1795
|
+
...(() => {
|
|
1796
|
+
const tiers = process.env.READ_DENY_BUILTIN_TIERS !== undefined ? parseNameListEnv("READ_DENY_BUILTIN_TIERS", process.env.READ_DENY_BUILTIN_TIERS, { emptySentinel: "none" }) : undefined;
|
|
1797
|
+
const exclude = process.env.READ_DENY_BUILTIN_EXCLUDE !== undefined ? parseNameListEnv("READ_DENY_BUILTIN_EXCLUDE", process.env.READ_DENY_BUILTIN_EXCLUDE) : undefined;
|
|
1798
|
+
assertReadDenyBuiltinsResolvable(tiers, exclude);
|
|
1799
|
+
return { ...(tiers !== undefined ? { readDenyBuiltinTiers: tiers } : {}), ...(exclude !== undefined ? { readDenyBuiltinExclude: exclude } : {}) };
|
|
1800
|
+
})(),
|
|
1676
1801
|
remoteExec: process.env.REMOTE_EXEC === "e2b" && process.env.E2B_API_KEY
|
|
1677
1802
|
? {
|
|
1678
1803
|
provider: "e2b",
|
|
@@ -2040,7 +2165,8 @@ const MEMORY_GROUP_KEYS = [
|
|
|
2040
2165
|
];
|
|
2041
2166
|
const AUTH_GROUP_KEYS = [
|
|
2042
2167
|
"authToken", "authTokens", "allowUnauthedWrites", "corsOrigins", "principalHeader", "requirePrincipal", "autonomy",
|
|
2043
|
-
"commandPolicy", "
|
|
2168
|
+
"commandPolicy", "compliancePosture", "lockedConfigKeys", "retentionPolicy",
|
|
2169
|
+
"operatorPrincipals", "principalJwtPubkeys", "principalJwtIss", "principalJwtAud",
|
|
2044
2170
|
"principalJwtMaxTtlSec", "bindHost", "bindHostSource",
|
|
2045
2171
|
];
|
|
2046
2172
|
const ORCHESTRATION_GROUP_KEYS = [
|
|
@@ -2051,7 +2177,7 @@ const ORCHESTRATION_GROUP_KEYS = [
|
|
|
2051
2177
|
"workflowSizeGuideline", "backgroundAgentRetentionMs", "backgroundAgentStaleRunningMs",
|
|
2052
2178
|
"backgroundAgentParkClaimStaleMs", "rosterRetentionMs", "scratchpadSweepTtlMs", "sandboxPkgSource",
|
|
2053
2179
|
"sessionAutoTitle", "selectEnvironmentTool", "envFactsEnabled", "toolDeferLongtail", "lspEnabled", "lspHostEnabled",
|
|
2054
|
-
"imageBakes", "readFace", "readDenyPatterns",
|
|
2180
|
+
"imageBakes", "readFace", "readDenyPatterns", "readDenyBuiltinTiers", "readDenyBuiltinExclude",
|
|
2055
2181
|
];
|
|
2056
2182
|
const LIMITS_HTTP_GROUP_KEYS = [
|
|
2057
2183
|
"port", "attachmentOrphanGraceMs", "workspaceFileMaxBytes", "attachmentMaxBytes", "attachmentMimeAllowlist",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TaskNotificationPayload, WorkflowRun } from "@sema-agent/core";
|
|
2
|
+
import type { RunRecord } from "../plugins/store-contracts.js";
|
|
2
3
|
/** [2687-cli] 幽灵行案的单源判别:一条 `task_notification` 只有在 **agent 族 × 终态** 时才允许打
|
|
3
4
|
* `onChildTerminal`(fleet「subagent 树」只渲 agent 子代)。`background_bash`/`monitor`/`external`
|
|
4
5
|
* 不属 agent fleet 树——它们此前每条都打,fleet-bus 的「无 tick 无 claim」臂给 b\* 与 m\* handle 合成
|
|
@@ -384,7 +385,7 @@ export declare function fleetRunPublisher(bus: FleetEventBus | undefined, run: {
|
|
|
384
385
|
/** [1839]①/(a) 案:`residuals` = 顶层 run 的终局残局键(有源才传,见 FleetTaskRow 上的键注)。
|
|
385
386
|
* 只有**真终态**(completed/failed/killed)帧带残局——parked(suspended/needs_review)的行还活着,
|
|
386
387
|
* 没有「终局」可言,误传也不上帧。 */
|
|
387
|
-
onTerminal: (status:
|
|
388
|
+
onTerminal: (status: RunStatusForFleet, residuals?: {
|
|
388
389
|
usage?: FleetTaskRow["usage"];
|
|
389
390
|
stoppedBy?: string;
|
|
390
391
|
transcriptId?: string;
|
|
@@ -438,8 +439,28 @@ export declare function fleetBackgroundChildPublisher(bus?: FleetEventBus, log?:
|
|
|
438
439
|
export declare function fleetRunLabels(objective: string | undefined): {
|
|
439
440
|
name: string;
|
|
440
441
|
};
|
|
442
|
+
/**
|
|
443
|
+
* `runStatusToFleet` 的入参**闭集**(A-032 P1-④)。两段:
|
|
444
|
+
* · `RunRecord["status"]` = `"running" | core.TaskStatus` —— run **行**状态词表,属主
|
|
445
|
+
* `plugins/store-contracts.ts`(core 加成员 ⇒ 下面那条 switch 的 `never` 臂收不敛 ⇒ tsc 红);
|
|
446
|
+
* · 四个**非行状态**的结算词:`queued`(未落行的排队态)、`error`(历史别名)、`killed`/`cancelled`
|
|
447
|
+
* (取消腿自己传,不是行上会出现的词)。
|
|
448
|
+
*
|
|
449
|
+
* 🔴 为什么必须是闭集而不是 `string`:开集签名让「词表加成员 ⇒ 编译红」在结构上不可能,而这条映射的
|
|
450
|
+
* miss 臂代价是**不可见的**(行折成 idle、removal 集不收 ⇒ fleet 永久滞留 stale 行)。2026-06-28
|
|
451
|
+
* on-box e2e 逮到的 `blocked` 泄漏就是这么来的;单员回归钉只钉住昨天的成员。
|
|
452
|
+
*/
|
|
453
|
+
export type RunStatusForFleet = RunRecord["status"] | "queued" | "killed" | "cancelled" | "error";
|
|
454
|
+
/**
|
|
455
|
+
* fleet 行的**离场**判据(`onTerminal` 的 removal 集)—— `FleetTaskStatus` 的穷举 switch。
|
|
456
|
+
*
|
|
457
|
+
* 与 {@link runStatusToFleet} 同族(A-032 P1-④ 证据段点名):此前是消费点手抄的
|
|
458
|
+
* `=== "completed" || === "failed" || === "killed"` 三词字面比对,fleet 词表加成员时同样没有编译期钉。
|
|
459
|
+
* park 态(`waiting` / `awaiting approval`)判 false 是承重的:行还活着,必须留在 fleet 上。
|
|
460
|
+
*/
|
|
461
|
+
export declare function isTerminalFleetStatus(status: FleetTaskStatus): boolean;
|
|
441
462
|
/** Map a service run status onto the neutral fleet vocabulary the shell renders. */
|
|
442
|
-
export declare function runStatusToFleet(status:
|
|
463
|
+
export declare function runStatusToFleet(status: RunStatusForFleet, opts?: {
|
|
443
464
|
awaitingPlanApproval?: boolean;
|
|
444
465
|
}): FleetTaskStatus;
|
|
445
466
|
//# sourceMappingURL=fleet-bus.d.ts.map
|
package/dist/fleet/fleet-bus.js
CHANGED
|
@@ -455,7 +455,7 @@ export function fleetRunPublisher(bus, run) {
|
|
|
455
455
|
settledLeg = true; // freeze — nothing publishes on this leg's publisher after the settle
|
|
456
456
|
const fleetStatus = runStatusToFleet(status);
|
|
457
457
|
// [1839]①/(a) 案:真终态帧带残局键(有源才带,键逐挑不 spread);parked 帧不带(行还活着)。
|
|
458
|
-
const terminal = fleetStatus
|
|
458
|
+
const terminal = isTerminalFleetStatus(fleetStatus); // A-032 P1-④:removal 集不再手抄(穷举属主谓词)
|
|
459
459
|
const res = terminal && residuals
|
|
460
460
|
? {
|
|
461
461
|
...(residuals.usage ? { usage: residuals.usage } : {}),
|
|
@@ -950,6 +950,36 @@ export function fleetRunLabels(objective) {
|
|
|
950
950
|
const name = oneLine.length > 48 ? `${oneLine.slice(0, 47)}…` : oneLine;
|
|
951
951
|
return { name };
|
|
952
952
|
}
|
|
953
|
+
/**
|
|
954
|
+
* fleet 行的**离场**判据(`onTerminal` 的 removal 集)—— `FleetTaskStatus` 的穷举 switch。
|
|
955
|
+
*
|
|
956
|
+
* 与 {@link runStatusToFleet} 同族(A-032 P1-④ 证据段点名):此前是消费点手抄的
|
|
957
|
+
* `=== "completed" || === "failed" || === "killed"` 三词字面比对,fleet 词表加成员时同样没有编译期钉。
|
|
958
|
+
* park 态(`waiting` / `awaiting approval`)判 false 是承重的:行还活着,必须留在 fleet 上。
|
|
959
|
+
*/
|
|
960
|
+
export function isTerminalFleetStatus(status) {
|
|
961
|
+
switch (status) {
|
|
962
|
+
case "completed":
|
|
963
|
+
case "failed":
|
|
964
|
+
case "killed":
|
|
965
|
+
return true;
|
|
966
|
+
case "queued":
|
|
967
|
+
case "running":
|
|
968
|
+
case "waiting":
|
|
969
|
+
case "stopping":
|
|
970
|
+
case "awaiting approval":
|
|
971
|
+
case "idle":
|
|
972
|
+
return false;
|
|
973
|
+
default: {
|
|
974
|
+
const unhandled = status; // FleetTaskStatus 加成员 ⇒ tsc 红,加成员的人当场回答「这个态算不算离场」
|
|
975
|
+
void unhandled;
|
|
976
|
+
// 运行期保守:未知 ⇒ 不摘行(误摘 = 把一条还在跑的任务从面板上抹掉;漏摘只是多一行)。
|
|
977
|
+
// 这条臂与 runStatusToFleet 的 miss 臂**不同源**:`FleetTaskStatus` 是本仓自有词表,只会被本仓
|
|
978
|
+
// 自己加成员,而那是编译期就红的事 —— 所以这里不接留痕(没有"运行期冒出个新词"的来路)。
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
953
983
|
/** Map a service run status onto the neutral fleet vocabulary the shell renders. */
|
|
954
984
|
export function runStatusToFleet(status, opts) {
|
|
955
985
|
if (opts?.awaitingPlanApproval)
|
|
@@ -975,8 +1005,17 @@ export function runStatusToFleet(status, opts) {
|
|
|
975
1005
|
case "killed":
|
|
976
1006
|
case "cancelled":
|
|
977
1007
|
return "killed";
|
|
978
|
-
default:
|
|
1008
|
+
default: {
|
|
1009
|
+
// 🔴 编译期执法点:词表加成员 ⇒ 这里收不敛到 `never` ⇒ tsc 红,加成员的人必须当场回答
|
|
1010
|
+
// 「这个新态在 fleet 上长什么样、行该不该离场」。
|
|
1011
|
+
const unhandled = status;
|
|
1012
|
+
// 运行期防线(#157:runtime miss 臂必须自陈)。这条臂**运行期仍可达**:`rowToRun` 把无约束的
|
|
1013
|
+
// SQL 文本列裸 cast 成联合,滚动升级里更新的副本写入的新状态词会被旧副本读到。方向照旧折 `idle`
|
|
1014
|
+
// (不改行为,改的是「静默」),但每次 miss 必须留痕 —— 否则「fleet 上为什么有一行永远不走的 idle」
|
|
1015
|
+
// 在遥测里没有任何痕迹,只能靠人眼在面板上发现。
|
|
1016
|
+
recordFailOpen("server.fleet.run-status-unmapped", String(unhandled));
|
|
979
1017
|
return "idle";
|
|
1018
|
+
}
|
|
980
1019
|
}
|
|
981
1020
|
}
|
|
982
1021
|
//# sourceMappingURL=fleet-bus.js.map
|