@sema-agent/server 7.21.0-rc.1 → 7.22.0-rc.1

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.
@@ -1948,6 +1948,26 @@ export function createHttpServer(rawDeps) {
1948
1948
  if (rsV && taskId)
1949
1949
  verifySink.onForwardEvent(e);
1950
1950
  },
1951
+ // 🔴 **#255(core 5.34.0 #253 / 判据帖 [3967] G1):status 第五席**。这条臂没有事件循环 ——
1952
+ // 旁边 resumeStream 臂的顶层 status 走它自己那条 for-await 的 `case "status"`(driveResume
1953
+ // switch),本臂在 5.33 时代整段重试期是**黑的**(座位那时还不存在)。core 5.34.0 的
1954
+ // `statusEmit` 双写(queue 照旧 + 座位喂**同一个** frozen 帧)把这条腿补齐,这里接的是
1955
+ // **同一条**投影链:`brain_retry_total` 计数 + 共享 `verifySink` 的白名单 builder/单写串行链。
1956
+ // ⚠️ 帧 frozen ⇒ 这条链上不许写帧字段;同步回调不能 await ⇒ fire-and-forget。下面那个
1957
+ // `.catch` 买的是**留痕**不是进程安全:`appendStatus` 返回的链步在 `LedgerSink.append` 内部
1958
+ // 已被挂上处理器(「失败不断链」)⇒ 没有逃逸的 rejection(实测:摘掉它 §4⑥b 仍绿);
1959
+ // 判别力由 §4⑥b 的「诊断行在场」断言看守(与 `src/runs.ts` 的 `statusSeat` 同款注)。
1960
+ // ⚠️ 与 `src/runs.ts` 的 `statusSeat` 同一条纪律:**只**挂在「只回结果」的这条臂上,
1961
+ // 旁边 resumeStream 臂那只 internals 不带它(带了就是同一帧两行账本 + 计数翻倍)。
1962
+ // 🔴 走 sink 的 **`appendStatus` 同步入链门**,不是 `onEvent` —— 后者先 `await flush()`
1963
+ // 才入链,同一拍后到的 `onForwardEvent`(同步入链)会抢到更小的序号,账本时间线倒挂
1964
+ // (codex 对抗复审 R2 [medium],红先复现后修;本臂与 `src/runs.ts` 的 `statusSeat` 同病同修)。
1965
+ onStatusEvent: (e) => {
1966
+ deps.metrics?.inc("brain_retry_total", { phase: String(e.phase) });
1967
+ if (rsV && taskId) {
1968
+ void verifySink.appendStatus(e).catch((err) => deps.logger?.warn?.("status_event_append_failed", { route: "resume-verify", taskId, err: err instanceof Error ? err.message : String(err) }));
1969
+ }
1970
+ },
1951
1971
  // bg 子代终态:与 resumeStream 臂逐字同款(理由全在那份孪生注里)。
1952
1972
  onTaskNotification: (n) => {
1953
1973
  if (n.task_type === "workflow")
package/dist/runs.js CHANGED
@@ -678,12 +678,42 @@ approval) {
678
678
  }
679
679
  : {}),
680
680
  };
