@sema-agent/server 1.304.0 → 1.305.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.
@@ -4263,7 +4263,7 @@ export function createHttpServer(deps) {
4263
4263
  sendJson(res, 413, {
4264
4264
  code: "blob_too_large_for_sql",
4265
4265
  errorCode: "blob_too_large_for_sql",
4266
- error: `blob of ${body.byteLength} bytes exceeds this deployment's SQL snapshot-blob cap (${deps.snapshotBlobSqlCapBytes} bytes; mysql-protocol packet limit) — configure MinIO object storage (MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY) for large snapshot blobs`,
4266
+ error: `blob of ${body.byteLength} bytes exceeds this deployment's SQL snapshot-blob cap (${deps.snapshotBlobSqlCapBytes} bytes — TiDB's txn-entry-size-limit is the low wall at 6MiB by default; the mysql-protocol packet limit sits above it) — configure MinIO object storage (MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY) for large snapshot blobs, or raise SNAPSHOT_BLOB_SQL_MAX_BYTES if your deployment lifted those limits`,
4267
4267
  });
4268
4268
  return;
4269
4269
  }
@@ -6821,7 +6821,9 @@ function routeLabel(_method, url) {
6821
6821
  return "other";
6822
6822
  }
6823
6823
  function workspaceContentType(relPath) {
6824
- const ext = relPath.slice(relPath.lastIndexOf(".") + 1).toLowerCase();
6824
+ const base = relPath.slice(relPath.lastIndexOf("/") + 1);
6825
+ const dot = base.lastIndexOf(".");
6826
+ const ext = dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
6825
6827
  const TABLE = {
6826
6828
  txt: "text/plain; charset=utf-8", md: "text/plain; charset=utf-8", log: "text/plain; charset=utf-8",
6827
6829
  json: "application/json; charset=utf-8", csv: "text/csv; charset=utf-8",
package/dist/http/tar.js CHANGED
@@ -19,7 +19,11 @@ export function splitTarPath(relPath) {
19
19
  }
20
20
  return null;
21
21
  }
22
+ const USTAR_MAX_SIZE = 8 * 1024 * 1024 * 1024 - 1;
22
23
  export function tarHeader(relPath, sizeBytes, mtimeSec) {
24
+ if (!Number.isFinite(sizeBytes) || sizeBytes < 0 || sizeBytes > USTAR_MAX_SIZE) {
25
+ throw new Error(`tar: size ${sizeBytes} not representable in a ustar header (12-octal-byte field, max 8GiB-1): ${relPath}`);
26
+ }
23
27
  const split = splitTarPath(relPath);
24
28
  if (split === null)
25
29
  throw new Error(`tar: path not representable in ustar (must be ≤255 bytes and splittable at "/"): ${relPath}`);
@@ -75,14 +75,24 @@ export async function sqlBlobSizes(dialect, pool, hashes) {
75
75
  if (hashes.length === 0)
76
76
  return new Map();
77
77
  const distinct = [...new Set(hashes)];
78
- if (dialect === "tidb") {
79
- const ph = distinct.map(() => "?").join(",");
80
- const [rows] = await pool.query(`SELECT blob_hash, byte_len FROM snapshot_blob WHERE blob_hash IN (${ph})`, distinct);
81
- return new Map(rows.map((r) => [String(r.blob_hash), Number(r.byte_len)]));
78
+ const CHUNK = 1000;
79
+ const out = new Map();
80
+ for (let i = 0; i < distinct.length; i += CHUNK) {
81
+ const part = distinct.slice(i, i + CHUNK);
82
+ if (dialect === "tidb") {
83
+ const ph = part.map(() => "?").join(",");
84
+ const [rows] = await pool.query(`SELECT blob_hash, byte_len FROM snapshot_blob WHERE blob_hash IN (${ph})`, part);
85
+ for (const r of rows)
86
+ out.set(String(r.blob_hash), Number(r.byte_len));
87
+ }
88
+ else {
89
+ const ph = part.map((_, j) => `$${j + 1}`).join(",");
90
+ const r = await pool.query(`SELECT blob_hash, byte_len FROM snapshot_blob WHERE blob_hash IN (${ph})`, part);
91
+ for (const row of r.rows)
92
+ out.set(row.blob_hash, Number(row.byte_len));
93
+ }
82
94
  }
83
- const ph = distinct.map((_, i) => `$${i + 1}`).join(",");
84
- const r = await pool.query(`SELECT blob_hash, byte_len FROM snapshot_blob WHERE blob_hash IN (${ph})`, distinct);
85
- return new Map(r.rows.map((row) => [row.blob_hash, Number(row.byte_len)]));
95
+ return out;
86
96
  }
87
97
  async function sqlDeleteIndexRowsPastGrace(dialect, pool, hashes) {
88
98
  if (hashes.length === 0)
@@ -250,14 +260,17 @@ export class MinioBlobBackend {
250
260
  }
251
261
  if (toDelete.length === 0)
252
262
  return 0;
263
+ let okCount = 0;
253
264
  await mapBounded(toDelete, MINIO_OP_CONCURRENCY, async (hash) => {
254
265
  try {
255
- await fetch(this.presign(hash, "DELETE"), { method: "DELETE" });
266
+ const res = await (this.cfg.fetchImpl ?? fetch)(this.presign(hash, "DELETE"), { method: "DELETE" });
267
+ if (res.ok || res.status === 404)
268
+ okCount++;
256
269
  }
257
270
  catch {
258
271
  }
259
272
  });
260
- return toDelete.length;
273
+ return okCount;
261
274
  }
262
275
  }
263
276
  //# sourceMappingURL=blob-backend.js.map
@@ -163,20 +163,26 @@ export class TiDBTaskAttachmentStore {
163
163
  const lister = this.bytes;
164
164
  if (typeof lister.listObjects !== "function")
165
165
  return 0;
166
- const candidates = [];
167
- for await (const o of lister.listObjects()) {
168
- if (nowMs - o.lastModifiedMs >= graceMs)
169
- candidates.push(o.hash);
170
- }
171
166
  let removed = 0;
172
- for (let i = 0; i < candidates.length; i += 200) {
173
- const batch = candidates.slice(i, i + 200);
167
+ let batch = [];
168
+ const flush = async () => {
169
+ if (batch.length === 0)
170
+ return;
174
171
  const referenced = await this.referencedShas(batch);
175
172
  const orphans = batch.filter((h) => !referenced.has(h));
176
173
  const safe = await this.filterStillOrphan(orphans, graceMs, nowMs);
177
174
  if (safe.length > 0)
178
175
  removed += await this.bytes.deleteBlobs(safe);
176
+ batch = [];
177
+ };
178
+ for await (const o of lister.listObjects()) {
179
+ if (nowMs - o.lastModifiedMs < graceMs)
180
+ continue;
181
+ batch.push(o.hash);
182
+ if (batch.length >= 200)
183
+ await flush();
179
184
  }
185
+ await flush();
180
186
  return removed;
181
187
  }
182
188
  async referencedShas(shas) {
@@ -269,20 +275,26 @@ export class PgTaskAttachmentStore {
269
275
  const lister = this.bytes;
270
276
  if (typeof lister.listObjects !== "function")
271
277
  return 0;
272
- const candidates = [];
273
- for await (const o of lister.listObjects()) {
274
- if (nowMs - o.lastModifiedMs >= graceMs)
275
- candidates.push(o.hash);
276
- }
277
278
  let removed = 0;
278
- for (let i = 0; i < candidates.length; i += 200) {
279
- const batch = candidates.slice(i, i + 200);
279
+ let batch = [];
280
+ const flush = async () => {
281
+ if (batch.length === 0)
282
+ return;
280
283
  const referenced = await this.referencedShas(batch);
281
284
  const orphans = batch.filter((h) => !referenced.has(h));
282
285
  const safe = await this.filterStillOrphan(orphans, graceMs, nowMs);
283
286
  if (safe.length > 0)
284
287
  removed += await this.bytes.deleteBlobs(safe);
288
+ batch = [];
289
+ };
290
+ for await (const o of lister.listObjects()) {
291
+ if (nowMs - o.lastModifiedMs < graceMs)
292
+ continue;
293
+ batch.push(o.hash);
294
+ if (batch.length >= 200)
295
+ await flush();
285
296
  }
297
+ await flush();
286
298
  return removed;
287
299
  }
288
300
  async referencedShas(shas) {
@@ -62,7 +62,7 @@ export function toolEndEventData(ev) {
62
62
  isError: ev.isError,
63
63
  ...(ev.output !== undefined ? { output: redactDeep(ev.output) } : {}),
64
64
  ...(ev.truncated ? { truncated: true } : {}),
65
- ...(typeof ev.totalChars === "number" ? { totalChars: ev.totalChars } : {}),
65
+ ...(Number.isFinite(ev.totalChars) ? { totalChars: ev.totalChars } : {}),
66
66
  ...(ev.structured !== undefined ? { structured: redactDeep(ev.structured) } : {}),
67
67
  ...identityFields(ev),
68
68
  };
@@ -241,7 +241,7 @@ export function projectEvents(events, opts = {}) {
241
241
  break;
242
242
  case "tool_end": {
243
243
  const callId = String(d.toolCallId ?? "");
244
- blocks.push({ type: "tool-result", callId, ...(typeof d.label === "string" ? { label: d.label } : {}), output: d.output, ...(d.isError ? { isError: true } : {}), ...(d.truncated ? { truncated: true } : {}), ...(typeof d.totalChars === "number" ? { totalChars: d.totalChars } : {}), ...(d.structured !== undefined ? { structured: d.structured } : {}), ...identityFields(d) });
244
+ blocks.push({ type: "tool-result", callId, ...(typeof d.label === "string" ? { label: d.label } : {}), output: d.output, ...(d.isError ? { isError: true } : {}), ...(d.truncated ? { truncated: true } : {}), ...(Number.isFinite(d.totalChars) ? { totalChars: d.totalChars } : {}), ...(d.structured !== undefined ? { structured: d.structured } : {}), ...identityFields(d) });
245
245
  break;
246
246
  }
247
247
  case "prompt_assembled":
@@ -307,7 +307,7 @@ export function mapTraceEvent(type, seq, data) {
307
307
  case "tool_start":
308
308
  return { event: "tool-call", data: { seq, id: data.toolCallId, name: data.toolName, ...(typeof data.label === "string" ? { label: data.label } : {}), input: data.args, ...identityFields(data) } };
309
309
  case "tool_end":
310
- return { event: "tool-result", data: { seq, callId: data.toolCallId, ...(typeof data.label === "string" ? { label: data.label } : {}), isError: Boolean(data.isError), ...(data.output !== undefined ? { output: data.output } : {}), ...(data.truncated ? { truncated: true } : {}), ...(typeof data.totalChars === "number" ? { totalChars: data.totalChars } : {}), ...(data.structured !== undefined ? { structured: data.structured } : {}), ...identityFields(data) } };
310
+ return { event: "tool-result", data: { seq, callId: data.toolCallId, ...(typeof data.label === "string" ? { label: data.label } : {}), isError: Boolean(data.isError), ...(data.output !== undefined ? { output: data.output } : {}), ...(data.truncated ? { truncated: true } : {}), ...(Number.isFinite(data.totalChars) ? { totalChars: data.totalChars } : {}), ...(data.structured !== undefined ? { structured: data.structured } : {}), ...identityFields(data) } };
311
311
  case "turn_end": {
312
312
  const tokens = toContractTokens(data.usage);
313
313
  return { event: "turn", data: { seq, ...(tokens ? { tokens } : {}) } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "1.304.0",
3
+ "version": "1.305.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",