@sema-agent/server 5.6.0 → 5.7.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/dist/config.js CHANGED
@@ -448,7 +448,7 @@ function parseStoreDomain(ctx) {
448
448
  const needsDb = needsTidb;
449
449
  return {
450
450
  sessionBackend,
451
- sessionCacheTtlSec: Number(env("SESSION_CACHE_TTL_SEC", "300")),
451
+ sessionCacheTtlSec: numEnv("SESSION_CACHE_TTL_SEC", "300"), // A2 同族:NaN ⇒ `> 0` 假 ⇒ 缓存静默关
452
452
  rewindSnapshotMaxMb: optFinitePositiveEnv("REWIND_SNAPSHOT_MAX_MB"), // soft knob (S20: bad value warns + default)
453
453
  dbBackend,
454
454
  dbBackendExplicit: dbBackendSet,
@@ -748,7 +748,7 @@ function parseModelDomain() {
748
748
  baseUrl: firstSetEnv("ANTHROPIC_BASE_URL"),
749
749
  version: process.env.ANTHROPIC_VERSION,
750
750
  cacheBreakpoints: (process.env.ANTHROPIC_CACHE_BREAKPOINTS ?? "true") === "true",
751
- ...(process.env.ANTHROPIC_MAX_RETRIES ? { maxRetries: Number(env("ANTHROPIC_MAX_RETRIES", "2")) } : {}),
751
+ ...(process.env.ANTHROPIC_MAX_RETRIES ? { maxRetries: numEnv("ANTHROPIC_MAX_RETRIES", "2") } : {}), // A1 同族:NaN 会把 SDK 重试面折没
752
752
  }
753
753
  : undefined,
754
754
  resilience: {
@@ -756,16 +756,19 @@ function parseModelDomain() {
756
756
  // timer only arms AFTER the first token (bumpIdle lives in sawContentToken), so with these
757
757
  // two off a stream that opens but never emits hangs unbounded — the one true blind spot the
758
758
  // fleet audit found. Values follow the [880] recommendation; =0 restores off.
759
- connectTimeoutMs: Number(env("MODEL_CONNECT_TIMEOUT_MS", "30000")),
760
- firstTokenTimeoutMs: Number(env("MODEL_FIRST_TOKEN_TIMEOUT_MS", "120000")),
759
+ // 鲁棒性批1 A1(2026-08-03):这五个是 TASK_TIMEOUT_SEC 同族的**危险方向**键——NaN 在 core 的
760
+ // `if (x && x > 0)` 门下判假 ⇒ 看门狗/熔断静默关(设了收紧值反而更松)。当年 gap-sweep 的棘轮
761
+ // 正则只抓 process.env 直读拼法,这批「裸 Number 包 env() helper」从盲区漏网;门已补第二正则。
762
+ connectTimeoutMs: numEnv("MODEL_CONNECT_TIMEOUT_MS", "30000"),
763
+ firstTokenTimeoutMs: numEnv("MODEL_FIRST_TOKEN_TIMEOUT_MS", "120000"),
761
764
  // BEHAVIOR CHANGE (CC 196 parity): mid-stream stall watchdog default ON at 5min —
762
765
  // after output starts, 5min of zero deltas cancels the stream as a retryable [network] failure
763
766
  // (retry/failover recovers; without it a hung gateway pins the turn until the user Escapes).
764
767
  // MODEL_IDLE_TIMEOUT_MS=0 restores off.
765
- idleTimeoutMs: Number(env("MODEL_IDLE_TIMEOUT_MS", "300000")),
768
+ idleTimeoutMs: numEnv("MODEL_IDLE_TIMEOUT_MS", "300000"),
766
769
  circuitBreaker: boolEnv("MODEL_CIRCUIT_BREAKER", false),
767
- failureThreshold: Number(env("MODEL_CB_FAILURE_THRESHOLD", "5")),
768
- cooldownMs: Number(env("MODEL_CB_COOLDOWN_MS", "30000")),
770
+ failureThreshold: numEnv("MODEL_CB_FAILURE_THRESHOLD", "5"),
771
+ cooldownMs: numEnv("MODEL_CB_COOLDOWN_MS", "30000"),
769
772
  },
770
773
  model,
771
774
  models,
@@ -1248,7 +1251,7 @@ function parseLimitsHttpDomain() {
1248
1251
  costQuotaWindowSec: numEnv("COST_QUOTA_WINDOW_SEC", "86400"),
1249
1252
  reapIntervalSec: numEnv("REAP_INTERVAL_SEC", "60"),
1250
1253
  runStaleSec: numEnv("REAP_RUN_STALE_SEC", "120"),
1251
- toolResultTtlSec: Number(env("TOOL_RESULT_TTL_SEC", "86400")),
1254
+ toolResultTtlSec: numEnv("TOOL_RESULT_TTL_SEC", "86400"), // A2 同族:NaN ⇒ reapOlderThan(NaN) 永不回收且被 .catch 吞
1252
1255
  };
1253
1256
  }
1254
1257
  /** 域:observability(可观测)—— 指标/追踪令牌、工具与思考轨迹开关、日志档位、OTLP 出口。 */
@@ -16,6 +16,10 @@ import { UsageReport } from "@sema-agent/registry-core/fleet";
16
16
  /** Bound on distinct principal×model keys per window — a runaway/multi-tenant burst collapses into
17
17
  * `__other__` instead of growing unbounded (same posture as budget.ts cardinalityGuard). */
18
18
  export declare const FLEET_USAGE_KEY_CAP = 1024;
19
+ /** C1(2026-08-03):announce/usage/deregister 三站点的 fetch 上界(fleet-lease `LEASE_FETCH_TIMEOUT_MS`
20
+ * 同款判据的普适形)。比 lease 的 2500 略宽——这三条不在提交热路径,5s 足以吃掉慢网 RTT,又远小于
21
+ * undici 默认 ~300s headersTimeout(此前半开 center 把 stop()/心跳各钉住到那个量级)。 */
22
+ export declare const FLEET_CLIENT_FETCH_TIMEOUT_MS = 5000;
19
23
  /**
20
24
  * Per-window principal×model usage accumulator, fed from the tracer's `brain.call` hook (which carries the
21
25
  * ALS principal — reaches council/team sub-task spend too, same attribution point costQuota uses). Sync,
@@ -16,6 +16,10 @@ import { InstanceAnnouncement, UsageReport, DEFAULT_HEARTBEAT_MS } from "@sema-a
16
16
  /** Bound on distinct principal×model keys per window — a runaway/multi-tenant burst collapses into
17
17
  * `__other__` instead of growing unbounded (same posture as budget.ts cardinalityGuard). */
18
18
  export const FLEET_USAGE_KEY_CAP = 1024;
19
+ /** C1(2026-08-03):announce/usage/deregister 三站点的 fetch 上界(fleet-lease `LEASE_FETCH_TIMEOUT_MS`
20
+ * 同款判据的普适形)。比 lease 的 2500 略宽——这三条不在提交热路径,5s 足以吃掉慢网 RTT,又远小于
21
+ * undici 默认 ~300s headersTimeout(此前半开 center 把 stop()/心跳各钉住到那个量级)。 */
22
+ export const FLEET_CLIENT_FETCH_TIMEOUT_MS = 5000;
19
23
  /**
20
24
  * Per-window principal×model usage accumulator, fed from the tracer's `brain.call` hook (which carries the
21
25
  * ALS principal — reaches council/team sub-task spend too, same attribution point costQuota uses). Sync,
@@ -122,7 +126,10 @@ export function startFleetClient(opts) {
122
126
  heartbeatMs,
123
127
  };
124
128
  try {
125
- const res = await f(`${base}/api/fleet/instances`, { method: "POST", headers, body: JSON.stringify(body) });
129
+ // 鲁棒性批1 C1(2026-08-03):三条对 center fetch 全部界上 abort(fleet-lease O1 同款判据的
130
+ // 普适形)——半开 center 下此前各挂 undici 默认 ~300s headersTimeout,stop() 的滚动更新被单个
131
+ // 半开连接钉住;心跳侧每 tick 叠一个在飞请求。
132
+ const res = await f(`${base}/api/fleet/instances`, { method: "POST", headers, body: JSON.stringify(body), signal: AbortSignal.timeout(FLEET_CLIENT_FETCH_TIMEOUT_MS) });
126
133
  if (!res.ok)
127
134
  throw new Error(`announce ${res.status}`);
128
135
  if (failStreak > 0)
@@ -147,7 +154,7 @@ export function startFleetClient(opts) {
147
154
  return; // nothing accrued — skip the POST entirely
148
155
  const report = { worker: opts.center.worker, instanceId: opts.instanceId, windowStartMs: start, windowEndMs, entries };
149
156
  try {
150
- const res = await f(`${base}/api/fleet/usage`, { method: "POST", headers, body: JSON.stringify({ reports: [report] }) });
157
+ const res = await f(`${base}/api/fleet/usage`, { method: "POST", headers, body: JSON.stringify({ reports: [report] }), signal: AbortSignal.timeout(FLEET_CLIENT_FETCH_TIMEOUT_MS) });
151
158
  if (!res.ok)
152
159
  throw new Error(`usage ${res.status}`);
153
160
  }
@@ -171,7 +178,7 @@ export function startFleetClient(opts) {
171
178
  clearInterval(uw);
172
179
  await flushUsage(); // final partial window — best-effort
173
180
  try {
174
- await f(`${base}/api/fleet/instances`, { method: "DELETE", headers, body: JSON.stringify({ instanceId: opts.instanceId, worker: opts.center.worker }) });
181
+ await f(`${base}/api/fleet/instances`, { method: "DELETE", headers, body: JSON.stringify({ instanceId: opts.instanceId, worker: opts.center.worker }), signal: AbortSignal.timeout(FLEET_CLIENT_FETCH_TIMEOUT_MS) });
175
182
  }
176
183
  catch {
177
184
  /* deregistration is best-effort — staleness (2× heartbeatMs) reaps us anyway */
@@ -1,4 +1,4 @@
1
- import { BackgroundAgentStoreError, } from "@sema-agent/core";
1
+ import { BackgroundAgentStoreError, STALE_RUNNING_REAP_ATTRIBUTION, } from "@sema-agent/core";
2
2
  import { pgSafeJsonStringify, pgSanitizeText } from "./pg-safe-json.js";
3
3
  export const BACKGROUND_AGENT_TABLE = "background_agents";
4
4
  const SUMMARY_COLS = "handle, owner, session_scoped, session_id, parent_session_id, root_session_id, name, agent_type, status, spawned_at_ms, updated_at_ms, settled_at_ms, usage_json";
@@ -66,11 +66,8 @@ function rowToSummary(r) {
66
66
  : {}),
67
67
  };
68
68
  }
