@sema-agent/server 3.2.1 → 3.4.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.
@@ -155,12 +155,25 @@ async function handleApprovalsAssistantBody(req, res, url, ctx, miss) {
155
155
  // 🔴 STRIP the resume `token` (the unexposed secret capability — resume is by sessionId, never the
156
156
  // token) AND the RAW `toolInput` (core bounds but does NOT redact it — redaction is
157
157
  // the consumer's job). Re-emit the REDACTED `input` instead.
158
- const { token: _token, toolInput: _rawToolInput, ...safe } = s;
159
- // `input` = a REDACTED preview of the projected (bounded, RAW) tool_input. Only `tool_approval` gates
160
- // carry it (resource_limit / plan_review have none ⇒ toolInput undefined ⇒ input null). NOT the old
161
- // bug (raw rich row + decide-binding); the decide-binding still lives only on /v1/approvals.
158
+ // 🔴 EXPLICIT projection([2029] cli 批准的收窄,辖域=仅本 inbox 面)—— 此前是「剥 token/toolInput、
159
+ // 其余整行展开」,于是 core summarizeCheckpoint tool_approval 门上填的 toolName/toolCallId/preview/
160
+ // principal/sourceTaskId/contentKind 全部漏上 wire(其中 preview 还没过脱敏,而它同源的 toolInput
161
+ // 显式脱敏的)。四客户端仓结构性零消费(inbox verb 无调用点,[2029] 取证),按 §2 原意收成显式白名单:
162
+ // §1 镜像键(OMIT-when-absent)+ 两个显式 nullable 的 enrichment(objective/input)。富行
163
+ // (toolName/toolCallId/decide 绑定)只住 /v1/approvals —— 那面有具名消费者(desktop session-host
164
+ // 冷恢复),不在本刀辖域。`input` = REDACTED preview,只有 tool_approval 门携带(其余 ⇒ null)。
162
165
  const input = s.toolInput != null ? redactedPreview(s.toolInput) : null;
163
- return { ...safe, objective: ctx?.body?.objective ?? null, input };
166
+ return {
167
+ sessionId: s.sessionId,
168
+ scope: s.scope,
169
+ createdAt: s.createdAt,
170
+ ...(s.gateKind !== undefined ? { gateKind: s.gateKind } : {}),
171
+ ...(s.severity !== undefined ? { severity: s.severity } : {}),
172
+ ...(s.spentMicroUsd !== undefined ? { spentMicroUsd: s.spentMicroUsd } : {}),
173
+ ...(s.deadline !== undefined ? { deadline: s.deadline } : {}),
174
+ objective: ctx?.body?.objective ?? null,
175
+ input,
176
+ };
164
177
  }))).sort((a, b) => (b.severity ?? 0) - (a.severity ?? 0)); // severity DESC (listByScope is created_at ASC; stable → oldest-first within a tier)
165
178
  sendJson(res, 200, { inbox }); // CheckpointSummary (token-stripped) + objective, severity-prioritized — the assistant single inbox
166
179
  return;
