@sema-agent/server 7.91.1 → 7.92.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 (71) hide show
  1. package/README.md +1 -1
  2. package/USAGE.md +9 -1
  3. package/dist/approval-ask-machine.d.ts +1 -1
  4. package/dist/approval-reconciler.d.ts +1 -1
  5. package/dist/approval.d.ts +1 -1
  6. package/dist/bench/s1/repair-oracle-adapter.d.ts +1 -1
  7. package/dist/bench/s1/runner-ctx.d.ts +1 -1
  8. package/dist/boot/runner-deps.d.ts +9 -9
  9. package/dist/capabilities/memory-notice.d.ts +27 -10
  10. package/dist/capabilities/memory-notice.js +21 -2
  11. package/dist/config-catalog.js +2 -1
  12. package/dist/config-center/types.d.ts +1 -1
  13. package/dist/config.d.ts +1 -1
  14. package/dist/env-facts.d.ts +1 -1
  15. package/dist/execution-lane-caps.d.ts +1 -1
  16. package/dist/file-history-disclosure.d.ts +2 -2
  17. package/dist/fleet/fleet-bus.d.ts +7 -1
  18. package/dist/fleet/fleet-bus.js +5 -3
  19. package/dist/http/admission.d.ts +1 -1
  20. package/dist/http/idempotency.d.ts +27 -0
  21. package/dist/http/route-ctx.d.ts +2 -5
  22. package/dist/http/routes/memory-compliance.d.ts +1 -1
  23. package/dist/http/routes/memory-origin.d.ts +2 -2
  24. package/dist/http/routes/rules.js +3 -1
  25. package/dist/http/routes/runs.js +3 -6
  26. package/dist/http/routes/session-sync.js +3 -2
  27. package/dist/http/routes/sessions.js +3 -2
  28. package/dist/http/routes/tasks.js +35 -19
  29. package/dist/http/wire-types.d.ts +29 -2
  30. package/dist/leader/wire.d.ts +1 -1
  31. package/dist/lsp/manager.d.ts +2 -2
  32. package/dist/memory-operator-faces.d.ts +13 -13
  33. package/dist/memory-scope.d.ts +2 -2
  34. package/dist/observability/fail-open.d.ts +1 -1
  35. package/dist/observability/fail-open.js +1 -1
  36. package/dist/observability/metrics.js +2 -1
  37. package/dist/orchestration/workflow-agent-session-index.d.ts +2 -2
  38. package/dist/peer-directory.d.ts +3 -2
  39. package/dist/permission-rule-vocab.d.ts +16 -1
  40. package/dist/plugins/file-run-store.d.ts +1 -1
  41. package/dist/plugins/local-session-store.d.ts +1 -1
  42. package/dist/plugins/memory-session-policy-store.d.ts +22 -3
  43. package/dist/plugins/memory-session-policy-store.js +11 -4
  44. package/dist/plugins/permission-rule-store-sql.js +16 -7
  45. package/dist/plugins/pg-pool.js +11 -0
  46. package/dist/plugins/session-policy-store-sql.d.ts +51 -3
  47. package/dist/plugins/session-policy-store-sql.js +77 -38
  48. package/dist/plugins/sql-errors.d.ts +25 -0
  49. package/dist/plugins/sql-errors.js +10 -0
  50. package/dist/plugins/store-backend.d.ts +5 -5
  51. package/dist/plugins/store-contracts.d.ts +2 -2
  52. package/dist/plugins/tidb-pool.d.ts +1 -1
  53. package/dist/plugins/tidb-pool.js +19 -0
  54. package/dist/rules-consent.d.ts +16 -9
  55. package/dist/rules-consent.js +14 -35
  56. package/dist/run-cancel-context.d.ts +1 -1
  57. package/dist/run-local.d.ts +1 -1
  58. package/dist/runtime-caps-resolver.d.ts +10 -10
  59. package/dist/runtime-governance.d.ts +2 -2
  60. package/dist/session-policy-wire.d.ts +25 -1
  61. package/dist/session-policy-wire.js +11 -0
  62. package/dist/spec-fields.d.ts +4 -4
  63. package/dist/task-cwd.d.ts +1 -1
  64. package/dist/task-settings.d.ts +2 -2
  65. package/dist/tool-approval.d.ts +1 -1
  66. package/dist/trace/core-keyset-guard.d.ts +11 -5
  67. package/dist/trace/project.d.ts +41 -1
  68. package/dist/trace/project.js +17 -1
  69. package/dist/trace/redact.d.ts +1 -1
  70. package/dist/trace/redact.js +44 -17
  71. package/package.json +3 -3
@@ -63,4 +63,15 @@ export function describeSessionRulesRecordProblem(p) {
63
63
  }
64
64
  }
65
65
  }
66
+ export function sessionPolicyErrorHttp(code) {
67
+ switch (code) {
68
+ case "conflict": return { status: 409, errorCode: "conflict" };
69
+ case "loosen_forbidden": return { status: 403, errorCode: "loosen_forbidden" };
70
+ case "corrupt": return { status: 500, errorCode: "corrupt" };
71
+ default: {
72
+ const unhandled = code;
73
+ throw new Error(`unhandled SessionPolicyError code: ${JSON.stringify(unhandled)}`);
74
+ }
75
+ }
76
+ }
66
77
  //# sourceMappingURL=session-policy-wire.js.map