69
- /** core 的共享归因句(`STALE_RUNNING_REAP_ATTRIBUTION`,dist/core/background-agent-store.js:44)——
70
- * **值拷贝**:core 5.3.0 未把它放上包根(exports 只有 ./bench 与 ./fixtures 两条子路,deep import 会被
71
- * exports 图拒绝),已在提货回执里请 core 上包根,到货后换 import。漂移钉=backgroundAgentStoreContract
72
- * 的 stale-running 条目逐字断言这句(store-contract-kit-db-integration §4.5,真双库)——core 改句必红。 */
73
- const STALE_RUNNING_REAP_ATTRIBUTION = "the host process was interrupted while this agent was running — its in-process state was lost. Check its worktree / output file for partial work before assuming the task landed (stale running row reaped).";
69
+ // 共享归因句:core 5.4.0 起上包根([2436] 请求→[2437] 兑现),值拷贝退役——同一常量,双生与
70
+ // InMemory 语义源头彻底同源;试剂盒条目仍逐字断言它(真双库),但漂移自此在编译期就不可能发生。
74
71
  /** core reap 的 staleRunning 翻转形(逐字段照抄 InMemory,background-agent-store.js:266-272):
75
72
  * summary **和 error** 都只在缺席时补共享归因句——error 半边 5.2.0 起就该有,双生此前只写 summary
76
73
  * 且是手写旧句(core 5.3.0 新试剂盒首跑两方言一致揪红,[2425] bg-agent B 族同源),真失败文本永不覆写。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -54,7 +54,7 @@
54
54
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
55
55
  },
56
56
  "dependencies": {
57
- "@sema-agent/core": "^5.3.0",
57
+ "@sema-agent/core": "^5.4.0",
58
58
  "@sema-agent/registry-core": "^0.13.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",