@sema-agent/server 7.56.0 → 7.57.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/README.md +3 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +101 -9
- package/dist/approval-ask-audit-store.d.ts +100 -1
- package/dist/approval-ask-audit-store.js +103 -2
- package/dist/approval-card.d.ts +148 -13
- package/dist/approval-card.js +82 -13
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +18 -0
- package/dist/auto-mode-face.d.ts +111 -0
- package/dist/auto-mode-face.js +99 -0
- package/dist/bench/s1/arms.js +5 -5
- package/dist/bench/s1/live-deps.js +11 -11
- package/dist/bench/s1/run-firm.js +3 -0
- package/dist/bench/s1/runner-ctx.d.ts +4 -0
- package/dist/bench/s1/runner-ctx.js +7 -0
- package/dist/boot/config-center.js +23 -1
- package/dist/boot/coordinators.js +13 -1
- package/dist/boot/parked-revive-gate.d.ts +7 -2
- package/dist/boot/parked-revive-gate.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +3 -1
- package/dist/boot/runner-deps.d.ts +12 -0
- package/dist/boot/runner-deps.js +28 -3
- package/dist/boot/runtime-caps.d.ts +19 -9
- package/dist/boot/runtime-caps.js +49 -21
- package/dist/boot/session-shell-gate-registry.d.ts +39 -0
- package/dist/boot/session-shell-gate-registry.js +21 -0
- package/dist/config-catalog.js +14 -5
- package/dist/config-types.d.ts +41 -10
- package/dist/config.d.ts +21 -0
- package/dist/config.js +40 -5
- package/dist/http/route-ctx.d.ts +75 -0
- package/dist/http/routes/a2a-serve.js +5 -3
- package/dist/http/routes/approvals-assistant.js +19 -6
- package/dist/http/routes/capabilities.js +21 -4
- package/dist/http/routes/memory-origin.d.ts +2 -2
- package/dist/http/routes/rules.js +3 -2
- package/dist/http/routes/runs.d.ts +0 -14
- package/dist/http/routes/runs.js +20 -8
- package/dist/http/routes/tasks.js +34 -8
- package/dist/http/routes/workflows.js +19 -4
- package/dist/http/server.d.ts +11 -1
- package/dist/http/server.js +215 -43
- package/dist/http/wire-types.d.ts +9 -4
- package/dist/leader/diffout.js +2 -1
- package/dist/leader/endpoint.js +45 -12
- package/dist/leader/fanout.js +6 -5
- package/dist/leader/leader.js +17 -14
- package/dist/leader/merge.js +17 -12
- package/dist/leader/planner.js +3 -2
- package/dist/leader/repair-oracle.js +6 -4
- package/dist/leader/repair-wire.js +5 -3
- package/dist/leader/wire.d.ts +30 -1
- package/dist/leader/wire.js +36 -19
- package/dist/main.js +43 -7
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +10 -0
- package/dist/observability/fail-open.d.ts +40 -0
- package/dist/observability/fail-open.js +19 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.d.ts +120 -0
- package/dist/observability/run-terminal-log.js +360 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
- package/dist/orchestration/workflow-completion-inbox.js +8 -0
- package/dist/permission-rule-vocab.d.ts +40 -0
- package/dist/permission-rule-vocab.js +17 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
- package/dist/plugins/checkpoint-store-sql.js +19 -3
- package/dist/plugins/file-run-store.d.ts +3 -34
- package/dist/plugins/file-run-store.js +19 -0
- package/dist/plugins/local-checkpoint-store.d.ts +10 -0
- package/dist/plugins/local-checkpoint-store.js +3 -0
- package/dist/plugins/memory-run-store.d.ts +3 -15
- package/dist/plugins/memory-run-store.js +19 -0
- package/dist/plugins/permission-rule-store-file.js +8 -3
- package/dist/plugins/permission-rule-store-sql.js +16 -10
- package/dist/plugins/remote-scratchpad.js +3 -2
- package/dist/plugins/run-store-sql.d.ts +3 -42
- package/dist/plugins/run-store-sql.js +30 -1
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +66 -1
- package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
- package/dist/plugins/workflow-run-store-sql.js +10 -2
- package/dist/rules-consent.js +20 -13
- package/dist/run-cancel-context.d.ts +13 -0
- package/dist/run-cancel-context.js +15 -0
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +4 -1
- package/dist/runs.js +41 -10
- package/dist/runtime-caps-resolver.d.ts +133 -17
- package/dist/runtime-caps-resolver.js +41 -3
- package/dist/task-settings.d.ts +57 -3
- package/dist/task-settings.js +78 -5
- package/dist/task-workflow.d.ts +32 -2
- package/dist/task-workflow.js +8 -3
- package/dist/tool-approval.d.ts +26 -3
- package/dist/tool-approval.js +96 -13
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +13 -4
- package/dist/trace/ledger-sink.js +14 -6
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +10 -1
- package/dist/trace/redact.d.ts +22 -11
- package/dist/trace/redact.js +655 -20
- package/dist/trace/sema-provenance.d.ts +26 -0
- package/dist/trace/sema-provenance.js +11 -0
- package/dist/turn-activity.d.ts +32 -2
- package/dist/turn-activity.js +29 -4
- package/package.json +2 -2
|
@@ -1,24 +1,42 @@
|
|
|
1
|
-
import { applyMemoryOptOutGrant, applyObserverEnvOptIn, centerEntitlementSourceWired, createPrincipalEntitlementsClient, scopedTokenNeedsWorker, } from "../runtime-caps-resolver.js";
|
|
1
|
+
import { applyGovernedVerdictAbsenceWarning, applyLocalAutoModeDeny, applyMemoryOptOutGrant, applyObserverEnvOptIn, centerEntitlementSourceWired, createPrincipalEntitlementsClient, scopedTokenNeedsWorker, } from "../runtime-caps-resolver.js";
|
|
2
2
|
export function assertMemoryCapturePolicyWirable(input) {
|
|
3
3
|
if (input.policy !== "governed")
|
|
4
4
|
return;
|
|
5
5
|
if (input.grantSourceWired)
|
|
6
6
|
return;
|
|
7
|
-
if (input.
|
|
7
|
+
if (input.centerEntitlementSourceWired === true)
|
|
8
8
|
return;
|
|
9
9
|
throw new Error(`MEMORY_CAPTURE_POLICY=governed requires a per-principal memory opt-out verdict source, and this deployment wires NONE ` +
|
|
10
10
|
`— under "governed" core REFUSES every session memory-capture opt-out whose verdict is absent (fail-closed, the posture's ` +
|
|
11
11
|
`own contract), so a governed worker without a source would refuse every \`memory.capture:"off"\` declaration and the ` +
|
|
12
|
-
`refusal would read as an entitlement denial rather than the misconfiguration it is.
|
|
13
|
-
|
|
14
|
-
`central ensureSchema and administered through /v1/admin/memory-optout)
|
|
15
|
-
`
|
|
12
|
+
`refusal would read as an entitlement denial rather than the misconfiguration it is. This version has TWO verdict sources ` +
|
|
13
|
+
`— wire either one: (1) the \`memory_optout_grant\` table on a SQL store backend (DB_BACKEND=mysql|pg; the table is ` +
|
|
14
|
+
`created by the central ensureSchema and administered through /v1/admin/memory-optout), or (2) a config center that ` +
|
|
15
|
+
`EMITS \`allowMemoryOptOut\` in each principal's entitlement caps (SEMA_REGISTRY_URL + SEMA_REGISTRY_TOKEN, ` +
|
|
16
|
+
`SEMA_REGISTRY_DRY_RUN off — dry-run observes without applying, so it is not a source; and note the key is OPTIONAL in ` +
|
|
17
|
+
`the entitlement schema: a center that never emits it leaves every verdict absent, i.e. every opt-out still refused). ` +
|
|
18
|
+
`Or set MEMORY_CAPTURE_POLICY=open (opt-outs take effect; only an explicit per-principal false denies) or ` +
|
|
16
19
|
`MEMORY_CAPTURE_POLICY=capture-required (every opt-out declaration refused, no source consulted).`);
|
|
17
20
|
}
|
|
21
|
+
function memoryVerdictAbsenceEffect(reason) {
|
|
22
|
+
switch (reason) {
|
|
23
|
+
case "anonymous_lane":
|
|
24
|
+
return 'an ANONYMOUS/system run (no principal) resolved no allowMemoryOptOut — the center entitlement source is per-principal and is never consulted without an identity, so on a center-only deployment the principal-less lane can NEVER obtain a verdict and core refuses every anonymous memory.capture:"off" (memory.capture_optout_denied). Only the local memory_optout_grant table answers for anonymous runs (its deployment-default row). One-shot per process.';
|
|
25
|
+
case "center_verdict_absent":
|
|
26
|
+
return 'governed posture, center-only verdict source, and this resolve produced NO allowMemoryOptOut for a NAMED principal (the center omitted the optional entitlement key, or the caps fetch degraded fail-closed) ⇒ core refuses that principal\'s memory.capture:"off" declarations (memory.capture_optout_denied). One-shot per process.';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
18
29
|
export function createRuntimeCaps(ctx) {
|
|
19
30
|
const { config, logger } = ctx;
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
const grantSourceWired = ctx.memoryOptOutGrant !== undefined;
|
|
32
|
+
assertMemoryCapturePolicyWirable({ policy: config.memoryCapturePolicy, grantSourceWired, centerEntitlementSourceWired: centerEntitlementSourceWired(config) });
|
|
33
|
+
const governedCenterOnlySource = config.memoryCapturePolicy === "governed" && !grantSourceWired && centerEntitlementSourceWired(config);
|
|
34
|
+
if (governedCenterOnlySource) {
|
|
35
|
+
logger.warn("memory_capture_governed_center_only_source", {
|
|
36
|
+
reason: "MEMORY_CAPTURE_POLICY=governed with NO local memory_optout_grant table: the only verdict source is the config center, and `allowMemoryOptOut` is an OPTIONAL entitlement key — for any principal whose caps omit it the verdict is ABSENT and core refuses that session's memory.capture:\"off\" (memory.capture_optout_denied, fail-closed). ANONYMOUS/system runs are worse off still: the center source is per-principal and is never consulted without an identity, so on this shape a principal-less run can never obtain a verdict at all. Verify the center emits allowMemoryOptOut for every principal, or wire a SQL backend (DB_BACKEND=mysql|pg) so the grant table answers (its deployment-default row also covers anonymous runs).",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (centerEntitlementSourceWired(config) && config.configCenter && scopedTokenNeedsWorker(config.configCenter.token, config.configCenter.worker)) {
|
|
22
40
|
logger.warn("runtime_caps_scoped_token_no_worker", {
|
|
23
41
|
reason: "SEMA_REGISTRY_TOKEN is a worker-scoped wpt_ token but SEMA_REGISTRY_WORKER is unset → center 403s per-principal caps → ALL workflow self-orchestration will be fail-closed denied. Set SEMA_REGISTRY_WORKER (the orchestrator normally injects it) or use the full SERVICE_PULL_TOKEN.",
|
|
24
42
|
});
|
|
@@ -32,18 +50,28 @@ export function createRuntimeCaps(ctx) {
|
|
|
32
50
|
})
|
|
33
51
|
: undefined;
|
|
34
52
|
const centerRuntimeCapsResolver = principalCaps?.resolveRuntimeCaps;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
principal,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
const compose = (base, observability) => {
|
|
54
|
+
const withObservers = applyObserverEnvOptIn(base, {
|
|
55
|
+
experimentalObserverAgents: config.experimentalObserverAgents,
|
|
56
|
+
requirePrincipal: config.requirePrincipal,
|
|
57
|
+
...(observability ? { warn: (msg, fields) => logger.warn(msg, fields) } : {}),
|
|
58
|
+
});
|
|
59
|
+
const withMemoryGrant = applyMemoryOptOutGrant(withObservers, ctx.memoryOptOutGrant, {
|
|
60
|
+
onFault: (err, principal) => logger.warn("memory_optout_grant_resolve_failed", {
|
|
61
|
+
principal,
|
|
62
|
+
err: String(err),
|
|
63
|
+
...(observability ? {} : { face: "capabilities-read" }),
|
|
64
|
+
effect: "allowMemoryOptOut left ABSENT for this resolve (never coined as false): open posture ⇒ the opt-out takes effect; governed ⇒ core refuses the run (verdict absent)",
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
const withLocalAutoModeDeny = applyLocalAutoModeDeny(withMemoryGrant, config.permissionsDisableAutoMode);
|
|
68
|
+
return applyGovernedVerdictAbsenceWarning(withLocalAutoModeDeny, {
|
|
69
|
+
enabled: observability && governedCenterOnlySource,
|
|
70
|
+
warn: (reason, principal) => logger.warn("memory_capture_governed_verdict_absent", { principal, reason, effect: memoryVerdictAbsenceEffect(reason) }),
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const runtimeCapsResolver = compose(centerRuntimeCapsResolver, true);
|
|
74
|
+
const runtimeCapsPeekResolver = compose(principalCaps?.peekRuntimeCaps, false);
|
|
75
|
+
return { principalCaps, centerRuntimeCapsResolver, runtimeCapsResolver, runtimeCapsPeekResolver };
|
|
48
76
|
}
|
|
49
77
|
//# sourceMappingURL=runtime-caps.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [ref] 件⑤(core PM 三仓扫查 [ref] server 条 ③,并入 [ref])—— 「本 run 是否**显式**声明了 bypassPermissions」的
|
|
3
|
+
* per-session 登记簿,供 `boot/runner-deps.ts` 的 `onError(phase:"config")` 汇点给 core 的 `shell-gate-off`
|
|
4
|
+
* 忠告定级。
|
|
5
|
+
*
|
|
6
|
+
* ## 为什么需要它
|
|
7
|
+
* core 在「shell 门 doctrine=off ∧ 挂了真可写 Bash」时经 `onError(phase:"config", classification:"shell-gate-off")`
|
|
8
|
+
* 发一条忠告(`prepare-hands-readface.js`),但它分不清 off 是**调用方显式选的**(server 把 `permissionMode:
|
|
9
|
+
* "bypassPermissions"` 翻成 `shellGate:"off"`,[ref] 表)还是**没表态落到 core 默认**(无壳直连部署,真值得
|
|
10
|
+
* 一条 warn)。这正是真实用户提案里「bypass 下照打 shell gate doctrine is off 告警」的根:正常态被当异常打。
|
|
11
|
+
* 汇点只拿到 `ctx.sessionId`,而生效模式是 server 在 resolve-spec 阶段④ 单点算出的(`effectivePermissionMode`),
|
|
12
|
+
* 所以由 resolve-spec 在算出 effMode 的那一行登记、汇点按 sessionId 回查。
|
|
13
|
+
*
|
|
14
|
+
* ## 键
|
|
15
|
+
* `auth.sessionId`:authorizer 在服务路径上**恒**铸 session id(`security.ts` `uuidv7()`,core 的无 sessionId
|
|
16
|
+
* 分支在服务路径不可达),且 spec.sessionId = 同一值 ⇒ core 回调的 `ctx.sessionId` 与登记键同源。
|
|
17
|
+
* 缺 authorizer 的裸装配(测试桩)拿不到键 ⇒ 不登记 ⇒ 汇点按「未声明」处理 = 仍 warn(退化方向=多一条告警,
|
|
18
|
+
* 不是少一条)。
|
|
19
|
+
*
|
|
20
|
+
* ## 已知界(codex r2 [medium],登记不修:属主在 core 回调契约)
|
|
21
|
+
* 键是 session 而非**执行**:同 session 并发第二次提交在 resolveSpec 后才被 409 拒,其登记已覆盖前一次——若被拒的
|
|
22
|
+
* 那次是 bypass 而在跑的那次不是,且忠告恰在这一窗内发出,则一条本该 warn 的 `shell-gate-off` 被降成 info(仍整行
|
|
23
|
+
* 落日志、字段齐全,执法零变)。窗 = 在跑 run 的 resolveSpec 到其 prepare 期忠告之间(毫秒级,且要同 session 并发
|
|
24
|
+
* 提交 + 模式相反)。执行唯一的归因需要 core 回调 `ctx` 带 taskId / shellGate 出处(已作 core 侧诉求登记),
|
|
25
|
+
* 到货后本簿退役。
|
|
26
|
+
*
|
|
27
|
+
* ## 界
|
|
28
|
+
* 有界 Map(插入序淘汰,默认 10k 条,与 caps 缓存同量级):session id 是开集,长命 worker 不能无界累积。
|
|
29
|
+
* 淘汰掉的旧会话再来忠告时同样回落「未声明」⇒ warn(同一退化方向)。每次 resolve 覆写(同 session 后一轮改
|
|
30
|
+
* 模式时以最近一轮为准;续跑四条 rebuild 腿都经 resolveSpec ⇒ 同律登记)。
|
|
31
|
+
*/
|
|
32
|
+
export interface SessionShellGateRegistry {
|
|
33
|
+
/** resolve-spec 阶段④:登记本 session 这一轮是否显式声明 bypassPermissions(= server 翻成 shellGate:"off")。 */
|
|
34
|
+
note(sessionId: string | undefined, explicitOff: boolean): void;
|
|
35
|
+
/** runner-deps 汇点:本 session 最近一轮是否显式声明了 off。未登记/已淘汰 ⇒ false(退化=照 warn)。 */
|
|
36
|
+
explicitlyOff(sessionId: string): boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function createSessionShellGateRegistry(cap?: number): SessionShellGateRegistry;
|
|
39
|
+
//# sourceMappingURL=session-shell-gate-registry.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function createSessionShellGateRegistry(cap = 10_000) {
|
|
2
|
+
const off = new Map();
|
|
3
|
+
return {
|
|
4
|
+
note(sessionId, explicitOff) {
|
|
5
|
+
if (sessionId === undefined)
|
|
6
|
+
return;
|
|
7
|
+
if (off.has(sessionId))
|
|
8
|
+
off.delete(sessionId);
|
|
9
|
+
else if (off.size >= cap) {
|
|
10
|
+
const oldest = off.keys().next();
|
|
11
|
+
if (!oldest.done)
|
|
12
|
+
off.delete(oldest.value);
|
|
13
|
+
}
|
|
14
|
+
off.set(sessionId, explicitOff);
|
|
15
|
+
},
|
|
16
|
+
explicitlyOff(sessionId) {
|
|
17
|
+
return off.get(sessionId) === true;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=session-shell-gate-registry.js.map
|
package/dist/config-catalog.js
CHANGED
|
@@ -254,7 +254,7 @@ export const CONFIG_CATALOG = [
|
|
|
254
254
|
r("MODEL_COST_CACHE_WRITE", "modelPlane", "number", "主模型 cache-write 单价", { staticDefault: "0", danger: BILL, resolve: (c) => c.model.cost?.cacheWrite ?? null, center: cModels("models[].cost.cacheWrite") }),
|
|
255
255
|
r("MODEL_COST_INPUT", "modelPlane", "number", "主模型 input 单价(NaN 会让成本天花板整条消失 ⇒ 坏值拒启)", { staticDefault: "0", danger: BILL, resolve: (c) => c.model.cost?.input ?? null, center: cModels("models[].cost.input") }),
|
|
256
256
|
r("MODEL_COST_OUTPUT", "modelPlane", "number", "主模型 output 单价", { staticDefault: "0", danger: BILL, resolve: (c) => c.model.cost?.output ?? null, center: cModels("models[].cost.output") }),
|
|
257
|
-
r("MODEL_DEFAULT_THINKING", "modelPlane", "enum", "主模型默认思考档(llm-core ThinkingLevel
|
|
257
|
+
r("MODEL_DEFAULT_THINKING", "modelPlane", "enum", "主模型默认思考档(llm-core ThinkingLevel;off=不设模型级默认;未知词拒启)", { enumValues: ["minimal", "low", "medium", "high", "xhigh", "max", "off"], derivedDefaultNote: "缺席/off ⇒ 不铸 defaultThinking(逐任务档由 spec>role 决定)", resolve: (c) => c.model.defaultThinking ?? null }),
|
|
258
258
|
r("MODEL_DEGRADE_AT_COST_FRACTION", "modelPlane", "number", "预算降级触发比(spent/budget;>1=实际别降级)", { staticDefault: "0.7", danger: BILL }),
|
|
259
259
|
r("MODEL_DEGRADE_ON", "modelPlane", "csv", "反应式降级触发词表(core DegradeReason 全集;拼错拒启)", { center: { domain: "models", path: "degrade", centerKey: "models", precedence: "center-wins", timing: "restart", restartSlice: "degrade-route" } }),
|
|
260
260
|
r("MODEL_DEGRADE_REACTIVE", "modelPlane", "boolean", "反应式降级开关(degrade-route 重启片只在它开时活)", { staticDefault: "false", center: { domain: "models", path: "degrade", centerKey: "models", precedence: "center-wins", timing: "restart", restartSlice: "degrade-route" } }),
|
|
@@ -271,7 +271,15 @@ export const CONFIG_CATALOG = [
|
|
|
271
271
|
r("MODEL_PRESENCE_PENALTY", "modelPlane", "number", "presence_penalty 直通"),
|
|
272
272
|
r("MODEL_PROVIDER", "modelPlane", "string", "主模型 provider(路由选 brain)", { derivedDefaultNote: "缺席:ANTHROPIC base+cred 同在 ⇒ anthropic,否则 gateway", resolve: (c) => c.model.provider, center: cModels("models[].provider") }),
|
|
273
273
|
r("MODEL_REASONING", "modelPlane", "boolean", "主模型 reasoning 声明", { staticDefault: "true", resolve: (c) => c.model.reasoning ?? null, center: cModels("models[].reasoning") }),
|
|
274
|
-
r("MODEL_REASONING_EFFORT_LEVELS", "modelPlane", "csv", "openai 端点接受的思考档集(
|
|
274
|
+
r("MODEL_REASONING_EFFORT_LEVELS", "modelPlane", "csv", "openai 端点接受的思考档集(六档子集;高档 clamp-down 不 422;anthropic 腿不铸此键;未知词拒启)", {
|
|
275
|
+
derivedDefaultNote: "缺席 ⇒ 不铸 compat,core 走保守梯(minimal|low|medium|high),xhigh/max 被 clamp 下来",
|
|
276
|
+
resolve: (c) => {
|
|
277
|
+
const compat = c.model.compat;
|
|
278
|
+
if (compat === undefined || !("reasoningEffortLevels" in compat))
|
|
279
|
+
return null;
|
|
280
|
+
return compat.reasoningEffortLevels ?? null;
|
|
281
|
+
},
|
|
282
|
+
}),
|
|
275
283
|
r("MODEL_ROUTER_ID", "modelPlane", "string", "leader 路由分类模型覆写", { derivedDefaultNote: "缺席 ⇒ MODEL_CHEAP_ID,再退 default", danger: BILL }),
|
|
276
284
|
r("MODEL_VERIFIER", "modelPlane", "string", "verify 门的异构 verifier(须命中目录;typo 回 default 并 boot warn=自评自批)", { derivedDefaultNote: "缺席/不在目录 ⇒ default(=implementer,去相关性失效警告)" }),
|
|
277
285
|
r("MODEL_VISION", "modelPlane", "boolean", "主模型 vision 声明(false 让 model_no_vision 预检真拦)", { staticDefault: "true", resolve: (c) => (Array.isArray(c.model.input) ? c.model.input.includes("image") : true), center: cModels("models[].vision") }),
|
|
@@ -286,6 +294,7 @@ export const CONFIG_CATALOG = [
|
|
|
286
294
|
r("OTEL_EXPORTER_OTLP_HEADERS", "observability", "secret", "OTLP 出口头(k=v CSV;常载鉴权头 ⇒ 按凭据处置)", { danger: SEC }),
|
|
287
295
|
r("OTEL_EXPORT_INTERVAL_MS", "observability", "number", "OTLP 批报周期(ms)", { staticDefault: "15000" }),
|
|
288
296
|
r("OTEL_SERVICE_NAME", "observability", "string", "OTLP service.name", { staticDefault: "sema-server" }),
|
|
297
|
+
r("PERMISSIONS_DISABLE_AUTO_MODE", "approval", "boolean", "auto 模式本地 org deny(CC/center/settings 键名 permissions.disableAutoMode 的 env 腿;tighten-only:每 principal caps.autoMode 折 false,center 授予翻不回;未设=不动 caps;词表外拒启)", { staticDefault: "false", danger: SEC, configKey: "permissionsDisableAutoMode", center: { domain: "permissions", path: "disableAutoMode", centerKey: "permissions.disableAutoMode", precedence: "env-wins", timing: "inert" } }),
|
|
289
298
|
r("PERMISSION_RULES_ENABLED", "approval", "boolean", "持久化权限规则车道总开关(撤销面已建 ⇒ 默认 ON)", { staticDefault: "true", danger: SEC, configKey: "permissionRulesEnabled" }),
|
|
290
299
|
r("PG_DATABASE", "store", "string", "PostgreSQL 库名", { danger: DATA }),
|
|
291
300
|
r("PG_HOST", "store", "string", "PostgreSQL 主机(在场即算 SQL 信号)", { danger: DATA }),
|
|
@@ -361,7 +370,7 @@ export const CONFIG_CATALOG = [
|
|
|
361
370
|
r("SERVICE_AUTH_TOKEN", "auth", "secret", "共享 service token(写面门;缺席且未开 ALLOW_UNAUTHED_WRITES ⇒ 拒提交)", { danger: SEC }),
|
|
362
371
|
r("SERVICE_AUTH_TOKENS", "auth", "secret", "per-system 凭据表(tok=system CSV;source 从凭据派生,绝不自报)", { danger: SEC }),
|
|
363
372
|
r("SESSION_AUTO_TITLE", "orchestration", "boolean", "首次提交自动铸 session 标题(一次 cheap 调用)", { staticDefault: "true", danger: BILL, configKey: "sessionAutoTitle" }),
|
|
364
|
-
r("SESSION_BACKEND", "store", "enum", "session 店引擎(auto=探到 DB 用 DB
|
|
373
|
+
r("SESSION_BACKEND", "store", "enum", "session 店引擎(auto=探到 DB 用 DB,探不到回内存;回显=boot 解析后的真引擎)", { enumValues: ["memory", "mysql", "auto"], danger: DATA, derivedDefaultNote: "缺席:显式 DB_BACKEND=mysql|pg ⇒ mysql,否则 memory", configKey: "sessionBackend", resolve: (c) => (c.sessionBackend === "tidb" ? "mysql" : c.sessionBackend) }),
|
|
365
374
|
r("SESSION_CACHE_TTL_SEC", "store", "number", "session 读缓存 TTL(秒;0=关)", { staticDefault: "300", configKey: "sessionCacheTtlSec" }),
|
|
366
375
|
r("SESSION_EVENTS_HOT_MS", "limitsHttp", "number", "session SSE 订阅热探针间隔(ms)", { derivedDefaultNote: "缺席 ⇒ 200" }),
|
|
367
376
|
r("SESSION_EVENTS_MAX_CONNS", "limitsHttp", "number", "session SSE 并发连接帽", { derivedDefaultNote: "缺席 ⇒ 256" }),
|
|
@@ -391,12 +400,12 @@ export const CONFIG_CATALOG = [
|
|
|
391
400
|
r("STREAM_APPROVAL_RECONCILE_BATCH", "approval", "number", "对账收敛器每 tick 每段行数帽(整数)", { staticDefault: "200" }),
|
|
392
401
|
r("STREAM_APPROVAL_REPLAY_MAX", "approval", "number", "开流 preamble 重放帽", { staticDefault: "50" }),
|
|
393
402
|
r("STREAM_ASK_WINDOW_MARGIN_MS", "approval", "number", "审批窗长三元安全余量(ms)", { staticDefault: "10000", configKey: "streamAskWindowMarginMs" }),
|
|
394
|
-
r("STREAM_ASK_WINDOW_MS", "approval", "number", "
|
|
403
|
+
r("STREAM_ASK_WINDOW_MS", "approval", "number", "活卡审批窗长(ms;所有腿生效,与流内协议上不上场无关;0=关窗⇒不发卡恒走无人值守终局;负值与超 Node 定时器上界 2147483647 的值拒启)", { staticDefault: "300000" }),
|
|
395
404
|
r("SYNC_IMPORT_LEASE_STALE_SEC", "memory", "number", "session-sync staged import 租约陈旧阈(秒)", { staticDefault: "600", configKey: "syncImportLeaseStaleSec" }),
|
|
396
405
|
r("TASK_MAX_OUTPUT_TOKENS_MAX", "limitsHttp", "number", "调用方自报 limits.maxOutputTokens 的运营方封顶(Math.min 钳显式请求值;坏值=封顶不生效+响亮 warn)", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 不封顶" }),
|
|
397
406
|
r("TASK_MAX_TURNS_MAX", "limitsHttp", "number", "调用方自报 limits.maxTurns 的运营方封顶(同族三旋钮;只钳显式请求值,不是全队默认限额)", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 不封顶" }),
|
|
398
407
|
r("TASK_TIMEOUT_MAX_SEC", "limitsHttp", "number", "调用方自报 limits.timeoutSec 的运营方封顶(同族三旋钮)", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 不封顶" }),
|
|
399
|
-
r("TASK_TIMEOUT_SEC", "limitsHttp", "number", "任务墙钟超时(
|
|
408
|
+
r("TASK_TIMEOUT_SEC", "limitsHttp", "number", "任务墙钟超时(秒);0/缺席=按 tenancy(单用户无墙;多租户 2400s、大任务 3600s);正值**只抬不降**(Math.max 基值);负值拒启", { staticDefault: "0", derivedDefaultNote: "0/缺席 ⇒ 单用户无墙、多租户 2400/3600s;要低于基值的配速请用 body.limits.maxWalltimeMs" }),
|
|
400
409
|
r("TOOL_APPROVAL_ENABLED", "approval", "boolean", "活体工具审批 HITL(onAsk 装配;关 ⇒ core resolveAsk 走 headless 缺省)", { danger: SEC, derivedDefaultNote: "posture 三态:显式词恒赢,缺席 = 单用户(REQUIRE_PRINCIPAL≠true)⇒ on / 多租户 ⇒ off", configKey: "toolApprovalEnabled" }),
|
|
401
410
|
r("TOOL_DEFER_LONGTAIL", "orchestration", "boolean", "长尾一方工具 defer 呈现(实验)", { staticDefault: "false", configKey: "toolDeferLongtail" }),
|
|
402
411
|
r("TOOL_RESULT_TTL_SEC", "limitsHttp", "number", "工具结果店 TTL(秒)", { staticDefault: "86400", danger: DATA, configKey: "toolResultTtlSec", center: cLimits("toolResultTtlSec") }),
|
package/dist/config-types.d.ts
CHANGED
|
@@ -145,16 +145,36 @@ export interface A2aServeConfig {
|
|
|
145
145
|
* the default base ref) live here so image-api injects only VETTED, server-fixed values into the build.sh argv —
|
|
146
146
|
* operator free-text never reaches the runner. */
|
|
147
147
|
/** [ref] 车3([ref] 流内审批协议)的配置面。总开关默认 **true**(clay 裁 2026-08-08,[ref] 翻真验证后;
|
|
148
|
-
* 树上已生效,发布线落在 7.4.0 之后的下一个发布)
|
|
148
|
+
* 树上已生效,发布线落在 7.4.0 之后的下一个发布);从属旋钮**除 `windowMs` 外**只在开关为真时生效
|
|
149
|
+
* ([ref]:`STREAM_ASK_WINDOW_MS` 是活卡窗长,与协议上不上场解耦 —— 逐条辖域见各字段域注)
|
|
149
150
|
* (语义与「谁需要 / 谁被伤 / 什么补偿」三问见 `ServiceConfigFlat.streamApproval` 的注)。 */
|
|
150
151
|
export interface StreamApprovalConfig {
|
|
151
152
|
/** 协议总开关。`STREAM_APPROVAL_ENABLED`,默认 **true**(clay 裁 2026-08-08,[ref] 翻真验证后;7.5.0 起)。显式 false ⇒ 全链逐字 7.3.0 前行为(唯一干净还原键)。
|
|
152
153
|
* 翻真要等回决端点(车4)与对账收敛器(车5)到位——在此之前是个不完整协议(有卡无正规回决口、
|
|
153
154
|
* 有 PARKING 无收敛器、跨副本回决无唤醒路径)。 */
|
|
154
155
|
enabled: boolean;
|
|
155
|
-
/**
|
|
156
|
-
* `STREAM_ASK_WINDOW_MS`,默认 **300000**(同裁与 sync 腿既有 5min 活卡窗对齐)
|
|
157
|
-
*
|
|
156
|
+
/** `ToolApprovalCoordinator` 的 `ttlMs` —— **一只活卡等人多久**([ref] §3.3 的「配置窗默认」)。
|
|
157
|
+
* `STREAM_ASK_WINDOW_MS`,默认 **300000**(同裁与 sync 腿既有 5min 活卡窗对齐)。
|
|
158
|
+
*
|
|
159
|
+
* 🔴 **辖域([ref] 起):无条件** —— 本值在**每一条**呈活卡的腿上生效,与流内协议上不上场无关
|
|
160
|
+
* (`DB_BACKEND=local` / 无 park 设施 / `STREAM_APPROVAL_ENABLED=false` 的部署同样按它计窗)。
|
|
161
|
+
* 旧文「开关为假 ⇒ 本值不参与,窗保持既有 `DEFAULT_APPROVAL_TTL_MS`(5min)」是**已修的病**,不是
|
|
162
|
+
* 设计:那让一根部署级旋钮的生效与否挂在装配在场性上,运维配了它、没有任何一行说它没生效
|
|
163
|
+
* (clay 令:部署级旋钮禁挂在场性派生腿)。缺省值与那个常量同为 300000 ⇒ **没配过这根 env 的部署
|
|
164
|
+
* 字节不变**;协议上不上场改变的是**持久层与协议帧**(ask 行 / 重放 / 迟到赎回),不是窗长。
|
|
165
|
+
*
|
|
166
|
+
* `0` = 运维显式关窗 ⇒ **不发任何审批卡**,每只受门 ask 在铸造时刻直接走 `unattendedAskOutcome()`
|
|
167
|
+
* (§3.3 窗=0 语义;[ref] 起两条车道同义 —— 此前 `0` 在协议不上场的腿上被静默读成「5min 活卡」)。
|
|
168
|
+
* 该形有 park 设施 ⇒ 全落 durable park(经 `POST /v1/approvals/:sessionId/decide` 兑现);无 park 设施
|
|
169
|
+
* 或 `UNATTENDED_APPROVAL_POLICY=deny` ⇒ core fail-closed 拒 = **受门工具全关**,故 boot 期有一行
|
|
170
|
+
* `stream_ask_window_zero` 点名(`boot/coordinators.ts`)。
|
|
171
|
+
* **坏值拒启两侧**([ref]):负值(`-1` 此前静默落成「立即到期」并白发一张没人赢得了的卡)与
|
|
172
|
+
* **超过 Node 定时器上界 `2147483647`(2^31−1 ms ≈ 24.8 天)的值**(codex r1-[high]:`setTimeout` 把更大的
|
|
173
|
+
* delay 静默折成 1ms ⇒ 想调长反而立即到期;跨旋钮门只管 `窗 + adhoc宽限 < orphanTtl`,而 orphanTtl 域上界
|
|
174
|
+
* 90 天,所以 30 天的窗此前合法通过)—— 两侧都点名拒启,不夹取。
|
|
175
|
+
*
|
|
176
|
+
* 窗到期的**去向**不由本键决定(`UNATTENDED_APPROVAL_POLICY` + park 设施在场性决定),本键只决定
|
|
177
|
+
* 「等多久」。悬挂 ask(零活流铸造,[ref] 案A)走的是 `unreachedTtlMs`,不是本键。 */
|
|
158
178
|
windowMs: number;
|
|
159
179
|
/** 开流重放(§5)每次最多投几张未决卡——**读面**的帽,超出只投最新的并记一次 warn,开流不失败。
|
|
160
180
|
* `STREAM_APPROVAL_REPLAY_MAX`,默认 **50**。 */
|
|
@@ -512,10 +532,12 @@ export interface ServiceConfigFlat {
|
|
|
512
532
|
* `open` 的故障极性**相反**,一个被静默折成缺省的拼写错误会让合规部署在故障窗里放行留存必需用户的 opt-out。
|
|
513
533
|
* core 侧 `prepareConfigDoors` 另有同判门(`config.memory_capture_policy`),两道同向不冲突。
|
|
514
534
|
*
|
|
515
|
-
* 🔴 **`governed` 的半配置拒启**:该姿态要求本部署有 per-principal verdict
|
|
516
|
-
* `memory_optout_grant` 表(S-2
|
|
517
|
-
*
|
|
535
|
+
* 🔴 **`governed` 的半配置拒启**:该姿态要求本部署有 per-principal verdict 源。源**两条**(析取,[ref] 起):
|
|
536
|
+
* SQL 后端上的 `memory_optout_grant` 表(S-2),或非 dry-run 的 config center 发 `EntitlementRuntimeCaps.allowMemoryOptOut`
|
|
537
|
+
* (settings-schema 1.4.0 到货)。两源皆无 ⇒ 每个 opt-out 声明都被 core 拒且没人知道为什么 ⇒
|
|
518
538
|
* `boot/runtime-caps.ts` 的 `assertMemoryCapturePolicyWirable` 在启动期拒启(与 consolidation 阀门 D1 同姿态)。
|
|
539
|
+
* ⚠️ [ref]:center 那一臂只证「源在场」,不证 center 会**发**这个可选键 —— center-only 的部署起动后仍可能次次拒,
|
|
540
|
+
* 两处 warn 把它响亮化(`memory_capture_governed_center_only_source` / `memory_capture_governed_verdict_absent`)。
|
|
519
541
|
*
|
|
520
542
|
* ⚠️ **部署席 ONLY**:TaskSpec 无同名键;不冻进 checkpoint(resume 腿跟当前部署配置走,core 成文)。
|
|
521
543
|
* 运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。 */
|
|
@@ -1270,6 +1292,13 @@ export interface ServiceConfigFlat {
|
|
|
1270
1292
|
* other value (incl. `"off"`) is treated as unset — this knob has no "off" value to opt BACK OUT with once a
|
|
1271
1293
|
* session's baseline already tightened elsewhere (tightenTaskSpec would reject that as a loosen anyway). */
|
|
1272
1294
|
manualModeShellGate?: "always" | "classify";
|
|
1295
|
+
/** [ref] ③(黑板 [ref]/[ref],core [ref] 合修)—— CC `permissions.disableAutoMode` 的本地镜像
|
|
1296
|
+
* (`PERMISSIONS_DISABLE_AUTO_MODE`,布尔词表旋钮,opt-in 缺省 false):本部署对**每个** principal 把
|
|
1297
|
+
* `RuntimeCaps.autoMode` 折成 `false` = org deny 位(core [ref] 三态谓词 `runtimeCaps?.autoMode !== false`
|
|
1298
|
+
* 的拒臂)。**tighten-only**:center 授予 `true` 翻不回;未设 = 不动 caps(无 center 的部署 caps 仍缺席 ⇒
|
|
1299
|
+
* 「缺席=不阻」⇒ 壳发 `permissionMode:"auto"` 即武装)。词表外的值 boot 拒启(安全轴旋钮不许静默回默认)。
|
|
1300
|
+
* 合成点 = `boot/runtime-caps.ts` 最外层(applyLocalAutoModeDeny);读面 = `/v1/capabilities.permissionModeAuto`。 */
|
|
1301
|
+
permissionsDisableAutoMode: boolean;
|
|
1273
1302
|
/** [ref]④ defer face (EXPERIMENTAL, default OFF): mark the default scenario's LONG-TAIL first-party tools
|
|
1274
1303
|
* (today = SelectEnvironment only) `defer:true` (core tool-disclosure: the model sees a placeholder until it
|
|
1275
1304
|
* ToolSearch-loads the schema). Assessment says the win is marginal for us (spec.tools total ≈4.2k tokens, of
|
|
@@ -1400,8 +1429,10 @@ export interface ServiceConfigFlat {
|
|
|
1400
1429
|
* `DEFAULT_APPROVAL_TTL_MS` 对齐——「开协议」不再顺带把窗砍短)。⚠️ 版本坐标:翻转**已在树上生效**,
|
|
1401
1430
|
* 发布线上落在 7.4.0 之后的下一个发布,所以一台自报 7.4.0 的**已发布** worker 仍是 OFF,别只按
|
|
1402
1431
|
* `/health` 的版本号推默认。显式 `STREAM_APPROVAL_ENABLED=false` 是**唯一干净还原键** ⇒ 全链逐字回到
|
|
1403
|
-
* 翻转前行为:不发 `approval_request` 帧、不落 `approval_ask`
|
|
1404
|
-
*
|
|
1432
|
+
* 翻转前行为:不发 `approval_request` 帧、不落 `approval_ask` 行、不起收敛器腿、`askStore` 不注入
|
|
1433
|
+
* 协调器。⚠️ **窗不在这份还原清单里了([ref])**:`STREAM_ASK_WINDOW_MS` 自此在关协议的部署上照样
|
|
1434
|
+
* 生效(缺省 300000 == `DEFAULT_APPROVAL_TTL_MS` ⇒ 没配过它的部署仍然字节不变),理由见
|
|
1435
|
+
* `StreamApprovalConfig.windowMs` 的辖域段。
|
|
1405
1436
|
*
|
|
1406
1437
|
* **旋钮为什么存在(2026-08-08 翻转前的成文前提,已被上面的裁定取代,留作沿革)**:那一版默认 false +
|
|
1407
1438
|
* 60s 窗,三问记的是那个世界——
|
|
@@ -1597,7 +1628,7 @@ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "ses
|
|
|
1597
1628
|
export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
|
|
1598
1629
|
/** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
|
|
1599
1630
|
* 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
|
|
1600
|
-
export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "manualModeShellGate">;
|
|
1631
|
+
export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "manualModeShellGate" | "permissionsDisableAutoMode">;
|
|
1601
1632
|
/** 组:memory(记忆面 + TOC 同步腿)。 */
|
|
1602
1633
|
export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
|
|
1603
1634
|
/** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
|
package/dist/config.d.ts
CHANGED
|
@@ -169,6 +169,27 @@ export declare const hostExecSpoolEnabled: () => boolean;
|
|
|
169
169
|
* a loud CONFIG_NOTICES line ("removed-1m"); the center lane's roster culture is explicit declaration.
|
|
170
170
|
* Shared by the env lane (main + cheap models) and the center lane (sema-registry toModel). */
|
|
171
171
|
export declare function applyAutoCompactWindow(m: Model, explicit?: number): "explicit" | "sonnet5" | "removed-1m" | "removed";
|
|
172
|
+
/**
|
|
173
|
+
* [ref] 件⑤ —— 思考档两键的**闭集词表**与拒启判据(`MODEL_DEFAULT_THINKING` / `MODEL_REASONING_EFFORT_LEVELS`)。
|
|
174
|
+
*
|
|
175
|
+
* 改前两键都走 `isThinkingLevel(...)` **过滤**:拼错的词被**静默丢掉**,而两根旋钮的失败方向恰好都是
|
|
176
|
+
* 「把运维的意图反向执行」——
|
|
177
|
+
* · `MODEL_DEFAULT_THINKING=hight` ⇒ `defaultThinking` 不铸键 ⇒ 全部任务回落到「无模型级默认档」,
|
|
178
|
+
* 运维以为整台机器抬到了 high;
|
|
179
|
+
* · `MODEL_REASONING_EFFORT_LEVELS=med,high` ⇒ 只剩 `["high"]`;写成 `med` 一个词则整键落空、
|
|
180
|
+
* `compat` 不铸 ⇒ 回落 core 的保守梯(`minimal|low|medium|high`),`xhigh`/`max` 被 clamp 下来。
|
|
181
|
+
* 运维配这根旋钮的**全部用途**就是告诉引擎「本端点吃得下更高档」,静默落空 = 高档永远上不去。
|
|
182
|
+
* 两条都是 `MODEL_DEGRADE_ON` / `TASK_TIMEOUT_SEC` 那条族律的同形([ref] A 档:坏值拒启,不静默折默认),
|
|
183
|
+
* 所以改成 boot 响亮拒 —— 与 `MODEL_DEGRADE_ON` 的 `parseDegradeOn` 逐字同款(点名坏词 + 列合法词)。
|
|
184
|
+
*
|
|
185
|
+
* 词表用 `Record<ThinkingLevel, true>` 铸 = 与 llm-core 锁步:上游加一档而这里不补 ⇒ **编译红**
|
|
186
|
+
* (`MODEL_DEGRADE_ON` 的 `Set<DegradeReason>` 同款纪律);目录行的 `enumValues` 由本表派生,
|
|
187
|
+
* 「文档写五档、代码收六档」那种分叉在类型层就不可能发生。
|
|
188
|
+
*/
|
|
189
|
+
type ModelThinkingLevel = NonNullable<Model["defaultThinking"]>;
|
|
190
|
+
/** 六档闭集(有序:低→高,取自上表的书写序),目录行 `enumValues` 与两条拒因文案的单一真源。
|
|
191
|
+
* (`CATALOG_RESTART_SLICES = Object.keys(RESTART_SLICE_SET)` 同款派生式。) */
|
|
192
|
+
export declare const THINKING_LEVELS: readonly ModelThinkingLevel[];
|
|
172
193
|
/**
|
|
173
194
|
* 一条工具名**永不匹配任何现役工具**的三种拼法,及其修正指引。审批/权限名单是 server 侧 ToolPolicy 的
|
|
174
195
|
* RAW 逐字比对(canonicalToolName 折叠面已随 core 5.0.0 [ref] 退役),所以写错的名字不会报错、只会静默
|
package/dist/config.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, formatUserScope,
|
|
5
|
+
import { BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, formatUserScope, PROTOCOL_TABLE, protocolOf, RECOMMENDED_SENSITIVE_PATTERNS, RETIRED_TOOL_NAMES, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules } from "@sema-agent/core";
|
|
6
6
|
import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES } from "@sema-agent/settings-schema";
|
|
7
7
|
import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
|
|
8
8
|
import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
|
|
@@ -521,7 +521,8 @@ function noticeAutoCompactAboveWindow(slot, envName, m) {
|
|
|
521
521
|
model: m.id,
|
|
522
522
|
value: String(act),
|
|
523
523
|
contextWindow: String(window),
|
|
524
|
-
|
|
524
|
+
clampedTo: String(window),
|
|
525
|
+
note: `${envName}=${act} is ABOVE the ${slot} model "${m.id}" physical context window (${window}) — core uses autoCompactTokens as the TRIGGER-side window and CLAMPS it to the physical window (resolveTriggerWindow), so this knob is a NO-OP here: the trigger geometry falls back to ${window} and core mints one config.autocompact_window_clamped notice per prepare. server neither clamps nor rejects the value (clamping is core's, at the read site); lower it below the window to make the knob do something — CC's own shape is window-33000.`,
|
|
525
526
|
},
|
|
526
527
|
});
|
|
527
528
|
}
|
|
@@ -637,8 +638,8 @@ function parseModelDomain() {
|
|
|
637
638
|
return "gateway";
|
|
638
639
|
})();
|
|
639
640
|
const extraBody = buildExtraBody();
|
|
640
|
-
const thinkingDefault = (
|
|
641
|
-
const effortLevels = (
|
|
641
|
+
const thinkingDefault = parseDefaultThinking(process.env.MODEL_DEFAULT_THINKING);
|
|
642
|
+
const effortLevels = parseReasoningEffortLevels(csv("MODEL_REASONING_EFFORT_LEVELS"));
|
|
642
643
|
const model = {
|
|
643
644
|
id: modelId,
|
|
644
645
|
name: modelId,
|
|
@@ -777,6 +778,32 @@ function parseModelDomain() {
|
|
|
777
778
|
: undefined,
|
|
778
779
|
};
|
|
779
780
|
}
|
|
781
|
+
const THINKING_LEVEL_TABLE = { minimal: true, low: true, medium: true, high: true, xhigh: true, max: true };
|
|
782
|
+
export const THINKING_LEVELS = Object.keys(THINKING_LEVEL_TABLE);
|
|
783
|
+
const isThinkingTier = (w) => THINKING_LEVELS.includes(w);
|
|
784
|
+
function parseDefaultThinking(raw) {
|
|
785
|
+
const v = raw?.trim();
|
|
786
|
+
if (v === undefined || v === "")
|
|
787
|
+
return undefined;
|
|
788
|
+
if (v === "off")
|
|
789
|
+
return undefined;
|
|
790
|
+
if (!isThinkingTier(v)) {
|
|
791
|
+
throw new Error(`env MODEL_DEFAULT_THINKING="${v}" is not a thinking tier — valid: ${THINKING_LEVELS.join(" | ")} | off ` +
|
|
792
|
+
`("off" = no model-level default). A typo used to be dropped SILENTLY, leaving the deployment on no default at all (#210 A 档).`);
|
|
793
|
+
}
|
|
794
|
+
return v;
|
|
795
|
+
}
|
|
796
|
+
function parseReasoningEffortLevels(words) {
|
|
797
|
+
if (words.length === 0)
|
|
798
|
+
return [];
|
|
799
|
+
const bad = words.filter((w) => !isThinkingTier(w));
|
|
800
|
+
if (bad.length > 0) {
|
|
801
|
+
throw new Error(`env MODEL_REASONING_EFFORT_LEVELS has unknown tier(s): ${bad.join(", ")} (valid: ${THINKING_LEVELS.join(" | ")})` +
|
|
802
|
+
`${bad.includes("off") ? ' — "off" belongs to MODEL_DEFAULT_THINKING (no model-level default), it is not an effort tier an endpoint can accept' : ""}. ` +
|
|
803
|
+
`Bad words used to be dropped SILENTLY, narrowing the declared ladder without a word (#210 A 档).`);
|
|
804
|
+
}
|
|
805
|
+
return words.filter(isThinkingTier);
|
|
806
|
+
}
|
|
780
807
|
const DEGRADE_ON_WORDS = new Set(["breaker_open", "rate_limit", "budget", "server_error", "last_resort"]);
|
|
781
808
|
function parseDegradeOn(words) {
|
|
782
809
|
if (words.length === 0)
|
|
@@ -853,6 +880,7 @@ export function formatUnmatchableToolNames(source, bad) {
|
|
|
853
880
|
return (`${source} names tool(s) that can never match a live tool (these lists are RAW whole-name comparisons — core 5.0.0 RB-476 removed the alias/auto-prefix folds): ` +
|
|
854
881
|
bad.map((b) => `${presentToolName(b.name)} → ${b.guidance}`).join("; ")).replace(CONTROL_AND_BIDI_STRIP_RE, "");
|
|
855
882
|
}
|
|
883
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
856
884
|
function streamApprovalConfig(gates) {
|
|
857
885
|
const cfg = {
|
|
858
886
|
enabled: boolEnv("STREAM_APPROVAL_ENABLED", true),
|
|
@@ -866,6 +894,12 @@ function streamApprovalConfig(gates) {
|
|
|
866
894
|
orphanTtlMs: numEnvBounded("STREAM_APPROVAL_ORPHAN_TTL_MS", String(7 * 24 * 60 * 60 * 1000), 60_000, 90 * 24 * 60 * 60 * 1000),
|
|
867
895
|
unreachedTtlMs: numEnvBounded("UNREACHED_ASK_TTL_MS", String(60 * 60 * 1000), 60_000, 24 * 60 * 60 * 1000),
|
|
868
896
|
};
|
|
897
|
+
if (cfg.windowMs < 0) {
|
|
898
|
+
throw new Error(`env STREAM_ASK_WINDOW_MS=${cfg.windowMs} must not be negative — 0 means "close the window" (every gated ask goes straight to the unattended outcome and NO approval card is emitted), any positive value is the live-card window in ms. A negative value used to emit a card nobody could ever win.`);
|
|
899
|
+
}
|
|
900
|
+
if (cfg.windowMs > MAX_TIMER_DELAY_MS) {
|
|
901
|
+
throw new Error(`env STREAM_ASK_WINDOW_MS=${cfg.windowMs} exceeds the Node timer ceiling ${MAX_TIMER_DELAY_MS} (2^31-1 ms ≈ 24.8 days) — setTimeout SILENTLY collapses a larger delay to 1ms, so the window you meant to lengthen would expire immediately and every gated ask would take the unattended route on the next tick. Pick a window at or below the ceiling (and remember STREAM_ASK_WINDOW_MS + STREAM_APPROVAL_ADHOC_GRACE_MS must stay strictly below STREAM_APPROVAL_ORPHAN_TTL_MS).`);
|
|
902
|
+
}
|
|
869
903
|
if (cfg.windowMs + cfg.adhocGraceMs >= cfg.orphanTtlMs) {
|
|
870
904
|
throw new Error(`env STREAM_ASK_WINDOW_MS=${cfg.windowMs} + STREAM_APPROVAL_ADHOC_GRACE_MS=${cfg.adhocGraceMs} must be STRICTLY LESS than STREAM_APPROVAL_ORPHAN_TTL_MS=${cfg.orphanTtlMs} — the adhoc arm fires at (createdAt + window + grace) while the orphan backstop fires at (createdAt + ttl); if the backstop wins, every adhoc-leg row is attributed "orphan_ttl_exceeded" instead of "adhoc_leg_no_durable_domain" (attribution honesty, design/172 §6.1)`);
|
|
871
905
|
}
|
|
@@ -945,6 +979,7 @@ function parseApprovalDomain(ctx) {
|
|
|
945
979
|
`The words are matched EXACTLY (lowercase, surrounding whitespace trimmed): "OFF"/"Classify" land here too, deliberately — a closed word table with no case-folding is what keeps "it looked accepted" from meaning "it did nothing". ` +
|
|
946
980
|
`Refusing to boot rather than treating a misspelled value as "unset": that silently ran the deployment with NO shell gate while the operator believed one was configured`);
|
|
947
981
|
})(process.env.MANUAL_MODE_SHELL_GATE),
|
|
982
|
+
permissionsDisableAutoMode: boolEnv("PERMISSIONS_DISABLE_AUTO_MODE", false),
|
|
948
983
|
mcpElicitation: {
|
|
949
984
|
enabled: boolEnv("MCP_ELICITATION_ENABLED", false),
|
|
950
985
|
throttle: {
|
|
@@ -1757,7 +1792,7 @@ const APPROVAL_GROUP_KEYS = [
|
|
|
1757
1792
|
"approvalRequire", "approvalDeny", "approvalTimeoutSec", "approvalAutoBudget", "approvalNeverAuto",
|
|
1758
1793
|
"approvalHmacKeys", "durableApproval", "directApprovalDoor", "directDoorActive", "resourceSuspend",
|
|
1759
1794
|
"resourceSuspendTtlSec", "resourceSuspendTotalTokens", "resourceSuspendTotalBudgetUsd", "resourceSuspendMaxSlices", "askQuestionEnabled", "questionThrottle", "toolApprovalEnabled", "permissionRulesEnabled", "permissionRulesEnabledExplicit", "streamAskWindowMarginMs", "streamApproval", "unattendedApprovalPolicy", "mcpElicitation",
|
|
1760
|
-
"sensitiveWritePatterns", "manualModeShellGate",
|
|
1795
|
+
"sensitiveWritePatterns", "manualModeShellGate", "permissionsDisableAutoMode",
|
|
1761
1796
|
];
|
|
1762
1797
|
const MEMORY_GROUP_KEYS = [
|
|
1763
1798
|
"memoryEngineEnabled", "memoryEngineDir", "memoryEngineRemoteLaneAllowed", "memoryEngineBackend", "memoryScope",
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -47,6 +47,24 @@ export interface RouteHelpers {
|
|
|
47
47
|
rateLimited(req: IncomingMessage, res: ServerResponse): boolean;
|
|
48
48
|
quotaExceeded(req: IncomingMessage, res: ServerResponse): boolean;
|
|
49
49
|
leaseDenied(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* 🔴 **[ref]:按**付费**方(不是请求方)判的成本配额准入 —— {@link quotaExceeded} 的孪生。**
|
|
52
|
+
*
|
|
53
|
+
* `quotaExceeded` 键在 `gatedPrincipal(req)` = 谁点的按钮;本门键在**这笔钱记谁的账**。两者在自助
|
|
54
|
+
* 场景恒相等,在「显式 operator 代别人动手」的场景不等 —— 那正是配额上限被绕过的那一支。
|
|
55
|
+
* 参数 `owner` 由调用点从**已属主校验过的那一行**取(run 行的 `owner` / checkpoint 的 scope),
|
|
56
|
+
* 本门不自己推身份(推得出来就说明调用点还有第二个身份源,那才是病)。
|
|
57
|
+
* `undefined`(匿名/开放行)⇒ 无付费方 ⇒ 不 gated,与 `quotaExceeded` 的成文语义同判。
|
|
58
|
+
* 拒 = 429 + `Retry-After`,码/文案/extras 与 `quotaExceeded` 逐字同一套。
|
|
59
|
+
* 位序纪律:**必须排在本路由的 owner 门之后**(反枚举 —— 否则 429 会变成「这行存在且它属主超顶了」
|
|
60
|
+
* 的谕示);且排在真正驱动模型之前。
|
|
61
|
+
*/
|
|
62
|
+
ownerQuotaDenied(res: ServerResponse, owner: string | undefined): boolean;
|
|
63
|
+
/** 🔴 **[ref]:{@link ownerQuotaDenied} 的 lease 孪生 —— 按**付费**方判的 fleet-lease 准入。**
|
|
64
|
+
* `leaseDenied` 键在请求方;本门键在付费方(`driveResumeLeg` 的 `fleetLease.admit(卡属主)` 早就是这个
|
|
65
|
+
* 语义,本位把它交给路由域)。两门在自助场景恒同答,在「operator 代别人动手」时不同 —— 那正是
|
|
66
|
+
* 「拿自己的额度去烧别人的账」的那一支。同样必须排在 owner 门之后、驱动之前。 */
|
|
67
|
+
ownerLeaseDenied(res: ServerResponse, owner: string | undefined): Promise<boolean>;
|
|
50
68
|
/** [ref]-T1:治理窗 pre-admission(异步 202 车道的唯一 429 出路;key 公式与 core 单源)。 */
|
|
51
69
|
usageWindowDenied(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
|
|
52
70
|
safeDecode(seg: string): string | null;
|
|
@@ -79,6 +97,17 @@ export interface RouteRequestState {
|
|
|
79
97
|
streamDetached: boolean;
|
|
80
98
|
/** 凭证派生的调用方系统身份(`source`)。handle() 的鉴权门是**唯一**赋值点,域模块只读。 */
|
|
81
99
|
source: string | null;
|
|
100
|
+
/**
|
|
101
|
+
* [ref] 件2(cli [ref] sema-bug4 (c)):本请求驱动的那条 run 的**终局**(`completed` / `failed` /
|
|
102
|
+
* `suspended` / …)与结构化失败码。`handle()` 的 `request` 日志行读它 —— 让「端点 200 ≠ 模型成功」
|
|
103
|
+
* 这条歧义消失(取证:模型 connect timeout 的那一刻,日志里唯一的行是 `request status:200`)。
|
|
104
|
+
*
|
|
105
|
+
* 赋值点在 `routes/tasks.ts`(`/v1/tasks` 与 `/v1/tasks/stream` 两腿的终局到手处);拿不到 run 结果
|
|
106
|
+
* (受理即拒 / 幂等重放 / 非任务端点)⇒ **键缺席**,绝不写 null —— 缺席 = 「这条请求没驱动 run」,
|
|
107
|
+
* 与「run 结果不明」是两句话,而 `null` 会把两者压成一个形。
|
|
108
|
+
*/
|
|
109
|
+
runStatus?: string;
|
|
110
|
+
runErrorCode?: string;
|
|
82
111
|
}
|
|
83
112
|
/** B4①(命名化):`prepareSpec` 的返回形(tasks.ts/runs.ts 两域共同消费的属性面)——原为 6 成员内联匿名对象。
|
|
84
113
|
* 命名后好处不只是过 B4 门:两个消费域现在都能对同一个符号做 `import type` 标注,而不是各自重推断结构。 */
|
|
@@ -141,6 +170,52 @@ export interface DriveResumeArgs {
|
|
|
141
170
|
* **唯一不设此位的**是内部 D-D SLA deny-sweep:它没有在等响应的人,提前受理对它零收益,而且那条
|
|
142
171
|
* 循环靠 `await` 串行给每 tick 的重活限流(见其调用处的登记注)。 */
|
|
143
172
|
acceptEarly?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* 🔴 **[ref](codex 交叉复审 r2-[medium] 验真后修):这条腿是**调用方发起**的吗?**
|
|
175
|
+
*
|
|
176
|
+
* `true` = 有人在 HTTP 上点了按钮(resume / plan_review / wake / decide / 赎回席)⇒ 吃按**付费方**
|
|
177
|
+
* (卡属主)判的成本配额准入(`ownerCostQuotaDenied`)。缺席/`false` = **部署自己的结算腿**
|
|
178
|
+
* (内部 D-D SLA deny-sweep)⇒ **不吃**那道门。
|
|
179
|
+
*
|
|
180
|
+
* 为什么系统腿必须豁免(亲核后改的判据,首版曾一并拦下 sweep):sweep 的活是把过了 SLA 的卡**结算掉**
|
|
181
|
+
* (graceful deny → 行收敛 → 释放 `task_active` claim)。拦住它并不能省钱,只会让超顶租户的卡**挂着**
|
|
182
|
+
* —— 而挂多久的兜底**比首版注释里写的差得多**:`checkpoint-store-sql.ts` 的 `reapExpired` 在
|
|
183
|
+
* **deadline 分支**上逐字排除了 `APPROVAL_GATE_KINDS`(`gate_kind NOT IN (…) OR tool_name='AskUserQuestion'`),
|
|
184
|
+
* 所以一张过期的 human / irreversible_ask 卡根本不走那条腿;真正兜住它的只有绝对 backstop
|
|
185
|
+
* `terminal_at_ms`(`TERMINAL_BACKSTOP_MS` 默认 **30 天**)。于是「拦 sweep」= 把一个由
|
|
186
|
+
* `APPROVAL_TIMEOUT_SEC` 界定的 SLA 换成「等成本窗滚过(`COST_QUOTA_WINDOW_SEC` 默认 24h)、最坏等 30 天」,
|
|
187
|
+
* 并且整段时间攥着会话 claim。**方向也自洽**:deny 是**停下**这条 run 的动作,为了省钱而拒绝执行「停」
|
|
188
|
+
* 与 preempt 路由早就成文的那句同一条(「否则超顶租户连自己的花费都停不下来」)。
|
|
189
|
+
*
|
|
190
|
+
* 判别子取 `req` 的在场与否 —— 这在 `resumeCheckpoint` 里**已经是**「系统腿 vs 调用方腿」的既有判据
|
|
191
|
+
* (绑定强制、parked 分流两处都写着 `req !== undefined` 与「内部 D-D SLA deny-sweep(req 缺席)豁免」),
|
|
192
|
+
* 本位只是把同一条判据显式化成一个有名字的位,而不是让下游去猜。
|
|
193
|
+
* ⚠️ **射程覆盖两道门**:紧邻的 fleet-lease 准入(`ownerFleetLeaseDenied`)读**同一位** —— [ref] 件⑤②
|
|
194
|
+
* (codex r3-[high])把它一并改吃了 `callerInitiated`,理由与三问逐字同 cost 半场。
|
|
195
|
+
* 🔴 本句是 [ref] R4 改写的(2026-09-03,7.57.0 发车前合并重扫 wf_1abaaa9a):此处原文写着「紧邻的
|
|
196
|
+
* fleet-lease 准入**没有**这条豁免(对 sweep 一视同仁),本批不动它,分歧已登记为遗留」—— 那是**同一批
|
|
197
|
+
* 件⑤② 落地之前**的话,件⑤② 修好后这条 JSDoc 没跟着改,于是注释与代码正相反(亲读
|
|
198
|
+
* `server.ts` 的 `ownerFleetLeaseDenied` 消费点:两处都是 `args.callerInitiated === true` /
|
|
199
|
+
* `req !== undefined`)。两道门在这一位上**无分歧**,[ref] 上也没有这一项。
|
|
200
|
+
*/
|
|
201
|
+
callerInitiated?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* 🔴 **[ref] codex 对抗复审 r1-F3 [medium](验真后修):本次请求的属主 lease 准入**已经做过了**。**
|
|
204
|
+
*
|
|
205
|
+
* `resumeCheckpoint` 在 parked 子代赎回分流**之前**先判一次属主 lease([ref] R3:那条腿提前 `return`,
|
|
206
|
+
* 结构上够不到 `driveResumeLeg` 的同一道门)。分流**未命中**时请求落回 legacy 腿,于是同一条请求、
|
|
207
|
+
* 同一个付费方会向 center 协商**两次**。cost 半场可以「过两次门」(本地同步、纯读);lease 半场**不行**:
|
|
208
|
+
* `FleetLeaseManager.admit` 的五条 fail-open 路径(center 不可达 / 5xx / 超时 / 401-403 / 畸形响应)
|
|
209
|
+
* 一律**不写缓存态**(亲读 `fleet-lease.ts` 的 `issueOrRenewInner`),而 single-flight 只合并**并发**调用 ——
|
|
210
|
+
* 两道门是串行的 ⇒ 半开 center 下一个请求连吃两次 `LEASE_FETCH_TIMEOUT_MS`,持续故障时整条 legacy
|
|
211
|
+
* resume 车道把控制面负载翻倍。
|
|
212
|
+
*
|
|
213
|
+
* 所以由上游把「已受理」这个事实传下来,`driveResumeLeg` 据此跳过重复协商。**安全性**:两处的键是
|
|
214
|
+
* **同一个表达式**(`decodeCheckpointScope(cp.scope)`,上游门与下游 `auth.principal` 同源),射程条件也
|
|
215
|
+
* 同一条(`req !== undefined` ⇔ `callerInitiated`)⇒ 跳过的是一次**逐字相同**的判定,不是一道门。
|
|
216
|
+
* 缺席/`false` ⇒ 照常自己判(其余三条驱动腿与 sweep 一字不变)。
|
|
217
|
+
*/
|
|
218
|
+
ownerLeaseAdmitted?: boolean;
|
|
144
219
|
}
|
|
145
220
|
/** 提交/续跑「腿」= 跨域共享的**有状态**长流程(不像 {@link RouteHelpers} 那样只依赖 deps/config:它们要写
|
|
146
221
|
* run log、发 fleet 帧、走 markResuming CAS、驱动模型)。A9 上批的停点就在这里——tasks/runs 两域共用
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { memoRedactedRead, redactErrorMessage } from "../../observability/run-terminal-log.js";
|
|
2
3
|
import { A2A_TASK_STATES } from "@sema-agent/core";
|
|
3
|
-
import { isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
4
|
+
import { isParkedRunStatus, isTerminalRunStatus } from "../../plugins/store-contracts.js";
|
|
4
5
|
import { HttpError } from "../../security.js";
|
|
5
6
|
import { scopedIdempotencyKey } from "../idempotency.js";
|
|
6
7
|
import { sendJson, sendError } from "../send.js";
|
|
@@ -80,10 +81,11 @@ function statusTextForRun(run, state) {
|
|
|
80
81
|
return "this task is suspended awaiting an approval decision on the serving deployment; it resumes once that decision is made";
|
|
81
82
|
if (run.result?.result)
|
|
82
83
|
return run.result.result;
|
|
84
|
+
const memo = (seat, raw) => (isTerminalRunStatus(run.status) ? memoRedactedRead(`a2a.${seat}`, raw, (v) => redactErrorMessage(v) ?? "") : (redactErrorMessage(raw) ?? ""));
|
|
83
85
|
if (run.error)
|
|
84
|
-
return run.error;
|
|
86
|
+
return memo("error", run.error);
|
|
85
87
|
if (run.result?.errorMessage)
|
|
86
|
-
return run.result.errorMessage;
|
|
88
|
+
return memo("errorMessage", run.result.errorMessage);
|
|
87
89
|
return undefined;
|
|
88
90
|
}
|
|
89
91
|
function taskFromRun(run, stale) {
|