@sema-agent/server 7.16.0-rc.1 → 7.16.0-rc.3
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/dist/config.js +33 -35
- package/dist/http/server.js +14 -11
- package/package.json +1 -1
package/dist/config.js
CHANGED
|
@@ -395,28 +395,33 @@ export function configKnobTable() {
|
|
|
395
395
|
function registerKnob(rec) {
|
|
396
396
|
CONFIG_KNOBS.set(rec.name, rec);
|
|
397
397
|
}
|
|
398
|
+
/** #241-rc2([3740]③→[3742] 裁定,双臂不留静默):布尔 env 的**闭集词表**解析。
|
|
399
|
+
* 放宽臂=收 true/false/1/0/yes/no/on/off(大小写不敏感)——`=1` 是运维直觉写法,意图明确按意图执行
|
|
400
|
+
* (operator-knob 宪法方向);拒启臂=未知词**抛错**(boot 拒启带词表指路,照 #186 响亮拒先例)——
|
|
401
|
+
* 行为承重键(DURABLE_APPROVAL 决定 checkpointStore 存在性 ⇒ /v1/approvals 路由存在性)静默回默认的
|
|
402
|
+
* 真实后果是「审批面整条静默缺席」([3740]② cli/test 真机链)。返回 undefined = raw 未设/空串(沿用
|
|
403
|
+
* `env()` 的空串=未设约定),由调用方走默认/legacy 分派。 */
|
|
404
|
+
const BOOL_TRUE_WORDS = new Set(["true", "1", "yes", "on"]);
|
|
405
|
+
const BOOL_FALSE_WORDS = new Set(["false", "0", "no", "off"]);
|
|
406
|
+
function parseBoolWord(name, raw) {
|
|
407
|
+
if (raw === undefined || raw === "")
|
|
408
|
+
return undefined;
|
|
409
|
+
const w = raw.toLowerCase();
|
|
410
|
+
if (BOOL_TRUE_WORDS.has(w))
|
|
411
|
+
return true;
|
|
412
|
+
if (BOOL_FALSE_WORDS.has(w))
|
|
413
|
+
return false;
|
|
414
|
+
throw new Error(`${name}=${JSON.stringify(raw)} is not a boolean — accepted (case-insensitive): true/false/1/0/yes/no/on/off. ` +
|
|
415
|
+
`Refusing to boot: a silently defaulted behavior knob is worse than a loud one ([3742]; e.g. DURABLE_APPROVAL ` +
|
|
416
|
+
`decides whether the checkpoint store — and with it /v1/approvals — exists at all).`);
|
|
417
|
+
}
|
|
398
418
|
function boolEnv(name, def) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
value = true;
|
|
406
|
-
source = "env";
|
|
407
|
-
}
|
|
408
|
-
else if (raw === "false") {
|
|
409
|
-
value = false;
|
|
410
|
-
source = "env";
|
|
411
|
-
}
|
|
412
|
-
else if (!CONFIG_KNOBS.has(name)) {
|
|
413
|
-
// Previously a silent default at every hand-written site. Guarded on first registration: the LAZILY
|
|
414
|
-
// re-read knobs (boolEnvWithLegacyNegated's host twins, read per exec) must not grow this array for the
|
|
415
|
-
// process lifetime on one typo — one typo, one warning. (复审 2026-07-30 F6:早期理由「IMAGE_BAKES_
|
|
416
|
-
// ENABLED/DURABLE_APPROVAL 一次 load 读两次」已随九域拆分的去重失效,懒读族成为本守卫的现行理由。)
|
|
417
|
-
CONFIG_WARNINGS.push({ env: name, raw });
|
|
418
|
-
}
|
|
419
|
-
}
|
|
419
|
+
// #241-rc2:词表解析(见 parseBoolWord 顶注)。未知词在 parseBoolWord 里抛 = boot 拒启——旧形的
|
|
420
|
+
// 「CONFIG_WARNINGS 一条 warn + 回默认」臂就此退役(懒读族的防重复 push 守卫随臂一起退役:未知词
|
|
421
|
+
// 现在到不了任何 push)。
|
|
422
|
+
const parsed = parseBoolWord(name, process.env[name]);
|
|
423
|
+
const value = parsed ?? def;
|
|
424
|
+
const source = parsed !== undefined ? "env" : "default";
|
|
420
425
|
registerKnob({ name, polarity: def ? "opt-out" : "opt-in", value, source });
|
|
421
426
|
return value;
|
|
422
427
|
}
|
|
@@ -443,20 +448,11 @@ export function boolEnvWithLegacyNegated(name, legacyNegatedName, def) {
|
|
|
443
448
|
const legacy = process.env[legacyNegatedName];
|
|
444
449
|
let value;
|
|
445
450
|
let source;
|
|
446
|
-
|
|
447
|
-
|
|
451
|
+
const parsedCanonical = parseBoolWord(name, raw); // #241-rc2:与 boolEnv 同一词表同一拒启臂(单一属主)
|
|
452
|
+
if (parsedCanonical !== undefined) {
|
|
453
|
+
value = parsedCanonical;
|
|
448
454
|
source = "env";
|
|
449
455
|
}
|
|
450
|
-
else if (raw !== undefined && raw !== "") {
|
|
451
|
-
if (!CONFIG_KNOBS.has(name)) {
|
|
452
|
-
// Same first-registration guard as boolEnv — the three consumers of this helper are the LAZILY re-read
|
|
453
|
-
// ones (remote-env-host reads per exec/probe), so an unguarded push would grow CONFIG_WARNINGS for the
|
|
454
|
-
// whole process lifetime on a single operator typo (复审 2026-07-29 #1).
|
|
455
|
-
CONFIG_WARNINGS.push({ env: name, raw });
|
|
456
|
-
}
|
|
457
|
-
value = def;
|
|
458
|
-
source = "default";
|
|
459
|
-
}
|
|
460
456
|
else if (legacy !== undefined && legacy !== "") {
|
|
461
457
|
// server 3.0.0([1989],clay 令「不做兼容」):负名旧 env = fail-loud 墓碑。设了(任何值,含
|
|
462
458
|
// "false"——设它的人同样以为它还生效)即拒启并指路新名。选拒启不选静默忽略:静默会让升级部署的
|
|
@@ -2105,8 +2101,10 @@ export function loadConfig() {
|
|
|
2105
2101
|
// self-report would omit the worst offenders. Its tri-state semantics are unchanged: an explicit literal wins,
|
|
2106
2102
|
// anything else (including a typo — deliberately NOT warned here, unlike boolEnv) falls to the posture default.
|
|
2107
2103
|
const postureOn = (name, infraReady = true) => {
|
|
2108
|
-
|
|
2109
|
-
|
|
2104
|
+
// #241-rc2:显式臂与 boolEnv 同词表同拒启(parseBoolWord 单一属主)——三态语义不变
|
|
2105
|
+
// (显式 true/显式 false/未设 ⇒ posture 默认),变的只是「显式」的判读:`=1` 是显式 true,
|
|
2106
|
+
// 未知词拒启而不是静默滑进 posture 默认。
|
|
2107
|
+
const explicit = parseBoolWord(name, process.env[name]);
|
|
2110
2108
|
const value = explicit ?? (singleUserTurnkey && infraReady);
|
|
2111
2109
|
registerKnob({ name, polarity: "posture", value, source: explicit === undefined ? "posture" : "env" });
|
|
2112
2110
|
return value;
|
package/dist/http/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { once } from "node:events";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { uuidv7, isThinkingLevel, expandTiers, resumeWithVerification, CheckpointError,
|
|
4
|
+
import { uuidv7, isThinkingLevel, expandTiers, resumeWithVerification, CheckpointError, defaultTaskRegistry, validatePendingSteer, subscribeWorkflow, GLOBAL_USAGE_KEY, usageRetryAfterMs } from "@sema-agent/core";
|
|
5
5
|
import { decideParkedAgent, findParkedAgentForCheckpoint } from "../parked-decide.js";
|
|
6
6
|
import { isModelAllowlisted, matchCatalogModel } from "../model-select.js";
|
|
7
7
|
import {} from "../config-center/facade.js";
|
|
@@ -16,6 +16,7 @@ import { runInBackground, evictIfConflict, stripCheckpointToken, TurnAnchorCaptu
|
|
|
16
16
|
import { looksLikeJwt } from "@sema-agent/registry-core/api/auth-bridge";
|
|
17
17
|
import {} from "../orchestration/workflow-agent-steer.js";
|
|
18
18
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKey, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
19
|
+
import { HANDS_BAND_TOOL_NAMES } from "../capabilities/hands-lane.js";
|
|
19
20
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../fleet/fleet-bus.js";
|
|
20
21
|
import { defaultSubagentTailBus, projectTailFrame } from "../fleet/subagent-tail-bus.js";
|
|
21
22
|
import { createApprovalCardEmitter, isApprovalGateKind, resolveApprovalLeg, resolveStreamApprovalGate } from "../tool-approval.js";
|
|
@@ -110,13 +111,14 @@ const MAX_IMAGES_PER_REQUEST = 20;
|
|
|
110
111
|
const MAX_IMAGE_BASE64_BYTES = 6 * 1024 * 1024; // a single inline image's base64 ≤ 6 MiB (< MAX_BODY, so REACHABLE — one image can't hog the whole 8 MiB budget); ~4.5 MiB decoded, generous for a screenshot/photo
|
|
111
112
|
// Task Trace API (S1): GET /v1/tasks (list) | /v1/tasks/:id/turns | /v1/tasks/:id/stream. The `:id` group +
|
|
112
113
|
// sub-route are both-or-neither, so a GET /v1/tasks/stream (no :id) does NOT match — the POST sync stream is unshadowed.
|
|
113
|
-
// Hand (executionEnv) tool names core mounts at the runner when an executionEnvFactory is configured
|
|
114
|
-
//
|
|
115
|
-
// the
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
114
|
+
// Hand (executionEnv) tool names core mounts at the runner when an executionEnvFactory is configured. These are
|
|
115
|
+
// NEVER in spec.tools, so the durable-resume satisfiability pre-check must union them when remote exec is on.
|
|
116
|
+
// #244([3751]): the authoritative set is the FULL band `HANDS_BAND_TOOL_NAMES` (capabilities/hands-lane.ts —
|
|
117
|
+
// effects table + the mounted complement Monitor/EnterWorktree/ExitWorktree/SendMessage/AgentTranscript, retired
|
|
118
|
+
// names filtered at the single owner). The old effects-only derivation left the complement out, so approving a
|
|
119
|
+
// parked complement-tool ask (the disconnect force-park population, e.g. Monitor on the cli host lane) was a
|
|
120
|
+
// permanent 422 conflict.pending_action_unsatisfiable — the re-presented card's answer could never propagate.
|
|
121
|
+
const HAND_TOOL_NAMES = HANDS_BAND_TOOL_NAMES;
|
|
120
122
|
/** #233 / A-002.8 —— 未知模型 400 里 `available` 列表的**名单过滤**:不给用户广告他点不了的模型。
|
|
121
123
|
* 名单缺席/空 ⇒ 与本件之前逐字同集(全目录去 `default` 别名);名单在场 ⇒ 只留名单准的那些。
|
|
122
124
|
* 判定单源 `isModelAllowlisted`(与三个执法点同一只),所以"列出来的"与"点了不被拒的"恒为同一批。 */
|
|
@@ -1527,12 +1529,13 @@ export function createHttpServer(rawDeps) {
|
|
|
1527
1529
|
// Typed pre-check: the pending tool must still exist in the rebuilt config; a scenario
|
|
1528
1530
|
// redeploy that removed it means the approval can no longer be applied — fail clearly, never a 500/silent.
|
|
1529
1531
|
// The "hand" tools are NOT in spec.tools — core mounts them at the runner from the executionEnvFactory
|
|
1530
|
-
// (`HAND_TOOL_NAMES` above
|
|
1532
|
+
// (`HAND_TOOL_NAMES` above = hands-lane.ts `HANDS_BAND_TOOL_NAMES`, the single owner; nothing is mirrored here).
|
|
1531
1533
|
// When this deployment has remote exec configured the rebuilt task WILL expose them, so union them in; else
|
|
1532
1534
|
// gating a hand tool (the common code-agent case) would always 422 on resume.
|
|
1533
1535
|
// #196 旁注:本 union 只看部署有没有 remoteExec,不看本任务的 hands lane —— 对 hands=none 的任务这是
|
|
1534
|
-
// **过近似**(那些工具根本没 mount,也就不可能有它们的 park 行),只影响 422 的 UX
|
|
1535
|
-
// band
|
|
1536
|
+
// **过近似**(那些工具根本没 mount,也就不可能有它们的 park 行),只影响 422 的 UX 宽严,不放大任何权限。
|
|
1537
|
+
// #244:union 必须是 band **全**名单——effects 表外的挂载补集(Monitor/EnterWorktree/…)当年漏了,
|
|
1538
|
+
// 断连 force-park 人群第一个踩中(approve 恒 422,重呈卡回答永不传导;test legB1-durable 实测指纹)。
|
|
1536
1539
|
// [2380] RB-476(5.0.0):Q6 折叠预检退役——满足性匹配按 RAW 名。pre-floor 旧名 checkpoint
|
|
1537
1540
|
// (`bash`/`run_workflow` 等)在此 422 诚实拒,与 core 自己的 resume.tool_unavailable 同方向
|
|
1538
1541
|
// ([2364] 令:耐久面响亮牺牲 + P-7 重开,不静默兜)。现役名恒自映射。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.16.0-rc.
|
|
3
|
+
"version": "7.16.0-rc.3",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|