@sema-agent/server 7.100.0 → 7.101.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/MIGRATION.md +22 -1
- package/README.md +12 -1
- package/README.zh-CN.md +9 -1
- package/USAGE.md +80 -9
- package/dist/approval-card.d.ts +24 -12
- package/dist/approval-card.js +4 -3
- package/dist/approval-policy-settlement.d.ts +31 -0
- package/dist/approval-policy-settlement.js +16 -0
- package/dist/approval-reconciler.d.ts +8 -1
- package/dist/approval-reconciler.js +5 -4
- package/dist/boot/config-center.js +86 -20
- package/dist/boot/execution-env.js +20 -2
- package/dist/boot/parked-revive-gate.js +1 -0
- package/dist/boot/reapers.js +1 -0
- package/dist/boot/resolve-spec.js +25 -8
- package/dist/boot/runner-deps.js +9 -3
- package/dist/boot/stage-02-registry-pull.js +9 -0
- package/dist/boot/stage-10-http-server.js +2 -0
- package/dist/boot/stores.js +4 -0
- package/dist/config-catalog.js +5 -3
- package/dist/config-center/apply-effective.d.ts +25 -0
- package/dist/config-center/apply-effective.js +144 -69
- package/dist/config-center/apply-ledger.d.ts +12 -2
- package/dist/config-center/apply-ledger.js +4 -1
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/http-client.d.ts +4 -1
- package/dist/config-center/http-client.js +15 -10
- package/dist/config-center/read-warnings.d.ts +115 -6
- package/dist/config-center/read-warnings.js +115 -5
- package/dist/config-center/types.d.ts +6 -0
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +15 -0
- package/dist/config-provider.d.ts +4 -0
- package/dist/config-provider.js +64 -9
- package/dist/config-types.d.ts +37 -8
- package/dist/config.js +87 -22
- package/dist/deployment-governance.d.ts +45 -12
- package/dist/deployment-governance.js +37 -9
- package/dist/env-name-allowlist-knobs.d.ts +37 -0
- package/dist/env-name-allowlist-knobs.js +24 -0
- package/dist/fleet/fleet-bus.d.ts +30 -4
- package/dist/fleet/fleet-bus.js +20 -4
- package/dist/fleet/fleet-reconciler.d.ts +29 -6
- package/dist/fleet/fleet-reconciler.js +109 -79
- package/dist/fleet-client.d.ts +2 -1
- package/dist/hooks/hook-llm.js +13 -4
- package/dist/host-lsp-manager.d.ts +14 -2
- package/dist/host-lsp-manager.js +7 -6
- package/dist/http/admission.js +22 -7
- package/dist/http/memory-store-refusal-reply.d.ts +24 -0
- package/dist/http/memory-store-refusal-reply.js +10 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +3 -3
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +3 -5
- package/dist/http/routes/memory-origin.js +3 -5
- package/dist/http/routes/sessions.js +7 -6
- package/dist/http/routes/side-query.js +10 -4
- package/dist/instruction-source-notice.d.ts +97 -0
- package/dist/instruction-source-notice.js +78 -0
- package/dist/memory-layer-legacy-lock.d.ts +14 -0
- package/dist/memory-layer-legacy-lock.js +74 -0
- package/dist/memory-operator-faces.js +1 -17
- package/dist/memory-store-refusal.d.ts +37 -0
- package/dist/memory-store-refusal.js +14 -0
- package/dist/model-entry-refusal.d.ts +130 -0
- package/dist/model-entry-refusal.js +157 -0
- package/dist/model-provider.d.ts +9 -3
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-select.d.ts +49 -4
- package/dist/model-select.js +48 -17
- package/dist/observability/fail-open.d.ts +13 -1
- package/dist/observability/fail-open.js +13 -1
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +27 -4
- package/dist/observability/secret-env-scrub.js +22 -7
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/local-session-store.d.ts +20 -25
- package/dist/plugins/local-session-store.js +111 -64
- package/dist/plugins/pg-session-storage.d.ts +7 -0
- package/dist/plugins/pg-session-storage.js +19 -0
- package/dist/plugins/remote-env-host.d.ts +3 -1
- package/dist/plugins/remote-env-host.js +3 -6
- package/dist/plugins/tidb-pool.js +5 -0
- package/dist/plugins/tidb-session-store.d.ts +13 -0
- package/dist/plugins/tidb-session-store.js +14 -0
- package/dist/plugins/web-search.d.ts +2 -1
- package/dist/plugins/web-search.js +43 -24
- package/dist/project-memory.d.ts +10 -0
- package/dist/project-memory.js +28 -14
- package/dist/run-local.d.ts +17 -15
- package/dist/run-local.js +115 -15
- package/dist/sealed-key.d.ts +5 -2
- package/dist/server-secret-env.d.ts +23 -1
- package/dist/server-secret-env.js +9 -0
- package/dist/tool-approval.d.ts +74 -31
- package/dist/tool-approval.js +102 -49
- package/dist/trace/core-keyset-guard.d.ts +27 -5
- package/dist/trace/engine-notice-wire.d.ts +27 -1
- package/dist/trace/engine-notice-wire.js +8 -0
- package/dist/trace/wire-projection-faces.d.ts +2 -2
- package/dist/trace/wire-projection-faces.js +2 -2
- package/package.json +2 -2
- package/dist/memory-layer-preflight.d.ts +0 -46
- package/dist/memory-layer-preflight.js +0 -121
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
import { formatConfigPath } from "@sema-agent/settings-schema";
|
|
2
|
+
export function describeSchemaIssues(error) {
|
|
3
|
+
const issues = schemaIssuesOf(error);
|
|
4
|
+
if (issues === undefined)
|
|
5
|
+
return undefined;
|
|
6
|
+
return issues
|
|
7
|
+
.map((raw) => {
|
|
8
|
+
const i = (typeof raw === "object" && raw !== null ? raw : {});
|
|
9
|
+
const field = Array.isArray(i.path) && i.path.length > 0 ? i.path.map(String).join(".") : "(entry)";
|
|
10
|
+
const what = i.code === "invalid_type" && typeof i.expected === "string" && typeof i.received === "string"
|
|
11
|
+
? `expected ${i.expected}, received ${i.received}`
|
|
12
|
+
: typeof i.code === "string"
|
|
13
|
+
? i.code
|
|
14
|
+
: "invalid";
|
|
15
|
+
return `${field}: ${what}`;
|
|
16
|
+
})
|
|
17
|
+
.join("; ");
|
|
18
|
+
}
|
|
19
|
+
function schemaIssuesOf(error) {
|
|
20
|
+
const issues = typeof error === "object" && error !== null ? error.issues : undefined;
|
|
21
|
+
return Array.isArray(issues) && issues.length > 0 ? issues : undefined;
|
|
22
|
+
}
|
|
23
|
+
export function describeSchemaIssueFields(error) {
|
|
24
|
+
const issues = schemaIssuesOf(error) ?? [];
|
|
25
|
+
const fields = issues.map((raw) => {
|
|
26
|
+
const p = typeof raw === "object" && raw !== null ? raw.path : undefined;
|
|
27
|
+
return Array.isArray(p) && p.length > 0 ? p.map(String).join(".") : "(entry)";
|
|
28
|
+
});
|
|
29
|
+
return [...new Set(fields)];
|
|
30
|
+
}
|
|
2
31
|
const MAX_ERROR_CHARS = 600;
|
|
3
32
|
const cap = (s) => s.slice(0, MAX_ERROR_CHARS);
|
|
4
33
|
const errorText = (e) => (e instanceof Error ? e.message : String(e));
|
|
@@ -24,19 +53,100 @@ export function foldEffectiveReadWarning(w) {
|
|
|
24
53
|
case "legacy-governance-lifted":
|
|
25
54
|
return null;
|
|
26
55
|
case "domain-defaulted":
|
|
27
|
-
return { domain: w.domain, error: cap(errorText(w.error)) };
|
|
56
|
+
return { scope: "domain", domain: w.domain, error: cap(errorText(w.error)) };
|
|
28
57
|
case "entry-dropped":
|
|
29
|
-
return { domain: w.domain, error: cap(`entry dropped by domain schema: ${
|
|
58
|
+
return { scope: "entry", domain: w.domain, entry: formatConfigPath(w.path), error: cap(`entry dropped by domain schema: ${describeSchemaIssues(w.error) ?? errorText(w.error)}`) };
|
|
30
59
|
case "unknown-keys-dropped":
|
|
31
|
-
return { domain: w.domain, error: keyList("unknown keys dropped by domain schema: ", w.keys, w.truncatedFrom) };
|
|
60
|
+
return { scope: "domain", domain: w.domain, error: keyList("unknown keys dropped by domain schema: ", w.keys, w.truncatedFrom) };
|
|
32
61
|
case "unknown-keys-carried":
|
|
33
|
-
return { domain: w.domain, error: keyList("unknown keys carried to consumer (spelling?): ", w.keys, w.truncatedFrom) };
|
|
62
|
+
return { scope: "domain", domain: w.domain, error: keyList("unknown keys carried to consumer (spelling?): ", w.keys, w.truncatedFrom) };
|
|
34
63
|
case "unread-config-file":
|
|
35
|
-
return { domain: w.domain ?? "config.d", error: cap(`unread config file ${w.file} (${w.why})`) };
|
|
64
|
+
return { scope: "domain", domain: w.domain ?? "config.d", error: cap(`unread config file ${w.file} (${w.why})`) };
|
|
36
65
|
default: {
|
|
37
66
|
const never = w;
|
|
38
67
|
throw new Error(`unhandled EffectiveReadWarning kind: ${JSON.stringify(never)}`);
|
|
39
68
|
}
|
|
40
69
|
}
|
|
41
70
|
}
|
|
71
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
72
|
+
export function droppedEntryIdentity(rawDoc, path) {
|
|
73
|
+
let at = rawDoc;
|
|
74
|
+
for (const seg of path) {
|
|
75
|
+
if (typeof seg === "number") {
|
|
76
|
+
if (!Array.isArray(at) || !Number.isInteger(seg) || seg < 0 || seg >= at.length)
|
|
77
|
+
return undefined;
|
|
78
|
+
at = at[seg];
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (!isRecord(at) || !Object.hasOwn(at, seg))
|
|
82
|
+
return undefined;
|
|
83
|
+
at = at[seg];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!isRecord(at) || typeof at.name !== "string")
|
|
87
|
+
return { kind: "nameless" };
|
|
88
|
+
return { kind: "named", name: at.name, ...(typeof at.id === "string" ? { id: at.id } : {}), ...(typeof at.provider === "string" ? { provider: at.provider } : {}), enabled: at.enabled !== false };
|
|
89
|
+
}
|
|
90
|
+
function isModelEntryDrop(w) {
|
|
91
|
+
return w.kind === "entry-dropped" && w.domain === "models" && w.path.length === 2 && w.path[0] === "models" && typeof w.path[1] === "number";
|
|
92
|
+
}
|
|
93
|
+
function sameRead(source, dropped) {
|
|
94
|
+
const rawModels = isRecord(source.raw) ? source.raw.models : undefined;
|
|
95
|
+
if (!Array.isArray(rawModels) || source.parsed === undefined)
|
|
96
|
+
return false;
|
|
97
|
+
if ([...dropped].some((i) => i >= rawModels.length))
|
|
98
|
+
return false;
|
|
99
|
+
const survivors = rawModels.filter((_, i) => !dropped.has(i));
|
|
100
|
+
return survivors.length === source.parsed.length && survivors.every((s, k) => isRecord(s) && isRecord(source.parsed[k]) && s.name === source.parsed[k].name);
|
|
101
|
+
}
|
|
102
|
+
export function splitFoldedReadWarnings(warnings, models) {
|
|
103
|
+
const domainErrors = [];
|
|
104
|
+
const droppedEntries = [];
|
|
105
|
+
for (const w of warnings) {
|
|
106
|
+
const f = foldEffectiveReadWarning(w);
|
|
107
|
+
if (f === null)
|
|
108
|
+
continue;
|
|
109
|
+
switch (f.scope) {
|
|
110
|
+
case "domain":
|
|
111
|
+
domainErrors.push({ domain: f.domain, error: f.error });
|
|
112
|
+
break;
|
|
113
|
+
case "entry":
|
|
114
|
+
droppedEntries.push({ domain: f.domain, entry: f.entry, error: f.error });
|
|
115
|
+
break;
|
|
116
|
+
default: {
|
|
117
|
+
const never = f;
|
|
118
|
+
throw new Error(`unhandled folded read-warning scope: ${JSON.stringify(never)}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const modelDrops = warnings.filter(isModelEntryDrop);
|
|
123
|
+
const readable = sameRead(models, new Set(modelDrops.map((w) => w.path[1])));
|
|
124
|
+
const droppedIdentities = [];
|
|
125
|
+
for (const w of modelDrops) {
|
|
126
|
+
const unproven = { entry: formatConfigPath(w.path), fields: describeSchemaIssueFields(w.error), issues: describeSchemaIssues(w.error) ?? "(schema issues unreadable)" };
|
|
127
|
+
const cell = readable ? droppedEntryIdentity(models.raw, w.path) : undefined;
|
|
128
|
+
if (cell === undefined) {
|
|
129
|
+
droppedIdentities.push(unproven);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
switch (cell.kind) {
|
|
133
|
+
case "nameless":
|
|
134
|
+
break;
|
|
135
|
+
case "named":
|
|
136
|
+
if (cell.enabled)
|
|
137
|
+
droppedIdentities.push({ ...unproven, name: cell.name, ...(cell.id !== undefined ? { id: cell.id } : {}), ...(cell.provider !== undefined ? { provider: cell.provider } : {}) });
|
|
138
|
+
break;
|
|
139
|
+
default: {
|
|
140
|
+
const never = cell;
|
|
141
|
+
throw new Error(`unhandled dropped-entry cell: ${JSON.stringify(never)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { domainErrors, droppedEntries, droppedIdentities };
|
|
146
|
+
}
|
|
147
|
+
export function withDroppedIdentities(effective, droppedIdentities) {
|
|
148
|
+
if (droppedIdentities.length === 0 || effective.models === undefined)
|
|
149
|
+
return effective;
|
|
150
|
+
return { ...effective, models: { ...effective.models, droppedIdentities: [...droppedIdentities] } };
|
|
151
|
+
}
|
|
42
152
|
//# sourceMappingURL=read-warnings.js.map
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { ToolContentOrigin } from "@sema-agent/core";
|
|
9
9
|
import type { CollabTemplateWire } from "../capabilities/collab-wire.js";
|
|
10
10
|
import type { Autonomy, CommandRule } from "../runtime-governance.js";
|
|
11
|
+
import type { DroppedModelIdentity } from "./read-warnings.js";
|
|
11
12
|
export interface CenterModel {
|
|
12
13
|
name: string;
|
|
13
14
|
id: string;
|
|
@@ -263,6 +264,11 @@ export interface EffectiveConfig {
|
|
|
263
264
|
default?: string;
|
|
264
265
|
tierGroups?: unknown;
|
|
265
266
|
activeTierGroup?: string;
|
|
267
|
+
/** 🔴 S-712 —— **本仓读腿铸的**载体(不是 wire 键:中心在模型域里写同名键会被 schema 当未知键剥掉并进候选门):这次读
|
|
268
|
+
* 被 schema 逐条丢掉的**启用**目录条目的身份。模型面 stage 把它们铸成 `config.model_entry_invalid` 墓碑进 `modelRefusals`,
|
|
269
|
+
* 不变量 `refused-names-not-minted` 据此判增广有没有把它们的名字铸到别的条目上。挂在 effective 上(而不是读结果旁边)是为了
|
|
270
|
+
* **随 effective 落 LKG**:重启从 LKG 起服时墓碑还在。形见 {@link DroppedModelIdentity}(纯身份,不含值)。 */
|
|
271
|
+
droppedIdentities?: DroppedModelIdentity[];
|
|
266
272
|
};
|
|
267
273
|
/** registry-core 0.7 collab domain (teams' successor) — consumed by the slice-1.5 projection
|
|
268
274
|
* (projectCollabToWorkflows → registerCollabWorkflows). Both lanes carry it: the remote worker /effective view
|
|
@@ -28,9 +28,9 @@ import type { ServiceConfigFlat } from "./config-types.js";
|
|
|
28
28
|
*/
|
|
29
29
|
export declare const LIVENESS_HEARTBEAT_MS = 30000;
|
|
30
30
|
/** 闭集:新增一条不变量必须在这里加词(拼错 = 编译红,而不是运行期悄悄不判)。 */
|
|
31
|
-
export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "model-route-endpoint" | "require-execution-lane";
|
|
31
|
+
export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "require-execution-lane";
|
|
32
32
|
/** 表读的键集(取自平铺配置型 ⇒ 键名/类型永不与真配置漂)。 */
|
|
33
|
-
export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "requireExecutionLane" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models">>;
|
|
33
|
+
export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "requireExecutionLane" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models" | "modelRefusals" | "tiers">>;
|
|
34
34
|
export interface CrossInvariantInput {
|
|
35
35
|
/** 现役配置(boot 期 = 候选自身)。 */
|
|
36
36
|
readonly live: CrossInvariantView;
|
|
@@ -2,6 +2,7 @@ import { assertGuardPatternsUsable } from "./deployment-governance.js";
|
|
|
2
2
|
import { modelProviderRefusal } from "./model-provider.js";
|
|
3
3
|
import { modelRouteUrlRefusal } from "./model-route-url.js";
|
|
4
4
|
import { modelRouteEndpointRefusal } from "./model-route-endpoint.js";
|
|
5
|
+
import { defaultModelRefusal, refusedNameMintedRefusal } from "./model-entry-refusal.js";
|
|
5
6
|
export const LIVENESS_HEARTBEAT_MS = 30_000;
|
|
6
7
|
const anyServiceAuth = (v) => Boolean(v.authToken) || Object.keys(v.authTokens ?? {}).length > 0;
|
|
7
8
|
const mustNarrowBind = (v) => Boolean(v.allowUnauthedWrites) && !anyServiceAuth(v);
|
|
@@ -166,6 +167,20 @@ export const CONFIG_CROSS_INVARIANTS = [
|
|
|
166
167
|
bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:model-route-endpoint" },
|
|
167
168
|
check: ({ live, next }) => (next.models === live.models ? undefined : modelRouteEndpointRefusal(next)),
|
|
168
169
|
},
|
|
170
|
+
{
|
|
171
|
+
id: "default-model-resolvable",
|
|
172
|
+
keys: ["models", "modelRefusals"],
|
|
173
|
+
restartReason: "default-model",
|
|
174
|
+
bootAnchor: { file: "src/boot/stage-02-registry-pull.ts", marker: "CROSS-INVARIANT:default-model-resolvable" },
|
|
175
|
+
check: ({ live, next }) => (next.models === live.models ? undefined : defaultModelRefusal(next, "catalog")),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "refused-names-not-minted",
|
|
179
|
+
keys: ["models", "tiers", "modelRefusals"],
|
|
180
|
+
restartReason: "refused-model-name",
|
|
181
|
+
bootAnchor: { file: "src/boot/stage-02-registry-pull.ts", marker: "CROSS-INVARIANT:refused-names-not-minted" },
|
|
182
|
+
check: ({ live, next }) => (next.models === live.models ? undefined : refusedNameMintedRefusal(next)),
|
|
183
|
+
},
|
|
169
184
|
];
|
|
170
185
|
export function crossInvariant(id) {
|
|
171
186
|
const inv = CONFIG_CROSS_INVARIANTS.find((i) => i.id === id);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DroppedConfigEntry } from "./config-center/read-warnings.js";
|
|
1
2
|
import { FileConfigStore } from "@sema-agent/settings-schema/node";
|
|
2
3
|
import { type EffectiveConfig as AgentConfigEffective, type EffectiveReadWarning } from "@sema-agent/settings-schema";
|
|
3
4
|
import { fetchEffective as remoteFetchEffective, fetchSkillContent as remoteFetchSkillContent, type EffectiveConfig } from "./config-center/facade.js";
|
|
@@ -20,6 +21,8 @@ import type { CenterEndpoint } from "./config-center/center-request.js";
|
|
|
20
21
|
* 路由进日志/审计流的消费者必须自己先脱敏。 */
|
|
21
22
|
/** `credentialIdentity`(S-651 / [ref] E-15):远程腿恒在场 = 这次拉取所用凭证的身份(分箱键来源);本地腿
|
|
22
23
|
* 缺席(没有凭证 —— config.d 就在本机盘上)。 */
|
|
24
|
+
/** `droppedEntries`(S-704 ②):schema 逐条丢掉的条目(坐标 + 字段诊断,已脱敏)—— **不**进候选门,调用方逐条响亮
|
|
25
|
+
* 点名、其余照用;缺席 = 这次读零逐条丢弃(与 `domainErrors` 同一种缺席即无的形)。 */
|
|
23
26
|
export type FetchEffectiveResult = {
|
|
24
27
|
effective: EffectiveConfig;
|
|
25
28
|
etag?: string;
|
|
@@ -27,6 +30,7 @@ export type FetchEffectiveResult = {
|
|
|
27
30
|
domain: string;
|
|
28
31
|
error: string;
|
|
29
32
|
}>;
|
|
33
|
+
droppedEntries?: DroppedConfigEntry[];
|
|
30
34
|
readWarnings: readonly EffectiveReadWarning[];
|
|
31
35
|
credentialIdentity?: string;
|
|
32
36
|
} | null;
|
package/dist/config-provider.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readMcpSourceLabel } from "./mcp-source-label.js";
|
|
2
2
|
import { createHmac, randomBytes } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { splitFoldedReadWarnings, withDroppedIdentities } from "./config-center/read-warnings.js";
|
|
4
|
+
import { readFile, stat } from "node:fs/promises";
|
|
4
5
|
import { FileConfigStore } from "@sema-agent/settings-schema/node";
|
|
5
6
|
import { findSkillContent, skillContentHash, refIntegrityIssues, createSiblingResolver, formatConfigPath, REF_CHECKED_DOMAINS, } from "@sema-agent/settings-schema";
|
|
6
7
|
import { redactSecrets } from "./trace/redact.js";
|
|
@@ -11,6 +12,24 @@ const OPERAND_FINGERPRINT_KEY = randomBytes(16);
|
|
|
11
12
|
function redactConfigError(text) {
|
|
12
13
|
return redactSecrets(text).replace(/"[^"]*"|'[^']*'/g, (m) => `"#${createHmac("sha256", OPERAND_FINGERPRINT_KEY).update(m.slice(1, -1)).digest("hex").slice(0, 8)}"`);
|
|
13
14
|
}
|
|
15
|
+
async function readRawDomainDoc(path) {
|
|
16
|
+
try {
|
|
17
|
+
const doc = JSON.parse(await readFile(path, "utf8"));
|
|
18
|
+
return { ok: true, doc };
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
return { ok: false, why: e instanceof Error ? e.message : String(e) };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function statSignature(path) {
|
|
25
|
+
try {
|
|
26
|
+
const st = await stat(path, { bigint: true });
|
|
27
|
+
return { ok: true, sig: `${st.ino}:${st.size}:${st.mtimeNs}:${st.ctimeNs}` };
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return { ok: false, why: e instanceof Error ? e.message : String(e) };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
14
33
|
export const BOOT_FETCH_DEFERRED = Symbol("boot-fetch-deferred");
|
|
15
34
|
export function raceBootFetch(fetch, budgetMs) {
|
|
16
35
|
if (budgetMs <= 0)
|
|
@@ -33,9 +52,13 @@ export class RemoteConfigProvider {
|
|
|
33
52
|
async fetchEffective(etag) {
|
|
34
53
|
const fn = this.deps.fetchEffective ?? remoteFetchEffective;
|
|
35
54
|
const r = await fn(this.cc, etag, undefined, this.cc.worker);
|
|
36
|
-
if (r === null || r.domainErrors === undefined)
|
|
55
|
+
if (r === null || (r.domainErrors === undefined && r.droppedEntries === undefined))
|
|
37
56
|
return r;
|
|
38
|
-
return {
|
|
57
|
+
return {
|
|
58
|
+
...r,
|
|
59
|
+
...(r.domainErrors !== undefined ? { domainErrors: r.domainErrors.map((de) => ({ domain: de.domain, error: redactConfigError(de.error) })) } : {}),
|
|
60
|
+
...(r.droppedEntries !== undefined ? { droppedEntries: r.droppedEntries.map((d) => ({ ...d, error: redactConfigError(d.error) })) } : {}),
|
|
61
|
+
};
|
|
39
62
|
}
|
|
40
63
|
async fetchSkillContent(contentHash) {
|
|
41
64
|
const fn = this.deps.fetchSkillContent ?? remoteFetchSkillContent;
|
|
@@ -60,15 +83,11 @@ export class LocalConfigProvider {
|
|
|
60
83
|
this.store = store ?? new FileConfigStore(root);
|
|
61
84
|
}
|
|
62
85
|
async fetchEffective(etag) {
|
|
63
|
-
const parseWarnings = [];
|
|
64
86
|
const readWarnings = [];
|
|
65
87
|
const store = this.#injectedStore ??
|
|
66
88
|
new FileConfigStore(this.root, {
|
|
67
89
|
onWarning: (w) => {
|
|
68
90
|
readWarnings.push(w);
|
|
69
|
-
const f = foldEffectiveReadWarning(w);
|
|
70
|
-
if (f !== null)
|
|
71
|
-
parseWarnings.push(f);
|
|
72
91
|
},
|
|
73
92
|
});
|
|
74
93
|
const { effective: eff, domainErrors } = await store.getEffective({ tolerant: true });
|
|
@@ -82,9 +101,45 @@ export class LocalConfigProvider {
|
|
|
82
101
|
if (issues.length > 0)
|
|
83
102
|
refErrors.push({ domain: d, error: issues.map((i) => `${formatConfigPath(i.path)}: ${redactConfigError(i.message)}`).join("; ") });
|
|
84
103
|
}
|
|
85
|
-
const
|
|
104
|
+
const modelDropped = readWarnings.some((w) => w.kind === "entry-dropped" && w.domain === "models");
|
|
105
|
+
const rawModels = modelDropped ? await this.#provenRawModelsDoc(store, eff.version) : undefined;
|
|
106
|
+
const folded = splitFoldedReadWarnings(readWarnings, { raw: rawModels, parsed: eff.models.models ?? [] });
|
|
107
|
+
const parseErrors = folded.domainErrors.map((w) => ({ domain: w.domain, error: redactConfigError(w.error) }));
|
|
108
|
+
const droppedEntries = folded.droppedEntries.map((d) => ({ ...d, error: redactConfigError(d.error) }));
|
|
86
109
|
const errors = [...domainErrors.map((de) => ({ domain: de.domain, error: redactConfigError(de.error) })), ...parseErrors, ...refErrors];
|
|
87
|
-
return {
|
|
110
|
+
return {
|
|
111
|
+
effective: withDroppedIdentities(mapToServiceEffective(eff, eff.version), folded.droppedIdentities),
|
|
112
|
+
etag: tag,
|
|
113
|
+
readWarnings,
|
|
114
|
+
...(errors.length > 0 ? { domainErrors: errors } : {}),
|
|
115
|
+
...(droppedEntries.length > 0 ? { droppedEntries } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async #provenRawModelsDoc(store, version) {
|
|
119
|
+
const path = store.domainPath("models");
|
|
120
|
+
const unproven = (why) => {
|
|
121
|
+
logger.warn("config_models_raw_unproven", { dir: this.root, why: redactConfigError(why), note: "the dropped model entries of this read cannot be identified from the exact snapshot that was parsed — the candidate's model plane is refused as a group (no tombstone is taken from another version of the file); the next poll re-reads" });
|
|
122
|
+
return undefined;
|
|
123
|
+
};
|
|
124
|
+
const s1 = await statSignature(path);
|
|
125
|
+
const raw = await readRawDomainDoc(path);
|
|
126
|
+
let again;
|
|
127
|
+
try {
|
|
128
|
+
again = (await (this.#injectedStore ?? new FileConfigStore(this.root)).getEffective({ tolerant: true })).effective.version;
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
again = `re-parse failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
132
|
+
}
|
|
133
|
+
const s2 = await statSignature(path);
|
|
134
|
+
if (!raw.ok)
|
|
135
|
+
return unproven(`config.d/models.json unreadable on the identity re-read: ${raw.why}`);
|
|
136
|
+
if (typeof again !== "number")
|
|
137
|
+
return unproven(again);
|
|
138
|
+
if (!s1.ok || !s2.ok || s1.sig !== s2.sig)
|
|
139
|
+
return unproven("config.d/models.json changed (or could not be stat-ed) while its identity re-read was being proven");
|
|
140
|
+
if (again !== version)
|
|
141
|
+
return unproven("config.d changed between the parse and the identity re-read (content version differs)");
|
|
142
|
+
return raw.doc;
|
|
88
143
|
}
|
|
89
144
|
async fetchSkillContent(contentHash) {
|
|
90
145
|
const skills = await this.store.getDomain("skills");
|
package/dist/config-types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { ReadFaceSource } from "./read-face-posture.js";
|
|
9
9
|
import type { PostureSource } from "./posture-source.js";
|
|
10
10
|
import type { SealedKeyPoison } from "./sealed-key.js";
|
|
11
|
+
import type { ModelEntryRefusal } from "./model-entry-refusal.js";
|
|
11
12
|
import type { A2aServerSpec, CompliancePosture, CrossSessionDialogExpiry, CrossSessionInboundSetting, FileHistoryRetentionPolicy, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy, WriteProtectedEntry } from "@sema-agent/core";
|
|
12
13
|
import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
|
|
13
14
|
import type { ElicitationThrottle } from "./elicitation.js";
|
|
@@ -465,6 +466,16 @@ export interface ServiceConfigFlat {
|
|
|
465
466
|
* Env path: empty;populated ONLY by applyEffective(重赋值;keyResolver 随 boot/refresh 重建)。 */
|
|
466
467
|
/** 值=解封明文 或 SealedKeyPoison 毒丸(解封失败标记;key-resolver 对其任务级 throw,恒不落 gateway key 兜底)。 */
|
|
467
468
|
modelApiKeys: Record<string, string | SealedKeyPoison>;
|
|
469
|
+
/**
|
|
470
|
+
* S-704 —— **已发布但本部署不可用**的目录条目:catalog NAME → 拒因(`model-entry-refusal.ts` 的闭集码
|
|
471
|
+
* `config.model_no_brain` / `config.model_entry_invalid` + 上游 id + 一句出路,不含任何值)。
|
|
472
|
+
*
|
|
473
|
+
* 这些条目**不在** `models` 里(不可点、不列、不当缺省、不进钥匙表 —— 永远不被改路到另一条 brain / 另一个端点);
|
|
474
|
+
* 本表只为一件事存在:一次**显式**选中它的请求(body.model / `@name` / settings.model / compactionModel /
|
|
475
|
+
* agents[].model / side-query / run-local / hook)拿到「为什么」(`request.model_unavailable` + 码),而不是
|
|
476
|
+
* 「没这模型」。env 腿恒空;只由 applyEffective 与 `models` **同世代同 commit** 整表重赋值(非粘)。
|
|
477
|
+
*/
|
|
478
|
+
modelRefusals: Readonly<Record<string, ModelEntryRefusal>>;
|
|
468
479
|
/** 配额倍率表(name+id 双键→ModelEntry.quotaWeight):weight-at-burn 的运行时源——tracer 在
|
|
469
480
|
* brain.call 记账点读它算 weightedTokens(lease 双轴+usage 批报)。registry apply IN-PLACE hot-apply;
|
|
470
481
|
* env lane 恒空(无倍率概念,weight=1 兜底)。 */
|
|
@@ -759,6 +770,8 @@ export interface ServiceConfigFlat {
|
|
|
759
770
|
timeoutMs?: number;
|
|
760
771
|
livenessMs?: number;
|
|
761
772
|
allowInternetAccess?: boolean;
|
|
773
|
+
/** S-713 —— `E2B_SANDBOX_ENV=NAME,NAME` 点到、且本进程环境里真有的键 ⇒ 原值(经唯一的「点名放行」谓词 `allowlistedEnv`,
|
|
774
|
+
* 空串值照带)。键缺席 = 旋钮未设 / 空串;名单形错(空段 / 非变量名形 / 点名名单型旋钮)拒启,源里没有的名按序号 warn。 */
|
|
762
775
|
sandboxEnv?: Record<string, string>;
|
|
763
776
|
mountPath?: string;
|
|
764
777
|
/** [ref]:崩溃遗留沙盒回收腿(E2B_ORPHAN_RECLAIM,默认 true;判据与 v1 边界见
|
|
@@ -843,6 +856,8 @@ export interface ServiceConfigFlat {
|
|
|
843
856
|
dropAllCaps?: boolean;
|
|
844
857
|
network?: "none" | "bridge" | "host";
|
|
845
858
|
commandTimeoutMs?: number;
|
|
859
|
+
/** S-713 —— `DOCKER_SANDBOX_ENV=NAME,NAME` 点到、且本进程环境里真有的键 ⇒ 原值(与 e2b 的 `sandboxEnv` 同一只解析器、
|
|
860
|
+
* 同一只谓词;每条 `docker exec` 以 `-e K=V` 注入)。键缺席 = 旋钮未设 / 空串。 */
|
|
846
861
|
env?: Record<string, string>;
|
|
847
862
|
};
|
|
848
863
|
/**
|
|
@@ -1578,6 +1593,16 @@ export interface ServiceConfigFlat {
|
|
|
1578
1593
|
* opt-out) is a **fail-loud tombstone** — it refuses boot naming `LSP_HOST_ENABLED`; `LSP_ENABLED=true` keeps
|
|
1579
1594
|
* its own meaning (the sandbox lane's opt-in above) and is unaffected. */
|
|
1580
1595
|
lspHostEnabled: boolean;
|
|
1596
|
+
/** S-699 —— host-lane language-server env **named allowlist** (`LSP_ENV_ALLOW=NAME,NAME`). The language server's env is
|
|
1597
|
+
* `scrubServerSecrets(process.env)`; each name here is put back **verbatim** from the service's own env on top of that
|
|
1598
|
+
* (`allowlistedEnv`, the same "named keys pass" predicate as the host shell's `inheritEnv:[names]`) — the deployer
|
|
1599
|
+
* explicitly accepts handing that value to code the language server runs. Unnamed secret-shaped keys stay stripped.
|
|
1600
|
+
* `[]` = nothing put back (the default: unset / empty string). A malformed list (empty segment / not an env-var name /
|
|
1601
|
+
* the name of a name-list knob — S-713: one parser for all three) refuses boot naming the key, without echoing the value. `REQUIRE_PRINCIPAL=true` ⇒ always `[]` (ignored, one boot
|
|
1602
|
+
* warn) — the multi-tenant posture keeps the full strip. Single-user: only names that EXIST in this process's env
|
|
1603
|
+
* make it into this list (an absent name is a per-index boot warn) — so every downstream echo of it (the scrub
|
|
1604
|
+
* disclosure's `allowed`, the config catalog) carries real key names only, never a value pasted in place of a name. */
|
|
1605
|
+
lspEnvAllow: readonly string[];
|
|
1581
1606
|
/** Graceful drain: how long SIGTERM waits for in-flight turns (live streams + bg/resume legs on this
|
|
1582
1607
|
* instance) to finish before the hard shutdown. During drain, new billable submissions get 503+Retry-After and
|
|
1583
1608
|
* /health carries `draining:true`. Default 10min (an interactive turn is minutes, not seconds — a
|
|
@@ -1733,8 +1758,9 @@ export interface ServiceConfigFlat {
|
|
|
1733
1758
|
* **不看任何客户端表态**(纯部署级;memory: operator-knob-must-be-unconditional —— [ref] shellGate 让
|
|
1734
1759
|
* 一个部署级旋钮的生死由客户端 posture 决定,同病犯过两次)。
|
|
1735
1760
|
*
|
|
1736
|
-
* 施加点 = `ToolApprovalCoordinator` 的构造参 `unattendedPolicy
|
|
1737
|
-
*
|
|
1761
|
+
* 施加点 = `ToolApprovalCoordinator` 的构造参 `unattendedPolicy`,五条「无人可答」的臂读同一
|
|
1762
|
+
* 个值;对账收敛器的同名构造参(S-706,`boot/reapers.ts` 恒传同一个部署值)只供它的孤儿段打同一只终态 claim ——
|
|
1763
|
+
* 两处读同一个值,于是崩溃恢复与 live 腿同答(设计稿 §1 的表:(a) 有店窗到期赢 CAS /(b) 无店窗到期 /(c) 店报错 catch /(d) windowZero 与
|
|
1738
1764
|
* headless 腿 /(e) 断连强转 + emit 全灭 + 写侧准入超限 + **投递集合入口活性过滤清空**([ref] 归
|
|
1739
1765
|
* 位:与「查无活流」同一件事实的两个时刻,详见 `askBroadcast` 的该臂注))。`"timeout"` 宿主自报只留在 (b)(c) ——
|
|
1740
1766
|
* 结束等待的确实是本仓的窗;其余三臂结束等待的是容量/连接/装配,自报恒缺席。
|
|
@@ -1762,13 +1788,16 @@ export interface ServiceConfigFlat {
|
|
|
1762
1788
|
* · **持久回放**(窗到期后向真源收敛、`ensureAsk` 幂等重入撞上已终结的行)读出 PARKING/PARKED 时
|
|
1763
1789
|
* **同样按旋钮改判** —— 它们与 (a) 是同一件事(赢家可能是别的副本或上一次调用),不统一的话同一只
|
|
1764
1790
|
* ask 的终局会取决于「谁赢了那把 CAS / 重试没重试」。人的真决议回放不受影响。
|
|
1765
|
-
* ·
|
|
1766
|
-
*
|
|
1791
|
+
* · `ensureAsk` 身份未定 / 坏行两臂**同样**改判(S-702,7.101.0;此前是唯一的例外,deny 部署上店抖动期仍会落 park):
|
|
1792
|
+
* 两臂的处置不变(不编造 wire 身份、不在身份未定时呈卡收人决),「不呈卡之后交什么」过同一个政策口 —— deny ⇒ 部署
|
|
1793
|
+
* 政策拒,本次插下的行同落政策结算(归属证不出的行不当场动 —— 可能正被别的登记呈卡 —— 由收敛器孤儿段收);
|
|
1794
|
+
* park ⇒ `"unavailable"` 逐字不变。
|
|
1767
1795
|
* · 🔴 `deny` 部署上政策结算**当场落行**(NP-2,7.100.0):窗到期 / emit 全灭两臂的店终局 claim 是 `refuse` 意图
|
|
1768
1796
|
* —— 这一只 ask 行 CAS 成 DECIDED(deny) + `settled_by="policy"`(按 ask 作用域,不碰批、不连坐兄弟),与交给
|
|
1769
1797
|
* 引擎的那一只同一个来源词 ⇒ 重入 / 收敛后重入回放同一个政策拒,不退化成裸 `false`(人拒)。修前行停 PARKING,
|
|
1770
|
-
* run 完成后收敛器转 DENIED
|
|
1771
|
-
*
|
|
1798
|
+
* run 完成后收敛器转 DENIED 且不带来源。崩溃恢复腿(收敛器孤儿段)与两条兜底臂调**同一只** claim(`settleByPolicy`,
|
|
1799
|
+
* S-702 / S-706,7.101.0),于是本旋钮也施加到收敛器上(装配点恒传)。仍按 PARKING 记账的只剩别的写者推进的行
|
|
1800
|
+
* (park 政策副本 / 切换政策之前)。
|
|
1772
1801
|
* · 🔴 `deny` 部署上**批是单 ask 批**(S-689 codex R1-F1;`deriveAskScopedBatchId`):批是 park 路由的单位,
|
|
1773
1802
|
* 整腿一批的前提是「批一关腿就随 park 结束」;deny 下引擎拿到部署政策拒后腿继续跑,整腿一批会让首只到期
|
|
1774
1803
|
* 把批关掉(同腿后续的卡人批不动)、把兄弟连坐成 VOID(裸 `false` = 人拒)。
|
|
@@ -1943,7 +1972,7 @@ export interface ServiceConfigFlat {
|
|
|
1943
1972
|
/** 组:store(持久化)—— DB 引擎三态、session 后端、SQL coords、快照 BLOB / SendUserFile 对象存储。 */
|
|
1944
1973
|
export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "sessionCacheTtlSec" | "storeProbeIntervalMs" | "storeLockWaitMs" | "dbBackend" | "dbBackendExplicit" | "localDataRoot" | "runStoreStrictHydrate" | "tidb" | "pg" | "dbQueryTimeoutMs" | "snapshotBlobStore" | "snapshotBlobSqlMaxBytes" | "snapshotBlobAllowSql" | "fileHistory" | "fileHistoryCapture" | "sendUserFile">;
|
|
1945
1974
|
/** 组:modelPlane(模型面)—— 网关坐标、Anthropic 路线、韧性旋钮、主/廉价 model entry、role 表、降级梯。 */
|
|
1946
|
-
export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "envModel" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
|
|
1975
|
+
export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "envModel" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelRefusals" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
|
|
1947
1976
|
/** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
|
|
1948
1977
|
* 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
|
|
1949
1978
|
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" | "autoModeClassifierFallback" | "crossSessionInbound" | "crossSessionDialogExpiry">;
|
|
@@ -1957,7 +1986,7 @@ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled"
|
|
|
1957
1986
|
* config 这一层它只是一句声明。 */
|
|
1958
1987
|
export type ServiceAuthConfig = Pick<ServiceConfigFlat, "authToken" | "authTokens" | "allowUnauthedWrites" | "corsOrigins" | "principalHeader" | "requirePrincipal" | "autonomy" | "commandPolicy" | "compliancePosture" | "lockedConfigKeys" | "configCatalogEnabled" | "retentionPolicy" | "retentionSweep" | "operatorPrincipals" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "authBridgeIssuer" | "principalJwtMaxTtlSec" | "bindHost" | "bindHostSource">;
|
|
1959
1988
|
/** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
|
|
1960
|
-
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "remoteExecHomeDir" | "requireExecutionLane" | "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" | "taskWriteFaceEscalation" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1989
|
+
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "remoteExecHomeDir" | "requireExecutionLane" | "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" | "lspEnvAllow" | "imageBakes" | "readFace" | "readFaceSource" | "taskWriteFaceEscalation" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1961
1990
|
/** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
|
|
1962
1991
|
export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachmentOrphanGraceMs" | "workspaceFileMaxBytes" | "attachmentMaxBytes" | "attachmentMimeAllowlist" | "attachmentUnboundTtlMs" | "infraCostRates" | "drainGraceMs" | "sighupIdleGraceMs" | "parentPid" | "engineAutoExit" | "engineLingerMs" | "engineOrphanLingerMs" | "rateLimitPerMin" | "maxTaskCostUsd" | "maxTaskTokens" | "maxPrincipalCostUsd" | "costQuotaWindowSec" | "reapIntervalSec" | "runStaleSec" | "toolResultTtlSec">;
|
|
1963
1992
|
/** 组:observability(可观测)。 */
|