@sema-agent/server 7.71.0 → 7.73.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.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/USAGE.md +12 -1
  3. package/dist/approval-card.d.ts +16 -17
  4. package/dist/approval-card.js +0 -9
  5. package/dist/boot/config-center.d.ts +6 -1
  6. package/dist/boot/config-center.js +3 -1
  7. package/dist/boot/memory-boundary.d.ts +27 -5
  8. package/dist/boot/memory-boundary.js +6 -6
  9. package/dist/boot/memory-consolidation.js +2 -1
  10. package/dist/boot/runner-deps.d.ts +32 -16
  11. package/dist/boot/runner-deps.js +3 -1
  12. package/dist/boot/stores.js +6 -4
  13. package/dist/boot/unpriced-ceiling-verdict.d.ts +15 -0
  14. package/dist/boot/unpriced-ceiling-verdict.js +39 -0
  15. package/dist/brain.js +2 -9
  16. package/dist/budget.d.ts +21 -2
  17. package/dist/budget.js +3 -3
  18. package/dist/capabilities/tool-defer.d.ts +18 -12
  19. package/dist/config-catalog.js +12 -12
  20. package/dist/config-center/apply-effective.js +2 -4
  21. package/dist/config-center/facade.d.ts +1 -1
  22. package/dist/config-center/restart-signal.js +2 -2
  23. package/dist/config-center/skills-mcp.js +15 -0
  24. package/dist/config-types.d.ts +83 -23
  25. package/dist/config-types.js +19 -1
  26. package/dist/config.d.ts +58 -0
  27. package/dist/config.js +76 -22
  28. package/dist/http/active-run-conflict.d.ts +3 -2
  29. package/dist/http/durable-run-leg.d.ts +64 -0
  30. package/dist/http/durable-run-leg.js +63 -0
  31. package/dist/http/route-ctx.d.ts +14 -0
  32. package/dist/http/route-ctx.js +3 -0
  33. package/dist/http/routes/approvals-assistant.js +3 -3
  34. package/dist/http/routes/capabilities.js +2 -2
  35. package/dist/http/routes/runs.js +23 -68
  36. package/dist/http/routes/workflows.js +1 -0
  37. package/dist/http/server.js +70 -24
  38. package/dist/main.js +4 -1
  39. package/dist/memory-bundle-engine.d.ts +4 -3
  40. package/dist/memory-bundle-engine.js +2 -1
  41. package/dist/memory-incident-sink.d.ts +37 -0
  42. package/dist/memory-incident-sink.js +13 -0
  43. package/dist/memory-operator-faces.d.ts +9 -3
  44. package/dist/memory-operator-faces.js +4 -3
  45. package/dist/memory-scope.d.ts +37 -1
  46. package/dist/memory-scope.js +12 -2
  47. package/dist/observability/fail-open.d.ts +1 -1
  48. package/dist/observability/fail-open.js +1 -1
  49. package/dist/observability/metrics.js +1 -1
  50. package/dist/observability/tool-trace.d.ts +18 -1
  51. package/dist/observability/tool-trace.js +4 -3
  52. package/dist/parked-decide.js +3 -2
  53. package/dist/plugins/e2b-orphan-reclaim.d.ts +4 -4
  54. package/dist/plugins/e2b-orphan-reclaim.js +18 -20
  55. package/dist/plugins/file-run-store.js +1 -0
  56. package/dist/plugins/memory-run-store.js +1 -0
  57. package/dist/plugins/run-store-sql.d.ts +5 -2
  58. package/dist/plugins/run-store-sql.js +5 -3
  59. package/dist/plugins/store-contracts.d.ts +41 -0
  60. package/dist/plugins/store-contracts.js +3 -2
  61. package/dist/run-cancel-context.js +1 -1
  62. package/dist/run-local.js +19 -8
  63. package/dist/runs.d.ts +16 -2
  64. package/dist/runs.js +2 -1
  65. package/dist/runtime-caps-resolver.js +5 -3
  66. package/dist/spec-fields.d.ts +4 -2
  67. package/dist/task-a2a.js +3 -2
  68. package/dist/task-mcp.js +3 -2
  69. package/dist/tool-approval.d.ts +26 -21
  70. package/dist/tool-approval.js +2 -2
  71. package/dist/trace/core-keyset-guard.d.ts +17 -5
  72. package/dist/trace/project.d.ts +55 -0
  73. package/dist/trace/project.js +45 -3
  74. package/dist/usage-analytics.d.ts +20 -2
  75. package/dist/usage-analytics.js +9 -1
  76. package/dist/wire-name-bound.d.ts +14 -0
  77. package/dist/wire-name-bound.js +13 -0
  78. package/package.json +2 -2
package/dist/runs.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { defaultTaskRegistry, type CascadeConfig, type CheckpointToken, type Runner, type SubagentSteerHandle, type TaskResult, type TaskSpec, type TaskStream, type TerminalCause } from "@sema-agent/core";
1
+ import { defaultTaskRegistry, type CascadeConfig, type CheckpointToken, type RunInternals, type Runner, type SubagentSteerHandle, type TaskResult, type TaskSpec, type TaskStream, type TerminalCause } from "@sema-agent/core";
2
2
  import type { SendUserFileEmitter } from "./capabilities/send-user-file-tool.js";
3
3
  import type { RunStore } from "./plugins/store-backend.js";
4
4
  import type { Metrics } from "./observability/metrics.js";
