@sema-agent/server 1.240.0 → 1.242.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 (74) hide show
  1. package/dist/budget.d.ts +19 -0
  2. package/dist/budget.d.ts.map +1 -1
  3. package/dist/budget.js +32 -0
  4. package/dist/budget.js.map +1 -1
  5. package/dist/config.d.ts +1 -0
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +1 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/env-facts.d.ts +9 -0
  10. package/dist/env-facts.d.ts.map +1 -1
  11. package/dist/env-facts.js +22 -0
  12. package/dist/env-facts.js.map +1 -1
  13. package/dist/http/server.d.ts +13 -0
  14. package/dist/http/server.d.ts.map +1 -1
  15. package/dist/http/server.js +118 -22
  16. package/dist/http/server.js.map +1 -1
  17. package/dist/main.js +6 -2
  18. package/dist/main.js.map +1 -1
  19. package/dist/plugins/memory-engine-pg.d.ts.map +1 -1
  20. package/dist/plugins/memory-engine-pg.js +22 -10
  21. package/dist/plugins/memory-engine-pg.js.map +1 -1
  22. package/dist/plugins/memory-sync-store-pg.d.ts.map +1 -1
  23. package/dist/plugins/memory-sync-store-pg.js +4 -3
  24. package/dist/plugins/memory-sync-store-pg.js.map +1 -1
  25. package/dist/plugins/pg-approval-store.d.ts.map +1 -1
  26. package/dist/plugins/pg-approval-store.js +2 -1
  27. package/dist/plugins/pg-approval-store.js.map +1 -1
  28. package/dist/plugins/pg-checkpoint-store.d.ts.map +1 -1
  29. package/dist/plugins/pg-checkpoint-store.js +7 -6
  30. package/dist/plugins/pg-checkpoint-store.js.map +1 -1
  31. package/dist/plugins/pg-image-bake.d.ts +1 -1
  32. package/dist/plugins/pg-image-bake.d.ts.map +1 -1
  33. package/dist/plugins/pg-image-bake.js +17 -15
  34. package/dist/plugins/pg-image-bake.js.map +1 -1
  35. package/dist/plugins/pg-image-index.d.ts.map +1 -1
  36. package/dist/plugins/pg-image-index.js +18 -9
  37. package/dist/plugins/pg-image-index.js.map +1 -1
  38. package/dist/plugins/pg-outcome-ledger.d.ts.map +1 -1
  39. package/dist/plugins/pg-outcome-ledger.js +5 -4
  40. package/dist/plugins/pg-outcome-ledger.js.map +1 -1
  41. package/dist/plugins/pg-run-store.d.ts.map +1 -1
  42. package/dist/plugins/pg-run-store.js +6 -5
  43. package/dist/plugins/pg-run-store.js.map +1 -1
  44. package/dist/plugins/pg-safe-json.d.ts +9 -0
  45. package/dist/plugins/pg-safe-json.d.ts.map +1 -0
  46. package/dist/plugins/pg-safe-json.js +90 -0
  47. package/dist/plugins/pg-safe-json.js.map +1 -0
  48. package/dist/plugins/pg-session-policy-store.d.ts.map +1 -1
  49. package/dist/plugins/pg-session-policy-store.js +4 -0
  50. package/dist/plugins/pg-session-policy-store.js.map +1 -1
  51. package/dist/plugins/pg-session-storage.d.ts.map +1 -1
  52. package/dist/plugins/pg-session-storage.js +5 -4
  53. package/dist/plugins/pg-session-storage.js.map +1 -1
  54. package/dist/plugins/pg-tool-result-store.d.ts.map +1 -1
  55. package/dist/plugins/pg-tool-result-store.js +2 -1
  56. package/dist/plugins/pg-tool-result-store.js.map +1 -1
  57. package/dist/plugins/pg-workflow-journal-store.d.ts.map +1 -1
  58. package/dist/plugins/pg-workflow-journal-store.js.map +1 -1
  59. package/dist/plugins/remote-env-e2b.d.ts.map +1 -1
  60. package/dist/plugins/remote-env-e2b.js +3 -1
  61. package/dist/plugins/remote-env-e2b.js.map +1 -1
  62. package/dist/plugins/remote-env-k8s.d.ts.map +1 -1
  63. package/dist/plugins/remote-env-k8s.js.map +1 -1
  64. package/dist/plugins/tidb-image-bake.d.ts +1 -1
  65. package/dist/plugins/tidb-image-bake.d.ts.map +1 -1
  66. package/dist/plugins/tidb-image-bake.js +3 -2
  67. package/dist/plugins/tidb-image-bake.js.map +1 -1
  68. package/dist/plugins/tidb-workflow-journal-store.d.ts.map +1 -1
  69. package/dist/plugins/tidb-workflow-journal-store.js.map +1 -1
  70. package/dist/tool-approval.d.ts +1 -0
  71. package/dist/tool-approval.d.ts.map +1 -1
  72. package/dist/tool-approval.js +13 -8
  73. package/dist/tool-approval.js.map +1 -1
  74. package/package.json +2 -2