@@ -233,9 +233,12 @@ async function handleCapabilitiesBody(req, res, url, ctx, miss) {
233
233
  // only on the single-user host lane with SCHEDULER_ENABLED (multi-tenant routes scheduling to center).
234
234
  scheduler: Boolean(deps.config.schedulerEnabled && deps.config.requirePrincipal !== true && deps.config.remoteExec?.provider === "host"),
235
235
  // 2c session-sync (P1d): the /v1/sessions/:id/sync/* peer routes (manifest/blobs/import/plan). True only when
236
- // the whole backend + the entry export seam + a snapshot store are all wired (so export/import + blob transfer
237
- // all resolve) — absent backend ⇒ false (the routes 501); "says yes ⟺ route works".
238
- sessionSync: Boolean(deps.backend && deps.sessionStorage?.exportEntries && deps.fileSnapshotStore),
236
+ // the whole backend + the entry export seam + the STAGED-import seam + a snapshot store are all wired (so
237
+ // export/import + blob transfer all resolve) — absent backend ⇒ false (the routes 501); "says yes ⟺ route works".
238
+ // [2024](cli 取证)beginImportStaging 必须入谓词:此前只查旧三项,而暖缓存包装店恰好转发了那三项、
239
+ // 漏了 staging 族 ⇒ caps 报 true 而 PUSH Phase A 恒 501 —— 「says yes ⟺ route works」被实测证否。
240
+ // 功能半场(包装店补转发)已同批修;这里是诚实半场,兜住任何「能导出但不能 staged 导入」的店形。
241
+ sessionSync: Boolean(deps.backend && deps.sessionStorage?.exportEntries && deps.sessionStorage?.beginImportStaging && deps.fileSnapshotStore),
239
242
  // SendUserFile(S3 双轨公网直链,契约 settled 2026-07-14):flag=READY 语义(F7 codex 审)——
240
243
  // emitter 存在(=工具真挂载,lane/租户/签发面三门在 main.ts 已判)∧ 公网端点在场(publicEndpoint
241
244
  // 缺席时 issue()/prepareDirectUpload() 一律 fail-loud,工具挂着也必失败)。"says yes ⟺ 调用真能
@@ -1,9 +1,9 @@
1
1
  import { once } from "node:events";
2
2
  import { randomBytes, createHash } from "node:crypto";
3
- import { sessionLogDigest, sessionLogDigestsComparable, uuidv7, StreamingImportValidator, SessionError, SessionPolicyError } from "@sema-agent/core";
3
+ import { sessionLogDigest, sessionLogDigestsComparable, StreamingImportValidator, SessionError, SessionPolicyError } from "@sema-agent/core";
4
4
  import { StringDecoder } from "node:string_decoder";
5
- import { exportSession, exportSessionManifest, importSession, classifySyncRelationshipByIds, SyncConflictError, stagingIdFor } from "../../session-sync.js";
6
- import { isUuidV7, isUuidShape } from "../../security.js";
5
+ import { exportSessionManifest, classifySyncRelationshipByIds, SyncConflictError } from "../../session-sync.js";
6
+ import { isUuidShape } from "../../security.js";
7
7
  import { sendJson, sendError, msg } from "../send.js";
8
8
  import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
9
9
  /** 2c session-sync PUT /sync/blobs/:hash per-blob upload cap. A snapshot blob is ONE file's bytes, bounded by core's
@@ -51,9 +51,11 @@ async function handleSessionSyncBody(req, res, url, ctx, miss) {
51
51
  const { pendingStagings, importLeases, touchImportLease, cleanupStaging } = ctx.local.sessionSync;
52
52
  // ── 2c session-sync (P1d) — the cloud as a SYNC PEER (docs/DESIGN-session-sync.md §15 + §5–§10) ──────────────
53
53
  // The real 2c topology is TWO PROCESSES (the local shell's local backend + this cloud service); these routes let
54
- // the local peer PULL a session's whole state out of the cloud or PUSH its own in. exportSession/importSession
55
- // (src/session-sync.ts) fan out across ALL four session subsystems (entries + fileSnapshot + sessionPolicy +
56
- // resumeAnchor) they need the WHOLE backend (deps.backend), not the individual stores. Every route is
54
+ // the local peer PULL a session's whole state out of the cloud or PUSH its own in. Both halves are STREAMING
55
+ // implementations that fan out across ALL four session subsystems (entries + fileSnapshot + sessionPolicy +
56
+ // resumeAnchor) IN-ROUTE (PULL = exportSessionManifest + keyset-paged NDJSON; PUSH = two-phase staging re-running
57
+ // the §8 fanout below) — they need the WHOLE backend (deps.backend), not the individual stores. The whole-bundle
58
+ // exportSession/importSession orchestrators are NOT called here (#51 死码判定,见 src/session-sync.ts 顶注). Every route is
57
59
  // owner-gated like fork/delete (§9): a PULL 404s a non-owner (no exfiltration oracle); a PUSH re-stamps the
58
60
  // imported owner to the AUTHENTICATED principal (the bundle's owner is ignored). 501 when no durable session
59
61
  // store is wired (parity with fork: capability says yes ⟺ the route resolves).
@@ -138,11 +138,17 @@ export interface ServiceStoreDeps {
138
138
  /** SendUserFile 账本(多租户治理面):scope↔object 映射的读面 GET /v1/sendfile-links。present ⇔ 签发面
139
139
  * 已配 AND 有 store backend(main.ts 建;写点在两条 send lane 的 withLedgerRecording)。 */
140
140
  sendFileLedger?: import("../plugins/send-file-ledger.js").SendFileLedger;
141
- /** 2c session-sync (P1d) — the WHOLE durable backend, so the `/v1/sessions/:id/sync/*` routes can call
142
- * exportSession/importSession (src/session-sync.ts), which fan out across ALL four session subsystems
143
- * (session entries + fileSnapshot blobs/manifests + sessionPolicy + resumeAnchor) in the §8 fail-closed atomic
144
- * order. The individual stores above are wired for the per-feature routes; the sync routes need the backend
145
- * itself because the orchestrator coordinates the four together (a route can't reassemble it from the parts —
141
+ /** 2c session-sync (P1d) — the WHOLE durable backend, so the `/v1/sessions/:id/sync/*` routes can fan out across
142
+ * ALL four session subsystems (session entries + fileSnapshot blobs/manifests + sessionPolicy + resumeAnchor)
143
+ * in the §8 fail-closed atomic order.
144
+ * 🔴 2026-07-29 纠正:这里原写「…so the routes can call **exportSession/importSession**」——**不成立**。
145
+ * 两条路由早已是**流式**实现(PULL=exportSessionManifest + keyset 分页 NDJSON;PUSH=两阶段 staging,
146
+ * §8 扇出**在路由内自己重写了一遍**,直接调各 store),`grep exportSession src/http/` 零调用点。
147
+ * 那两个整包编排器现为 **test-only**(见 src/session-sync.ts 顶注的判定与机器门)。留着这句错指路的
148
+ * 代价是:下一个人会以为整包路径还活着,并按它去推断内存/原子性行为(而 importSession 恰是 §1.5 的
149
+ * OOM 形)。理由仍成立、结论改了 —— backend 依旧要整个传,因为四个 store 必须同源。
150
+ * The individual stores above are wired for the per-feature routes; the sync routes need the backend
151
+ * itself because they coordinate the four together (a route can't reassemble it from the parts —
146
152
  * `backend.session()`/`fileSnapshot()`/`sessionPolicy()`/`resumeAnchor()` must come from ONE backend). Present
147
153
  * whenever a durable backend (tidb/pg/local) is; absent on the env-only/no-backend worker (the routes 501). */
148
154
  backend?: StoreBackend;
@@ -6,7 +6,6 @@ import { decideParkedAgent, findParkedAgentForCheckpoint } from "../parked-decid
6
6
  import { matchCatalogModel } from "../model-select.js";
7
7
  import {} from "../config-center/facade.js";
8
8
  import { HttpError, principalFrom, verifiedPrincipal, setSsoPrincipal, ssoVerifiedPrincipal, setSsoScope, isUuidV7, verifyDirectDoorProof } from "../security.js";
9
- import { exportSession, importSession } from "../session-sync.js";
10
9
  import {} from "../memory-sync.js";
11
10
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../task-settings.js";
12
11
  import { parseHooksConfig } from "../hooks/hook-runner.js";
package/dist/main.js CHANGED
@@ -676,9 +676,11 @@ async function main() {
676
676
  imageBakes,
677
677
  outcomeSink: outcomeSink ? outcomeSink : undefined, // P2: GET /v1/outcomes 读面(design/73 §7.2 仪表盘底料)
678
678
  sendFileLedger: sendFileLedger ? sendFileLedger : undefined, // SendUserFile 账本读面:GET /v1/sendfile-links(owner-scope 镜像 sessions list)
679
- // 2c session-sync (P1d): the WHOLE backend so the /v1/sessions/:id/sync/* routes can call exportSession/
680
- // importSession (they fan out across session entries + fileSnapshot + sessionPolicy + resumeAnchor, which must
681
- // come from ONE backend in the §8 atomic order). Undefined-safe: the env-only/no-backend worker 501s the routes.
679
+ // 2c session-sync (P1d): the WHOLE backend, because the /v1/sessions/:id/sync/* routes fan out across session
680
+ // entries + fileSnapshot + sessionPolicy + resumeAnchor, which must come from ONE backend in the §8 atomic
681
+ // order. (2026-07-29 纠正:原写「so the routes can call exportSession/importSession」——路由早已是流式实现,
682
+ // 不调那两个整包编排器;它们现为 test-only,判定见 src/session-sync.ts 顶注。理由不变,指路改对。)
683
+ // Undefined-safe: the env-only/no-backend worker 501s the routes.
682
684
  backend: backend ? backend : undefined,
683
685
  };
684
686
  /** 进程内活体协调面(本副本 only,重启即空:HITL 协调器/可 steer 句柄注册表/总线/收件箱) */
@@ -1,5 +1,6 @@
1
1
  import type { AcquiredSession, SessionStore, SessionTreeEntry } from "@sema-agent/core";
2
2
  import type { SessionListItem } from "../security.js";
3
+ import type { StagingHandle } from "../session-sync-kernel.js";
3
4
  export interface CachingOptions {
4
5
  /** Idle seconds a woken session is kept warm. 0 disables caching (pure passthrough). */
5
6
  ttlSec: number;
@@ -79,6 +80,17 @@ export declare class CachingSessionStore implements SessionStore {
79
80
  /** 轴A #1(1.254):core B-17 forget 面(evict 本地 + 转发 inner 可选面)。 */
80
81
  forget?: (sessionId: string) => void | Promise<void>;
81
82
  readonly importEntries?: (sessionId: string, owner: string | null, entries: SessionTreeEntry[]) => Promise<void>;
83
+ /** [2024](cli 取证)2c session-sync P1d-β — delegated STAGED import seam. 此前 staging 族不在本类的逐方法
84
+ * 转发白名单里 ⇒ 默认部署(SESSION_CACHE_TTL_SEC=300 把真店包进本类)PUSH Phase A 恒 501
85
+ * `capability.session_store_required`,而 capabilities().sessionSync 谓词三项(backend/exportEntries/
86
+ * fileSnapshotStore)都被转发了 ⇒ 照报 true(能力面谎)。转发形:handle 的 `commit` 是唯一写真 id 的
87
+ * 时刻,成功后逐出该 id 的暖 handle + S25 指纹(importEntries/replaceEntries 同纪律);`abort` 只丢
88
+ * staging 行、不碰真 id ⇒ 不逐出。 */
89
+ readonly beginImportStaging?: (realSessionId: string, token: string) => StagingHandle;
90
+ /** Staging 清扫面(kernel `ImportStagingStore.sweepStagingSessions`)。当下 reaper 走 `backend.session()`
91
+ * 裸店、不经本包装,但 security.ts 的 OwnerAwareSessionStore 契约列了它 —— 经 sessionStorage 消费的
92
+ * 未来调用方不该静默 no-op,与 beginImportStaging 同批补齐(同一类白名单缺口)。 */
93
+ readonly sweepStagingSessions?: () => Promise<number>;
82
94
  /** 2c session-sync (§7/§8) — delegated IDEMPOTENT replace; like importEntries it writes durable state under
83
95
  * `sessionId`, so a stale warm handle for that id is evicted on success (next acquire re-wakes from the new log). */
84
96
  readonly replaceEntries?: (sessionId: string, owner: string | null, entries: SessionTreeEntry[]) => Promise<void>;
@@ -55,6 +55,17 @@ export class CachingSessionStore {
55
55
  /** 轴A #1(1.254):core B-17 forget 面(evict 本地 + 转发 inner 可选面)。 */
56
56
  forget;
57
57
  importEntries;
58
+ /** [2024](cli 取证)2c session-sync P1d-β — delegated STAGED import seam. 此前 staging 族不在本类的逐方法
59
+ * 转发白名单里 ⇒ 默认部署(SESSION_CACHE_TTL_SEC=300 把真店包进本类)PUSH Phase A 恒 501
60
+ * `capability.session_store_required`,而 capabilities().sessionSync 谓词三项(backend/exportEntries/
61
+ * fileSnapshotStore)都被转发了 ⇒ 照报 true(能力面谎)。转发形:handle 的 `commit` 是唯一写真 id 的
62
+ * 时刻,成功后逐出该 id 的暖 handle + S25 指纹(importEntries/replaceEntries 同纪律);`abort` 只丢
63
+ * staging 行、不碰真 id ⇒ 不逐出。 */
64
+ beginImportStaging;
65
+ /** Staging 清扫面(kernel `ImportStagingStore.sweepStagingSessions`)。当下 reaper 走 `backend.session()`
66
+ * 裸店、不经本包装,但 security.ts 的 OwnerAwareSessionStore 契约列了它 —— 经 sessionStorage 消费的
67
+ * 未来调用方不该静默 no-op,与 beginImportStaging 同批补齐(同一类白名单缺口)。 */
68
+ sweepStagingSessions;
58
69
  /** 2c session-sync (§7/§8) — delegated IDEMPOTENT replace; like importEntries it writes durable state under
59
70
  * `sessionId`, so a stale warm handle for that id is evicted on success (next acquire re-wakes from the new log). */
60
71
  replaceEntries;
@@ -116,6 +127,29 @@ export class CachingSessionStore {
116
127
  this.evict(sessionId);
117
128
  };
118
129
  }
130
+ if (ownerAware.beginImportStaging) {
131
+ // [2024] staged import(P1d-β)经包装店贯通:handle 逐字段代理,只有 `commit`(唯一写真 id 的时刻)
132
+ // 加了成功后逐出——与上面 importEntries/replaceEntries 完全同款的纪律;abort 不写真 id,不逐出。
133
+ const begin = ownerAware.beginImportStaging.bind(inner);
134
+ this.beginImportStaging = (realSessionId, token) => {
135
+ const h = begin(realSessionId, token);
136
+ return {
137
+ stagingId: h.stagingId,
138
+ realSessionId: h.realSessionId,
139
+ appendBatch: (entries) => h.appendBatch(entries),
140
+ commit: async (owner, o) => {
141
+ const r = await h.commit(owner, o);
142
+ if (this.cache.has(realSessionId))
143
+ opts.onConflictEvict?.(realSessionId); // S25: a warm entry was live → stale-affinity fingerprint
144
+ this.evict(realSessionId);
145
+ return r;
146
+ },
147
+ abort: () => h.abort(),
148
+ };
149
+ };
150
+ }
151
+ if (ownerAware.sweepStagingSessions)
152
+ this.sweepStagingSessions = ownerAware.sweepStagingSessions.bind(inner);
119
153
  if (ownerAware.deleteSession) {
120
154
  // A durable delete must also drop the deleted session from THIS warm cache (else a stale cached handle
121
155
  // could keep serving a purged session until its idle TTL). Wrap to evict on success.
@@ -89,7 +89,26 @@ export interface SessionManifest {
89
89
  *
90
90
  * NB (P1d-α): the HTTP PULL no longer uses this — it uses {@link exportSessionManifest} (entries lifted off the wire)
91
91
  * + the keyset-paged `exportEntriesStream` NDJSON route so memory is bounded by ROW COUNT, not the whole-log array.
92
- * exportSession (entries-inline) STAYS for the local same-process import path, P1d-β (PUSH), and the tests.
92
+ *
93
+ * 🔴 **TEST-ONLY(2026-07-29 死码判定,#51 / [2012] cli 反查;上一版这段 NB 的结论已过期)**。
94
+ * 上面那句「STAYS for the local same-process import path, P1d-β (PUSH), and the tests」**三条里两条已不成立**:
95
+ * 逐条亲验(`grep -rn 'exportSession\|importSession' src/`)——
96
+ * · **本地同进程 import 路径**:不存在这样的调用点(全仓零处);
97
+ * · **P1d-β(PUSH)**:路由改成了两阶段 staging + NDJSON 流,§8 扇出**在路由里自己重写了一遍**
98
+ * (`src/http/routes/session-sync.ts` 的 commit 链直接调 `handle.commit` / `importManifest` / `putRules` /
99
+ * `resumeAnchorStore`),**不经过** {@link importSession};
100
+ * · **tests**:成立,且是**唯一**成立的一条(三个测试文件在用)。
101
+ * 还有两处**只 import 不调用**的死接线(`src/http/server.ts` / `src/http/routes/session-sync.ts`)——本次已摘;
102
+ * 它们能活到今天是因为 tsconfig 没开 `noUnusedLocals`。这两个函数也**不在发布公面**上
103
+ * (`package.json` 的 exports 只有 `.` / `./main` / `./package.json`,`src/index.ts` 不 re-export 本模块)。
104
+ *
105
+ * **为什么本批只判定 + 上机器门,没有直接删**:两个仍在用它们的测试文件
106
+ * (`session-sync-fanout-characterization` / `session-sync-fanout-crash-recovery`)是 §8 崩溃/重试幂等语义的
107
+ * **唯一可执行验证**,并且其中 B4/G1 两笔 `it.fails` 记的是 `classifySyncRelationshipByIds` 的**分类缺口**——
108
+ * 而那个分类器**在活路由上仍被调用**(`routes/session-sync.ts:312` / `:632`)。直接删函数=连带删掉两套
109
+ * 覆盖与一笔**仍然有效**的欠账记账,那正是本仓 `vitest.config.ts` 里写死的判据「覆盖丢失伪装成绿,
110
+ * 比假红更坏」。⇒ **删除的前置条件**是先把 B4/G1 重锚到活的流式路径上;做完那件,这两个函数连同它们的
111
+ * 测试可一并摘除。判定与机器门见 `test/session-sync-routes.test.ts` 的「死码判定」describe。
93
112
  */
94
113
  export declare function exportSession(sessionId: string, srcBackend: StoreBackend): Promise<{
95
114
  bundle: SessionBundle;
@@ -30,7 +30,26 @@ function ownerAware(backend) {
30
30
  *
31
31
  * NB (P1d-α): the HTTP PULL no longer uses this — it uses {@link exportSessionManifest} (entries lifted off the wire)
32
32
  * + the keyset-paged `exportEntriesStream` NDJSON route so memory is bounded by ROW COUNT, not the whole-log array.
33
- * exportSession (entries-inline) STAYS for the local same-process import path, P1d-β (PUSH), and the tests.
33
+ *
34
+ * 🔴 **TEST-ONLY(2026-07-29 死码判定,#51 / [2012] cli 反查;上一版这段 NB 的结论已过期)**。
35
+ * 上面那句「STAYS for the local same-process import path, P1d-β (PUSH), and the tests」**三条里两条已不成立**:
36
+ * 逐条亲验(`grep -rn 'exportSession\|importSession' src/`)——
37
+ * · **本地同进程 import 路径**:不存在这样的调用点(全仓零处);
38
+ * · **P1d-β(PUSH)**:路由改成了两阶段 staging + NDJSON 流,§8 扇出**在路由里自己重写了一遍**
39
+ * (`src/http/routes/session-sync.ts` 的 commit 链直接调 `handle.commit` / `importManifest` / `putRules` /
40
+ * `resumeAnchorStore`),**不经过** {@link importSession};
41
+ * · **tests**:成立,且是**唯一**成立的一条(三个测试文件在用)。
42
+ * 还有两处**只 import 不调用**的死接线(`src/http/server.ts` / `src/http/routes/session-sync.ts`)——本次已摘;
43
+ * 它们能活到今天是因为 tsconfig 没开 `noUnusedLocals`。这两个函数也**不在发布公面**上
44
+ * (`package.json` 的 exports 只有 `.` / `./main` / `./package.json`,`src/index.ts` 不 re-export 本模块)。
45
+ *
46
+ * **为什么本批只判定 + 上机器门,没有直接删**:两个仍在用它们的测试文件
47
+ * (`session-sync-fanout-characterization` / `session-sync-fanout-crash-recovery`)是 §8 崩溃/重试幂等语义的
48
+ * **唯一可执行验证**,并且其中 B4/G1 两笔 `it.fails` 记的是 `classifySyncRelationshipByIds` 的**分类缺口**——
49
+ * 而那个分类器**在活路由上仍被调用**(`routes/session-sync.ts:312` / `:632`)。直接删函数=连带删掉两套
50
+ * 覆盖与一笔**仍然有效**的欠账记账,那正是本仓 `vitest.config.ts` 里写死的判据「覆盖丢失伪装成绿,
51
+ * 比假红更坏」。⇒ **删除的前置条件**是先把 B4/G1 重锚到活的流式路径上;做完那件,这两个函数连同它们的
52
+ * 测试可一并摘除。判定与机器门见 `test/session-sync-routes.test.ts` 的「死码判定」describe。
34
53
  */
35
54
  export async function exportSession(sessionId, srcBackend) {
36
55
  const session = ownerAware(srcBackend);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "3.2.1",
3
+ "version": "3.4.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",
@@ -53,8 +53,8 @@
53
53
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
54
54
  },
55
55
  "dependencies": {
56
- "@sema-agent/core": "^2.1.0",
57
- "@sema-agent/registry-core": "^0.10.21",
56
+ "@sema-agent/core": "^2.2.0",
57
+ "@sema-agent/registry-core": "^0.10.24",
58
58
  "e2b": "^2.28.0",
59
59
  "libsodium-wrappers": "^0.8.4",
60
60
  "mysql2": "^3.22.4",
@@ -67,7 +67,7 @@
67
67
  "sharp": "^0.35.3"
68
68
  },
69
69
  "devDependencies": {
70
- "@sema-agent/sdk": "^1.0.0",
70
+ "@sema-agent/sdk": "^1.1.0",
71
71
  "@types/libsodium-wrappers": "^0.7.14",
72
72
  "@types/node": "22.10.2",
73
73
  "@types/pg": "^8.20.0",