@@ -282,7 +282,21 @@ pendingEngineNotices?: readonly {
282
282
  message: string;
283
283
  detail?: Record<string, unknown>;
284
284
  sessionId?: unknown;
285
- }[]): Promise<void>;
285
+ }[],
286
+ /**
287
+ * 🔴 **S-218:部署方铸的 TRUSTED run internals**(今天唯一成员 `workflowParkedResume`)。
288
+ *
289
+ * 它**不是** `TaskSpec` 字段、不是工具参数、不进请求体 —— 模型永远不能自己决定一次审批。铸点只有
290
+ * 一个:`/decide` 的 workflow 第三条车道(判据属主 = `parked-decide.ts` 的 `planWorkflowParkedDecide`
291
+ * 顶注 + `http/durable-run-leg.ts` 的 `TrustedRunInternals`)。本腿只负责把它摊进**下面那只**共享
292
+ * 供给座席,core 的 `prepareTask` 据它给 Workflow 工具挂 `parkedResume` dep。
293
+ *
294
+ * 位置在座席对象的**最后**:座席里那几位(onActivity/onForwardEvent/onTaskNotification/onSubagentSpawn)
295
+ * 是本腿自己的执行面供给,调用方无权覆盖它们 —— 真要撞名,撞的也只能是本腿自己新加的位,而那时
296
+ * `TrustedRunInternals` 的白名单会在编译期先红一次(它只开部署方可以决定的那一格)。
297
+ * 缺席 ⇒ 座席与本位存在之前逐字相同。
298
+ */
299
+ trustedInternals?: Pick<RunInternals, "workflowParkedResume">): Promise<void>;
286
300
  /** How often a running instance refreshes its run's updated_at (liveness, independent of events).
287
301
  * Must stay strictly below `runStaleSec` (asserted at startup AND on every hot config candidate) or the
288
302
  * reaper would race live runs. [ref]:数值的单源是 `config-invariants.ts`(那条不变量的属主),两个消费面
package/dist/runs.js CHANGED
@@ -160,7 +160,7 @@ export function evictIfConflict(runner, sessionId, result) {
160
160
  runner.sessions.evict?.(sessionId);
161
161
  }
162
162
  }
163
- export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, parkCheckpointReader, pendingEngineNotices) {
163
+ export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, parkCheckpointReader, pendingEngineNotices, trustedInternals) {
164
164
  const startedAt = Date.now();
165
165
  metrics?.addGauge("runs_active", 1);
166
166
  fleetPublisher?.onStart();
@@ -311,6 +311,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
311
311
  },
312
312
  }
313
313
  : {}),
314
+ ...(trustedInternals ?? {}),
314
315
  };
315
316
  const statusSeat = {
316
317
  onStatusEvent: (e) => {
@@ -45,6 +45,8 @@ export function toCoreRuntimeCaps(ent) {
45
45
  caps.allowObservers = ent.allowObservers;
46
46
  if (typeof autoMode === "boolean")
47
47
  caps.autoMode = autoMode;
48
+ if (caps.autoMode === false)
49
+ caps.autoModeDenySource = "org";
48
50
  if (typeof ent.allowMemoryOptOut === "boolean")
49
51
  caps.allowMemoryOptOut = ent.allowMemoryOptOut;
50
52
  return Object.keys(caps).length ? caps : undefined;
@@ -142,7 +144,7 @@ export function createPrincipalEntitlementsClient(opts) {
142
144
  catch {
143
145
  e = new Error("center fetch failed with an unstringifiable rejection reason");
144
146
  }
145
- const entry = { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, expiresAt: now() + denyTtlMs };
147
+ const entry = { caps: { allowWorkflows: false, allowFork: false, autoMode: false, autoModeDenySource: "org" }, expiresAt: now() + denyTtlMs };
146
148
  if (!recordDeny)
147
149
  readMintedDeny.add(entry);
148
150
  putCache(principal, entry);
@@ -231,7 +233,7 @@ export function applyLocalAutoModeDeny(base, deny) {
231
233
  if (!deny)
232
234
  return base;
233
235
  if (base === undefined)
234
- return () => ({ autoMode: false });
235
- return async (principal) => ({ ...(await base(principal)), autoMode: false });
236
+ return () => ({ autoMode: false, autoModeDenySource: "local" });
237
+ return async (principal) => ({ ...(await base(principal)), autoMode: false, autoModeDenySource: "local" });
236
238
  }
237
239
  //# sourceMappingURL=runtime-caps-resolver.js.map
@@ -96,7 +96,8 @@ export declare function taskAgentsSpecFragment(raw: unknown, requirePrincipal: b
96
96
  export declare function retainBackgroundProcessesFromBody(raw: unknown, requirePrincipal: boolean | undefined, warn: (event: string, fields: Record<string, unknown>) => void): true | undefined;
97
97
  /** [ref]② — body.{excludeTools,deferTools} → `TaskSpec.{excludeTools,deferTools}`(core 1.314 工具面
98
98
  * 控制批)。语义:excludeTools=roster 真卸载(schema 字节不上 wire,manifest 如实收窄);deferTools=
99
- * [ref] 延迟披露(占位上 wire,ToolSearch 激活 materialize)。两位 tighten-only(core 继承不变量:
99
+ * [ref] 延迟披露(占位上 wire,ToolSearch 激活 materialize;**core 7.15.0 [ref] 起它是唯一不受那道
100
+ * 10% 阈值门的延迟来源** —— 按字面恒延迟、不计量,引擎自选的候选集才受门)。两位 tighten-only(core 继承不变量:
100
101
  * union 进全部子 spawn 路径),per-run 工具面收窄无跨租户面 ⇒ 无租户门(interactiveTools 同姿势)。
101
102
  * DEFENSIVE(resume 重放不过 HTTP 400 门):非数组 ⇒ undefined;项非非空字符串 ⇒ 逐项 DROP;空数组 ⇒
102
103
  * undefined(不挂键=core 缺省,byte-compat)。HTTP 门另有 fail-loud 400(提交面邻居姿势)。 */
@@ -109,7 +110,8 @@ export declare function toolNameListFromBody(raw: unknown): string[] | undefined
109
110
  * **呈现**轴,它与 `deferMode` / `deferTools` 的延迟披露轴**无耦合** —— 上一行提 deferTools 只是在借它
110
111
  * 的「无租户门」姿势作类比。「引擎缺省 simple」这句本身在 core 5.40.0 上仍逐字为真
111
112
  * (`core/types.d.ts:133`:default "simple"),本批亲验未变。