@@ -65,15 +65,15 @@ export declare function taskAgentsFromBody(raw: unknown, requirePrincipal: boole
65
65
  /** [ref]②/[ref](cli 认领消费)—— fresh-submit 的 agents[].model **目录成员资格**门(形状门
66
66
  * {@link validateTaskAgents} 通过之后跑;调用方铸 400 `request.unknown_reference` + `available` 枚举)。
67
67
  * 病灶=报错太晚太远:server 持目录却不查,错到 core prepare 才炸 `config.agent.unknown_model`
68
- * (dist/agents/subagent.js:1258),以「run failed」形出现而非 400;cli resolver 只挡词法形,目录
68
+ * (dist/agents/subagent.js),以「run failed」形出现而非 400;cli resolver 只挡词法形,目录
69
69
  * 成员资格只有 server 持有。
70
70
  * 三条判定纪律(全部 core dist 亲读):
71
- * - **只查 string 形**:core 只对 `typeof a.model === "string"` 走 `resolveModel`(subagent.js:1253);
71
+ * - **只查 string 形**:core 只对 `typeof a.model === "string"` 走 `resolveModel`(subagent.js);
72
72
  * Model 对象形 core 原样信任不查目录([ref]④ 契约:自带完整 Model 配置,其 id 本就不必在目录)——
73
73
  * 对象形没有「晚失败」病灶,这里同拒会是射程外的行为收紧,不做。
74
74
  * - **`catalog` 必须是 tier 展开形**(调用方传 `expandTiers(config.models, config.tiers) ?? config.models`,
75
75
  * resolve-spec.ts wireCatalog 同源纪律):core Runner 构造期把 deps.models 换成展开目录
76
- * (runtask.js:1472),agentCatalog.models 即它 —— 裸 config.models 会把合法档位词/CC 别名误杀 400。
76
+ * (runtask.js),agentCatalog.models 即它 —— 裸 config.models 会把合法档位词/CC 别名误杀 400。
77
77
  * - **判定=`Object.hasOwn` 纯键查**,刻意不用 `matchCatalogModel`:那是 body.model 门的四形判
78
78
  * (name/tier/alias/**id**),而 core 的 `resolveModel` 是纯键查(roles.js:`models?.[ref]`)——
79
79
  * 多认 id 形会放行一个 core prepare 必炸的值(门假绿,病灶照旧)。hasOwn 顺带挡原型链键。
@@ -111,7 +111,7 @@ export declare function toolNameListFromBody(raw: unknown): string[] | undefined
111
111
  * ⚠️ **这不是一条 defer 注**([ref] 件⑤ 对表结论,core [ref] 同判):本段说的是 `promptProfile` 这根
112
112
  * **呈现**轴,它与 `deferMode` / `deferTools` 的延迟披露轴**无耦合** —— 上一行提 deferTools 只是在借它
113
113
  * 的「无租户门」姿势作类比。「引擎缺省 simple」这句本身在 core 5.40.0 上仍逐字为真
114
- * (`core/types.d.ts:133`:default "simple"),本批亲验未变。
114
+ * (`core/types.d.ts`:default "simple"),本批亲验未变。
115
115
  * 真正的 defer 轴对表(core 5.40.0 起 `deferMode:"auto"` 不再 sweep 内建;7.15.0 起它不再是第二道门,
116
116
  * 只是把候选集拓宽后走同一道阈值门)记在
117
117
  * `src/capabilities/tool-defer.ts` 的头注 —— 别把两根轴的结论写到一处。
@@ -35,7 +35,7 @@ export declare function normalizeAbsolutePathLexically(p: string): string;
35
35
  * **词法**判域:`dir` 是不是 `target` 本身或它的祖先(= `target` ⊆ `dir`,「等于 ∪ 后代」)。
36
36
  *
37
37
  * 这是 core `assertExemptDirsDoNotCoverRoot` 那条律的本仓同形(亲读安装树
38
- * `dist/core/fs-write-gate-policy.js:11-28` → `swallowedFenceRoot` → `isWithin(root, p)`:
38
+ * `dist/core/fs-write-gate-policy.js` → `swallowedFenceRoot` → `isWithin(root, p)`:
39
39
  * 先各自词法归一,再判 `p === root` 或 `p` 以 `root + "/"` 起头)。**两端都只做词法**,不 realpath ——
40
40
  * 写门的构造期本来就没有 I/O(core 同理),承诺 canonical 是承诺一件此刻做不到的事。
41
41
  *
@@ -396,8 +396,8 @@ export interface FsWriteGateWiring {
396
396
  * `boot/resolve-spec.ts` / `boot/parked-revive-gate.ts` 把**同一个值**递成本字段。两处同源是承重的:
397
397
  * 各算一份就会出现「422 门放行而引擎判 unreadable」或反过来。
398
398
  *
399
- * 🔴 **S-213⑦ 改真话**(亲读 core 7.12.0 `dist/core/permission-rule-model.js:365-372` /
400
- * `:340-350`,推翻本注此前的两句):
399
+ * 🔴 **S-213⑦ 改真话**(亲读 core 7.12.0 `dist/core/permission-rule-model.js` /
400
+ * 的 `resolvePathPattern`,推翻本注此前的两句):
401
401
  * · core 对缺席的 `home` **不**回落 `os.homedir()` —— `resolvePathPattern` 走
402
402
  * `lexicalNormalAbsolutePathOf(baseValueOf("home", bases) ?? "")`,base 缺席 ⇒ `undefined` ⇒ 直接回
403
403
  * `{missingBase:"home"}`,该调用的射程判 `unreadable`(fail-closed 的 ask,人来清),从不猜一个目录。
@@ -653,7 +653,7 @@ export declare function resolveStreamApprovalGate(input: StreamApprovalGateInput
653
653
  *
654
654
  * 三源(§8.3)里本仓可执行的是后两源;第一源 `forceDurableGate` 在 HTTP 装配时刻不可解析(§14 §12-5
655
655
  * 亲裁:不可读则该源不做,core 自身的 `runtimeCaps?.forceDurableGate` 消费臂结构性兜底——7.0.1
656
- * `prepare-task.js` 的 durableMandate/contentMandate(:1415/:1417)与 durableQuestionFace(:2136)等),
656
+ * `prepare-task.js` 的 durableMandate/contentMandate 与 durableQuestionFace 等),
657
657
  * 故不在本函数内。
658
658
  *
659
659
  * 🔴 **[ref](半接线旋钮族)**:`windowZero` 的第一源(`STREAM_ASK_WINDOW_MS=0`)自此**不再挂在
@@ -36,7 +36,7 @@ type _GuardProvenanceReverse = AssertAllKeysHandled<Exclude<ProvenanceProjected
36
36
  type AgentMessageProjected = "from" | "body";
37
37
  type _GuardAgentMessage = AssertAllKeysHandled<Exclude<keyof NonNullable<TaskNotificationPayload["agentMessage"]>, AgentMessageProjected>>;
38
38
  type _GuardAgentMessageReverse = AssertAllKeysHandled<Exclude<AgentMessageProjected, keyof NonNullable<TaskNotificationPayload["agentMessage"]>>>;
39
- type BgNotifProjected = "taskId" | "sessionId" | "seq" | "status" | "summary" | "stoppedBy" | "resumable" | "recentSteps" | "editedFiles" | "usage" | "transcriptId" | "rootSessionId" | "parentTaskId" | "parentToolCallId" | "completionId";
39
+ type BgNotifProjected = "taskId" | "sessionId" | "seq" | "status" | "summary" | "stoppedBy" | "resumable" | "recentSteps" | "editedFiles" | "usage" | "transcriptId" | "rootSessionId" | "parentTaskId" | "parentToolCallId" | "completionId" | "spawnName";
40
40
  type BgNotifExcluded = "kind" | "sessionScoped" | "owner" | "scope" | "description" | "agentType" | "name" | "currentAction" | "currentTool" | "parentSessionId" | "startedAt" | "workflowRunId" | "progressTaskId" | "progressParentTaskId" | "model";
41
41
  type _GuardBgNotif = AssertAllKeysHandled<Exclude<keyof BackgroundChildEvent, BgNotifProjected | BgNotifExcluded>>;
42
42
  type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNotifExcluded, keyof BackgroundChildEvent>>;
@@ -94,9 +94,13 @@ type _GuardWiringFleet = AssertAllKeysHandled<Exclude<keyof WiringSection<"fleet
94
94
  type _GuardWiringFleetReverse = AssertAllKeysHandled<Exclude<WiringFleetHandled, keyof WiringSection<"fleet">>>;
95
95
  type WiringMcpEntry = WiringSection<"mcp">[number];
96
96
  type WiringMcpExcluded = "error";
97
- type WiringMcpHandled = "name" | "source" | "status" | "errorCode" | "delivered" | "httpStatus" | "toolCount" | WiringMcpExcluded;
97
+ type WiringMcpHandled = "name" | "source" | "status" | "errorCode" | "delivered" | "httpStatus" | "toolCount" | "liveness" | WiringMcpExcluded;
98
98
  type _GuardWiringMcp = AssertAllKeysHandled<Exclude<keyof WiringMcpEntry, WiringMcpHandled>>;
99
99
  type _GuardWiringMcpReverse = AssertAllKeysHandled<Exclude<WiringMcpHandled, keyof WiringMcpEntry>>;
100
+ type WiringMcpLiveness = NonNullable<WiringMcpEntry["liveness"]>;
101
+ type WiringMcpLivenessHandled = "state" | "observedAt" | "errorCode";
102
+ type _GuardWiringMcpLiveness = AssertAllKeysHandled<Exclude<keyof WiringMcpLiveness, WiringMcpLivenessHandled>>;
103
+ type _GuardWiringMcpLivenessReverse = AssertAllKeysHandled<Exclude<WiringMcpLivenessHandled, keyof WiringMcpLiveness>>;
100
104
  type WiringRulesHandled = "storeWired" | "syncWired" | "orgGoverned";
101
105
  type _GuardWiringRules = AssertAllKeysHandled<Exclude<keyof WiringSection<"permissionRules">, WiringRulesHandled>>;
102
106
  type _GuardWiringRulesReverse = AssertAllKeysHandled<Exclude<WiringRulesHandled, keyof WiringSection<"permissionRules">>>;
@@ -180,12 +184,12 @@ type _GuardMcpReconnect = AssertAllKeysHandled<Exclude<keyof McpReconnectResult,
180
184
  type _NoDoubleBookMcpReconnect = AssertAllKeysHandled<Extract<McpReconnectProjected, McpReconnectWithheld>>;
181
185
  type _GuardMcpReconnectReverse = AssertAllKeysHandled<Exclude<McpReconnectProjected | McpReconnectWithheld, keyof McpReconnectResult>>;
182
186
  type McpRedialStatusProjected = "name" | "status" | "errorCode" | "httpStatus" | "delivered" | "serverInfo" | "error" | "transportClosed";
183
- type McpRedialStatusWithheld = "toolNames";
187
+ type McpRedialStatusWithheld = "toolNames" | "liveness";
184
188
  type _GuardMcpRedialStatus = AssertAllKeysHandled<Exclude<keyof McpServerStatus, McpRedialStatusProjected | McpRedialStatusWithheld>>;
185
189
  type _NoDoubleBookMcpRedialStatus = AssertAllKeysHandled<Extract<McpRedialStatusProjected, McpRedialStatusWithheld>>;
186
190
  type _GuardMcpRedialStatusReverse = AssertAllKeysHandled<Exclude<McpRedialStatusProjected | McpRedialStatusWithheld, keyof McpServerStatus>>;
187
191
  type McpPanelStatusProjected = "name" | "status" | "serverInfo" | "toolNames" | "error";
188
- type McpPanelStatusWithheld = "errorCode" | "httpStatus" | "delivered" | "transportClosed";
192
+ type McpPanelStatusWithheld = "errorCode" | "httpStatus" | "delivered" | "transportClosed" | "liveness";
189
193
  type _GuardMcpPanelStatus = AssertAllKeysHandled<Exclude<keyof McpServerStatus, McpPanelStatusProjected | McpPanelStatusWithheld>>;
190
194
  type _NoDoubleBookMcpPanelStatus = AssertAllKeysHandled<Extract<McpPanelStatusProjected, McpPanelStatusWithheld>>;
191
195
  type _GuardMcpPanelStatusReverse = AssertAllKeysHandled<Exclude<McpPanelStatusProjected | McpPanelStatusWithheld, keyof McpServerStatus>>;
@@ -288,7 +292,9 @@ export type CoreKeysetGuards = [
288
292
  _NoDoubleBookMcpPanelStatus,
289
293
  _GuardMcpPanelStatusReverse,
290
294
  _GuardMcpListingIncomplete,
291
- _GuardMcpListingIncompleteReverse
295
+ _GuardMcpListingIncompleteReverse,
296
+ _GuardWiringMcpLiveness,
297
+ _GuardWiringMcpLivenessReverse
292
298
  ];
293
299
  export {};
294
300
  //# sourceMappingURL=core-keyset-guard.d.ts.map
@@ -331,7 +331,9 @@ export declare function toolDisclosureEventData(ev: {
331
331
  /** The `task_progress` payload (a subagent's live progress tick) — EXPLICIT WHITELIST shared by all
332
332
  * three legs (bg runs.ts + resume append + the sync live SSE frame): taskId/parent linkage/token rollup verbatim,
333
333
  * `name` UNTRUSTED (core sanitizes for rendering, does NOT secret-redact — a taskName can be secret-shaped) →
334
- * `redactSecrets`. Never the delegated objective/args. E2 identity rides (eventId/
334
+ * `redactSecrets`; `spawnName` is the TRUSTED-chain teammate KEY beside it (core 7.25.0 [ref] S0 — validated at the
335
+ * spawn, so it is an identity, not a label) and rides VERBATIM: redacting an identity key does not make it safe,
336
+ * it makes it WRONG (two legal names collapsing onto one value — see the key's own note). Never the delegated objective/args. E2 identity rides (eventId/
335
337
  * parentToolCallId are core-mint uuidv7 identifiers, not content — consumers bind concurrent subagent ticks
336
338
  * exactly by them; the 1.72 whitelist dropped them, leaving a FIFO heuristic). */
337
339
  export declare function taskProgressEventData(ev: {
@@ -339,6 +341,7 @@ export declare function taskProgressEventData(ev: {
339
341
  taskType?: string;
340
342
  parentTaskId?: string;
341
343
  name?: string;
344
+ spawnName?: string;
342
345
  status?: string;
343
346
  usage?: unknown;
344
347
  currentAction?: string;
@@ -585,6 +588,22 @@ export declare function workspaceChangedEventData(ev: {
585
588
  * (「connection state is a fact about this leg's attempt, not about the assembly」)。
586
589
  * ⚠️ 空数组 ≠ 缺席:core 顶注逐字「an empty array is "this leg declared no servers", absence is an older
587
590
  * mint or an external derivation」⇒ 空数组照铸,别折成缺席。
591
+ * 🔴 **⑤ `liveness`(core 7.24.3 [ref] / S-402 提货件)—— 裁:租户可见,整条记录投,逐键挑**。
592
+ * `{state, observedAt, errorCode?}` = 引擎**最后一次观察**到的「这台还够得着吗」。受众判据与同行
593
+ * 其余键**同一条**(同主体同受众、引擎派生的闭形值、非 governance 侧信道 —— 它同样不进
594
+ * `configFingerprint`),所以这一格不需要新的裁定,只需要新的**读法**。
595
+ * 🔴 **契约一行(client-core CC-36 的那句话):活性位独立于名册申报与 re-dial 结果。**
596
+ * `status` 是拨号那一刻的判决且 core **有意冻结**(服务器中途死了仍读 `connected`);re-dial 回执的
597
+ * `outcome` 是一次动作的判词;这一格是连接层最后学到的事,自带时戳。三者**合法地可以不一致** ——
598
+ * `status:"failed"` 与 `liveness.state:"reachable"` 同行是真行(服务器答了握手、答的是协议错:
599
+ * 活着,且配错了)。⇒ 本仓**不合并、不互相校验、不据一个推另一个**(与 ① 里「两条 MCP 读面同名
600
+ * 不同源、合并只会造出『哪个才算数』」逐字同源)。
601
+ * ⚠️ **缺席是第四读数,不是一个词**:core 顶注逐字「a consumer must not fold absence into
602
+ * `unknown`」——`unknown` 是**更强**的断言(引擎看了、看不出来)。
603
+ * 🔴 **但本仓出口上的「缺席」比 core 源端的更宽**(codex r1 [medium] 采纳):core 那边是
604
+ * 压根没有观察(`skipped` 的行没走到 transport / `invalid_config` 的声明从没被拨过 / 老 mint),
605
+ * 而经过本投影后**判形没过也落缺席**(词出闭集 / `observedAt` 非有限数 / 整只不是对象)。
606
+ * ⇒ 对消费端,缺席只能读成「**没有可用的观察记录**」,**不许**据它反推这条腿拨没拨过号。
588
607
  * · `writeProtection`(`{ targetView: "spelling-only" | "target" }`;core 7.20.1 [ref] / clay [ref] 丙,
589
608
  * S-361 提货件)—— **裁:租户可见,逐字透传**。它是 per-leg 的读面:「这条腿的写保护判官,拿到的是
590
609
  * 哪一种目标读法」——`"target"` = 判官握着执行环境,拼法没命中时会在**解析后的真目标**上再判一次;
@@ -699,6 +718,27 @@ export declare function wiringManifestOperatorEventData(ev: {
699
718
  eventId?: unknown;
700
719
  parentToolCallId?: unknown;
701
720
  }): Record<string, unknown>;
721
+ /**
722
+ * `wiring_manifest` 的**唯一分叉点**(S-528)—— 两条知道调用方身份的 live 消费腿共用它:
723
+ * `/v1/tasks/stream` 的 SSE 帧,与 `POST /v1/tasks`(非流式)200 体里的 `wiringManifest` 起手回执。
724
+ *
725
+ * 🔴 **为什么是一只函数,而不是两处 `cond ? operator : tenant`**:那个三元判的是「这份产物给谁看」——
726
+ * 一件治理面的事([ref] 件一)。写两份就有漂的位置:core 往 operator 面加第 N 段时,跟上的那一处与没跟上
727
+ * 的那一处会让**同一次提交**在两条腿上投出两种宽度,而错的方向是把 operator-only 面发给租户。两条腿从此
728
+ * 只认这一只,新的第三条腿照抄它即可(禁再写一次三元)。
729
+ *
730
+ * 判据本身(`explicitOperatorOk`)仍由调用腿**每请求求一次**再传进来:身份解析要读请求凭据与部署名单,
731
+ * 那是 http 层的事,本文件(投影层)不认识 `req`,也不该认识。
732
+ *
733
+ * `operatorFace` 的两个值都是**正面事实**,没有第三态(所以是 boolean 不是三值):
734
+ * · `true` = 本请求的调用方经 `explicitOperatorOk` 判为 operator ⇒ 投 {@link wiringManifestOperatorEventData};
735
+ * · `false` = 其余全部(**含**「本部署根本没配 operator 名单」)⇒ 投 {@link wiringManifestEventData} 的租户形。
736
+ */
737
+ export declare function wiringManifestFaceEventData(ev: {
738
+ manifest?: unknown;
739
+ eventId?: unknown;
740
+ parentToolCallId?: unknown;
741
+ }, operatorFace: boolean): Record<string, unknown>;
702
742
  /**
703
743
  * `human_input`(core 5.14.0 [ref] 新 `TaskEvent` 臂)—— 「谁把什么喂进了这条 run」的生命周期账本
704
744
  * 帧(objective / 实时 steer / nextTurn / parked-steer 续跑 / wake 五个载体共用一个投影点)。
@@ -1,5 +1,5 @@
1
1
  import { redactErrorMessage, redactModelText } from "../observability/run-terminal.js";
2
- import { MCP_DELIVERY_VERDICTS, screenGateOutcome, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
2
+ import { MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, screenGateOutcome, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
3
3
  import { Value } from "typebox/value";
4
4
  import { recordFailOpen } from "../observability/fail-open.js";
5
5
  import { redactSecrets, redactDeep } from "./redact.js";
@@ -273,6 +273,7 @@ export function taskProgressEventData(ev) {
273
273
  ...(typeof ev.taskType === "string" && ev.taskType.length > 0 ? { taskType: ev.taskType } : {}),
274
274
  ...(ev.parentTaskId ? { parentTaskId: ev.parentTaskId } : {}),
275
275
  ...(ev.name ? { name: redactSecrets(ev.name) } : {}),
276
+ ...(ev.spawnName ? { spawnName: ev.spawnName } : {}),
276
277
  ...(typeof ev.model === "string" && ev.model.length > 0 ? { model: ev.model } : {}),
277
278
  ...(ev.status !== undefined ? { status: ev.status } : {}),
278
279
  ...(ev.usage ? { usage: ev.usage } : {}),
@@ -507,6 +508,17 @@ export function wiringManifestEventData(ev) {
507
508
  const status = str(e.status);
508
509
  if (name === undefined || status === undefined)
509
510
  continue;
511
+ const liveness = (() => {
512
+ const rec = sec(e.liveness);
513
+ if (rec === undefined)
514
+ return undefined;
515
+ const state = str(rec.state);
516
+ const observedAt = num(rec.observedAt);
517
+ if (state === undefined || !MCP_LIVENESS_STATES.includes(state) || observedAt === undefined)
518
+ return undefined;
519
+ const errorCode = str(rec.errorCode);
520
+ return { state, observedAt, ...(errorCode === undefined ? {} : { errorCode }) };
521
+ })();
510
522
  rows.push({
511
523
  name,
512
524
  status,
@@ -515,6 +527,7 @@ export function wiringManifestEventData(ev) {
515
527
  ...(str(e.errorCode) !== undefined ? { errorCode: e.errorCode } : {}),
516
528
  ...(typeof e.delivered === "string" && MCP_DELIVERY_VERDICTS.includes(e.delivered) ? { delivered: e.delivered } : {}),
517
529
  ...(num(e.httpStatus) !== undefined ? { httpStatus: e.httpStatus } : {}),
530
+ ...(liveness !== undefined ? { liveness } : {}),
518
531
  });
519
532
  }
520
533
  return rows;
@@ -585,6 +598,9 @@ export function wiringManifestOperatorEventData(ev) {
585
598
  ...(str(m.configFingerprint) !== undefined ? { configFingerprint: m.configFingerprint } : {}),
586
599
  };
587
600
  }
601
+ export function wiringManifestFaceEventData(ev, operatorFace) {
602
+ return operatorFace ? wiringManifestOperatorEventData(ev) : wiringManifestEventData(ev);
603
+ }
588
604
  export function humanInputEventData(ev) {
589
605
  const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
590
606
  const a = typeof ev.actor === "object" && ev.actor !== null && !Array.isArray(ev.actor) ? { ...ev.actor } : {};
@@ -74,7 +74,7 @@ export declare function resetPpkScanBytesForTest(): void;
74
74
  *
75
75
  * 🔴 **词义如实**(codex r1 [medium] 三,采纳):这一维说的是「命中的是**哪一条臂**」,**不是**
76
76
  * 「这段文本真的被上游截过」—— server 收到的是纯字节,core 的 `clip()` 是 `slice(0, 80)` **不留标记**
77
- * (亲读 `@sema-agent/core/dist/agents/subagent-steps.js:9-11`),截过与天生就短在 wire 上不可区分。
77
+ * (亲读 `@sema-agent/core/dist/agents/subagent-steps.js`),截过与天生就短在 wire 上不可区分。
78
78
  * 所以一个 `arm="fragment"` 计数可能来自残片,也可能来自一个恰好落在阈值之上的 look-alike;
79
79
  * core [ref] 落地后这一支**趋零是证据、不归零不是证明**。别把它当截断事实的度量用。 */
80
80
  /**
@@ -590,9 +590,11 @@ function collectStickySpans(a, text, at, out) {
590
590
  }
591
591
  }
592
592
  const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
593
+ const LEXICAL_KEYWORD_ARMS = new Set();
593
594
  const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => {
594
595
  const re = new RegExp(`(?:${keywordSrc}\\s+${atLeast(valueClass, min)}${tailSrc}` +
595
596
  `|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${atLeast(valueClass, min)}${tailSrc})`, "g");
597
+ LEXICAL_KEYWORD_ARMS.add(re);
596
598
  return re;
597
599
  };
598
600
  const CREDENTIAL_NAME_WORDS = [
@@ -1515,6 +1517,7 @@ const armPool = (() => {
1515
1517
  rank,
1516
1518
  order,
1517
1519
  phase: 1,
1520
+ lexical: LEXICAL_KEYWORD_ARMS.has(re),
1518
1521
  collect: boundary === undefined
1519
1522
  ? (text, _json, _observe, out) => collectRegexSpans(arm, text, out)
1520
1523
  : (text, _json, _observe, out) => collectValueArmSpans(boundary, text, out),
@@ -1530,6 +1533,7 @@ const armPool = (() => {
1530
1533
  rank: ARM_RANK.structural,
1531
1534
  order,
1532
1535
  phase,
1536
+ lexical: false,
1533
1537
  collect: (text, json, observe, out) => scan(text, json, observe, (start, end) => out.push({ start, end, repl, rank: ARM_RANK.structural, order })),
1534
1538
  retry: undefined,
1535
1539
  headSrc: undefined,
@@ -1554,14 +1558,32 @@ const armPool = (() => {
1554
1558
  members.push(regexMember(row, ARM_RANK.shape, members.length, false));
1555
1559
  for (const row of FRAGMENT_SECRET_PATTERNS)
1556
1560
  members.push(regexMember(row, ARM_RANK.fragment, members.length, false));
1561
+ for (const [re] of KEYWORD_SECRET_PATTERNS) {
1562
+ if (!LEXICAL_KEYWORD_ARMS.has(re))
1563
+ throw new Error(`redact: keyword arm /${re.source}/ is not registered as lexical — build it with keywordArm() so the bounded-unit scope can exclude it`);
1564
+ }
1557
1565
  const retryMembers = members.filter((m) => m.retry !== undefined);
1558
1566
  const phase1 = members.filter((m) => m.phase === 1);
1559
1567
  const phase2 = members.filter((m) => m.phase === 2);
1568
+ const laneOf = (scope) => {
1569
+ const inScope = (m) => {
1570
+ switch (scope) {
1571
+ case "free-text": return true;
1572
+ case "bounded-unit": return !m.lexical;
1573
+ }
1574
+ };
1575
+ return { phase1: phase1.filter(inScope), phase2: phase2.filter(inScope), retry: retryMembers.filter(inScope) };
1576
+ };
1577
+ const LANES = {
1578
+ "free-text": laneOf("free-text"),
1579
+ "bounded-unit": laneOf("bounded-unit"),
1580
+ };
1560
1581
  const headScreen = new RegExp(`(?:${retryMembers.map((m) => m.headSrc).join("|")})`, "y");
1561
1582
  const headFirstUnit = unionMembers(retryMembers.map((m) => m.headFirst)) ?? new Uint32Array(0x10000 >>> 5).fill(0xffffffff);
1562
1583
  poolArmCount = members.length;
1563
1584
  poolBoundaryArmCount = retryMembers.length;
1564
- return (text, wantCount, json, observe) => {
1585
+ return (text, wantCount, json, observe, scope) => {
1586
+ const lane = LANES[scope];
1565
1587
  const spans = [];
1566
1588
  let tried;
1567
1589
  const freshCovered = (from) => {
@@ -1586,25 +1608,25 @@ const armPool = (() => {
1586
1608
  return at.sort((x, y) => x - y);
1587
1609
  };
1588
1610
  const closeCoveragePositions = (from) => {
1589
- if (retryMembers.length === 0 || spans.length === from)
1611
+ if (lane.retry.length === 0 || spans.length === from)
1590
1612
  return;
1591
1613
  for (let at = freshCovered(from); at.length > 0;) {
1592
1614
  const before = spans.length;
1593
- for (const m of retryMembers)
1615
+ for (const m of lane.retry)
1594
1616
  m.retry(text, at, spans);
1595
1617
  at = freshCovered(before);
1596
1618
  }
1597
1619
  };
1598
1620
  setScanEvidence(NO_CREDENTIAL_SPANS);
1599
- for (const m of phase1)
1621
+ for (const m of lane.phase1)
1600
1622
  m.collect(text, json, observe, spans);
1601
1623
  closeCoveragePositions(0);
1602
- if (phase2.length > 0) {
1624
+ if (lane.phase2.length > 0) {
1603
1625
  let evidence = credentialSpans(text, spans, false);
1604
1626
  for (let level = 0;; level += 1) {
1605
1627
  const before = spans.length;
1606
1628
  setScanEvidence(evidence);
1607
- for (const m of phase2)
1629
+ for (const m of lane.phase2)
1608
1630
  m.collect(text, json, observe && level === 0, spans);
1609
1631
  closeCoveragePositions(before);
1610
1632
  forensicLevels += 1;
@@ -1646,8 +1668,8 @@ let onRedaction;
1646
1668
  export function setRedactionObserver(fn) {
1647
1669
  onRedaction = fn;
1648
1670
  }
1649
- function applyRules(text, wantCount, json, observe) {
1650
- return armPool(text, wantCount, json, observe);
1671
+ function applyRules(text, wantCount, json, observe, scope) {
1672
+ return armPool(text, wantCount, json, observe, scope);
1651
1673
  }
1652
1674
  function reportHits(hits) {
1653
1675
  if (onRedaction === undefined)
@@ -1660,20 +1682,20 @@ function reportHits(hits) {
1660
1682
  }
1661
1683
  }
1662
1684
  }
1663
- function runRedaction(text, observe, json, wantHits) {
1685
+ function runRedaction(text, observe, json, wantHits, scope) {
1664
1686
  const wantCount = wantHits || (onRedaction !== undefined && observe);
1665
1687
  const normalized = normalizeForRedaction(text);
1666
- const r = applyRules(normalized, wantCount, json, observe);
1688
+ const r = applyRules(normalized, wantCount, json, observe, scope);
1667
1689
  forgetEscapedSeparatorTails();
1668
1690
  if (observe)
1669
1691
  reportHits(r.hits);
1670
1692
  return { text: r.out, hits: r.hits, spans: r.kept };
1671
1693
  }
1672
1694
  export function redactSecrets(text, opts) {
1673
- return runRedaction(text, opts?.observe ?? true, opts?.json === true, false).text;
1695
+ return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text").text;
1674
1696
  }
1675
1697
  export function redactSecretsWithArms(text, opts) {
1676
- const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true);
1698
+ const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text");
1677
1699
  return { text: r.text, arms: new Set(r.hits.map((h) => h.arm)) };
1678
1700
  }
1679
1701
  const REDACT_MAX_DEPTH = 64;
@@ -1752,6 +1774,9 @@ function decodeToFixedPoint(s, maxRounds = 3) {
1752
1774
  }
1753
1775
  return { text: cur, residue: cur.includes("%") };
1754
1776
  }
1777
+ const MARKER_CHARS = ["«", "»"];
1778
+ const MARKER_BYTE_SPELLINGS = MARKER_CHARS.map((ch) => String.fromCharCode(...new TextEncoder().encode(ch)));
1779
+ const carriesRedactionMarker = (spelled, byteView) => MARKER_CHARS.some((ch) => spelled.includes(ch)) || MARKER_BYTE_SPELLINGS.some((b) => byteView.includes(b));
1755
1780
  const VIEW_FILLER_PREFERENCE = ["a", "z", "_", "-", "0"];
1756
1781
  let armValueClassViewsCache;
1757
1782
  function armValueClassViews() {
@@ -1772,13 +1797,15 @@ function boundedUnitView(unit, inClass, filler) {
1772
1797
  }
1773
1798
  function unitCarriesCredential(unit) {
1774
1799
  const decoded = decodeToFixedPoint(unit);
1800
+ if (carriesRedactionMarker(unit, decoded.text))
1801
+ return { hit: false, unreadable: true };
1775
1802
  const views = new Set();
1776
1803
  for (const spelling of [unit, decoded.text])
1777
1804
  for (const v of armValueClassViews())
1778
1805
  views.add(boundedUnitView(spelling, v.inClass, v.filler));
1779
1806
  views.delete("");
1780
- const hit = [...views].some((view) => redactSecrets(view) !== view);
1781
- return { hit, residue: decoded.residue };
1807
+ const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit").text !== view);
1808
+ return { hit, unreadable: decoded.residue };
1782
1809
  }
1783
1810
  function displaySafePathname(pathname) {
1784
1811
  const parts = pathname.split("/");
@@ -1788,7 +1815,7 @@ function displaySafePathname(pathname) {
1788
1815
  if (seg === "")
1789
1816
  continue;
1790
1817
  const verdict = unitCarriesCredential(seg);
1791
- if (!verdict.hit && !verdict.residue)
1818
+ if (!verdict.hit && !verdict.unreadable)
1792
1819
  continue;
1793
1820
  if (isAllRedactionTokens(redactSecrets(seg)))
1794
1821
  continue;
@@ -1808,7 +1835,7 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
1808
1835
  const boundary = normalized.indexOf("?");
1809
1836
  if (boundary < 0)
1810
1837
  return false;
1811
- if (runRedaction(canonical, false, false, false).spans.some((sp) => sp.end > boundary))
1838
+ if (runRedaction(canonical, false, false, false, "free-text").spans.some((sp) => sp.end > boundary))
1812
1839
  return true;
1813
1840
  for (const unit of normalized.slice(boundary + 1).split("&").flatMap((sg) => {
1814
1841
  const eq = sg.indexOf("=");
@@ -1817,7 +1844,7 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
1817
1844
  if (unit === "")
1818
1845
  continue;
1819
1846
  const verdict = unitCarriesCredential(unit);
1820
- if (verdict.hit || verdict.residue)
1847
+ if (verdict.hit || verdict.unreadable)
1821
1848
  return true;
1822
1849
  const decoded = decodeToFixedPoint(unit).text;
1823
1850
  if (decoded.includes("://") || decoded.includes("@"))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.91.1",
3
+ "version": "7.92.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",
@@ -59,7 +59,7 @@
59
59
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
60
60
  },
61
61
  "dependencies": {
62
- "@sema-agent/core": "7.24.2",
62
+ "@sema-agent/core": "7.25.0",
63
63
  "@sema-agent/settings-schema": "3.0.0",
64
64
  "e2b": "^2.28.0",
65
65
  "libsodium-wrappers": "^0.8.4",
@@ -74,7 +74,7 @@
74
74
  "sharp": "^0.35.3"
75
75
  },
76
76
  "devDependencies": {
77
- "@sema-agent/sdk": "9.10.0",
77
+ "@sema-agent/sdk": "10.0.0",
78
78
  "@types/libsodium-wrappers": "^0.7.14",
79
79
  "@types/node": "22.10.2",
80
80
  "@types/pg": "^8.20.0",