681
+ // 🔴 **#255(core 5.34.0 #253 / 判据帖 [3967] G1):status 第五席** —— 与上面四席**不同**,它只在
682
+ // 「只回结果」的两条腿上展开,**绝不**挂到 `runTaskStream` 那一位。理由是 core 的 `statusEmit` 是
683
+ // **双写**(`queue.push` 照旧 + 座位在场即喂**同一个** frozen 帧对象):普通流形的那条 queue 就是本
684
+ // 函数自己 `for await` 的流 —— 下面 loop 里 `sink.onEvent(ev)` 已落账、`metrics.inc` 已计数 ⇒ 两处
685
+ // 都写 = **同一帧两行账本 + 计数翻倍**。组合腿(verify/cascade)没有这条流(引擎把内部腿的 queue
686
+ // 自己排干丢弃),座位是它**唯一**的载体,这正是 5.34.0 开这一席的理由。
687
+ // (反向钉:`test/issue190-status-wire-disclosure.test.ts` §4⑤ —— 夹具照 core 的真姿势 dual-write,
688
+ // 普通流形上账本必须恰五行不是十行。)
689
+ //
690
+ // ⚠️ **帧是 frozen 的**:core 在双写前 `Object.freeze(frame)`,所以这条链上任何一环都不许写帧字段
691
+ // (改一下再往下传会当场 TypeError)。`brainStatusEventData` 是纯读投影,不改源帧。
692
+ // ⚠️ **同步回调不能 await**:失败留痕不断链(孪生姿势 = `onForwardEvent` 的 fire-and-forget 写)。
693
+ // ⚠️ 下面那个 `.catch` **买的不是进程安全**(别照抄别处的「逃逸 rejection 杀进程」说法):
694
+ // `sink.appendStatus` 返回的是**链步**promise,而 `LedgerSink.append` 内部已经给每一步挂了处理器
695
+ // (`chain = step.then(ok, err)`,「失败不断链」)⇒ 调用方 `void` 掉也不会有逃逸的 rejection
696
+ // (实测:摘掉这个 `.catch`,issue190 §4⑥/⑥b 仍绿)。它买的是**留痕** —— 披露行被静默丢掉时至少
697
+ // 有一行诊断可查;判别力由 §4⑥ 的「诊断行在场」断言看守。
698
+ const statusSeat = {
699
+ onStatusEvent: (e) => {
700
+ // S4 P0-a 计数腿:与普通流形 loop 的 `case "status"`、sync 腿、resume 腿同键同标签。
701
+ metrics?.inc("brain_retry_total", { phase: String(e.phase) });
702
+ // 账本腿:共享 LedgerSink 的 **`appendStatus` 同步入链门**(与 `onEvent` 的 `case "status"` 共用
703
+ // 同一个白名单 builder ⇒ 行的 type/data 与其余三腿逐字同形;判据 = issue190 §2/§3/§4 共用一份
704
+ // EXPECTED_ROW_DATA)。🔴 **别改回 `sink.onEvent(e)`**:那条路先 `await flush()` 才入链,同一拍
705
+ // 后到的 `onForwardEvent`(同步入链)会抢到更小的序号 ⇒ 账本时间线倒挂(codex 对抗复审 R2
706
+ // [medium],红先复现后修;反向钉 = issue190 §4⑤b)。
707
+ void sink.appendStatus(e).catch((err) => completionDiagLog?.("status_event_append_failed", { route: "bg-run-composite", taskId, err: err instanceof Error ? err.message : String(err) }));
708
+ },
709
+ };
681
710
  if (verify !== undefined) {
682
711
  // Developer-mode verification gate (1.44): multi-round (impl → adversarial verify → fix loop), not
683
712
  // a stream — await the result and persist it terminal. The durable event log gets just the `done`
684
713
  // (the verifier subtask runs on its own session). `result.verification` carries the verdict.
685
714
  // #253(core 5.33.0 #246):第 4 位 = 供给座席,core 逐字转发给实现腿 + 验证器腿 + 每一轮修复腿。
686
- const result = await runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, fwdInternals);
715
+ // #255(core 5.34.0):`statusSeat` 只在这两条「只回结果」腿上展开(理由见其顶注)
716
+ const result = await runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat });
687
717
  await flush();
688
718
  evictIfConflict(runner, spec.sessionId, result);
689
719
  // A nested suspend under the verify gate is mapped by core to status:"failed" carrying the capability
@@ -700,7 +730,7 @@ approval) {
700
730
  // Cascade (1.45): cheap→strong ladder; multi-attempt cold re-runs, not a stream. Persist terminal.
701
731
  // #253(core 5.33.0 #246):第 4 位 = 供给座席,core 逐字转发给**每一级 rung**(顺序跑,一只 sink
702
732
  // 按梯序拼接收到各级的帧 —— core 的 ATTRIBUTION 注写明 ToolActivity 自身不带 rung 身份)。
703
- const result = await runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, fwdInternals);
733
+ const result = await runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat });
704
734
  await flush();
705
735
  evictIfConflict(runner, spec.sessionId, result);
706
736
  await flushPromptManifest(); // [998]②: same no-stream posture as the verify leg above