112
- * 真正的 defer 轴对表(core 5.40.0 起 `deferMode:"auto"` 不再 sweep 内建)记在
113
+ * 真正的 defer 轴对表(core 5.40.0 起 `deferMode:"auto"` 不再 sweep 内建;7.15.0 起它不再是第二道门,
114
+ * 只是把候选集拓宽后走同一道阈值门)记在
113
115
  * `src/capabilities/tool-defer.ts` 的头注 —— 别把两根轴的结论写到一处。
114
116
  * DEFENSIVE(resume 重放不过 HTTP 400 门):非法值 ⇒ undefined;提交面另有 fail-loud 400(邻居姿势)。 */
115
117
  export declare function promptProfileFromBody(raw: unknown): "simple" | "classic" | undefined;
package/dist/task-a2a.js CHANGED
@@ -1,4 +1,5 @@
1
- import { resolveComplianceDenies, TOOL_WIRE_NAME_MAX_CHARS } from "@sema-agent/core";
1
+ import { resolveComplianceDenies } from "@sema-agent/core";
2
+ import { isUsableWireName } from "./wire-name-bound.js";
2
3
  import { HttpError } from "./security.js";
3
4
  export const MAX_REQUEST_A2A_PEERS = 32;
4
5
  export function a2aInjectionHonored(config) {
@@ -19,7 +20,7 @@ function normalizeA2aPeer(e) {
19
20
  if (!e || typeof e !== "object")
20
21
  return null;
21
22
  const o = e;
22
- if (typeof o.name !== "string" || o.name.length === 0 || o.name.length > TOOL_WIRE_NAME_MAX_CHARS)
23
+ if (!isUsableWireName(o.name))
23
24
  return null;
24
25
  if (typeof o.url !== "string" || !HTTP_URL_RE.test(o.url))
25
26
  return null;
package/dist/task-mcp.js CHANGED
@@ -1,4 +1,5 @@
1
- import { mcpInjectionDroppedNotice, resolveComplianceDenies, TOOL_WIRE_NAME_MAX_CHARS } from "@sema-agent/core";
1
+ import { mcpInjectionDroppedNotice, resolveComplianceDenies } from "@sema-agent/core";
2
+ import { isUsableWireName } from "./wire-name-bound.js";
2
3
  import { HttpError } from "./security.js";
3
4
  import { CONTENT_ORIGIN_WORDS, describeBadContentOrigin, isContentOriginWord } from "./mcp-content-origin.js";
4
5
  import { readMcpSourceLabel } from "./mcp-source-label.js";
@@ -26,7 +27,7 @@ function normalizeServer(e, noteBadOrigin) {
26
27
  if (!e || typeof e !== "object")
27
28
  return bad("entry");
28
29
  const o = e;
29
- if (typeof o.name !== "string" || o.name.length === 0 || o.name.length > TOOL_WIRE_NAME_MAX_CHARS)
30
+ if (!isUsableWireName(o.name))
30
31
  return bad("name");
31
32
  const t = o.transport;
32
33
  if (!t || typeof t !== "object")
@@ -126,11 +126,16 @@ export interface ToolApprovalFrame {
126
126
  requiresRealApproval?: true;
127
127
  /**
128
128
  * **S-125③/[ref]**(core 7.5.0;**ADDITIVE**,`"tool_approval"` only)—— 这只 ask 的**出身**:
129
- * 谁提的这一问,一个 core 闭集词(`ASK_ORIGINS` **11 员**,core 7.9.0 现值:content_question /
130
- * unresolvable / org_unavailable / org_rule / rule_store_unavailable / hook / ask_rule /
129
+ * 谁提的这一问,一个 core 闭集词(`ASK_ORIGINS` **11 员**,core 7.14.0 现值:content_question /
130
+ * ancestor_marked / org_unavailable / org_rule / rule_store_unavailable / hook / ask_rule /
131
131
  * denial_limit_fallback / shell_gate_tighten / safety_tighten / policy)。引擎在门上盖章(core d.ts:
132
132
  * 「engine-stamped at the gate, never a policy's claim」),`AskRequest.origin` 逐字透传。
133
133
  *
134
+ * ⚠️ **core 7.14.0 [ref] C2 BREAKING**:`unresolvable` → `ancestor_marked`,**无别名**。server 侧零形变
135
+ * (本键一直是 `string` 逐字透传),但**消费端的 switch 要改词**;7.14.0 之前 park 下来、行上存着
136
+ * `unresolvable` 的老记录,在 core 每一道 `isAskOrigin` 筛上自此都是非成员(投影处不回显 ⇒ 本键缺席),
137
+ * 而那一行的 `org` 事实不受影响。
138
+ *
134
139
  * 🔴 **echo-only,零消费**:自动车道的资格判据全在 core(`classifierMayAnswer` 的闭表),server
135
140
  * **不许**据本键自铸第二张资格表 —— 那是同一语义面两个写者(源头修复纪律),与
136
141
  * `denialLimitFallback` 顶注那条「不据 autoDenyAfterMs 自铸第二只定时器」同一条禁令。
@@ -144,26 +149,26 @@ export interface ToolApprovalFrame {
144
149
  */
145
150
  origin?: string;
146
151
  /**
147
- * **S-185 / [ref]**(core 7.10.0;**ADDITIVE**,`"tool_approval"` only)—— 这一问**为什么**到了人手上:
148
- * 分类器被咨询过、而它**跑不了**。`cause` = core 的闭集词
149
- * (`AUTO_MODE_UNAVAILABLE_CAUSES`;core 7.12.0 起 = `error` / `timeout`),`AskRequest.classifierUnavailable`
150
- * 逐字透传。core 7.12.0 / [ref] 起 ask 上这一位只由**策略层自声明**铸(引擎自己的那份改骑 deny —— 全文
151
- * 在 `approval-card.ts` 同名键的顶注)。
152
- *
153
- * 🔴 **缺席 ≠「分类器好着呢」**:缺席同时覆盖三形 —— 分类器答上了 / 这只 ask 没资格走分类器
154
- * (`classifierMayAnswer` 的闭表)/ 本部署压根没接分类器。消费端**禁**把缺席读成对分类器健康的断言。
155
- * 另注:`parse_error`(分类器跑了但答得出格)**不盖这一位** —— 那是另一句话,core 顶注逐字。
156
- *
157
- * 🔴 **不枚举词表**(值形是 `{cause: string}` 而不是本仓自铸的联合):词表单一属主在 core,抄一份会把
158
- * core 加的新词吞成缺席。窄读只判「对象 ∧ `cause` 非空串 ∧ 无多余成员」(`readClassifierUnavailable`,
159
- * 帧与卡的**唯一**铸造点 ⇒ 两面结构性同值)。
160
- *
161
- * 🔴 **echo-only,零消费**:熔断/超时的处置全在 core 的分类器站,server 不许据本键自铸第二套回落判据
162
- * —— 同 `origin` / `denialLimitFallback` 两条顶注的同一条禁令(同一语义面两个写者)。
152
+ * **[ref] C3**(core 7.14.0;**ADDITIVE**,`"tool_approval"` only)—— `origin: "rule_store_unavailable"`
153
+ * 的**机制判别位**:持久规则车道这一次**读不下来的是哪一半**。core 闭二词
154
+ * (`RULE_STORE_UNREADABLE_KINDS`):`"store"` = 接上的规则店读不了(读失败 / 超时);`"call"` = 店读到了,
155
+ * 但**这条命令**按人的 deny/ask 行读不出来(收紧 lexer 的 `unreadable`:展开里含规则词、引号没闭、语法
156
+ * 错——具体那句在 `message` 里)。`AskRequest.ruleStoreUnreadable` 逐字透传。
157
+ *
158
+ * 🔴 **在场 ⇔ `origin === "rule_store_unavailable"`**(core 顶注逐字)。两词对审批人是两件事:`store`
159
+ * 指向「去看规则店 / 网络」,`call` 指向「这条命令的写法让规则判不了」——一个 origin 词对这两件事同形。
160
+ * 缺席 = 车道把店和这条命令都读通了,**或**本部署没接规则车道;消费端禁把缺席读成对规则店健康的断言。
161
+ *
162
+ * 🔴 **不枚举词表**(值形是 `string` 而不是本仓自铸的联合):词表单一属主在 core
163
+ * (`isRuleStoreUnreadable` 是它的成员判),抄一份会把 core 加的新词吞成缺席 —— 与 {@link origin} 逐字同规。
164
+ *
165
+ * 🔴 **echo-only,零消费**:策略自声明的值在 core 的**唯一**盖章点就被抹掉了,server 不重算、不再筛第二遍,
166
+ * 更不许据本键自铸第二张车道资格表 —— 同 `origin` / `denialLimitFallback` 两条顶注的同一条禁令。
167
+ *
168
+ * ⚰️ 本键的前任 `classifierUnavailable`(S-185 / [ref])随 core 7.14.0 [ref] **整族退役**:引擎那份事实改骑
169
+ * `GateDisposition.denied.cause`(`tool_end.gate` / `permissionDenied.gate`),ask 面自此没有这一位。
163
170
  */
164
- classifierUnavailable?: {
165
- cause: string;
166
- };
171
+ ruleStoreUnreadable?: string;
167
172
  /**
168
173
  * **E-14**([ref]② Trojan Source 族;**ADDITIVE**,`"tool_approval"` only)—— 这只 ask 的**工具输入**
169
174
  * (`AskRequest.args`)里含 bidi 控制符(LRM/RLM、嵌入/覆写、隔离符;字符类属主 `text-bidi.ts`)。
@@ -8,7 +8,7 @@ import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerpri
8
8
  import { createLogger } from "./observability/logger.js";
9
9
  import { recordFailOpen } from "./observability/fail-open.js";
10
10
  import { deriveAskId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
11
- import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readClassifierUnavailable, readProbeCause, readRuleEvidence, readRuleOffersAbsence, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
11
+ import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readRuleEvidence, readRuleOffersAbsence, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
12
12
  import { projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
13
13
  import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
14
14
  const defaultLogger = createLogger();
@@ -844,7 +844,7 @@ export class ToolApprovalCoordinator {
844
844
  ...(ruleLaneMaterial !== undefined ? { ruleOffers: ruleLaneMaterial.offers } : {}),
845
845
  ...((a) => (a !== undefined ? { ruleOffersAbsence: a } : {}))(readRuleOffersAbsence(req)),
846
846
  ...((f) => (f !== undefined ? { denialLimitFallback: f } : {}))(readDenialLimitFallback(req)),
847
- ...((c) => (c !== undefined ? { classifierUnavailable: c } : {}))(readClassifierUnavailable(req)),
847
+ ...(typeof req.ruleStoreUnreadable === "string" && req.ruleStoreUnreadable.length > 0 ? { ruleStoreUnreadable: req.ruleStoreUnreadable } : {}),
848
848
  ...((c) => (c !== undefined ? { probeCause: c } : {}))(readProbeCause(req)),
849
849
  ...((c) => (c !== undefined ? { ruleEvidence: c } : {}))(readRuleEvidence(req)),
850
850
  ...(bounded.omitted ? { argsOmitted: true } : { args: bounded.args }),
@@ -17,7 +17,7 @@
17
17
  * ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
18
18
  * TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
19
19
  */
20
- import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, Settlement, PausedCause } from "@sema-agent/core";
20
+ import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun } from "@sema-agent/core";
21
21
  import type { ToolApprovalFrame } from "../tool-approval.js";
22
22
  /** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。
23
23
  * 🔴 **导出**([ref] U1/U2):本仓「词表闭集,未处置成员=编译红」的钉法只许有**一个**形 ——
@@ -43,7 +43,7 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
43
43
  type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
44
44
  type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
45
45
  type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
46
- type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "classifierUnavailable";
46
+ type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
47
47
  type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "probeReason" | "hasBidiControls" | "previewWithheld" | "execCwd";
48
48
  type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
49
49
  type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
@@ -103,7 +103,7 @@ type _GuardWiringRulesReverse = AssertAllKeysHandled<Exclude<WiringRulesHandled,
103
103
  type WiringModelGateHandled = "class" | "removed" | "restore";
104
104
  type _GuardWiringModelGate = AssertAllKeysHandled<Exclude<keyof WiringSection<"modelGate">, WiringModelGateHandled>>;
105
105
  type _GuardWiringModelGateReverse = AssertAllKeysHandled<Exclude<WiringModelGateHandled, keyof WiringSection<"modelGate">>>;
106
- type WiringAutoModeHandled = "armed" | "reason";
106
+ type WiringAutoModeHandled = "armed" | "reason" | "deniedSource";
107
107
  type _GuardWiringAutoMode = AssertAllKeysHandled<Exclude<keyof WiringSection<"autoMode">, WiringAutoModeHandled>>;
108
108
  type _GuardWiringAutoModeReverse = AssertAllKeysHandled<Exclude<WiringAutoModeHandled, keyof WiringSection<"autoMode">>>;
109
109
  type WiringGovernanceHandled = "audience" | "lockedConfig" | "compliance" | "memoryAdmission" | "retention";
@@ -117,7 +117,7 @@ type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTrace
117
117
  /** `T` 非 `never` 时收敛到 `never`(空集 ⇒ 残留 `"EMPTY"` ⇒ tsc 红)。 */
118
118
  type AssertNonEmpty<T> = [T] extends [never] ? "EMPTY" : never;
119
119
  type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<PermissionTraceKind>>;
120
- type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "classifierUnavailable";
120
+ type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "ruleStoreUnreadable";
121
121
  type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
122
122
  type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
123
123
  type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
@@ -138,12 +138,20 @@ type SettlementWhoKeys = Settlement["who"] extends infer W ? (W extends object ?
138
138
  type GateOutcomeProjected = "disposition" | "settlement" | "origin";
139
139
  type _GuardGateOutcome = AssertAllKeysHandled<Exclude<keyof GateOutcome, GateOutcomeProjected>>;
140
140
  type _GuardGateOutcomeReverse = AssertAllKeysHandled<Exclude<GateOutcomeProjected, keyof GateOutcome>>;
141
+ type GateDispositionKeys = GateDisposition extends infer D ? (D extends object ? keyof D : never) : never;
142
+ type GateDispositionProjected = "kind" | "deniedBy" | "cause";
143
+ type _GuardGateDisposition = AssertAllKeysHandled<Exclude<GateDispositionKeys, GateDispositionProjected>>;
144
+ type _GuardGateDispositionReverse = AssertAllKeysHandled<Exclude<GateDispositionProjected, GateDispositionKeys>>;
141
145
  type SettlementProjected = "kind" | "who" | "when" | "note";
142
146
  type _GuardSettlement = AssertAllKeysHandled<Exclude<SettlementKeys, SettlementProjected>>;
143
147
  type _GuardSettlementReverse = AssertAllKeysHandled<Exclude<SettlementProjected, SettlementKeys>>;
144
148
  type SettlementWhoProjected = "party" | "approver" | "window";
145
149
  type _GuardSettlementWho = AssertAllKeysHandled<Exclude<SettlementWhoKeys, SettlementWhoProjected>>;
146
150
  type _GuardSettlementWhoReverse = AssertAllKeysHandled<Exclude<SettlementWhoProjected, SettlementWhoKeys>>;
151
+ type WorkflowRunForwarded = "id" | "scope" | "status" | "startedAt" | "endedAt" | "createdAt" | "rev" | "name" | "description" | "result" | "error" | "errorCode" | "agentFailures" | "stats" | "phases" | "agents" | "groups";
152
+ type WorkflowRunStripped = "sourceTaskId" | "originatingSessionId" | "effectiveArgs" | "budgetOvershoot" | "timeoutInterruption" | "resultFull" | "completionId" | "resume" | "journalSkips";
153
+ type _GuardWorkflowRun = AssertAllKeysHandled<Exclude<keyof WorkflowRun, WorkflowRunForwarded | WorkflowRunStripped>>;
154
+ type _GuardWorkflowRunReverse = AssertAllKeysHandled<Exclude<WorkflowRunForwarded | WorkflowRunStripped, keyof WorkflowRun>>;
147
155
  export type CoreKeysetGuards = [
148
156
  _GuardNotification,
149
157
  _GuardNotificationReverse,
@@ -208,10 +216,14 @@ export type CoreKeysetGuards = [
208
216
  _GuardPausedCauseReverse,
209
217
  _GuardGateOutcome,
210
218
  _GuardGateOutcomeReverse,
219
+ _GuardGateDisposition,
220
+ _GuardGateDispositionReverse,
211
221
  _GuardSettlement,
212
222
  _GuardSettlementReverse,
213
223
  _GuardSettlementWho,
214
- _GuardSettlementWhoReverse
224
+ _GuardSettlementWhoReverse,
225
+ _GuardWorkflowRun,
226
+ _GuardWorkflowRunReverse
215
227
  ];
216
228
  export {};
217
229
  //# sourceMappingURL=core-keyset-guard.d.ts.map
@@ -119,7 +119,62 @@ export declare function toolStartEventData(ev: {
119
119
  eventId?: string;
120
120
  parentToolCallId?: string;
121
121
  }): Record<string, unknown>;
122
+ /**
123
+ * 🔴 **筛子是一只总函数**(S-239,codex 对抗复审 r1 [medium] 亲核复现后修)—— 上面纪律②许诺的
124
+ * 「坏形 ⇒ report + withhold,**不抛**」此前是靠**逐条枚举**坏形兑现的,枚举不到的那些照样抛:
125
+ * `projectSettlement` 对 `note` / `who.approver` 先 `redactSecrets` 再截断,而 `redactSecrets` 在 ≥2^23
126
+ * 的单段上爆回溯栈(`RangeError`,S-102/S-106 登记的既有上界,见 `redact.ts` 该行顶注),两条叶各能抛一次。
127
+ *
128
+ * 抛出去的代价按腿算,而且一条比一条重:写腿落账那处的 `.catch` 看不到**同步**投影期的异常;读腿上它
129
+ * 直接逃出 `formatEvent` / 投影函数 —— 账本裸回放腿的那一次会当场断流(那一行不上帧、游标不前进、后面的
130
+ * `done` 永远到不了、重连恒复现同一处),而 `gate-outcome-defective` 那一格还不涨。
131
+ *
132
+ * ⇒ 兑现方式从「枚举」换成**结构**:任何逃出投影的异常同走既有的那一口 report + withhold。规则因此变少
133
+ * (不是「守住已知的 N 种坏形」而是「坏字节一律 withhold」),上面那几道具名形门留着只为**诊断串更准**。
134
+ * ⛔ **刻意不动**「先脱敏后截断」的次序:截断在前会把跨界的密钥切成半截明文留在输出里,而且它只治
135
+ * `note` 一条叶(`approver` 同病)—— 那是给一个类打两个特判,正是本仓要消掉的形。
136
+ */
122
137
  export declare function screenedGate(v: unknown): GateOutcome | undefined;
138
+ /**
139
+ * 🔴 **耐久行的**门记录读口(7.72.0;`screenedGate` 的读腿孪生)—— 先把本仓自己写过的**旧拼法**读成它
140
+ * 今天的名字({@link RETIRED_ASK_ORIGIN_SPELLINGS}),再走与写腿**同一只**筛子。
141
+ *
142
+ * 为什么读腿要多这一步而写腿不要:读到的字节可能是**本仓上一个版本**写下的(账本回放、park 行的
143
+ * `resolvedOutcome`),而 core 7.14.0 [ref] C2 把 `ASK_ORIGINS` 的一个词改了名 —— 不认旧拼法,那些行就整条
144
+ * withhold,连 `disposition` / `settlement`(谁拒的、人怎么结的)一起在回放面上消失。写腿面对的则是**当下**
145
+ * 引擎铸的记录,它铸不出退役词,所以那里出现退役词 = producer 契约漂了 = 要 report + withhold 的事。
146
+ *
147
+ * 不改原对象(调用方与账本共享引用):归一时换一只信封,原记录一个字节不动。
148
+ */
149
+ export declare function screenedStoredGate(v: unknown): GateOutcome | undefined;
150
+ /**
151
+ * 🔴 **账本裸回放面的读腿一步**(S-239)—— `GET /v1/runs/:id/events`(`http/routes/runs.ts` 的
152
+ * `formatEvent`)对每一行走这一口,把行上的 `GateOutcome` 交给**同一只** {@link screenedStoredGate}。
153
+ *
154
+ * ## 它补的是哪一格
155
+ * 7.72.0 把耐久读边界收成一只筛子,但点名的读腿只有两条({@link toolResultFieldsOf} 的投影回放、
156
+ * `/decide` 幂等回放体)。**账本裸回放**是第三条,而它此前只过 `redactLedgerEventData` —— 那只函数
157
+ * 对 `tool_end` 型**逐字不动**(它管的是 `done.result` / `failed.errorMessage` / park 行三族)。
158
+ * 于是同一条账本行在本仓三张读面上有**两种**读法:
159
+ * · 本仓 7.64.0–7.71.0 自己写下的 `origin:"unresolvable"` —— 投影面读成今天的名字,裸回放面原样
160
+ * 透出一个 core 今天判**非成员**的词(消费端按闭集分派 ⇒ 落进它的 default 臂);
161
+ * · 第三方 producer / 坏世代写下的集外词与破不变量的记录 —— 投影面整条 withhold + 计数,裸回放面
162
+ * 直接上 wire,而 `gate-outcome-defective` 那一格不涨(运维看不见有 producer 在发坏记录)。
163
+ *
164
+ * ## 为什么是这一形(而不是在路由里加一只筛)
165
+ * 筛子仍然只有一只,归一表仍然只有一张 —— 本函数只是把**第三条读腿**接到它上面,规则因此从
166
+ * 「三条读腿里有两条筛」回到「凡耐久读腿上的 `GateOutcome` 一律过 `screenedStoredGate`,零例外」。
167
+ * 它按**型**分派而不是按调用点,所以将来第二个携带 `GateOutcome` 的账本型只需把
168
+ * {@link GATE_BEARING_LEDGER_EVENT_TYPE} 扩成词表,读腿一行都不用改。
169
+ *
170
+ * ## 与写腿的关系(别再走回头路)
171
+ * 写腿的对偶是 `redactLedgerEventData`(三孪生 store 的 `appendEvent` 落库前),它**不该**长出本函数
172
+ * 这一步:归一只认读腿,写腿保持严格(理由全文见 {@link RETIRED_ASK_ORIGIN_SPELLINGS} 的边界划清二)。
173
+ * 两只函数因此是「共用的脱敏汇点 + 读腿独有的筛」这一层关系,不是同一只函数的两种模式。
174
+ *
175
+ * 不改原对象(调用方与账本共享引用):有变更时换一只信封,原记录一个字节不动。
176
+ */
177
+ export declare function screenedStoredEventData(type: string, data: unknown): unknown;
123
178
  /** The durable `tool_end` payload (shared by both append sites). E1: the model-facing `output` is UNTRUSTED RAW
124
179
  * from core (file/shell/API bodies) — core only SIZE-bounds it (`truncated`), REDACTION is ours (same contract as
125
180
  * `tool_start.args`); never re-fed to a model. E2: identity. Absent `output` ⇒ the tool produced no content body.
@@ -86,7 +86,19 @@ export function toolStartEventData(ev) {
86
86
  return { toolCallId: ev.toolCallId, toolName: ev.toolName, ...(ev.label !== undefined ? { label: ev.label } : {}), args: redactDeep(ev.args), ...identityFields(ev) };
87
87
  }
88
88
  const SETTLEMENT_NOTE_MAX = 500;
89
+ const RETIRED_ASK_ORIGIN_SPELLINGS = {
90
+ unresolvable: "ancestor_marked",
91
+ };
89
92
  export function screenedGate(v) {
93
+ try {
94
+ return screenedGateOrThrow(v);
95
+ }
96
+ catch (e) {
97
+ recordFailOpen("server.trace.gate-outcome-defective", `projection threw: ${e instanceof Error ? `${e.name}: ${e.message}` : "non-Error throw"}`);
98
+ return undefined;
99
+ }
100
+ }
101
+ function screenedGateOrThrow(v) {
90
102
  if (v === null || typeof v !== "object")
91
103
  return undefined;
92
104
  const g = v;
@@ -96,6 +108,7 @@ export function screenedGate(v) {
96
108
  return undefined;
97
109
  }
98
110
  const shapeDefect = (d.kind === "denied" && typeof d.deniedBy !== "string" && "disposition.deniedBy is not a string") ||
111
+ (d.kind === "denied" && d.cause !== undefined && typeof d.cause !== "string" && "disposition.cause is not a string") ||
99
112
  (g.settlement !== undefined && (g.settlement === null || typeof g.settlement !== "object") && "settlement is not an object") ||
100
113
  (g.settlement !== undefined && g.settlement !== null && typeof g.settlement.kind !== "string" && "settlement.kind is not a string") ||
101
114
  (g.origin !== undefined && typeof g.origin !== "string" && "origin is not a string") ||
@@ -115,11 +128,37 @@ export function screenedGate(v) {
115
128
  return undefined;
116
129
  }
117
130
  return {
118
- disposition: d.kind === "denied" ? { kind: "denied", deniedBy: d.deniedBy } : { kind: "allowed" },
131
+ disposition: d.kind === "denied"
132
+ ? { kind: "denied", deniedBy: d.deniedBy, ...(d.cause === undefined ? {} : { cause: d.cause }) }
133
+ : { kind: "allowed" },
119
134
  ...(settlement === undefined ? {} : { settlement }),
120
135
  ...(g.origin === undefined ? {} : { origin: g.origin }),
121
136
  };
122
137
  }
138
+ export function screenedStoredGate(v) {
139
+ if (v === null || typeof v !== "object")
140
+ return undefined;
141
+ const g = v;
142
+ const renamed = typeof g.origin === "string" ? RETIRED_ASK_ORIGIN_SPELLINGS[g.origin] : undefined;
143
+ return screenedGate(renamed === undefined ? g : { ...g, origin: renamed });
144
+ }
145
+ const GATE_BEARING_LEDGER_EVENT_TYPE = "tool_end";
146
+ export function screenedStoredEventData(type, data) {
147
+ if (type !== GATE_BEARING_LEDGER_EVENT_TYPE)
148
+ return data;
149
+ if (data === null || typeof data !== "object" || Array.isArray(data))
150
+ return data;
151
+ const d = data;
152
+ if (d.gate === undefined)
153
+ return data;
154
+ const gate = screenedStoredGate(d.gate);
155
+ if (gate === undefined) {
156
+ const withheld = { ...d };
157
+ delete withheld.gate;
158
+ return withheld;
159
+ }
160
+ return { ...d, gate };
161
+ }
123
162
  const SETTLEMENT_PARTIES = new Set(["person", "engine", "host", "none"]);
124
163
  function projectSettlement(st) {
125
164
  const whoV = st.who;
@@ -329,7 +368,10 @@ export function wiringManifestEventData(ev) {
329
368
  const reason = str(autoIn.reason);
330
369
  if (armed === undefined || reason === undefined)
331
370
  return undefined;
332
- return armed === (reason === "armed") ? { armed, reason } : undefined;
371
+ if (armed !== (reason === "armed"))
372
+ return undefined;
373
+ const deniedSource = str(autoIn.deniedSource);
374
+ return { armed, reason, ...(deniedSource === undefined ? {} : { deniedSource }) };
333
375
  })();
334
376
  const toolsIn = m.tools;
335
377
  const tools = toolsIn === undefined ? undefined : Value.Check(ToolRoster, toolsIn) ? toolsIn : (recordFailOpen("server.trace.tool-roster-malformed", "wiring_manifest.tools did not fit core ToolRoster"), undefined);
@@ -514,7 +556,7 @@ export function toolCallFieldsOf(d) {
514
556
  };
515
557
  }
516
558
  export function toolResultFieldsOf(d) {
517
- const gate = d.gate === undefined ? undefined : screenedGate(d.gate);
559
+ const gate = d.gate === undefined ? undefined : screenedStoredGate(d.gate);
518
560
  return {
519
561
  callId: String(d.toolCallId ?? ""),
520
562
  ...(typeof d.label === "string" ? { label: d.label } : {}),
@@ -26,6 +26,11 @@ export interface UsageRow {
26
26
  stats?: {
27
27
  tokens?: number;
28
28
  modelUsage?: Record<string, UsageModelEntry>;
29
+ /** core 7.14.0 [ref] C-b(`TaskResult.stats.usageMissing`):这一行的用量数字是**下界**不是测量值
30
+ * —— 至少有一轮模型没报用量,或整条结果是宿主/编排车道自铸的(一个模型轮都没跑过)。
31
+ * `true` 或缺席,**永不 `false`**;缺席 ⇔ 每轮都报了用量。fold 侧翻成既有的
32
+ * {@link UsageTotals.estimated}(见 `foldRow`),**不开第二条不精确轴**。 */
33
+ usageMissing?: true;
29
34
  };
30
35
  }
31
36
  /**
@@ -57,17 +62,30 @@ export interface UsageTotals {
57
62
  * ②有 per-model echo 行**缺数值键**(该键按 0 计入,总量偏低;成本键缺席时尤其:少算 ≠ 免费);
58
63
  * ③(S10/V2-⑤)有行的 {@link UsageModelEntry.usageBasis} 缺席或非本读端认识的分量制——存量行某族
59
64
  * 历史上把 cache 计入 inputTokens,tokensIn 求和会**双算**,口径不可分辨。
60
- * 三者都只表达「别把这个总数当精确值」,消费端一视同仁。 */
65
+ * ④(7.72.0 / core 7.14.0 [ref] C-b)有行自陈 `stats.usageMissing` —— 那一行的数字是**下界**:至少一轮
66
+ * 模型没报用量,或整条结果是宿主自铸的零结果(取消打断 / 零执行的治理拒绝)。`tokens: 0` 在那种行上读作
67
+ * 「未知」而不是「免费」。
68
+ * 四者都只表达「别把这个总数当精确值」,消费端一视同仁。 */
61
69
  estimated: boolean;
62
70
  }
63
71
  /** GET /v1/usage/summary — 窗内总量。 */
64
72
  export declare function usageSummary(rows: UsageRow[]): UsageTotals;
65
73
  export type UsageMetric = "tasks" | "tokensIn" | "tokensOut" | "costUsd";
66
74
  export type UsageGranularity = "hour" | "day";
67
- /** GET /v1/usage/series — 时间序列(UTC 桶;空桶不发行,消费方按需补零)。 */
75
+ /**
76
+ * GET /v1/usage/series — 时间序列(UTC 桶;空桶不发行,消费方按需补零)。
77
+ *
78
+ * 🔴 **行带 `estimated`**(S-228):与 {@link usageSummary} / {@link usageBreakdown} **同轴同谓词** ——
79
+ * 三个读面的桶都是同一只 {@link UsageTotals},由同一条 {@link foldRow} 折出来,所以「这个数字不精确」
80
+ * 的四个来源(旧行 fallback / per-model echo 缺键 / 缺 `usageBasis` 的 legacy 口径 / 行自陈
81
+ * `usageMissing` 的下界)在三面上是**同一句话**。修前只有 series 的出口把这一位扔了:桶里塞进一条
82
+ * 「没量到」的行,折线与一段真的低消耗在 wire 上逐字同形 —— 而那两件事的处置相反。
83
+ * 不新开判据、不加第二条轴:出口多投一个已经算好的位。
84
+ */
68
85
  export declare function usageSeries(rows: UsageRow[], metric: UsageMetric, granularity: UsageGranularity): Array<{
69
86
  t: string;
70
87
  v: number;
88
+ estimated: boolean;
71
89
  }>;
72
90
  export type UsageDimension = "principal" | "model";
73
91
  /** GET /v1/usage/breakdown — 按维度分桶。dimension=model 时按 per-model echo 展开(一行可贡献多个 model 桶;
@@ -17,8 +17,13 @@ function foldModelEntry(t, d) {
17
17
  if (d.usageBasis !== "uncached-components-v1")
18
18
  t.estimated = true;
19
19
  }
20
+ function markRowLowerBound(t, row) {
21
+ if (row.stats?.usageMissing === true)
22
+ t.estimated = true;
23
+ }
20
24
  function foldRow(t, row) {
21
25
  t.tasks += 1;
26
+ markRowLowerBound(t, row);
22
27
  const mu = row.stats?.modelUsage;
23
28
  if (mu && Object.keys(mu).length > 0) {
24
29
  for (const d of Object.values(mu))
@@ -49,7 +54,7 @@ export function usageSeries(rows, metric, granularity) {
49
54
  }
50
55
  return [...buckets.entries()]
51
56
  .sort((a, b) => a[0] - b[0])
52
- .map(([b, t]) => ({ t: new Date(b).toISOString(), v: metric === "costUsd" ? round6(t.costUsd) : t[metric] }));
57
+ .map(([b, t]) => ({ t: new Date(b).toISOString(), v: metric === "costUsd" ? round6(t.costUsd) : t[metric], estimated: t.estimated }));
53
58
  }
54
59
  export function usageBreakdown(rows, dimension) {
55
60
  const buckets = new Map();
@@ -70,6 +75,7 @@ export function usageBreakdown(rows, dimension) {
70
75
  const t = bucket(model);
71
76
  t.tasks += 1;
72
77
  foldModelEntry(t, d);
78
+ markRowLowerBound(t, r);
73
79
  }
74
80
  }
75
81
  else {
@@ -92,6 +98,8 @@ export function projectUsageStats(result) {
92
98
  const mu = stats.modelUsage;
93
99
  if (mu && typeof mu === "object" && !Array.isArray(mu))
94
100
  out.modelUsage = mu;
101
+ if (stats.usageMissing === true)
102
+ out.usageMissing = true;
95
103
  return out;
96
104
  }
97
105
  catch {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 这个申报名能用吗 —— 四个收口共用的**那一只**谓词。
3
+ *
4
+ * 严判:非串、空串、超 `TOOL_WIRE_NAME_MAX_CHARS` 一律不可用。本层不做截断/规范化 —— 截短一个名字
5
+ * 就是**换一个身份**(core 自己 7.13.0 起正是为此把截断改成拒),而被换掉身份的那台 server 上挂的
6
+ * allowTools / toolFaces 收窄声明会一起对不上号。
7
+ */
8
+ export declare function isUsableWireName(v: unknown): v is string;
9
+ /**
10
+ * 坏名的**可打印**摘要(进 warn 元数据)——中和 + 截长,绝不把任意长度的申报串原样回显进日志。
11
+ * 长度如实带上:运维要的是「我那个名字长了多少」,不是一屏乱码。
12
+ */
13
+ export declare function describeBadWireName(v: unknown): string;
14
+ //# sourceMappingURL=wire-name-bound.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { TOOL_WIRE_NAME_MAX_CHARS } from "@sema-agent/core";
2
+ export function isUsableWireName(v) {
3
+ return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS;
4
+ }
5
+ export function describeBadWireName(v) {
6
+ if (typeof v !== "string")
7
+ return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
8
+ if (v.length === 0)
9
+ return "empty";
10
+ const head = v.length > 32 ? `${JSON.stringify(v.slice(0, 32))}…` : JSON.stringify(v);
11
+ return `${head} (${v.length} chars > ${TOOL_WIRE_NAME_MAX_CHARS})`;
12
+ }
13
+ //# sourceMappingURL=wire-name-bound.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.71.0",
3
+ "version": "7.73.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": "7.13.0",
57
+ "@sema-agent/core": "7.16.0",
58
58
  "@sema-agent/settings-schema": "1.10.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",