@stackbone/sdk 0.1.0-alpha.1 → 0.1.0-alpha.2

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.
package/index.js CHANGED
@@ -1786,9 +1786,9 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
1786
1786
  var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
1787
1787
  var extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
1788
1788
  var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
1789
- var uuid = /* @__PURE__ */ __name((version2) => {
1790
- if (!version2) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
1791
- return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
1789
+ var uuid = /* @__PURE__ */ __name((version3) => {
1790
+ if (!version3) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
1791
+ return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version3}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
1792
1792
  }, "uuid");
1793
1793
  var uuid4 = /* @__PURE__ */ uuid(4);
1794
1794
  var uuid6 = /* @__PURE__ */ uuid(6);
@@ -14972,10 +14972,10 @@ function fromJSONSchema(schema, params) {
14972
14972
  if (typeof schema === "boolean") {
14973
14973
  return schema ? z.any() : z.never();
14974
14974
  }
14975
- const version2 = detectVersion(schema, params?.defaultTarget);
14975
+ const version3 = detectVersion(schema, params?.defaultTarget);
14976
14976
  const defs = schema.$defs || schema.definitions || {};
14977
14977
  const ctx = {
14978
- version: version2,
14978
+ version: version3,
14979
14979
  defs,
14980
14980
  refs: /* @__PURE__ */ new Map(),
14981
14981
  processing: /* @__PURE__ */ new Set(),
@@ -16644,6 +16644,34 @@ function pickDefinedAsStrings(source) {
16644
16644
  }
16645
16645
  __name(pickDefinedAsStrings, "pickDefinedAsStrings");
16646
16646
 
16647
+ // src/lib/facade-helpers.ts
16648
+ function remapHttpNotFound(code, domainCode, message, meta3) {
16649
+ if (code !== "http_not_found") return null;
16650
+ const error48 = {
16651
+ code: domainCode,
16652
+ message
16653
+ };
16654
+ if (meta3) error48.meta = meta3;
16655
+ return err(error48);
16656
+ }
16657
+ __name(remapHttpNotFound, "remapHttpNotFound");
16658
+ function validateStringArray(values, field, errorCode) {
16659
+ if (!Array.isArray(values) || values.length === 0) {
16660
+ return err({
16661
+ code: errorCode,
16662
+ message: `\`${field}\` must be a non-empty array.`
16663
+ });
16664
+ }
16665
+ if (values.some((v) => typeof v !== "string" || !v.trim())) {
16666
+ return err({
16667
+ code: errorCode,
16668
+ message: `\`${field}[*]\` must be non-empty strings.`
16669
+ });
16670
+ }
16671
+ return null;
16672
+ }
16673
+ __name(validateStringArray, "validateStringArray");
16674
+
16647
16675
  // src/facades/config/config.ts
16648
16676
  var ConfigFacade = class {
16649
16677
  static {
@@ -16665,7 +16693,11 @@ var ConfigFacade = class {
16665
16693
  const gateResult = await this._gate();
16666
16694
  if (gateResult.error) return err(gateResult.error);
16667
16695
  const result = await this._http.get(`/api/config/${encodeURIComponent(key)}`);
16668
- if (result.error) return remapNotFound(result.error.code, key) ?? result;
16696
+ if (result.error) {
16697
+ return remapHttpNotFound(result.error.code, "config_not_found", `Config key \`${key}\` is not set for this agent.`, {
16698
+ key
16699
+ }) ?? result;
16700
+ }
16669
16701
  return ok(result.data.value);
16670
16702
  }
16671
16703
  /**
@@ -16675,18 +16707,8 @@ var ConfigFacade = class {
16675
16707
  * is `Partial<T>`).
16676
16708
  */
16677
16709
  async getMany(keys) {
16678
- if (!Array.isArray(keys) || keys.length === 0) {
16679
- return err({
16680
- code: "config_invalid_request",
16681
- message: "`keys` must be a non-empty array."
16682
- });
16683
- }
16684
- if (keys.some((k) => !k.trim())) {
16685
- return err({
16686
- code: "config_invalid_request",
16687
- message: "`keys[*]` must be non-empty strings."
16688
- });
16689
- }
16710
+ const invalid = validateStringArray(keys, "keys", "config_invalid_request");
16711
+ if (invalid) return invalid;
16690
16712
  const gateResult = await this._gate();
16691
16713
  if (gateResult.error) return err(gateResult.error);
16692
16714
  const result = await this._http.get("/api/config", {
@@ -16705,17 +16727,6 @@ var ConfigFacade = class {
16705
16727
  return ok(values);
16706
16728
  }
16707
16729
  };
16708
- function remapNotFound(code, key) {
16709
- if (code !== "http_not_found") return null;
16710
- return err({
16711
- code: "config_not_found",
16712
- message: `Config key \`${key}\` is not set for this agent.`,
16713
- meta: {
16714
- key
16715
- }
16716
- });
16717
- }
16718
- __name(remapNotFound, "remapNotFound");
16719
16730
 
16720
16731
  // src/facades/connections/connections.ts
16721
16732
  var ConnectionsFacade = class {
@@ -16801,7 +16812,11 @@ var SecretsFacade = class {
16801
16812
  const gateResult = await this._gate();
16802
16813
  if (gateResult.error) return err(gateResult.error);
16803
16814
  const result = await this._http.get(`/api/secrets/${encodeURIComponent(name)}`);
16804
- if (result.error) return remapNotFound2(result.error.code, name) ?? result;
16815
+ if (result.error) {
16816
+ return remapHttpNotFound(result.error.code, "secrets_not_found", `Secret \`${name}\` is not registered for this organization.`, {
16817
+ name
16818
+ }) ?? result;
16819
+ }
16805
16820
  if (typeof result.data.value !== "string") {
16806
16821
  return err({
16807
16822
  code: "secrets_invalid_response",
@@ -16814,23 +16829,13 @@ var SecretsFacade = class {
16814
16829
  return ok(result.data.value);
16815
16830
  }
16816
16831
  /**
16817
- * Names absent from the workspace come back as omissions in the response —
16832
+ * Names absent from the organization come back as omissions in the response —
16818
16833
  * the returned map only contains entries the control plane actually has.
16819
16834
  * Callers that need "all-or-nothing" semantics should diff the keys.
16820
16835
  */
16821
16836
  async getMany(names) {
16822
- if (!Array.isArray(names) || names.length === 0) {
16823
- return err({
16824
- code: "secrets_invalid_request",
16825
- message: "`names` must be a non-empty array."
16826
- });
16827
- }
16828
- if (names.some((n) => !n.trim())) {
16829
- return err({
16830
- code: "secrets_invalid_request",
16831
- message: "`names[*]` must be non-empty strings."
16832
- });
16833
- }
16837
+ const invalid = validateStringArray(names, "names", "secrets_invalid_request");
16838
+ if (invalid) return invalid;
16834
16839
  const gateResult = await this._gate();
16835
16840
  if (gateResult.error) return err(gateResult.error);
16836
16841
  const result = await this._http.get("/api/secrets", {
@@ -16849,17 +16854,6 @@ var SecretsFacade = class {
16849
16854
  return ok(values);
16850
16855
  }
16851
16856
  };
16852
- function remapNotFound2(code, name) {
16853
- if (code !== "http_not_found") return null;
16854
- return err({
16855
- code: "secrets_not_found",
16856
- message: `Secret \`${name}\` is not registered for this workspace.`,
16857
- meta: {
16858
- name
16859
- }
16860
- });
16861
- }
16862
- __name(remapNotFound2, "remapNotFound");
16863
16857
 
16864
16858
  // src/lib/http-client.ts
16865
16859
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([
@@ -17742,6 +17736,11 @@ async function aggregateRunCost(runId, options) {
17742
17736
  };
17743
17737
  }
17744
17738
  __name(aggregateRunCost, "aggregateRunCost");
17739
+
17740
+ // package.json
17741
+ var version2 = "0.1.0-alpha.2";
17742
+
17743
+ // src/observability/logger.ts
17745
17744
  var LOG_LEVELS = {
17746
17745
  trace: 10,
17747
17746
  debug: 20,
@@ -17955,7 +17954,7 @@ var OtlpHttpDestination = class OtlpHttpDestination2 {
17955
17954
  };
17956
17955
  var SDK_SCOPE = {
17957
17956
  name: "@stackbone/sdk",
17958
- version: "0.0.1"
17957
+ version: version2
17959
17958
  };
17960
17959
  function buildOtlpLogsBody(records, resourceAttributes) {
17961
17960
  return {
@@ -17988,11 +17987,12 @@ function toOtlpLogRecord(record2) {
17988
17987
  };
17989
17988
  }
17990
17989
  __name(toOtlpLogRecord, "toOtlpLogRecord");
17990
+ var SEVERITY_TEXT_BY_LEVEL = Object.fromEntries(Object.entries(LOG_LEVELS).map(([name, value]) => [
17991
+ value,
17992
+ name.toUpperCase()
17993
+ ]));
17991
17994
  function severityText(level) {
17992
- for (const [name, value] of Object.entries(LOG_LEVELS)) {
17993
- if (value === level) return name.toUpperCase();
17994
- }
17995
- return "INFO";
17995
+ return SEVERITY_TEXT_BY_LEVEL[level] ?? "INFO";
17996
17996
  }
17997
17997
  __name(severityText, "severityText");
17998
17998
  function toOtlpAttributes(attrs) {
@@ -18160,11 +18160,11 @@ var RunStepsSpanProcessor = class {
18160
18160
  }
18161
18161
  }
18162
18162
  buildRow(span) {
18163
- const runId = readString(span.attributes[RUN_ID_ATTRIBUTE]);
18164
- if (!runId) return null;
18165
18163
  const spanId = span.spanContext().spanId;
18166
18164
  const stepId = this.stepIdBySpanId.get(spanId) ?? this.newId();
18167
18165
  this.stepIdBySpanId.delete(spanId);
18166
+ const runId = readString(span.attributes[RUN_ID_ATTRIBUTE]);
18167
+ if (!runId) return null;
18168
18168
  const parentSpanId = span.parentSpanContext?.spanId ?? span.parentSpanId ?? null;
18169
18169
  const parentStepId = parentSpanId ? this.stepIdBySpanId.get(parentSpanId) ?? null : null;
18170
18170
  const startedAt = formatHrTime(span.startTime);
@@ -18614,6 +18614,41 @@ function isUnsupportedModelError(error48) {
18614
18614
  }
18615
18615
  __name(isUnsupportedModelError, "isUnsupportedModelError");
18616
18616
 
18617
+ // src/modules/rag/errors.ts
18618
+ var PG_UNDEFINED_TABLE = "42P01";
18619
+ var RAG_SCHEMA_MISSING_HINT = "run `stackbone db migrate add-rag` and `stackbone db migrate up`";
18620
+ function isTaggedError(cause) {
18621
+ return cause instanceof Error;
18622
+ }
18623
+ __name(isTaggedError, "isTaggedError");
18624
+ function toRagError(cause, message, fallbackCode = "rag_error") {
18625
+ if (isTaggedError(cause) && cause.code === PG_UNDEFINED_TABLE) {
18626
+ return {
18627
+ code: "rag_schema_missing",
18628
+ message: `RAG tables are not installed \u2014 ${RAG_SCHEMA_MISSING_HINT}.`,
18629
+ cause,
18630
+ meta: {
18631
+ hint: RAG_SCHEMA_MISSING_HINT
18632
+ }
18633
+ };
18634
+ }
18635
+ if (isTaggedError(cause)) {
18636
+ const error48 = {
18637
+ code: cause.code ?? fallbackCode,
18638
+ message: `${message}: ${cause.message}`,
18639
+ cause
18640
+ };
18641
+ if (cause.meta) error48.meta = cause.meta;
18642
+ return error48;
18643
+ }
18644
+ return {
18645
+ code: fallbackCode,
18646
+ message,
18647
+ cause
18648
+ };
18649
+ }
18650
+ __name(toRagError, "toRagError");
18651
+
18617
18652
  // src/modules/rag/jobs.ts
18618
18653
  function createSqlJobWriter(sql) {
18619
18654
  return {
@@ -18639,7 +18674,7 @@ function createSqlJobWriter(sql) {
18639
18674
  jobId: row.id
18640
18675
  });
18641
18676
  } catch (cause) {
18642
- return err(toJobsError(cause, "Job start failed"));
18677
+ return err(toRagError(cause, "Job start failed", "rag_jobs_error"));
18643
18678
  }
18644
18679
  },
18645
18680
  async progress({ jobId, processedChunks, totalChunks, currentDocument }) {
@@ -18678,7 +18713,7 @@ function createSqlJobWriter(sql) {
18678
18713
  },
18679
18714
  async isCancelled({ jobId }) {
18680
18715
  const rows = await sql`
18681
- SELECT id, status FROM stackbone_rag_jobs WHERE id = ${jobId}
18716
+ SELECT status FROM stackbone_rag_jobs WHERE id = ${jobId} LIMIT 1
18682
18717
  `;
18683
18718
  return rows[0]?.status === "cancelled";
18684
18719
  }
@@ -18686,10 +18721,6 @@ function createSqlJobWriter(sql) {
18686
18721
  }
18687
18722
  __name(createSqlJobWriter, "createSqlJobWriter");
18688
18723
  async function ensureCollection(sql, name) {
18689
- const existing = await sql`
18690
- SELECT id FROM stackbone_rag_collections WHERE name = ${name} LIMIT 1
18691
- `;
18692
- if (existing[0]) return existing[0].id;
18693
18724
  const inserted = await sql`
18694
18725
  INSERT INTO stackbone_rag_collections (name) VALUES (${name})
18695
18726
  ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
@@ -18697,37 +18728,11 @@ async function ensureCollection(sql, name) {
18697
18728
  `;
18698
18729
  const row = inserted[0];
18699
18730
  if (!row) {
18700
- throw new Error(`stackbone_rag_collections insert returned no row for ${name}`);
18731
+ throw new Error(`stackbone_rag_collections upsert returned no row for ${name}`);
18701
18732
  }
18702
18733
  return row.id;
18703
18734
  }
18704
18735
  __name(ensureCollection, "ensureCollection");
18705
- function toJobsError(cause, message) {
18706
- if (cause instanceof Error) {
18707
- const tagged = cause;
18708
- if (tagged.code === "42P01") {
18709
- return {
18710
- code: "rag_schema_missing",
18711
- message: "RAG tables are not installed \u2014 run `stackbone db migrate add-rag` and `stackbone db migrate up`.",
18712
- cause,
18713
- meta: {
18714
- hint: "run `stackbone db migrate add-rag` and `stackbone db migrate up`"
18715
- }
18716
- };
18717
- }
18718
- return {
18719
- code: tagged.code ?? "rag_jobs_error",
18720
- message: `${message}: ${cause.message}`,
18721
- cause
18722
- };
18723
- }
18724
- return {
18725
- code: "rag_jobs_error",
18726
- message,
18727
- cause
18728
- };
18729
- }
18730
- __name(toJobsError, "toJobsError");
18731
18736
  var PDF_MIME_TYPES = /* @__PURE__ */ new Set([
18732
18737
  "application/pdf",
18733
18738
  "application/x-pdf"
@@ -18787,10 +18792,12 @@ __name(looksLikePdf, "looksLikePdf");
18787
18792
  var DEFAULT_NAMESPACE = "default";
18788
18793
  var META_KEY_DIMENSIONS = "dimensions";
18789
18794
  var META_KEY_DISTANCE = "distance";
18795
+ var schemaVerifiedDimensions = /* @__PURE__ */ new WeakMap();
18790
18796
  async function ensureSchema(sql, dimensions) {
18791
18797
  if (!Number.isInteger(dimensions) || dimensions <= 0 || dimensions > 8192) {
18792
18798
  throw new Error(`rag.ensureSchema: invalid dimensions ${dimensions}. Must be a positive integer <= 8192.`);
18793
18799
  }
18800
+ if (schemaVerifiedDimensions.get(sql) === dimensions) return;
18794
18801
  const existing = await sql`
18795
18802
  SELECT value::int AS value FROM _rag_meta WHERE key = ${META_KEY_DIMENSIONS}
18796
18803
  `.catch(() => []);
@@ -18805,6 +18812,7 @@ async function ensureSchema(sql, dimensions) {
18805
18812
  };
18806
18813
  throw error48;
18807
18814
  }
18815
+ schemaVerifiedDimensions.set(sql, dimensions);
18808
18816
  return;
18809
18817
  }
18810
18818
  await sql.begin(async (tx) => {
@@ -18824,6 +18832,7 @@ async function ensureSchema(sql, dimensions) {
18824
18832
  ON CONFLICT (key) DO NOTHING
18825
18833
  `;
18826
18834
  });
18835
+ schemaVerifiedDimensions.set(sql, dimensions);
18827
18836
  }
18828
18837
  __name(ensureSchema, "ensureSchema");
18829
18838
  async function reset(sql) {
@@ -18831,29 +18840,31 @@ async function reset(sql) {
18831
18840
  await tx`DROP TABLE IF EXISTS rag_chunks`;
18832
18841
  await tx`DROP TABLE IF EXISTS _rag_meta`;
18833
18842
  });
18843
+ schemaVerifiedDimensions.delete(sql);
18834
18844
  }
18835
18845
  __name(reset, "reset");
18836
- async function ingestDocument(sql, docId, namespace, rows) {
18846
+ async function clearDocument(sql, docId, namespace) {
18847
+ await sql`
18848
+ DELETE FROM rag_chunks
18849
+ WHERE namespace = ${namespace} AND doc_id = ${docId}
18850
+ `;
18851
+ }
18852
+ __name(clearDocument, "clearDocument");
18853
+ async function ingestDocument(sql, rows) {
18837
18854
  if (rows.length === 0) return 0;
18838
- await sql.begin(async (tx) => {
18839
- await tx`
18840
- DELETE FROM rag_chunks
18841
- WHERE namespace = ${namespace} AND doc_id = ${docId}
18842
- `;
18843
- const placeholders = rows.map((_, i) => {
18844
- const base = i * 6;
18845
- return `($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}, $${base + 5}::vector, $${base + 6}::jsonb)`;
18846
- }).join(",");
18847
- const params = rows.flatMap((row) => [
18848
- row.docId,
18849
- row.chunkIdx,
18850
- row.namespace,
18851
- row.content,
18852
- formatVector(row.embedding),
18853
- JSON.stringify(row.metadata)
18854
- ]);
18855
- await tx.unsafe(`INSERT INTO rag_chunks (doc_id, chunk_idx, namespace, content, embedding, metadata) VALUES ${placeholders}`, params);
18856
- });
18855
+ const placeholders = rows.map((_, i) => {
18856
+ const base = i * 6;
18857
+ return `($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}, $${base + 5}::vector, $${base + 6}::jsonb)`;
18858
+ }).join(",");
18859
+ const params = rows.flatMap((row) => [
18860
+ row.docId,
18861
+ row.chunkIdx,
18862
+ row.namespace,
18863
+ row.content,
18864
+ formatVector(row.embedding),
18865
+ JSON.stringify(row.metadata)
18866
+ ]);
18867
+ await sql.unsafe(`INSERT INTO rag_chunks (doc_id, chunk_idx, namespace, content, embedding, metadata) VALUES ${placeholders}`, params);
18857
18868
  return rows.length;
18858
18869
  }
18859
18870
  __name(ingestDocument, "ingestDocument");
@@ -18923,8 +18934,6 @@ var CHUNK_INDEXES_DDL = `
18923
18934
 
18924
18935
  // src/modules/rag/pipeline.ts
18925
18936
  var DEFAULT_TOP_K = 5;
18926
- var RAG_SCHEMA_MISSING_HINT = "run `stackbone db migrate add-rag` and `stackbone db migrate up`";
18927
- var PG_UNDEFINED_TABLE = "42P01";
18928
18937
  var RagPipeline = class {
18929
18938
  static {
18930
18939
  __name(this, "RagPipeline");
@@ -18989,19 +18998,16 @@ var RagPipeline = class {
18989
18998
  if (started.error) return err(started.error);
18990
18999
  jobId = started.data.jobId;
18991
19000
  }
18992
- const emit = /* @__PURE__ */ __name(async (event) => {
18993
- if (onProgress) await onProgress(event);
18994
- }, "emit");
18995
- const startEvent = {
19001
+ await onProgress?.({
18996
19002
  type: "started",
18997
19003
  jobId: jobId ?? request.id,
18998
19004
  totalChunks
18999
- };
19000
- await emit(startEvent);
19005
+ });
19001
19006
  try {
19002
19007
  await ensureSchema(sql.data, dimensions);
19003
19008
  const needsBatching = Boolean(writer || onProgress);
19004
19009
  const batchSize = this._progressBatchSize ?? (needsBatching ? 1 : rows.length || 1);
19010
+ await clearDocument(sql.data, request.id, namespace);
19005
19011
  let processed = 0;
19006
19012
  for (let offset = 0; offset < rows.length; offset += batchSize) {
19007
19013
  if (writer && jobId) {
@@ -19021,37 +19027,33 @@ var RagPipeline = class {
19021
19027
  }
19022
19028
  }
19023
19029
  const slice = rows.slice(offset, offset + batchSize);
19024
- await ingestDocument(sql.data, request.id, namespace, slice);
19030
+ await ingestDocument(sql.data, slice);
19025
19031
  processed += slice.length;
19026
- const progressEvent = {
19032
+ const currentDocument = {
19033
+ source: request.id,
19034
+ ordinal: processed - 1
19035
+ };
19036
+ await onProgress?.({
19027
19037
  type: "progress",
19028
19038
  jobId: jobId ?? request.id,
19029
19039
  processedChunks: processed,
19030
19040
  totalChunks,
19031
- currentDocument: {
19032
- source: request.id,
19033
- ordinal: processed - 1
19034
- }
19035
- };
19036
- await emit(progressEvent);
19041
+ currentDocument
19042
+ });
19037
19043
  if (writer && jobId) {
19038
19044
  await writer.progress({
19039
19045
  jobId,
19040
19046
  processedChunks: processed,
19041
19047
  totalChunks,
19042
- currentDocument: {
19043
- source: request.id,
19044
- ordinal: processed - 1
19045
- }
19048
+ currentDocument
19046
19049
  });
19047
19050
  }
19048
19051
  }
19049
- const completedEvent = {
19052
+ await onProgress?.({
19050
19053
  type: "completed",
19051
19054
  jobId: jobId ?? request.id,
19052
19055
  totalChunks
19053
- };
19054
- await emit(completedEvent);
19056
+ });
19055
19057
  if (writer && jobId) await writer.complete({
19056
19058
  jobId,
19057
19059
  totalChunks
@@ -19062,12 +19064,11 @@ var RagPipeline = class {
19062
19064
  });
19063
19065
  } catch (cause) {
19064
19066
  const mapped = toRagError(cause, "Ingest failed");
19065
- const failedEvent = {
19067
+ await onProgress?.({
19066
19068
  type: "failed",
19067
19069
  jobId: jobId ?? request.id,
19068
19070
  error: mapped.message
19069
- };
19070
- await emit(failedEvent);
19071
+ });
19071
19072
  if (writer && jobId) await writer.fail({
19072
19073
  jobId,
19073
19074
  error: mapped.message
@@ -19227,37 +19228,6 @@ function validateIngest(request, hasEmbedder) {
19227
19228
  return ok(true);
19228
19229
  }
19229
19230
  __name(validateIngest, "validateIngest");
19230
- function isTaggedError(cause) {
19231
- return cause instanceof Error;
19232
- }
19233
- __name(isTaggedError, "isTaggedError");
19234
- function toRagError(cause, message) {
19235
- if (isTaggedError(cause) && cause.code === PG_UNDEFINED_TABLE) {
19236
- return {
19237
- code: "rag_schema_missing",
19238
- message: `RAG tables are not installed \u2014 ${RAG_SCHEMA_MISSING_HINT}.`,
19239
- cause,
19240
- meta: {
19241
- hint: RAG_SCHEMA_MISSING_HINT
19242
- }
19243
- };
19244
- }
19245
- if (isTaggedError(cause)) {
19246
- const error48 = {
19247
- code: cause.code ?? "rag_error",
19248
- message: `${message}: ${cause.message}`,
19249
- cause
19250
- };
19251
- if (cause.meta) error48.meta = cause.meta;
19252
- return error48;
19253
- }
19254
- return {
19255
- code: "rag_error",
19256
- message,
19257
- cause
19258
- };
19259
- }
19260
- __name(toRagError, "toRagError");
19261
19231
 
19262
19232
  // src/modules/rag/rag.ts
19263
19233
  var DEFAULT_BATCH_SIZE4 = 128;
@@ -19269,10 +19239,14 @@ var RagModule = class {
19269
19239
  _resolved;
19270
19240
  _getAi;
19271
19241
  _gate;
19272
- constructor(_resolved, _getAi, gate) {
19242
+ _testSqlOverride;
19243
+ _testJobWriterFactory;
19244
+ constructor(_resolved, _getAi, gate, testDeps) {
19273
19245
  this._resolved = _resolved;
19274
19246
  this._getAi = _getAi;
19275
19247
  this._gate = gate ?? createModuleGate("rag", _resolved);
19248
+ if (testDeps?.sqlProvider) this._testSqlOverride = testDeps.sqlProvider;
19249
+ if (testDeps?.jobWriterFactory) this._testJobWriterFactory = testDeps.jobWriterFactory;
19276
19250
  }
19277
19251
  async ingest(request) {
19278
19252
  const gateResult = await this._gate();
@@ -19305,7 +19279,8 @@ var RagModule = class {
19305
19279
  if (gateResult.error) return err(gateResult.error);
19306
19280
  const sql = this._testSqlOverride ? this._testSqlOverride() : this.sql();
19307
19281
  if (sql.error) return err(sql.error);
19308
- const writer = this._testJobWriter ?? createSqlJobWriter(sql.data);
19282
+ const writerFactory = this._testJobWriterFactory ?? createSqlJobWriter;
19283
+ const writer = writerFactory(sql.data);
19309
19284
  const channel = createProgressChannel();
19310
19285
  const resolved = resolveIngestModel(request, this._resolved);
19311
19286
  const requestWithHook = {
@@ -19336,7 +19311,7 @@ var RagModule = class {
19336
19311
  return r;
19337
19312
  }).catch((cause) => {
19338
19313
  channel.close();
19339
- return err(toRagError2(cause, "Ingest failed"));
19314
+ return err(toRagError(cause, "Ingest failed"));
19340
19315
  });
19341
19316
  return ok({
19342
19317
  jobId,
@@ -19344,10 +19319,6 @@ var RagModule = class {
19344
19319
  result
19345
19320
  });
19346
19321
  }
19347
- /** Test-only seam — assigned by `ingest-async.spec.ts`. */
19348
- _testJobWriter;
19349
- /** Test-only seam — assigned by `ingest-async.spec.ts`. */
19350
- _testSqlOverride;
19351
19322
  async delete(ids, options) {
19352
19323
  const gateResult = await this._gate();
19353
19324
  if (gateResult.error) return err(gateResult.error);
@@ -19379,7 +19350,7 @@ var RagModule = class {
19379
19350
  await reset(sql.data);
19380
19351
  return ok(void 0);
19381
19352
  } catch (cause) {
19382
- return err(toRagError2(cause, "Reset failed"));
19353
+ return err(toRagError(cause, "Reset failed"));
19383
19354
  }
19384
19355
  }
19385
19356
  /**
@@ -19407,22 +19378,18 @@ var RagModule = class {
19407
19378
  * `Result.err` with the same `database_not_configured` shape `client.database`
19408
19379
  * raises, instead of throwing through the public surface.
19409
19380
  */
19410
- // `_resolved` is reserved for future per-config overrides (e.g. routing
19411
- // RAG queries through a logical replica). The URL itself is owned by the
19412
- // shared handle.
19413
19381
  sql() {
19414
- void this._resolved;
19415
19382
  try {
19416
19383
  return ok(getDatabaseHandle().$client);
19417
19384
  } catch (cause) {
19418
- if (isTaggedError2(cause) && cause.code === "database_not_configured") {
19385
+ if (isTaggedError(cause) && cause.code === "database_not_configured") {
19419
19386
  return err({
19420
19387
  code: "database_not_configured",
19421
19388
  message: cause.message,
19422
19389
  cause
19423
19390
  });
19424
19391
  }
19425
- return err(toRagError2(cause, "Database handle unavailable"));
19392
+ return err(toRagError(cause, "Database handle unavailable"));
19426
19393
  }
19427
19394
  }
19428
19395
  async withPipeline(embedder, run) {
@@ -19543,27 +19510,6 @@ function resolveRetrieveModel(request, resolved) {
19543
19510
  };
19544
19511
  }
19545
19512
  __name(resolveRetrieveModel, "resolveRetrieveModel");
19546
- function isTaggedError2(cause) {
19547
- return cause instanceof Error;
19548
- }
19549
- __name(isTaggedError2, "isTaggedError");
19550
- function toRagError2(cause, message) {
19551
- if (isTaggedError2(cause)) {
19552
- const error48 = {
19553
- code: cause.code ?? "rag_error",
19554
- message: `${message}: ${cause.message}`,
19555
- cause
19556
- };
19557
- if (cause.meta) error48.meta = cause.meta;
19558
- return error48;
19559
- }
19560
- return {
19561
- code: "rag_error",
19562
- message,
19563
- cause
19564
- };
19565
- }
19566
- __name(toRagError2, "toRagError");
19567
19513
  var DEFAULT_SIGNED_URL_TTL_SECONDS = 3600;
19568
19514
  var StorageModule = class {
19569
19515
  static {
@@ -19765,29 +19711,19 @@ var StorageBucket = class {
19765
19711
  return ok(`${base}/${bucket}/${encodeS3Key(fullKey)}`);
19766
19712
  }
19767
19713
  async getSignedUploadUrl(key, options) {
19768
- const gateResult = await this.gate();
19769
- if (gateResult.error) return err(gateResult.error);
19770
- const resolved = this.resolve(key);
19771
- if (resolved.error) return err(resolved.error);
19772
- const { client, bucket, fullKey } = resolved.data;
19773
- const expiresIn = options?.expiresIn ?? DEFAULT_SIGNED_URL_TTL_SECONDS;
19774
- try {
19775
- const url2 = await getSignedUrl(client, new PutObjectCommand({
19776
- Bucket: bucket,
19777
- Key: fullKey,
19778
- ContentType: options?.contentType
19779
- }), {
19780
- expiresIn
19781
- });
19782
- return ok({
19783
- url: url2,
19784
- expiresAt: new Date(Date.now() + expiresIn * 1e3)
19785
- });
19786
- } catch (cause) {
19787
- return err(toStorageError("Signing upload URL failed", cause));
19788
- }
19714
+ return this.signUrl(key, options, "Signing upload URL failed", ({ bucket, fullKey }) => new PutObjectCommand({
19715
+ Bucket: bucket,
19716
+ Key: fullKey,
19717
+ ContentType: options?.contentType
19718
+ }));
19789
19719
  }
19790
19720
  async getSignedDownloadUrl(key, options) {
19721
+ return this.signUrl(key, options, "Signing download URL failed", ({ bucket, fullKey }) => new GetObjectCommand({
19722
+ Bucket: bucket,
19723
+ Key: fullKey
19724
+ }));
19725
+ }
19726
+ async signUrl(key, options, failureMessage, buildCommand) {
19791
19727
  const gateResult = await this.gate();
19792
19728
  if (gateResult.error) return err(gateResult.error);
19793
19729
  const resolved = this.resolve(key);
@@ -19795,9 +19731,9 @@ var StorageBucket = class {
19795
19731
  const { client, bucket, fullKey } = resolved.data;
19796
19732
  const expiresIn = options?.expiresIn ?? DEFAULT_SIGNED_URL_TTL_SECONDS;
19797
19733
  try {
19798
- const url2 = await getSignedUrl(client, new GetObjectCommand({
19799
- Bucket: bucket,
19800
- Key: fullKey
19734
+ const url2 = await getSignedUrl(client, buildCommand({
19735
+ bucket,
19736
+ fullKey
19801
19737
  }), {
19802
19738
  expiresIn
19803
19739
  });
@@ -19806,7 +19742,7 @@ var StorageBucket = class {
19806
19742
  expiresAt: new Date(Date.now() + expiresIn * 1e3)
19807
19743
  });
19808
19744
  } catch (cause) {
19809
- return err(toStorageError("Signing download URL failed", cause));
19745
+ return err(toStorageError(failureMessage, cause));
19810
19746
  }
19811
19747
  }
19812
19748
  /**