@@ -1,3 +1,4 @@
1
+ import { brainStatusEventData } from "./project.js";
1
2
  import { NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
2
3
  import type { LedgerEventType } from "./ledger-events.js";
3
4
  /**
@@ -33,6 +34,23 @@ export interface LedgerSink {
33
34
  onEvent(ev: {
34
35
  type: string;
35
36
  }): Promise<void>;
37
+ /**
38
+ * #255(core 5.34.0 的 `RunInternals.onStatusEvent` 第五席):**「只回结果」腿**的 `status` 行写口。
39
+ *
40
+ * 与 {@link onEvent} 的 `case "status"` 共用**同一个**白名单 builder(`brainStatusEventData`),差别只有
41
+ * 一处:本门**同步入链** —— 在回调进入的那一刻就占住序号槽,而不是先 `await flush()` 再入链。
42
+ *
43
+ * 🔴 **为什么必须同步入链**(codex 对抗复审 R2 [medium],红先复现后修):`onForwardEvent` 是同步入链的,
44
+ * 而 core 的第五席与 forward sink 都是**同步回调**、可能同一拍相邻到达。走 `onEvent` 的话那一次
45
+ * `await flush()` 会让**后到**的 forward 帧抢到**更小**的序号 ⇒ durable 时间线倒挂(消费端读到「子代
46
+ * 进度」排在触发它的那次重连之前)。钉在 `test/issue190-status-wire-disclosure.test.ts` §4⑤b。
47
+ *
48
+ * 🔴 **为什么可以不 flush**:本门只服务「只回结果」的两条腿(verify/cascade、resume-verify),它们的
49
+ * sink **从不收** `text_delta`/`reasoning_delta` —— `onForwardEvent` 有意不逐块持久化子代文本,而
50
+ * `onEvent` 在这些腿上根本没有调用点 ⇒ 没有可被倒挂的已缓冲文本。真有文本缓冲的腿(普通流形 / sync
51
+ * detach / resume stream)走 `onEvent` 或自己那份 `case`,那里的 **flush-first 纪律一个字不动**。
52
+ */
53
+ appendStatus(ev: Parameters<typeof brainStatusEventData>[0]): Promise<void>;
36
54
  /** Forward-sink (subagent) events → ledger, bg-leg posture: fire-and-forget with SYNCHRONOUS seq allocation
37
55
  * (sink-vs-loop appends get unique seqs), same whitelist+redact builders (forwarded events are UNTRUSTED
38
56
  * RAW — §E1). Child text/reasoning deltas are deliberately NOT persisted per-chunk (a child's turn text has
@@ -191,6 +191,10 @@ export function createLedgerSink(opts) {
191
191
  void append("tool_end", toolEndEventData(e)).catch(() => undefined);
192
192
  }
193
193
  };
194
+ // #255:第五席的写口 —— 一行,但**位置**是它的全部:`append` 在这里被**同步**调用,序号槽在回调进入
195
+ // 的那一刻就占住(理由与判据全文见接口上的 doc 注)。别为了「与 onEvent 同形」把它改写成
196
+ // `onEvent({type:"status",…})`:那一次 `await flush()` 正是被修掉的病。
197
+ const appendStatus = (ev) => append("status", brainStatusEventData(ev));
194
198
  const appendParked = (kind, result) =>
195
199
  // Never the capability checkpointToken — only the non-secret gate (search [18] Q1b).
196
200
  append(kind, { gate: result?.checkpointGate ?? null });
@@ -203,6 +207,6 @@ export function createLedgerSink(opts) {
203
207
  }
204
208
  await appendDone(result);
205
209
  };
206
- return { append, flush, onEvent, onForwardEvent, appendParked, appendDone, onDone };
210
+ return { append, flush, onEvent, appendStatus, onForwardEvent, appendParked, appendDone, onDone };
207
211
  }
208
212
  //# sourceMappingURL=ledger-sink.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.21.0-rc.1",
3
+ "version": "7.22.0-rc.1",
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.33.0",
57
+ "@sema-agent/core": "^5.34.0",
58
58
  "@sema-agent/registry-core": "^0.16.0",
59
59
  "e2b": "^2.28.0",
60
60
  "libsodium-wrappers": "^0.8.4",