@@ -25,11 +25,13 @@ import { RateLimiter } from "../observability/rate-limit.js";
25
25
  import { withPrincipal } from "../observability/principal-context.js";
26
26
  import { projectEvents, runSummary, mapTraceEvent, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
27
27
  import { createLedgerSink } from "../trace/ledger-sink.js";
28
+ import { cacheFamilyOfMirror } from "../budget.js";
28
29
  import { projectArtifacts } from "../trace/artifacts.js";
29
30
  import { composeSupervisorCost, infraCost, infraUsageFromEvents, hasInfraPricing } from "../finance/cost-taxonomy.js";
30
31
  import { redactSecrets, redactDeep, redactedPreview } from "../trace/redact.js";
31
32
  import { usageSummary, usageSeries, usageBreakdown } from "../usage-analytics.js";
32
33
  import { IdempotencyCache } from "./idempotency.js";
34
+ import { pgHasUnstorable } from "../plugins/pg-safe-json.js";
33
35
  export const MAX_USER_SKILLS = 10;
34
36
  export const MAX_SKILL_CONTENT_CHARS = 1_048_576;
35
37
  export const MAX_SYSTEM_PROMPT_CHARS = 16_384;
@@ -669,6 +671,7 @@ export function createHttpServer(deps) {
669
671
  mcpElicitation: Boolean(deps.elicitation),
670
672
  askUserQuestion: Boolean(deps.question),
671
673
  toolApproval: Boolean(deps.toolApproval),
674
+ sideQuery: true,
672
675
  sessionList: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
673
676
  sessionSearch: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
674
677
  sessionFork: Boolean(deps.sessionStorage?.fork),
@@ -707,6 +710,77 @@ export function createHttpServer(deps) {
707
710
  sendJson(res, 200, { models, default: deps.config.model.id, defaultModel: { id: deps.config.model.id, ...(defaultName ? { name: defaultName } : {}) } });
708
711
  return;
709
712
  }
713
+ if (req.method === "POST" && url === "/v1/side-query") {
714
+ const ac = new AbortController();
715
+ const onClose = () => ac.abort();
716
+ res.on("close", onClose);
717
+ try {
718
+ if (rateLimited(req, res) || quotaExceeded(req, res) || (await leaseDenied(req, res)))
719
+ return;
720
+ const principal = gatedPrincipal(req, deps.config);
721
+ if (deps.config.requirePrincipal && !principal) {
722
+ sendJson(res, 401, { error: `missing principal header '${deps.config.principalHeader}'` });
723
+ return;
724
+ }
725
+ let body;
726
+ try {
727
+ const raw = await readJson(req);
728
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
729
+ throw new HttpError(400, "body must be a JSON object (SideQuerySpec shape)");
730
+ body = raw;
731
+ }
732
+ catch (e) {
733
+ const he = e instanceof HttpError ? e : new HttpError(400, msg(e));
734
+ sendJson(res, he.status, { error: he.message });
735
+ return;
736
+ }
737
+ if (!Array.isArray(body.messages) || body.messages.length === 0) {
738
+ sendJson(res, 400, { error: "messages must be a non-empty array (SideQueryMessage[])" });
739
+ return;
740
+ }
741
+ if (body.thinking !== undefined && !isThinkingLevel(body.thinking)) {
742
+ sendJson(res, 400, { error: "invalid thinking level" });
743
+ return;
744
+ }
745
+ if (ac.signal.aborted)
746
+ return;
747
+ const spec = {
748
+ messages: body.messages,
749
+ ...(typeof body.model === "string" ? { model: body.model } : {}),
750
+ ...(typeof body.modelRole === "string" ? { modelRole: body.modelRole } : {}),
751
+ ...(isThinkingLevel(body.thinking) ? { thinking: body.thinking } : {}),
752
+ ...(typeof body.systemPrompt === "string" ? { systemPrompt: body.systemPrompt } : {}),
753
+ ...(Array.isArray(body.tools) ? { tools: body.tools } : {}),
754
+ ...(typeof body.maxOutputTokens === "number" && Number.isFinite(body.maxOutputTokens) && body.maxOutputTokens > 0 ? { maxOutputTokens: Math.floor(body.maxOutputTokens) } : {}),
755
+ signal: ac.signal,
756
+ };
757
+ try {
758
+ const catalogSnapshot = Object.values(deps.runner.agentCatalog?.models ?? {}).map((m) => ({ id: m?.id, api: m?.api, params: m?.params?.promptCacheFamily ? { promptCacheFamily: m.params.promptCacheFamily } : undefined }));
759
+ const result = await deps.runner.sideQuery(spec);
760
+ const rr = result;
761
+ const modelId = typeof rr.model === "string" ? rr.model : "unknown";
762
+ const byId = catalogSnapshot.filter((m) => m.id === modelId);
763
+ const families = new Set(byId.map((m) => cacheFamilyOfMirror(m)));
764
+ const family = families.size === 1 ? [...families][0] : undefined;
765
+ deps.sideQueryAccounting?.(principal, { model: modelId, ...(family ? { family } : {}), usage: rr.usage });
766
+ deps.metrics?.inc?.("side_query_total", { result: "ok" });
767
+ if (!res.writableEnded)
768
+ sendJson(res, 200, result);
769
+ }
770
+ catch (e) {
771
+ deps.metrics?.inc?.("side_query_total", { result: "error" });
772
+ if (!res.writableEnded && !ac.signal.aborted) {
773
+ const m = msg(e);
774
+ const isValidation = /^Unknown model ref |^No model for role |requires a non-empty messages array/.test(m);
775
+ sendJson(res, isValidation ? 400 : 500, { error: redactSecrets(m) });
776
+ }
777
+ }
778
+ }
779
+ finally {
780
+ res.off("close", onClose);
781
+ }
782
+ return;
783
+ }
710
784
  if (req.method === "POST" && (url === "/v1/tasks" || url === "/v1/tasks/stream")) {
711
785
  const rawIdem = headerStr(req.headers["idempotency-key"]);
712
786
  const idemKey = rawIdem ? scopedIdempotencyKey(rawIdem, source, gatedPrincipal(req, deps.config)) : undefined;
@@ -5469,7 +5543,7 @@ async function ingestBakeLine(bakes, idx, bake, line, registry) {
5469
5543
  const cancelled = doneStatus === "CANCELLED";
5470
5544
  const errorText = typeof line.error === "string" ? redactSecrets(line.error) : null;
5471
5545
  const tag = typeof line.tag === "string" ? line.tag : bake.tag;
5472
- const digest = typeof line.digest === "string" && line.digest.length > 0 ? line.digest : null;
5546
+ let digest = typeof line.digest === "string" && line.digest.length > 0 ? line.digest : null;
5473
5547
  let indexId = bake.indexId;
5474
5548
  let repo = bake.repo;
5475
5549
  let ref = bake.ref;
@@ -5484,27 +5558,35 @@ async function ingestBakeLine(bakes, idx, bake, line, registry) {
5484
5558
  const fresh = await bakes.getBake(bakeId);
5485
5559
  const nonTerminal = !!fresh && (fresh.status === "queued" || fresh.status === "running");
5486
5560
  if (manifestBody && typeof manifestBody === "object" && nonTerminal) {
5487
- registerSteps.push({ step: "register", status: "running" });
5488
- try {
5489
- const profile = bake.profile && bake.profile !== "custom" ? bake.profile : (manifestBody.profile && manifestBody.profile !== "custom" ? manifestBody.profile : "custom");
5490
- repo = registry ? `${registry}/${profile}` : `sema-images/${profile}`;
5491
- manifestSha = typeof line.manifestSha === "string" ? line.manifestSha : (typeof line.manifest?.sha256 === "string" ? String(line.manifest.sha256) : bake.manifestSha);
5492
- const entry = manifestToIndexEntry(manifestBody, { repo, digest, status: "building", manifestSha, sizeBytes: manifestBody.sizeBytes ?? null, signed: false });
5493
- entry.visibility = "public";
5494
- entry.tenantId = null;
5495
- indexId = await idx.registerBuilding(entry);
5496
- ref = `${repo}@${digest}`;
5497
- await bakes.setIndexId(bakeId, indexId);
5498
- registerSteps.push({ step: "register", status: "ok" });
5499
- }
5500
- catch (err) {
5561
+ const profile = bake.profile && bake.profile !== "custom" ? bake.profile : (manifestBody.profile && manifestBody.profile !== "custom" ? manifestBody.profile : "custom");
5562
+ repo = registry ? `${registry}/${profile}` : `sema-images/${profile}`;
5563
+ manifestSha = typeof line.manifestSha === "string" ? line.manifestSha : (typeof line.manifest?.sha256 === "string" ? String(line.manifest.sha256) : bake.manifestSha);
5564
+ if (pgHasUnstorable(digest) || pgHasUnstorable(repo) || pgHasUnstorable(manifestSha)) {
5501
5565
  indexId = null;
5502
- ref = `${repo}@${digest}`;
5503
- finalStatus = "FAILED";
5504
- registerDowngraded = true;
5505
- finalExit = exitCode ?? 0;
5506
- finalError = redactSecrets(`index register failed after a COMPLETE build (built-but-unregistered partial): ${String(err?.message ?? err)}`);
5507
- registerSteps.push({ step: "register", status: "fail", reason: "index register failed (TiDB?)" });
5566
+ registerSteps.push({ step: "register", status: "skipped", reason: "identity fields contained unstorable bytes" });
5567
+ }
5568
+ else {
5569
+ registerSteps.push({ step: "register", status: "running" });
5570
+ try {
5571
+ const entry = manifestToIndexEntry(manifestBody, { repo, digest, status: "building", manifestSha, sizeBytes: manifestBody.sizeBytes ?? null, signed: false });
5572
+ entry.visibility = "public";
5573
+ entry.tenantId = null;
5574
+ indexId = await idx.registerBuilding(entry);
5575
+ ref = `${repo}@${digest}`;
5576
+ const bound = await bakes.setIndexId(bakeId, indexId);
5577
+ if (!bound)
5578
+ indexId = null;
5579
+ registerSteps.push({ step: "register", status: "ok" });
5580
+ }
5581
+ catch (err) {
5582
+ indexId = null;
5583
+ ref = `${repo}@${digest}`;
5584
+ finalStatus = "FAILED";
5585
+ registerDowngraded = true;
5586
+ finalExit = exitCode ?? 0;
5587
+ finalError = redactSecrets(`index register failed after a COMPLETE build (built-but-unregistered partial): ${String(err?.message ?? err)}`);
5588
+ registerSteps.push({ step: "register", status: "fail", reason: "index register failed (TiDB?)" });
5589
+ }
5508
5590
  }
5509
5591
  }
5510
5592
  }
@@ -5519,6 +5601,19 @@ async function ingestBakeLine(bakes, idx, bake, line, registry) {
5519
5601
  const conflictBands = errorCode === "band-conflict" && Array.isArray(line.conflictBands)
5520
5602
  ? line.conflictBands.filter((b) => typeof b === "string")
5521
5603
  : null;
5604
+ const identityInvalid = (typeof digest === "string" && pgHasUnstorable(digest)) ||
5605
+ (typeof repo === "string" && pgHasUnstorable(repo)) ||
5606
+ (typeof ref === "string" && pgHasUnstorable(ref)) ||
5607
+ (typeof manifestSha === "string" && pgHasUnstorable(manifestSha));
5608
+ if (identityInvalid) {
5609
+ finalStatus = "FAILED";
5610
+ digest = null;
5611
+ repo = null;
5612
+ ref = null;
5613
+ manifestSha = null;
5614
+ indexId = null;
5615
+ finalError = finalError ? `${finalError}; identity fields contained unstorable bytes` : "terminal frame identity fields contained unstorable bytes";
5616
+ }
5522
5617
  const finalState = (finalStatus === "COMPLETE" ? "COMPLETE" : finalStatus === "CANCELLED" ? "CANCELLED" : "FAILED");
5523
5618
  const coarse = coarseStatusForState(finalState);
5524
5619
  let affectedRows = 0;
@@ -5576,7 +5671,8 @@ function headerStr(v) {
5576
5671
  return Array.isArray(v) ? v[0] : v;
5577
5672
  }
5578
5673
  function isBillableSubmitPath(url) {
5579
- return (url === "/v1/tasks" ||
5674
+ return (url === "/v1/side-query" ||
5675
+ url === "/v1/tasks" ||
5580
5676
  url === "/v1/tasks/stream" ||
5581
5677
  url === "/v1/runs" ||
5582
5678
  url === "/v1/leader" ||