@mastra/factory 0.2.0-alpha.0 → 0.2.1-alpha.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 (51) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/capabilities/intake.d.ts +45 -0
  3. package/dist/capabilities/intake.d.ts.map +1 -1
  4. package/dist/factory.js +923 -552
  5. package/dist/factory.js.map +1 -1
  6. package/dist/index.js +923 -552
  7. package/dist/index.js.map +1 -1
  8. package/dist/integrations/github/integration.d.ts.map +1 -1
  9. package/dist/integrations/github/integration.js +127 -12
  10. package/dist/integrations/github/integration.js.map +1 -1
  11. package/dist/integrations/github/project-lock.d.ts +24 -2
  12. package/dist/integrations/github/project-lock.d.ts.map +1 -1
  13. package/dist/integrations/github/project-lock.js +43 -10
  14. package/dist/integrations/github/project-lock.js.map +1 -1
  15. package/dist/integrations/github/routes.js +42 -11
  16. package/dist/integrations/github/routes.js.map +1 -1
  17. package/dist/integrations/linear/agent-tools.js.map +1 -1
  18. package/dist/integrations/linear/integration.d.ts.map +1 -1
  19. package/dist/integrations/linear/integration.js +60 -1
  20. package/dist/integrations/linear/integration.js.map +1 -1
  21. package/dist/integrations/linear/routes.js.map +1 -1
  22. package/dist/integrations/platform/api-client.js +1 -1
  23. package/dist/integrations/platform/api-client.js.map +1 -1
  24. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
  25. package/dist/integrations/platform/github/event-worker.js +7 -1
  26. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  27. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  28. package/dist/integrations/platform/github/integration.js +106 -14
  29. package/dist/integrations/platform/github/integration.js.map +1 -1
  30. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  31. package/dist/integrations/platform/linear/integration.js +92 -1
  32. package/dist/integrations/platform/linear/integration.js.map +1 -1
  33. package/dist/routes/fs.d.ts +16 -0
  34. package/dist/routes/fs.d.ts.map +1 -1
  35. package/dist/routes/fs.js +103 -3
  36. package/dist/routes/fs.js.map +1 -1
  37. package/dist/routes/oauth.js +564 -478
  38. package/dist/routes/oauth.js.map +1 -1
  39. package/dist/routes/projects.d.ts.map +1 -1
  40. package/dist/routes/projects.js +1 -0
  41. package/dist/routes/projects.js.map +1 -1
  42. package/dist/routes/surface.d.ts.map +1 -1
  43. package/dist/routes/surface.js +717 -526
  44. package/dist/routes/surface.js.map +1 -1
  45. package/dist/server-error.js +1 -1
  46. package/dist/server-error.js.map +1 -1
  47. package/dist/storage/domains/source-control/base.d.ts +0 -1
  48. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  49. package/dist/storage/domains/source-control/base.js +6 -7
  50. package/dist/storage/domains/source-control/base.js.map +1 -1
  51. package/package.json +6 -6
@@ -18882,46 +18882,49 @@ Note: This migration may take some time for large tables.
18882
18882
  config: input.config,
18883
18883
  observedTimezone: input.observedTimezone
18884
18884
  };
18885
- await this.#client.execute({
18886
- sql: `INSERT INTO "${OM_TABLE}" (
18887
- id, "lookupKey", scope, "resourceId", "threadId",
18888
- "activeObservations", "activeObservationsPendingUpdate",
18889
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
18890
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
18891
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
18892
- "observedTimezone", "createdAt", "updatedAt"
18893
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
18894
- args: [
18895
- id,
18896
- lookupKey,
18897
- input.scope,
18898
- input.resourceId,
18899
- input.threadId || null,
18900
- "",
18901
- null,
18902
- "initial",
18903
- JSON.stringify(input.config),
18904
- 0,
18905
- null,
18906
- null,
18907
- 0,
18908
- 0,
18909
- 0,
18910
- false,
18911
- false,
18912
- false,
18913
- // isBufferingObservation
18914
- false,
18915
- // isBufferingReflection
18916
- 0,
18917
- // lastBufferedAtTokens
18918
- null,
18919
- // lastBufferedAtTime
18920
- input.observedTimezone || null,
18921
- now.toISOString(),
18922
- now.toISOString()
18923
- ]
18924
- });
18885
+ await withClientWriteLock(
18886
+ this.#client,
18887
+ () => this.#client.execute({
18888
+ sql: `INSERT INTO "${OM_TABLE}" (
18889
+ id, "lookupKey", scope, "resourceId", "threadId",
18890
+ "activeObservations", "activeObservationsPendingUpdate",
18891
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
18892
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
18893
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
18894
+ "observedTimezone", "createdAt", "updatedAt"
18895
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
18896
+ args: [
18897
+ id,
18898
+ lookupKey,
18899
+ input.scope,
18900
+ input.resourceId,
18901
+ input.threadId || null,
18902
+ "",
18903
+ null,
18904
+ "initial",
18905
+ JSON.stringify(input.config),
18906
+ 0,
18907
+ null,
18908
+ null,
18909
+ 0,
18910
+ 0,
18911
+ 0,
18912
+ false,
18913
+ false,
18914
+ false,
18915
+ // isBufferingObservation
18916
+ false,
18917
+ // isBufferingReflection
18918
+ 0,
18919
+ // lastBufferedAtTokens
18920
+ null,
18921
+ // lastBufferedAtTime
18922
+ input.observedTimezone || null,
18923
+ now.toISOString(),
18924
+ now.toISOString()
18925
+ ]
18926
+ })
18927
+ );
18925
18928
  return record;
18926
18929
  } catch (error) {
18927
18930
  throw new MastraError(
@@ -18938,53 +18941,56 @@ Note: This migration may take some time for large tables.
18938
18941
  async insertObservationalMemoryRecord(record) {
18939
18942
  try {
18940
18943
  const lookupKey = this.getOMKey(record.threadId, record.resourceId);
18941
- await this.#client.execute({
18942
- sql: `INSERT INTO "${OM_TABLE}" (
18943
- id, "lookupKey", scope, "resourceId", "threadId",
18944
- "activeObservations", "activeObservationsPendingUpdate",
18945
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
18946
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
18947
- "observedMessageIds", "bufferedObservationChunks",
18948
- "bufferedReflection", "bufferedReflectionTokens", "bufferedReflectionInputTokens",
18949
- "reflectedObservationLineCount",
18950
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection",
18951
- "lastBufferedAtTokens", "lastBufferedAtTime",
18952
- "observedTimezone", metadata, "createdAt", "updatedAt"
18953
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
18954
- args: [
18955
- record.id,
18956
- lookupKey,
18957
- record.scope,
18958
- record.resourceId,
18959
- record.threadId || null,
18960
- record.activeObservations || "",
18961
- null,
18962
- record.originType || "initial",
18963
- record.config ? JSON.stringify(record.config) : null,
18964
- record.generationCount || 0,
18965
- record.lastObservedAt ? record.lastObservedAt.toISOString() : null,
18966
- null,
18967
- record.pendingMessageTokens || 0,
18968
- record.totalTokensObserved || 0,
18969
- record.observationTokenCount || 0,
18970
- record.observedMessageIds ? JSON.stringify(record.observedMessageIds) : null,
18971
- record.bufferedObservationChunks ? JSON.stringify(record.bufferedObservationChunks) : null,
18972
- record.bufferedReflection || null,
18973
- record.bufferedReflectionTokens ?? null,
18974
- record.bufferedReflectionInputTokens ?? null,
18975
- record.reflectedObservationLineCount ?? null,
18976
- record.isObserving || false,
18977
- record.isReflecting || false,
18978
- record.isBufferingObservation || false,
18979
- record.isBufferingReflection || false,
18980
- record.lastBufferedAtTokens || 0,
18981
- record.lastBufferedAtTime ? record.lastBufferedAtTime.toISOString() : null,
18982
- record.observedTimezone || null,
18983
- record.metadata ? JSON.stringify(record.metadata) : null,
18984
- record.createdAt.toISOString(),
18985
- record.updatedAt.toISOString()
18986
- ]
18987
- });
18944
+ await withClientWriteLock(
18945
+ this.#client,
18946
+ () => this.#client.execute({
18947
+ sql: `INSERT INTO "${OM_TABLE}" (
18948
+ id, "lookupKey", scope, "resourceId", "threadId",
18949
+ "activeObservations", "activeObservationsPendingUpdate",
18950
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
18951
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
18952
+ "observedMessageIds", "bufferedObservationChunks",
18953
+ "bufferedReflection", "bufferedReflectionTokens", "bufferedReflectionInputTokens",
18954
+ "reflectedObservationLineCount",
18955
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection",
18956
+ "lastBufferedAtTokens", "lastBufferedAtTime",
18957
+ "observedTimezone", metadata, "createdAt", "updatedAt"
18958
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
18959
+ args: [
18960
+ record.id,
18961
+ lookupKey,
18962
+ record.scope,
18963
+ record.resourceId,
18964
+ record.threadId || null,
18965
+ record.activeObservations || "",
18966
+ null,
18967
+ record.originType || "initial",
18968
+ record.config ? JSON.stringify(record.config) : null,
18969
+ record.generationCount || 0,
18970
+ record.lastObservedAt ? record.lastObservedAt.toISOString() : null,
18971
+ null,
18972
+ record.pendingMessageTokens || 0,
18973
+ record.totalTokensObserved || 0,
18974
+ record.observationTokenCount || 0,
18975
+ record.observedMessageIds ? JSON.stringify(record.observedMessageIds) : null,
18976
+ record.bufferedObservationChunks ? JSON.stringify(record.bufferedObservationChunks) : null,
18977
+ record.bufferedReflection || null,
18978
+ record.bufferedReflectionTokens ?? null,
18979
+ record.bufferedReflectionInputTokens ?? null,
18980
+ record.reflectedObservationLineCount ?? null,
18981
+ record.isObserving || false,
18982
+ record.isReflecting || false,
18983
+ record.isBufferingObservation || false,
18984
+ record.isBufferingReflection || false,
18985
+ record.lastBufferedAtTokens || 0,
18986
+ record.lastBufferedAtTime ? record.lastBufferedAtTime.toISOString() : null,
18987
+ record.observedTimezone || null,
18988
+ record.metadata ? JSON.stringify(record.metadata) : null,
18989
+ record.createdAt.toISOString(),
18990
+ record.updatedAt.toISOString()
18991
+ ]
18992
+ })
18993
+ );
18988
18994
  } catch (error) {
18989
18995
  throw new MastraError(
18990
18996
  {
@@ -19001,26 +19007,29 @@ Note: This migration may take some time for large tables.
19001
19007
  try {
19002
19008
  const now = /* @__PURE__ */ new Date();
19003
19009
  const observedMessageIdsJson = input.observedMessageIds ? JSON.stringify(input.observedMessageIds) : null;
19004
- const result = await this.#client.execute({
19005
- sql: `UPDATE "${OM_TABLE}" SET
19006
- "activeObservations" = ?,
19007
- "lastObservedAt" = ?,
19008
- "pendingMessageTokens" = 0,
19009
- "observationTokenCount" = ?,
19010
- "totalTokensObserved" = "totalTokensObserved" + ?,
19011
- "observedMessageIds" = ?,
19012
- "updatedAt" = ?
19013
- WHERE id = ?`,
19014
- args: [
19015
- input.observations,
19016
- input.lastObservedAt.toISOString(),
19017
- input.tokenCount,
19018
- input.tokenCount,
19019
- observedMessageIdsJson,
19020
- now.toISOString(),
19021
- input.id
19022
- ]
19023
- });
19010
+ const result = await withClientWriteLock(
19011
+ this.#client,
19012
+ () => this.#client.execute({
19013
+ sql: `UPDATE "${OM_TABLE}" SET
19014
+ "activeObservations" = ?,
19015
+ "lastObservedAt" = ?,
19016
+ "pendingMessageTokens" = 0,
19017
+ "observationTokenCount" = ?,
19018
+ "totalTokensObserved" = "totalTokensObserved" + ?,
19019
+ "observedMessageIds" = ?,
19020
+ "updatedAt" = ?
19021
+ WHERE id = ?`,
19022
+ args: [
19023
+ input.observations,
19024
+ input.lastObservedAt.toISOString(),
19025
+ input.tokenCount,
19026
+ input.tokenCount,
19027
+ observedMessageIdsJson,
19028
+ now.toISOString(),
19029
+ input.id
19030
+ ]
19031
+ })
19032
+ );
19024
19033
  if (result.rowsAffected === 0) {
19025
19034
  throw new MastraError({
19026
19035
  id: createStorageErrorId("LIBSQL", "UPDATE_ACTIVE_OBSERVATIONS", "NOT_FOUND"),
@@ -19047,78 +19056,21 @@ Note: This migration may take some time for large tables.
19047
19056
  }
19048
19057
  async createReflectionGeneration(input) {
19049
19058
  try {
19050
- const id = crypto.randomUUID();
19051
- const now = /* @__PURE__ */ new Date();
19052
- const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
19053
- const record = {
19054
- id,
19055
- scope: input.currentRecord.scope,
19056
- threadId: input.currentRecord.threadId,
19057
- resourceId: input.currentRecord.resourceId,
19058
- createdAt: now,
19059
- updatedAt: now,
19060
- lastObservedAt: input.currentRecord.lastObservedAt,
19061
- originType: "reflection",
19062
- generationCount: input.currentRecord.generationCount + 1,
19063
- activeObservations: input.reflection,
19064
- totalTokensObserved: input.currentRecord.totalTokensObserved,
19065
- observationTokenCount: input.tokenCount,
19066
- pendingMessageTokens: 0,
19067
- isReflecting: false,
19068
- isObserving: false,
19069
- isBufferingObservation: false,
19070
- isBufferingReflection: false,
19071
- lastBufferedAtTokens: 0,
19072
- lastBufferedAtTime: null,
19073
- config: input.currentRecord.config,
19074
- metadata: input.currentRecord.metadata,
19075
- observedTimezone: input.currentRecord.observedTimezone
19076
- };
19077
- await this.#client.execute({
19078
- sql: `INSERT INTO "${OM_TABLE}" (
19079
- id, "lookupKey", scope, "resourceId", "threadId",
19080
- "activeObservations", "activeObservationsPendingUpdate",
19081
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
19082
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
19083
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
19084
- "observedTimezone", metadata, "createdAt", "updatedAt"
19085
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
19086
- args: [
19087
- id,
19088
- lookupKey,
19089
- record.scope,
19090
- record.resourceId,
19091
- record.threadId || null,
19092
- input.reflection,
19093
- null,
19094
- "reflection",
19095
- JSON.stringify(record.config),
19096
- input.currentRecord.generationCount + 1,
19097
- record.lastObservedAt?.toISOString() || null,
19098
- now.toISOString(),
19099
- record.pendingMessageTokens,
19100
- record.totalTokensObserved,
19101
- record.observationTokenCount,
19102
- false,
19103
- // isObserving
19104
- false,
19105
- // isReflecting
19106
- false,
19107
- // isBufferingObservation
19108
- false,
19109
- // isBufferingReflection
19110
- 0,
19111
- // lastBufferedAtTokens
19112
- null,
19113
- // lastBufferedAtTime
19114
- record.observedTimezone || null,
19115
- record.metadata ? JSON.stringify(record.metadata) : null,
19116
- now.toISOString(),
19117
- now.toISOString()
19118
- ]
19059
+ return await withClientWriteLock(this.#client, async () => {
19060
+ const tx = await this.#client.transaction("write");
19061
+ try {
19062
+ const record = await this.#insertReflectionGeneration(tx, input);
19063
+ await tx.commit();
19064
+ return record;
19065
+ } catch (error) {
19066
+ if (!tx.closed) await tx.rollback();
19067
+ throw error;
19068
+ }
19119
19069
  });
19120
- return record;
19121
19070
  } catch (error) {
19071
+ if (error instanceof MastraError) {
19072
+ throw error;
19073
+ }
19122
19074
  throw new MastraError(
19123
19075
  {
19124
19076
  id: createStorageErrorId("LIBSQL", "CREATE_REFLECTION_GENERATION", "FAILED"),
@@ -19130,12 +19082,94 @@ Note: This migration may take some time for large tables.
19130
19082
  );
19131
19083
  }
19132
19084
  }
19085
+ /**
19086
+ * Inserts a new reflection-generation row. Called inside a locked write
19087
+ * transaction — either from {@link createReflectionGeneration} (standalone)
19088
+ * or from {@link swapBufferedReflectionToActive} (as part of a compound
19089
+ * swap). Must not acquire the write lock itself; the caller owns it.
19090
+ */
19091
+ async #insertReflectionGeneration(tx, input) {
19092
+ const id = crypto.randomUUID();
19093
+ const now = /* @__PURE__ */ new Date();
19094
+ const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
19095
+ const record = {
19096
+ id,
19097
+ scope: input.currentRecord.scope,
19098
+ threadId: input.currentRecord.threadId,
19099
+ resourceId: input.currentRecord.resourceId,
19100
+ createdAt: now,
19101
+ updatedAt: now,
19102
+ lastObservedAt: input.currentRecord.lastObservedAt,
19103
+ originType: "reflection",
19104
+ generationCount: input.currentRecord.generationCount + 1,
19105
+ activeObservations: input.reflection,
19106
+ totalTokensObserved: input.currentRecord.totalTokensObserved,
19107
+ observationTokenCount: input.tokenCount,
19108
+ pendingMessageTokens: 0,
19109
+ isReflecting: false,
19110
+ isObserving: false,
19111
+ isBufferingObservation: false,
19112
+ isBufferingReflection: false,
19113
+ lastBufferedAtTokens: 0,
19114
+ lastBufferedAtTime: null,
19115
+ config: input.currentRecord.config,
19116
+ metadata: input.currentRecord.metadata,
19117
+ observedTimezone: input.currentRecord.observedTimezone
19118
+ };
19119
+ await tx.execute({
19120
+ sql: `INSERT INTO "${OM_TABLE}" (
19121
+ id, "lookupKey", scope, "resourceId", "threadId",
19122
+ "activeObservations", "activeObservationsPendingUpdate",
19123
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
19124
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
19125
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
19126
+ "observedTimezone", metadata, "createdAt", "updatedAt"
19127
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
19128
+ args: [
19129
+ id,
19130
+ lookupKey,
19131
+ record.scope,
19132
+ record.resourceId,
19133
+ record.threadId || null,
19134
+ input.reflection,
19135
+ null,
19136
+ "reflection",
19137
+ JSON.stringify(record.config),
19138
+ input.currentRecord.generationCount + 1,
19139
+ record.lastObservedAt?.toISOString() || null,
19140
+ now.toISOString(),
19141
+ record.pendingMessageTokens,
19142
+ record.totalTokensObserved,
19143
+ record.observationTokenCount,
19144
+ false,
19145
+ // isObserving
19146
+ false,
19147
+ // isReflecting
19148
+ false,
19149
+ // isBufferingObservation
19150
+ false,
19151
+ // isBufferingReflection
19152
+ 0,
19153
+ // lastBufferedAtTokens
19154
+ null,
19155
+ // lastBufferedAtTime
19156
+ record.observedTimezone || null,
19157
+ record.metadata ? JSON.stringify(record.metadata) : null,
19158
+ now.toISOString(),
19159
+ now.toISOString()
19160
+ ]
19161
+ });
19162
+ return record;
19163
+ }
19133
19164
  async setReflectingFlag(id, isReflecting) {
19134
19165
  try {
19135
- const result = await this.#client.execute({
19136
- sql: `UPDATE "${OM_TABLE}" SET "isReflecting" = ?, "updatedAt" = ? WHERE id = ?`,
19137
- args: [isReflecting, (/* @__PURE__ */ new Date()).toISOString(), id]
19138
- });
19166
+ const result = await withClientWriteLock(
19167
+ this.#client,
19168
+ () => this.#client.execute({
19169
+ sql: `UPDATE "${OM_TABLE}" SET "isReflecting" = ?, "updatedAt" = ? WHERE id = ?`,
19170
+ args: [isReflecting, (/* @__PURE__ */ new Date()).toISOString(), id]
19171
+ })
19172
+ );
19139
19173
  if (result.rowsAffected === 0) {
19140
19174
  throw new MastraError({
19141
19175
  id: createStorageErrorId("LIBSQL", "SET_REFLECTING_FLAG", "NOT_FOUND"),
@@ -19162,10 +19196,13 @@ Note: This migration may take some time for large tables.
19162
19196
  }
19163
19197
  async setObservingFlag(id, isObserving) {
19164
19198
  try {
19165
- const result = await this.#client.execute({
19166
- sql: `UPDATE "${OM_TABLE}" SET "isObserving" = ?, "updatedAt" = ? WHERE id = ?`,
19167
- args: [isObserving, (/* @__PURE__ */ new Date()).toISOString(), id]
19168
- });
19199
+ const result = await withClientWriteLock(
19200
+ this.#client,
19201
+ () => this.#client.execute({
19202
+ sql: `UPDATE "${OM_TABLE}" SET "isObserving" = ?, "updatedAt" = ? WHERE id = ?`,
19203
+ args: [isObserving, (/* @__PURE__ */ new Date()).toISOString(), id]
19204
+ })
19205
+ );
19169
19206
  if (result.rowsAffected === 0) {
19170
19207
  throw new MastraError({
19171
19208
  id: createStorageErrorId("LIBSQL", "SET_OBSERVING_FLAG", "NOT_FOUND"),
@@ -19202,7 +19239,7 @@ Note: This migration may take some time for large tables.
19202
19239
  sql = `UPDATE "${OM_TABLE}" SET "isBufferingObservation" = ?, "updatedAt" = ? WHERE id = ?`;
19203
19240
  args = [isBuffering, nowStr, id];
19204
19241
  }
19205
- const result = await this.#client.execute({ sql, args });
19242
+ const result = await withClientWriteLock(this.#client, () => this.#client.execute({ sql, args }));
19206
19243
  if (result.rowsAffected === 0) {
19207
19244
  throw new MastraError({
19208
19245
  id: createStorageErrorId("LIBSQL", "SET_BUFFERING_OBSERVATION_FLAG", "NOT_FOUND"),
@@ -19229,10 +19266,13 @@ Note: This migration may take some time for large tables.
19229
19266
  }
19230
19267
  async setBufferingReflectionFlag(id, isBuffering) {
19231
19268
  try {
19232
- const result = await this.#client.execute({
19233
- sql: `UPDATE "${OM_TABLE}" SET "isBufferingReflection" = ?, "updatedAt" = ? WHERE id = ?`,
19234
- args: [isBuffering, (/* @__PURE__ */ new Date()).toISOString(), id]
19235
- });
19269
+ const result = await withClientWriteLock(
19270
+ this.#client,
19271
+ () => this.#client.execute({
19272
+ sql: `UPDATE "${OM_TABLE}" SET "isBufferingReflection" = ?, "updatedAt" = ? WHERE id = ?`,
19273
+ args: [isBuffering, (/* @__PURE__ */ new Date()).toISOString(), id]
19274
+ })
19275
+ );
19236
19276
  if (result.rowsAffected === 0) {
19237
19277
  throw new MastraError({
19238
19278
  id: createStorageErrorId("LIBSQL", "SET_BUFFERING_REFLECTION_FLAG", "NOT_FOUND"),
@@ -19260,10 +19300,13 @@ Note: This migration may take some time for large tables.
19260
19300
  async clearObservationalMemory(threadId, resourceId) {
19261
19301
  try {
19262
19302
  const lookupKey = this.getOMKey(threadId, resourceId);
19263
- await this.#client.execute({
19264
- sql: `DELETE FROM "${OM_TABLE}" WHERE "lookupKey" = ?`,
19265
- args: [lookupKey]
19266
- });
19303
+ await withClientWriteLock(
19304
+ this.#client,
19305
+ () => this.#client.execute({
19306
+ sql: `DELETE FROM "${OM_TABLE}" WHERE "lookupKey" = ?`,
19307
+ args: [lookupKey]
19308
+ })
19309
+ );
19267
19310
  } catch (error) {
19268
19311
  throw new MastraError(
19269
19312
  {
@@ -19278,13 +19321,16 @@ Note: This migration may take some time for large tables.
19278
19321
  }
19279
19322
  async setPendingMessageTokens(id, tokenCount) {
19280
19323
  try {
19281
- const result = await this.#client.execute({
19282
- sql: `UPDATE "${OM_TABLE}" SET
19283
- "pendingMessageTokens" = ?,
19284
- "updatedAt" = ?
19285
- WHERE id = ?`,
19286
- args: [tokenCount, (/* @__PURE__ */ new Date()).toISOString(), id]
19287
- });
19324
+ const result = await withClientWriteLock(
19325
+ this.#client,
19326
+ () => this.#client.execute({
19327
+ sql: `UPDATE "${OM_TABLE}" SET
19328
+ "pendingMessageTokens" = ?,
19329
+ "updatedAt" = ?
19330
+ WHERE id = ?`,
19331
+ args: [tokenCount, (/* @__PURE__ */ new Date()).toISOString(), id]
19332
+ })
19333
+ );
19288
19334
  if (result.rowsAffected === 0) {
19289
19335
  throw new MastraError({
19290
19336
  id: createStorageErrorId("LIBSQL", "SET_PENDING_MESSAGE_TOKENS", "NOT_FOUND"),
@@ -19311,25 +19357,34 @@ Note: This migration may take some time for large tables.
19311
19357
  }
19312
19358
  async updateObservationalMemoryConfig(input) {
19313
19359
  try {
19314
- const selectResult = await this.#client.execute({
19315
- sql: `SELECT config FROM "${OM_TABLE}" WHERE id = ?`,
19316
- args: [input.id]
19317
- });
19318
- if (selectResult.rows.length === 0) {
19319
- throw new MastraError({
19320
- id: createStorageErrorId("LIBSQL", "UPDATE_OM_CONFIG", "NOT_FOUND"),
19321
- text: `Observational memory record not found: ${input.id}`,
19322
- domain: ErrorDomain.STORAGE,
19323
- category: ErrorCategory.THIRD_PARTY,
19324
- details: { id: input.id }
19325
- });
19326
- }
19327
- const row = selectResult.rows[0];
19328
- const existing = row.config ? JSON.parse(row.config) : {};
19329
- const merged = this.deepMergeConfig(existing, input.config);
19330
- await this.#client.execute({
19331
- sql: `UPDATE "${OM_TABLE}" SET config = ?, "updatedAt" = ? WHERE id = ?`,
19332
- args: [JSON.stringify(merged), (/* @__PURE__ */ new Date()).toISOString(), input.id]
19360
+ await withClientWriteLock(this.#client, async () => {
19361
+ const tx = await this.#client.transaction("write");
19362
+ try {
19363
+ const selectResult = await tx.execute({
19364
+ sql: `SELECT config FROM "${OM_TABLE}" WHERE id = ?`,
19365
+ args: [input.id]
19366
+ });
19367
+ if (selectResult.rows.length === 0) {
19368
+ throw new MastraError({
19369
+ id: createStorageErrorId("LIBSQL", "UPDATE_OM_CONFIG", "NOT_FOUND"),
19370
+ text: `Observational memory record not found: ${input.id}`,
19371
+ domain: ErrorDomain.STORAGE,
19372
+ category: ErrorCategory.THIRD_PARTY,
19373
+ details: { id: input.id }
19374
+ });
19375
+ }
19376
+ const row = selectResult.rows[0];
19377
+ const existing = row.config ? JSON.parse(row.config) : {};
19378
+ const merged = this.deepMergeConfig(existing, input.config);
19379
+ await tx.execute({
19380
+ sql: `UPDATE "${OM_TABLE}" SET config = ?, "updatedAt" = ? WHERE id = ?`,
19381
+ args: [JSON.stringify(merged), (/* @__PURE__ */ new Date()).toISOString(), input.id]
19382
+ });
19383
+ await tx.commit();
19384
+ } catch (error) {
19385
+ if (!tx.closed) await tx.rollback();
19386
+ throw error;
19387
+ }
19333
19388
  });
19334
19389
  } catch (error) {
19335
19390
  if (error instanceof MastraError) {
@@ -19352,63 +19407,72 @@ Note: This migration may take some time for large tables.
19352
19407
  async updateBufferedObservations(input) {
19353
19408
  try {
19354
19409
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
19355
- const current = await this.#client.execute({
19356
- sql: `SELECT "bufferedObservationChunks" FROM "${OM_TABLE}" WHERE id = ?`,
19357
- args: [input.id]
19358
- });
19359
- if (!current.rows || current.rows.length === 0) {
19360
- throw new MastraError({
19361
- id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
19362
- text: `Observational memory record not found: ${input.id}`,
19363
- domain: ErrorDomain.STORAGE,
19364
- category: ErrorCategory.THIRD_PARTY,
19365
- details: { id: input.id }
19366
- });
19367
- }
19368
- const row = current.rows[0];
19369
- let existingChunks = [];
19370
- if (row.bufferedObservationChunks) {
19410
+ await withClientWriteLock(this.#client, async () => {
19411
+ const tx = await this.#client.transaction("write");
19371
19412
  try {
19372
- const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
19373
- existingChunks = Array.isArray(parsed) ? parsed : [];
19374
- } catch {
19375
- existingChunks = [];
19413
+ const current = await tx.execute({
19414
+ sql: `SELECT "bufferedObservationChunks" FROM "${OM_TABLE}" WHERE id = ?`,
19415
+ args: [input.id]
19416
+ });
19417
+ if (!current.rows || current.rows.length === 0) {
19418
+ throw new MastraError({
19419
+ id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
19420
+ text: `Observational memory record not found: ${input.id}`,
19421
+ domain: ErrorDomain.STORAGE,
19422
+ category: ErrorCategory.THIRD_PARTY,
19423
+ details: { id: input.id }
19424
+ });
19425
+ }
19426
+ const row = current.rows[0];
19427
+ let existingChunks = [];
19428
+ if (row.bufferedObservationChunks) {
19429
+ try {
19430
+ const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
19431
+ existingChunks = Array.isArray(parsed) ? parsed : [];
19432
+ } catch {
19433
+ existingChunks = [];
19434
+ }
19435
+ }
19436
+ const newChunk = {
19437
+ id: `ombuf-${randomUUID3()}`,
19438
+ cycleId: input.chunk.cycleId,
19439
+ observations: input.chunk.observations,
19440
+ tokenCount: input.chunk.tokenCount,
19441
+ messageIds: input.chunk.messageIds,
19442
+ messageTokens: input.chunk.messageTokens,
19443
+ lastObservedAt: input.chunk.lastObservedAt,
19444
+ createdAt: /* @__PURE__ */ new Date(),
19445
+ suggestedContinuation: input.chunk.suggestedContinuation,
19446
+ currentTask: input.chunk.currentTask,
19447
+ threadTitle: input.chunk.threadTitle,
19448
+ extractedValues: input.chunk.extractedValues,
19449
+ extractionFailures: input.chunk.extractionFailures
19450
+ };
19451
+ const newChunks = [...existingChunks, newChunk];
19452
+ const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
19453
+ const result = await tx.execute({
19454
+ sql: `UPDATE "${OM_TABLE}" SET
19455
+ "bufferedObservationChunks" = ?,
19456
+ "lastBufferedAtTime" = COALESCE(?, "lastBufferedAtTime"),
19457
+ "updatedAt" = ?
19458
+ WHERE id = ?`,
19459
+ args: [JSON.stringify(newChunks), lastBufferedAtTime, nowStr, input.id]
19460
+ });
19461
+ if (result.rowsAffected === 0) {
19462
+ throw new MastraError({
19463
+ id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
19464
+ text: `Observational memory record not found: ${input.id}`,
19465
+ domain: ErrorDomain.STORAGE,
19466
+ category: ErrorCategory.THIRD_PARTY,
19467
+ details: { id: input.id }
19468
+ });
19469
+ }
19470
+ await tx.commit();
19471
+ } catch (error) {
19472
+ if (!tx.closed) await tx.rollback();
19473
+ throw error;
19376
19474
  }
19377
- }
19378
- const newChunk = {
19379
- id: `ombuf-${randomUUID3()}`,
19380
- cycleId: input.chunk.cycleId,
19381
- observations: input.chunk.observations,
19382
- tokenCount: input.chunk.tokenCount,
19383
- messageIds: input.chunk.messageIds,
19384
- messageTokens: input.chunk.messageTokens,
19385
- lastObservedAt: input.chunk.lastObservedAt,
19386
- createdAt: /* @__PURE__ */ new Date(),
19387
- suggestedContinuation: input.chunk.suggestedContinuation,
19388
- currentTask: input.chunk.currentTask,
19389
- threadTitle: input.chunk.threadTitle,
19390
- extractedValues: input.chunk.extractedValues,
19391
- extractionFailures: input.chunk.extractionFailures
19392
- };
19393
- const newChunks = [...existingChunks, newChunk];
19394
- const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
19395
- const result = await this.#client.execute({
19396
- sql: `UPDATE "${OM_TABLE}" SET
19397
- "bufferedObservationChunks" = ?,
19398
- "lastBufferedAtTime" = COALESCE(?, "lastBufferedAtTime"),
19399
- "updatedAt" = ?
19400
- WHERE id = ?`,
19401
- args: [JSON.stringify(newChunks), lastBufferedAtTime, nowStr, input.id]
19402
19475
  });
19403
- if (result.rowsAffected === 0) {
19404
- throw new MastraError({
19405
- id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
19406
- text: `Observational memory record not found: ${input.id}`,
19407
- domain: ErrorDomain.STORAGE,
19408
- category: ErrorCategory.THIRD_PARTY,
19409
- details: { id: input.id }
19410
- });
19411
- }
19412
19476
  } catch (error) {
19413
19477
  if (error instanceof MastraError) {
19414
19478
  throw error;
@@ -19427,150 +19491,160 @@ Note: This migration may take some time for large tables.
19427
19491
  async swapBufferedToActive(input) {
19428
19492
  try {
19429
19493
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
19430
- const current = await this.#client.execute({
19431
- sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
19432
- args: [input.id]
19433
- });
19434
- if (!current.rows || current.rows.length === 0) {
19435
- throw new MastraError({
19436
- id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_TO_ACTIVE", "NOT_FOUND"),
19437
- text: `Observational memory record not found: ${input.id}`,
19438
- domain: ErrorDomain.STORAGE,
19439
- category: ErrorCategory.THIRD_PARTY,
19440
- details: { id: input.id }
19441
- });
19442
- }
19443
- const row = current.rows[0];
19444
- let chunks = [];
19445
- if (row.bufferedObservationChunks) {
19494
+ return await withClientWriteLock(this.#client, async () => {
19495
+ const tx = await this.#client.transaction("write");
19446
19496
  try {
19447
- const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
19448
- chunks = Array.isArray(parsed) ? parsed : [];
19449
- } catch {
19450
- chunks = [];
19451
- }
19452
- }
19453
- if (chunks.length === 0) {
19454
- return {
19455
- chunksActivated: 0,
19456
- messageTokensActivated: 0,
19457
- observationTokensActivated: 0,
19458
- messagesActivated: 0,
19459
- activatedCycleIds: [],
19460
- activatedMessageIds: []
19461
- };
19462
- }
19463
- const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
19464
- const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
19465
- let cumulativeMessageTokens = 0;
19466
- let bestOverBoundary = 0;
19467
- let bestOverTokens = 0;
19468
- let bestUnderBoundary = 0;
19469
- let bestUnderTokens = 0;
19470
- for (let i = 0; i < chunks.length; i++) {
19471
- cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
19472
- const boundary2 = i + 1;
19473
- if (cumulativeMessageTokens >= targetMessageTokens) {
19474
- if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
19475
- bestOverBoundary = boundary2;
19476
- bestOverTokens = cumulativeMessageTokens;
19497
+ const current = await tx.execute({
19498
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
19499
+ args: [input.id]
19500
+ });
19501
+ if (!current.rows || current.rows.length === 0) {
19502
+ throw new MastraError({
19503
+ id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_TO_ACTIVE", "NOT_FOUND"),
19504
+ text: `Observational memory record not found: ${input.id}`,
19505
+ domain: ErrorDomain.STORAGE,
19506
+ category: ErrorCategory.THIRD_PARTY,
19507
+ details: { id: input.id }
19508
+ });
19477
19509
  }
19478
- } else {
19479
- if (cumulativeMessageTokens > bestUnderTokens) {
19480
- bestUnderBoundary = boundary2;
19481
- bestUnderTokens = cumulativeMessageTokens;
19510
+ const row = current.rows[0];
19511
+ let chunks = [];
19512
+ if (row.bufferedObservationChunks) {
19513
+ try {
19514
+ const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
19515
+ chunks = Array.isArray(parsed) ? parsed : [];
19516
+ } catch {
19517
+ chunks = [];
19518
+ }
19482
19519
  }
19483
- }
19484
- }
19485
- const maxOvershoot = retentionFloor * 0.95;
19486
- const overshoot = bestOverTokens - targetMessageTokens;
19487
- const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
19488
- const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
19489
- const minRemaining = Math.min(1e3, retentionFloor);
19490
- let chunksToActivate;
19491
- if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) {
19492
- chunksToActivate = bestOverBoundary;
19493
- } else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
19494
- chunksToActivate = bestOverBoundary;
19495
- } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
19496
- chunksToActivate = bestUnderBoundary;
19497
- } else if (bestOverBoundary > 0) {
19498
- chunksToActivate = bestOverBoundary;
19499
- } else {
19500
- chunksToActivate = 1;
19501
- }
19502
- const activatedChunks = chunks.slice(0, chunksToActivate);
19503
- const remainingChunks = chunks.slice(chunksToActivate);
19504
- const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
19505
- const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
19506
- const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
19507
- const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
19508
- const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
19509
- const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
19510
- const latestChunk = activatedChunks[activatedChunks.length - 1];
19511
- const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
19512
- const lastObservedAtStr = lastObservedAt.toISOString();
19513
- const existingActive = row.activeObservations || "";
19514
- const existingTokenCount = Number(row.observationTokenCount || 0);
19515
- const boundary = `
19520
+ if (chunks.length === 0) {
19521
+ await tx.commit();
19522
+ return {
19523
+ chunksActivated: 0,
19524
+ messageTokensActivated: 0,
19525
+ observationTokensActivated: 0,
19526
+ messagesActivated: 0,
19527
+ activatedCycleIds: [],
19528
+ activatedMessageIds: []
19529
+ };
19530
+ }
19531
+ const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
19532
+ const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
19533
+ let cumulativeMessageTokens = 0;
19534
+ let bestOverBoundary = 0;
19535
+ let bestOverTokens = 0;
19536
+ let bestUnderBoundary = 0;
19537
+ let bestUnderTokens = 0;
19538
+ for (let i = 0; i < chunks.length; i++) {
19539
+ cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
19540
+ const boundary2 = i + 1;
19541
+ if (cumulativeMessageTokens >= targetMessageTokens) {
19542
+ if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
19543
+ bestOverBoundary = boundary2;
19544
+ bestOverTokens = cumulativeMessageTokens;
19545
+ }
19546
+ } else {
19547
+ if (cumulativeMessageTokens > bestUnderTokens) {
19548
+ bestUnderBoundary = boundary2;
19549
+ bestUnderTokens = cumulativeMessageTokens;
19550
+ }
19551
+ }
19552
+ }
19553
+ const maxOvershoot = retentionFloor * 0.95;
19554
+ const overshoot = bestOverTokens - targetMessageTokens;
19555
+ const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
19556
+ const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
19557
+ const minRemaining = Math.min(1e3, retentionFloor);
19558
+ let chunksToActivate;
19559
+ if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) {
19560
+ chunksToActivate = bestOverBoundary;
19561
+ } else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
19562
+ chunksToActivate = bestOverBoundary;
19563
+ } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
19564
+ chunksToActivate = bestUnderBoundary;
19565
+ } else if (bestOverBoundary > 0) {
19566
+ chunksToActivate = bestOverBoundary;
19567
+ } else {
19568
+ chunksToActivate = 1;
19569
+ }
19570
+ const activatedChunks = chunks.slice(0, chunksToActivate);
19571
+ const remainingChunks = chunks.slice(chunksToActivate);
19572
+ const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
19573
+ const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
19574
+ const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
19575
+ const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
19576
+ const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
19577
+ const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
19578
+ const latestChunk = activatedChunks[activatedChunks.length - 1];
19579
+ const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
19580
+ const lastObservedAtStr = lastObservedAt.toISOString();
19581
+ const existingActive = row.activeObservations || "";
19582
+ const existingTokenCount = Number(row.observationTokenCount || 0);
19583
+ const boundary = `
19516
19584
 
19517
19585
  --- message boundary (${lastObservedAt.toISOString()}) ---
19518
19586
 
19519
19587
  `;
19520
- const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
19521
- const newTokenCount = existingTokenCount + activatedTokens;
19522
- const existingPending = Number(row.pendingMessageTokens || 0);
19523
- const newPending = Math.max(0, existingPending - activatedMessageTokens);
19524
- const updateResult = await this.#client.execute({
19525
- sql: `UPDATE "${OM_TABLE}" SET
19526
- "activeObservations" = ?,
19527
- "observationTokenCount" = ?,
19528
- "pendingMessageTokens" = ?,
19529
- "bufferedObservationChunks" = ?,
19530
- "lastObservedAt" = ?,
19531
- "updatedAt" = ?
19532
- WHERE id = ?
19533
- AND "bufferedObservationChunks" IS NOT NULL
19534
- AND "bufferedObservationChunks" != '[]'`,
19535
- args: [
19536
- newActive,
19537
- newTokenCount,
19538
- newPending,
19539
- remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
19540
- lastObservedAtStr,
19541
- nowStr,
19542
- input.id
19543
- ]
19588
+ const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
19589
+ const newTokenCount = existingTokenCount + activatedTokens;
19590
+ const existingPending = Number(row.pendingMessageTokens || 0);
19591
+ const newPending = Math.max(0, existingPending - activatedMessageTokens);
19592
+ const updateResult = await tx.execute({
19593
+ sql: `UPDATE "${OM_TABLE}" SET
19594
+ "activeObservations" = ?,
19595
+ "observationTokenCount" = ?,
19596
+ "pendingMessageTokens" = ?,
19597
+ "bufferedObservationChunks" = ?,
19598
+ "lastObservedAt" = ?,
19599
+ "updatedAt" = ?
19600
+ WHERE id = ?
19601
+ AND "bufferedObservationChunks" IS NOT NULL
19602
+ AND "bufferedObservationChunks" != '[]'`,
19603
+ args: [
19604
+ newActive,
19605
+ newTokenCount,
19606
+ newPending,
19607
+ remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
19608
+ lastObservedAtStr,
19609
+ nowStr,
19610
+ input.id
19611
+ ]
19612
+ });
19613
+ await tx.commit();
19614
+ if (updateResult.rowsAffected === 0) {
19615
+ return {
19616
+ chunksActivated: 0,
19617
+ messageTokensActivated: 0,
19618
+ observationTokensActivated: 0,
19619
+ messagesActivated: 0,
19620
+ activatedCycleIds: [],
19621
+ activatedMessageIds: []
19622
+ };
19623
+ }
19624
+ const latestChunkHints = activatedChunks[activatedChunks.length - 1];
19625
+ return {
19626
+ chunksActivated: activatedChunks.length,
19627
+ messageTokensActivated: activatedMessageTokens,
19628
+ observationTokensActivated: activatedTokens,
19629
+ messagesActivated: activatedMessageCount,
19630
+ activatedCycleIds,
19631
+ activatedMessageIds,
19632
+ observations: activatedContent,
19633
+ perChunk: activatedChunks.map((c) => ({
19634
+ cycleId: c.cycleId ?? "",
19635
+ messageTokens: c.messageTokens ?? 0,
19636
+ observationTokens: c.tokenCount,
19637
+ messageCount: c.messageIds.length,
19638
+ observations: c.observations
19639
+ })),
19640
+ suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
19641
+ currentTask: latestChunkHints?.currentTask ?? void 0
19642
+ };
19643
+ } catch (error) {
19644
+ if (!tx.closed) await tx.rollback();
19645
+ throw error;
19646
+ }
19544
19647
  });
19545
- if (updateResult.rowsAffected === 0) {
19546
- return {
19547
- chunksActivated: 0,
19548
- messageTokensActivated: 0,
19549
- observationTokensActivated: 0,
19550
- messagesActivated: 0,
19551
- activatedCycleIds: [],
19552
- activatedMessageIds: []
19553
- };
19554
- }
19555
- const latestChunkHints = activatedChunks[activatedChunks.length - 1];
19556
- return {
19557
- chunksActivated: activatedChunks.length,
19558
- messageTokensActivated: activatedMessageTokens,
19559
- observationTokensActivated: activatedTokens,
19560
- messagesActivated: activatedMessageCount,
19561
- activatedCycleIds,
19562
- activatedMessageIds,
19563
- observations: activatedContent,
19564
- perChunk: activatedChunks.map((c) => ({
19565
- cycleId: c.cycleId ?? "",
19566
- messageTokens: c.messageTokens ?? 0,
19567
- observationTokens: c.tokenCount,
19568
- messageCount: c.messageIds.length,
19569
- observations: c.observations
19570
- })),
19571
- suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
19572
- currentTask: latestChunkHints?.currentTask ?? void 0
19573
- };
19574
19648
  } catch (error) {
19575
19649
  if (error instanceof MastraError) {
19576
19650
  throw error;
@@ -19589,28 +19663,31 @@ Note: This migration may take some time for large tables.
19589
19663
  async updateBufferedReflection(input) {
19590
19664
  try {
19591
19665
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
19592
- const result = await this.#client.execute({
19593
- sql: `UPDATE "${OM_TABLE}" SET
19594
- "bufferedReflection" = CASE
19595
- WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
19596
- THEN "bufferedReflection" || char(10) || char(10) || ?
19597
- ELSE ?
19598
- END,
19599
- "bufferedReflectionTokens" = COALESCE("bufferedReflectionTokens", 0) + ?,
19600
- "bufferedReflectionInputTokens" = COALESCE("bufferedReflectionInputTokens", 0) + ?,
19601
- "reflectedObservationLineCount" = ?,
19602
- "updatedAt" = ?
19603
- WHERE id = ?`,
19604
- args: [
19605
- input.reflection,
19606
- input.reflection,
19607
- input.tokenCount,
19608
- input.inputTokenCount,
19609
- input.reflectedObservationLineCount,
19610
- nowStr,
19611
- input.id
19612
- ]
19613
- });
19666
+ const result = await withClientWriteLock(
19667
+ this.#client,
19668
+ () => this.#client.execute({
19669
+ sql: `UPDATE "${OM_TABLE}" SET
19670
+ "bufferedReflection" = CASE
19671
+ WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
19672
+ THEN "bufferedReflection" || char(10) || char(10) || ?
19673
+ ELSE ?
19674
+ END,
19675
+ "bufferedReflectionTokens" = COALESCE("bufferedReflectionTokens", 0) + ?,
19676
+ "bufferedReflectionInputTokens" = COALESCE("bufferedReflectionInputTokens", 0) + ?,
19677
+ "reflectedObservationLineCount" = ?,
19678
+ "updatedAt" = ?
19679
+ WHERE id = ?`,
19680
+ args: [
19681
+ input.reflection,
19682
+ input.reflection,
19683
+ input.tokenCount,
19684
+ input.inputTokenCount,
19685
+ input.reflectedObservationLineCount,
19686
+ nowStr,
19687
+ input.id
19688
+ ]
19689
+ })
19690
+ );
19614
19691
  if (result.rowsAffected === 0) {
19615
19692
  throw new MastraError({
19616
19693
  id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_REFLECTION", "NOT_FOUND"),
@@ -19637,55 +19714,64 @@ Note: This migration may take some time for large tables.
19637
19714
  }
19638
19715
  async swapBufferedReflectionToActive(input) {
19639
19716
  try {
19640
- const current = await this.#client.execute({
19641
- sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
19642
- args: [input.currentRecord.id]
19643
- });
19644
- if (!current.rows || current.rows.length === 0) {
19645
- throw new MastraError({
19646
- id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NOT_FOUND"),
19647
- text: `Observational memory record not found: ${input.currentRecord.id}`,
19648
- domain: ErrorDomain.STORAGE,
19649
- category: ErrorCategory.THIRD_PARTY,
19650
- details: { id: input.currentRecord.id }
19651
- });
19652
- }
19653
- const row = current.rows[0];
19654
- const bufferedReflection = row.bufferedReflection || "";
19655
- const reflectedLineCount = Number(row.reflectedObservationLineCount || 0);
19656
- if (!bufferedReflection) {
19657
- throw new MastraError({
19658
- id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
19659
- text: "No buffered reflection to swap",
19660
- domain: ErrorDomain.STORAGE,
19661
- category: ErrorCategory.USER,
19662
- details: { id: input.currentRecord.id }
19663
- });
19664
- }
19665
- const currentObservations = row.activeObservations || "";
19666
- const allLines = currentObservations.split("\n");
19667
- const unreflectedLines = allLines.slice(reflectedLineCount);
19668
- const unreflectedContent = unreflectedLines.join("\n").trim();
19669
- const newObservations = unreflectedContent ? `${bufferedReflection}
19717
+ return await withClientWriteLock(this.#client, async () => {
19718
+ const tx = await this.#client.transaction("write");
19719
+ try {
19720
+ const current = await tx.execute({
19721
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
19722
+ args: [input.currentRecord.id]
19723
+ });
19724
+ if (!current.rows || current.rows.length === 0) {
19725
+ throw new MastraError({
19726
+ id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NOT_FOUND"),
19727
+ text: `Observational memory record not found: ${input.currentRecord.id}`,
19728
+ domain: ErrorDomain.STORAGE,
19729
+ category: ErrorCategory.THIRD_PARTY,
19730
+ details: { id: input.currentRecord.id }
19731
+ });
19732
+ }
19733
+ const row = current.rows[0];
19734
+ const bufferedReflection = row.bufferedReflection || "";
19735
+ const reflectedLineCount = Number(row.reflectedObservationLineCount || 0);
19736
+ if (!bufferedReflection) {
19737
+ throw new MastraError({
19738
+ id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
19739
+ text: "No buffered reflection to swap",
19740
+ domain: ErrorDomain.STORAGE,
19741
+ category: ErrorCategory.USER,
19742
+ details: { id: input.currentRecord.id }
19743
+ });
19744
+ }
19745
+ const currentObservations = row.activeObservations || "";
19746
+ const allLines = currentObservations.split("\n");
19747
+ const unreflectedLines = allLines.slice(reflectedLineCount);
19748
+ const unreflectedContent = unreflectedLines.join("\n").trim();
19749
+ const newObservations = unreflectedContent ? `${bufferedReflection}
19670
19750
 
19671
19751
  ${unreflectedContent}` : bufferedReflection;
19672
- const newRecord = await this.createReflectionGeneration({
19673
- currentRecord: input.currentRecord,
19674
- reflection: newObservations,
19675
- tokenCount: input.tokenCount
19752
+ const newRecord = await this.#insertReflectionGeneration(tx, {
19753
+ currentRecord: input.currentRecord,
19754
+ reflection: newObservations,
19755
+ tokenCount: input.tokenCount
19756
+ });
19757
+ const nowStr = (/* @__PURE__ */ new Date()).toISOString();
19758
+ await tx.execute({
19759
+ sql: `UPDATE "${OM_TABLE}" SET
19760
+ "bufferedReflection" = NULL,
19761
+ "bufferedReflectionTokens" = NULL,
19762
+ "bufferedReflectionInputTokens" = NULL,
19763
+ "reflectedObservationLineCount" = NULL,
19764
+ "updatedAt" = ?
19765
+ WHERE id = ?`,
19766
+ args: [nowStr, input.currentRecord.id]
19767
+ });
19768
+ await tx.commit();
19769
+ return newRecord;
19770
+ } catch (error) {
19771
+ if (!tx.closed) await tx.rollback();
19772
+ throw error;
19773
+ }
19676
19774
  });
19677
- const nowStr = (/* @__PURE__ */ new Date()).toISOString();
19678
- await this.#client.execute({
19679
- sql: `UPDATE "${OM_TABLE}" SET
19680
- "bufferedReflection" = NULL,
19681
- "bufferedReflectionTokens" = NULL,
19682
- "bufferedReflectionInputTokens" = NULL,
19683
- "reflectedObservationLineCount" = NULL,
19684
- "updatedAt" = ?
19685
- WHERE id = ?`,
19686
- args: [nowStr, input.currentRecord.id]
19687
- });
19688
- return newRecord;
19689
19775
  } catch (error) {
19690
19776
  if (error instanceof MastraError) {
19691
19777
  throw error;
@@ -26436,12 +26522,16 @@ function invalidateCustomProvidersSnapshots(tenant) {
26436
26522
  // src/routes/fs.ts
26437
26523
  import { lstat, open, readdir, realpath, stat } from "fs/promises";
26438
26524
  import { homedir } from "os";
26439
- import { isAbsolute, join, resolve, sep } from "path";
26525
+ import { isAbsolute, join, posix as posixPath, resolve, sep } from "path";
26526
+ import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
26440
26527
  import { detectProject, getResourceIdOverride } from "@mastra/code-sdk/utils/project";
26441
26528
  import { registerApiRoute as registerApiRoute2 } from "@mastra/core/server";
26442
26529
  var MAX_TEXT_FILE_BYTES = 512 * 1024;
26443
26530
  var TEXT_DECODER = new TextDecoder("utf-8", { fatal: true });
26444
26531
  var APPROVED_RENDERED_ROOTS = /* @__PURE__ */ new Set([".artifacts"]);
26532
+ function loose2(c) {
26533
+ return c;
26534
+ }
26445
26535
  function resolveFsRoot(root) {
26446
26536
  return resolve(root && root.trim() ? root : homedir());
26447
26537
  }
@@ -26622,6 +26712,91 @@ async function listArtifacts(root, workspacePath) {
26622
26712
  entries: listing.entries
26623
26713
  };
26624
26714
  }
26715
+ async function resolveAuthorizedSession(c, deps, workspacePath) {
26716
+ if (!deps) return null;
26717
+ const session = await deps.sessions.getBySessionId(workspacePath);
26718
+ if (!session) return null;
26719
+ if (deps.auth.enabled()) {
26720
+ await deps.auth.ensureUser(c);
26721
+ const tenant = deps.auth.tenant(c);
26722
+ if (!tenant || tenant.orgId !== session.orgId || tenant.userId !== session.userId) {
26723
+ throw new Error("Session is not available to the current user");
26724
+ }
26725
+ }
26726
+ return session;
26727
+ }
26728
+ async function sessionSandbox(fleet, session) {
26729
+ if (!fleet.enabled || !session.sandboxId || !session.sandboxWorkdir) return null;
26730
+ let sandbox;
26731
+ try {
26732
+ sandbox = await fleet.reattachSandbox(session.sandboxId, { workingDirectory: session.sandboxWorkdir });
26733
+ } catch {
26734
+ return null;
26735
+ }
26736
+ return {
26737
+ sandbox,
26738
+ filesystem: new SandboxFilesystem({ sandbox, workdir: session.sandboxWorkdir }),
26739
+ workdir: session.sandboxWorkdir
26740
+ };
26741
+ }
26742
+ async function listSessionRenderedPath(fleet, session, renderedRoot) {
26743
+ const safeRoot = assertApprovedRenderedRoot(renderedRoot);
26744
+ const rootPath = posixPath.join(session.sandboxWorkdir ?? "", safeRoot);
26745
+ const empty = { workspacePath: session.sessionId, root: safeRoot, rootPath, entries: [] };
26746
+ const handle = await sessionSandbox(fleet, session);
26747
+ if (!handle) return empty;
26748
+ const quotedRoot = `'${rootPath.replace(/'/g, `'\\''`)}'`;
26749
+ const result = await handle.sandbox.executeCommand(
26750
+ "sh",
26751
+ [
26752
+ "-c",
26753
+ `test -d ${quotedRoot} && find ${quotedRoot} -mindepth 1 -printf '%y\\t%s\\t%T@\\t%p\\n' 2>/dev/null || true`
26754
+ ],
26755
+ { timeout: 3e4 }
26756
+ );
26757
+ if (result.exitCode !== 0) return empty;
26758
+ const entries = [];
26759
+ for (const line of result.stdout.split("\n")) {
26760
+ if (!line) continue;
26761
+ const [type, sizeStr, mtimeStr, ...pathParts] = line.split(" ");
26762
+ const fullPath = pathParts.join(" ");
26763
+ if (!fullPath || !fullPath.startsWith(`${rootPath}/`)) continue;
26764
+ const relativePath = fullPath.slice(rootPath.length + 1);
26765
+ entries.push({
26766
+ name: posixPath.basename(relativePath),
26767
+ path: relativePath,
26768
+ type: type === "d" ? "directory" : "file",
26769
+ size: type === "d" ? 0 : Number(sizeStr) || 0,
26770
+ updatedAt: new Date((Number(mtimeStr) || 0) * 1e3).toISOString()
26771
+ });
26772
+ }
26773
+ entries.sort((a, b) => a.path.localeCompare(b.path));
26774
+ return { workspacePath: session.sessionId, root: safeRoot, rootPath, entries };
26775
+ }
26776
+ async function readSessionWorkspaceFile(fleet, session, path) {
26777
+ const safePath = assertRelativePath(path, "path");
26778
+ assertApprovedRenderedRoot(safePath.split("/")[0] ?? "");
26779
+ const handle = await sessionSandbox(fleet, session);
26780
+ if (!handle) throw new Error("Session workspace is not available");
26781
+ const { filesystem } = handle;
26782
+ const info = await filesystem.stat(safePath);
26783
+ if (info.type === "directory") throw new Error("Path is a directory");
26784
+ const buffer = await filesystem.readFile(safePath);
26785
+ const truncated = buffer.length > MAX_TEXT_FILE_BYTES;
26786
+ const base = {
26787
+ workspacePath: session.sessionId,
26788
+ path: safePath,
26789
+ name: posixPath.basename(safePath),
26790
+ size: buffer.length,
26791
+ updatedAt: info.modifiedAt.toISOString()
26792
+ };
26793
+ try {
26794
+ const content = TEXT_DECODER.decode(truncated ? buffer.subarray(0, MAX_TEXT_FILE_BYTES) : buffer);
26795
+ return { ...base, contentType: "text", content, truncated };
26796
+ } catch {
26797
+ return { ...base, contentType: "unsupported" };
26798
+ }
26799
+ }
26625
26800
  function resolveCodebase(projectPath) {
26626
26801
  const info = detectProject(projectPath);
26627
26802
  const override = getResourceIdOverride(info.rootPath);
@@ -26635,6 +26810,7 @@ function resolveCodebase(projectPath) {
26635
26810
  }
26636
26811
  function buildFsRoutes(options = {}) {
26637
26812
  const root = resolveFsRoot(options.root);
26813
+ const sessionFs = options.sessionFs;
26638
26814
  return [
26639
26815
  registerApiRoute2("/web/fs/list", {
26640
26816
  method: "GET",
@@ -26674,10 +26850,14 @@ function buildFsRoutes(options = {}) {
26674
26850
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
26675
26851
  if (!renderedRoot) return c.json({ error: "Missing required query param: root" }, 400);
26676
26852
  try {
26853
+ const session = await resolveAuthorizedSession(loose2(c), sessionFs, workspacePath);
26854
+ if (session && sessionFs) {
26855
+ return c.json(await listSessionRenderedPath(sessionFs.fleet, session, renderedRoot));
26856
+ }
26677
26857
  return c.json(await listWorkspaceRenderedPath(root, workspacePath, renderedRoot));
26678
26858
  } catch (error) {
26679
26859
  const message = error instanceof Error ? error.message : String(error);
26680
- const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") ? 403 : 500;
26860
+ const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") || message.includes("not available") ? 403 : 500;
26681
26861
  return c.json({ error: message }, status);
26682
26862
  }
26683
26863
  }
@@ -26691,10 +26871,14 @@ function buildFsRoutes(options = {}) {
26691
26871
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
26692
26872
  if (!path) return c.json({ error: "Missing required query param: path" }, 400);
26693
26873
  try {
26874
+ const session = await resolveAuthorizedSession(loose2(c), sessionFs, workspacePath);
26875
+ if (session && sessionFs) {
26876
+ return c.json(await readSessionWorkspaceFile(sessionFs.fleet, session, path));
26877
+ }
26694
26878
  return c.json(await readWorkspaceFile(root, workspacePath, path));
26695
26879
  } catch (error) {
26696
26880
  const message = error instanceof Error ? error.message : String(error);
26697
- const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") ? 403 : message.includes("directory") ? 400 : 500;
26881
+ const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") || message.includes("not available") ? 403 : message.includes("directory") ? 400 : message.includes("not found") ? 404 : 500;
26698
26882
  return c.json({ error: message }, status);
26699
26883
  }
26700
26884
  }
@@ -26720,7 +26904,7 @@ function buildFsRoutes(options = {}) {
26720
26904
 
26721
26905
  // src/routes/intake.ts
26722
26906
  import { registerApiRoute as registerApiRoute3 } from "@mastra/core/server";
26723
- function loose2(c) {
26907
+ function loose3(c) {
26724
26908
  return c;
26725
26909
  }
26726
26910
  function sanitizeIdList(value) {
@@ -26784,7 +26968,7 @@ var IntakeRoutes = class extends Route {
26784
26968
  method: "GET",
26785
26969
  requiresAuth: false,
26786
26970
  handler: async (c) => {
26787
- const tenant = await this.#resolveTenant(loose2(c));
26971
+ const tenant = await this.#resolveTenant(loose3(c));
26788
26972
  if ("response" in tenant) return tenant.response;
26789
26973
  await intake.ensureReady();
26790
26974
  const config = await intake.getConfig({ ...tenant, integrationIds });
@@ -26795,7 +26979,7 @@ var IntakeRoutes = class extends Route {
26795
26979
  method: "PUT",
26796
26980
  requiresAuth: false,
26797
26981
  handler: async (c) => {
26798
- const tenant = await this.#resolveTenant(loose2(c));
26982
+ const tenant = await this.#resolveTenant(loose3(c));
26799
26983
  if ("response" in tenant) return tenant.response;
26800
26984
  let body;
26801
26985
  try {
@@ -26810,7 +26994,7 @@ var IntakeRoutes = class extends Route {
26810
26994
  await intake.ensureReady();
26811
26995
  await intake.saveConfig({ ...tenant, config });
26812
26996
  await audit.emit({
26813
- context: loose2(c),
26997
+ context: loose3(c),
26814
26998
  input: {
26815
26999
  action: "factory.intake.config_updated",
26816
27000
  targets: [{ type: "intake_config", id: tenant.orgId }],
@@ -26829,7 +27013,7 @@ var IntakeRoutes = class extends Route {
26829
27013
  method: "GET",
26830
27014
  requiresAuth: false,
26831
27015
  handler: async (c) => {
26832
- const tenant = await this.#resolveTenant(loose2(c));
27016
+ const tenant = await this.#resolveTenant(loose3(c));
26833
27017
  if ("response" in tenant) return tenant.response;
26834
27018
  const pages = await Promise.all(
26835
27019
  integrations.map(async (integration) => ({
@@ -26848,7 +27032,7 @@ var IntakeRoutes = class extends Route {
26848
27032
  method: "GET",
26849
27033
  requiresAuth: false,
26850
27034
  handler: async (c) => {
26851
- const tenant = await this.#resolveTenant(loose2(c));
27035
+ const tenant = await this.#resolveTenant(loose3(c));
26852
27036
  if ("response" in tenant) return tenant.response;
26853
27037
  const cursors = decodeCursor(c.req.query("cursor"));
26854
27038
  if (!cursors) return c.json({ error: "invalid_cursor" }, 400);
@@ -27227,7 +27411,7 @@ var OAUTH_FLOWS = {
27227
27411
  }
27228
27412
  }
27229
27413
  };
27230
- function loose3(c) {
27414
+ function loose4(c) {
27231
27415
  return c;
27232
27416
  }
27233
27417
  var localSessionsPromise;
@@ -27297,14 +27481,14 @@ var OAuthRoutes = class extends Route {
27297
27481
  method: "POST",
27298
27482
  requiresAuth: false,
27299
27483
  handler: async (c) => {
27300
- const ctx = await resolveCredentialContext({ c: loose3(c), auth, credentials: modelCredentials });
27484
+ const ctx = await resolveCredentialContext({ c: loose4(c), auth, credentials: modelCredentials });
27301
27485
  if ("response" in ctx) return ctx.response;
27302
27486
  const provider = c.req.param("provider");
27303
27487
  const flow = OAUTH_FLOWS[provider];
27304
27488
  if (!flow) {
27305
27489
  return c.json({ error: "oauth_not_supported", message: `Provider does not support web sign-in` }, 404);
27306
27490
  }
27307
- const body = await readJsonBody(loose3(c));
27491
+ const body = await readJsonBody(loose4(c));
27308
27492
  if (typeof body.mode === "string" && body.mode !== flow.kind) {
27309
27493
  return c.json({ error: "invalid_mode", message: `Unsupported mode for ${provider}: ${body.mode}` }, 400);
27310
27494
  }
@@ -27341,12 +27525,12 @@ var OAuthRoutes = class extends Route {
27341
27525
  method: "POST",
27342
27526
  requiresAuth: false,
27343
27527
  handler: async (c) => {
27344
- const ctx = await resolveCredentialContext({ c: loose3(c), auth, credentials: modelCredentials });
27528
+ const ctx = await resolveCredentialContext({ c: loose4(c), auth, credentials: modelCredentials });
27345
27529
  if ("response" in ctx) return ctx.response;
27346
27530
  const provider = c.req.param("provider");
27347
27531
  const flow = OAUTH_FLOWS[provider];
27348
27532
  if (!flow?.complete) return c.json({ error: "oauth_not_supported" }, 404);
27349
- const body = await readJsonBody(loose3(c));
27533
+ const body = await readJsonBody(loose4(c));
27350
27534
  const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
27351
27535
  const code = typeof body.code === "string" ? body.code.trim() : "";
27352
27536
  if (!sessionId || !code) return c.json({ error: "Missing required fields: sessionId, code" }, 400);
@@ -27376,12 +27560,12 @@ var OAuthRoutes = class extends Route {
27376
27560
  method: "POST",
27377
27561
  requiresAuth: false,
27378
27562
  handler: async (c) => {
27379
- const ctx = await resolveCredentialContext({ c: loose3(c), auth, credentials: modelCredentials });
27563
+ const ctx = await resolveCredentialContext({ c: loose4(c), auth, credentials: modelCredentials });
27380
27564
  if ("response" in ctx) return ctx.response;
27381
27565
  const provider = c.req.param("provider");
27382
27566
  const flow = OAUTH_FLOWS[provider];
27383
27567
  if (!flow?.poll) return c.json({ error: "oauth_not_supported" }, 404);
27384
- const body = await readJsonBody(loose3(c));
27568
+ const body = await readJsonBody(loose4(c));
27385
27569
  const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
27386
27570
  if (!sessionId) return c.json({ error: "Missing required field: sessionId" }, 400);
27387
27571
  const session = await loadOwnedSession({ ctx, provider, sessionId });
@@ -27432,7 +27616,7 @@ var OAuthRoutes = class extends Route {
27432
27616
  method: "DELETE",
27433
27617
  requiresAuth: false,
27434
27618
  handler: async (c) => {
27435
- const ctx = await resolveCredentialContext({ c: loose3(c), auth, credentials: modelCredentials });
27619
+ const ctx = await resolveCredentialContext({ c: loose4(c), auth, credentials: modelCredentials });
27436
27620
  if ("response" in ctx) return ctx.response;
27437
27621
  const provider = c.req.param("provider");
27438
27622
  const sessionId = c.req.param("sessionId");
@@ -27445,7 +27629,7 @@ var OAuthRoutes = class extends Route {
27445
27629
  method: "DELETE",
27446
27630
  requiresAuth: false,
27447
27631
  handler: async (c) => {
27448
- const ctx = await resolveCredentialContext({ c: loose3(c), auth, credentials: modelCredentials });
27632
+ const ctx = await resolveCredentialContext({ c: loose4(c), auth, credentials: modelCredentials });
27449
27633
  if ("response" in ctx) return ctx.response;
27450
27634
  const provider = c.req.param("provider");
27451
27635
  const authProviderId = getAuthProviderId(provider);
@@ -27511,7 +27695,7 @@ var MAX_SCOPE_LENGTH = 2048;
27511
27695
  var MAX_ARGUMENTS_LENGTH2 = 16384;
27512
27696
  var SKILL_NAME_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
27513
27697
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
27514
- function loose4(context) {
27698
+ function loose5(context) {
27515
27699
  return context;
27516
27700
  }
27517
27701
  function parseBody(value) {
@@ -27585,7 +27769,7 @@ var SkillRoutes = class extends Route {
27585
27769
  const { controllerId, controller, authorizeSessionAddress: customAuthorize } = this.deps;
27586
27770
  const authorize = customAuthorize ?? ((context, address) => this.#authorizeSessionAddress(context, address));
27587
27771
  const handleSkillRequest = async (context, dispatch) => {
27588
- const c = loose4(context);
27772
+ const c = loose5(context);
27589
27773
  if (c.req.param("controllerId") !== controllerId) {
27590
27774
  return c.json({ error: "controller_not_found", message: "Agent controller not found." }, 404);
27591
27775
  }
@@ -27886,7 +28070,7 @@ function computeFactoryMetrics(items, opts) {
27886
28070
  }
27887
28071
 
27888
28072
  // src/routes/work-items.ts
27889
- function loose5(c) {
28073
+ function loose6(c) {
27890
28074
  return c;
27891
28075
  }
27892
28076
  var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -28204,7 +28388,7 @@ var WorkItemRoutes = class extends Route {
28204
28388
  method: "GET",
28205
28389
  requiresAuth: false,
28206
28390
  handler: async (c) => {
28207
- const resolved = await this.#resolveProject(loose5(c));
28391
+ const resolved = await this.#resolveProject(loose6(c));
28208
28392
  if ("response" in resolved) return resolved.response;
28209
28393
  await workItems.ensureReady();
28210
28394
  const items = await workItems.list({
@@ -28219,11 +28403,11 @@ var WorkItemRoutes = class extends Route {
28219
28403
  method: "GET",
28220
28404
  requiresAuth: false,
28221
28405
  handler: async (c) => {
28222
- const resolved = await this.#resolveProject(loose5(c));
28406
+ const resolved = await this.#resolveProject(loose6(c));
28223
28407
  if ("response" in resolved) return resolved.response;
28224
28408
  const { windowStart, windowEnd } = parseMetricsRange(
28225
- loose5(c).req.query("from"),
28226
- loose5(c).req.query("to"),
28409
+ loose6(c).req.query("from"),
28410
+ loose6(c).req.query("to"),
28227
28411
  /* @__PURE__ */ new Date()
28228
28412
  );
28229
28413
  await workItems.ensureReady();
@@ -28239,7 +28423,7 @@ var WorkItemRoutes = class extends Route {
28239
28423
  method: "GET",
28240
28424
  requiresAuth: false,
28241
28425
  handler: async (c) => {
28242
- const resolved = await this.#resolveProject(loose5(c));
28426
+ const resolved = await this.#resolveProject(loose6(c));
28243
28427
  if ("response" in resolved) return resolved.response;
28244
28428
  await queueHealth.ensureReady();
28245
28429
  const stored = await queueHealth.getConfig(resolved.orgId, resolved.factoryProjectId);
@@ -28251,7 +28435,7 @@ var WorkItemRoutes = class extends Route {
28251
28435
  method: "GET",
28252
28436
  requiresAuth: false,
28253
28437
  handler: async (c) => {
28254
- const context = loose5(c);
28438
+ const context = loose6(c);
28255
28439
  const resolved = await this.#resolveProject(context);
28256
28440
  if ("response" in resolved) return resolved.response;
28257
28441
  const cursorRaw = context.req.query("before");
@@ -28276,7 +28460,7 @@ var WorkItemRoutes = class extends Route {
28276
28460
  method: "POST",
28277
28461
  requiresAuth: false,
28278
28462
  handler: async (c) => {
28279
- const context = loose5(c);
28463
+ const context = loose6(c);
28280
28464
  const resolved = await this.#resolveProject(context);
28281
28465
  if ("response" in resolved) return resolved.response;
28282
28466
  const decisionId = context.req.param("decisionId");
@@ -28297,9 +28481,9 @@ var WorkItemRoutes = class extends Route {
28297
28481
  method: "POST",
28298
28482
  requiresAuth: false,
28299
28483
  handler: async (c) => {
28300
- const resolved = await this.#resolveProject(loose5(c));
28484
+ const resolved = await this.#resolveProject(loose6(c));
28301
28485
  if ("response" in resolved) return resolved.response;
28302
- const body = await readJson(loose5(c));
28486
+ const body = await readJson(loose6(c));
28303
28487
  if (body === void 0) return c.json({ error: "Invalid JSON body" }, 400);
28304
28488
  const input = parseCreateWorkItem(body);
28305
28489
  if (!input) return c.json({ error: "invalid_work_item" }, 400);
@@ -28342,7 +28526,7 @@ var WorkItemRoutes = class extends Route {
28342
28526
  }
28343
28527
  item = await workItems.getForProject(resolved.orgId, resolved.factoryProjectId, item.id) ?? item;
28344
28528
  await audit.emit({
28345
- context: loose5(c),
28529
+ context: loose6(c),
28346
28530
  input: {
28347
28531
  action: "factory.work_item.created",
28348
28532
  factoryProjectId: resolved.factoryProjectId,
@@ -28353,7 +28537,7 @@ var WorkItemRoutes = class extends Route {
28353
28537
  } else {
28354
28538
  const { stages: _stages, sessions: _sessions, ...boundedPatch } = input;
28355
28539
  await this.#auditWorkItemPatch({
28356
- context: loose5(c),
28540
+ context: loose6(c),
28357
28541
  item,
28358
28542
  previous: result.previous,
28359
28543
  patch: boundedPatch
@@ -28373,11 +28557,11 @@ var WorkItemRoutes = class extends Route {
28373
28557
  method: "POST",
28374
28558
  requiresAuth: false,
28375
28559
  handler: async (c) => {
28376
- const resolved = await this.#resolveProject(loose5(c));
28560
+ const resolved = await this.#resolveProject(loose6(c));
28377
28561
  if ("response" in resolved) return resolved.response;
28378
- const workItemId = loose5(c).req.param("workItemId");
28562
+ const workItemId = loose6(c).req.param("workItemId");
28379
28563
  if (!workItemId || !UUID_RE2.test(workItemId)) return c.json({ error: "Work item not found" }, 404);
28380
- const parsed = parseTransitionBody(await readJson(loose5(c)));
28564
+ const parsed = parseTransitionBody(await readJson(loose6(c)));
28381
28565
  if (!parsed) return c.json({ error: "invalid_transition_request" }, 400);
28382
28566
  if (!transitionService) {
28383
28567
  return c.json({ error: "factory_transition_unavailable" }, 503);
@@ -28395,7 +28579,7 @@ var WorkItemRoutes = class extends Route {
28395
28579
  }
28396
28580
  });
28397
28581
  await audit.emit({
28398
- context: loose5(c),
28582
+ context: loose6(c),
28399
28583
  input: {
28400
28584
  action: result.status === "accepted" ? "factory.work_item.stage_moved" : "factory.work_item.transition_rejected",
28401
28585
  factoryProjectId: resolved.factoryProjectId,
@@ -28417,14 +28601,14 @@ var WorkItemRoutes = class extends Route {
28417
28601
  method: "POST",
28418
28602
  requiresAuth: false,
28419
28603
  handler: async (c) => {
28420
- const resolved = await this.#resolveProject(loose5(c));
28604
+ const resolved = await this.#resolveProject(loose6(c));
28421
28605
  if ("response" in resolved) return resolved.response;
28422
28606
  if (!startCoordinator) {
28423
28607
  return c.json({ error: "factory_start_unavailable" }, 503);
28424
28608
  }
28425
- const input = parseStartBody(await readJson(loose5(c)), resolved, resolved.factoryProjectId);
28609
+ const input = parseStartBody(await readJson(loose6(c)), resolved, resolved.factoryProjectId);
28426
28610
  if (!input) return c.json({ error: "invalid_factory_start" }, 400);
28427
- input.requestContext = loose5(c).get("requestContext");
28611
+ input.requestContext = loose6(c).get("requestContext");
28428
28612
  input.defaultModelId = resolved.defaultModelId ?? void 0;
28429
28613
  if (!input.workItem.id && ((input.workItem.input.stages ?? ["intake"]).length !== 1 || (input.workItem.input.stages ?? ["intake"])[0] !== "intake")) {
28430
28614
  return c.json(
@@ -28443,7 +28627,7 @@ var WorkItemRoutes = class extends Route {
28443
28627
  throw error;
28444
28628
  }
28445
28629
  await audit.emit({
28446
- context: loose5(c),
28630
+ context: loose6(c),
28447
28631
  input: {
28448
28632
  action: "factory.run.started",
28449
28633
  factoryProjectId: resolved.factoryProjectId,
@@ -28465,11 +28649,11 @@ var WorkItemRoutes = class extends Route {
28465
28649
  method: "PATCH",
28466
28650
  requiresAuth: false,
28467
28651
  handler: async (c) => {
28468
- const tenant = await this.#resolveTenant(loose5(c));
28652
+ const tenant = await this.#resolveTenant(loose6(c));
28469
28653
  if ("response" in tenant) return tenant.response;
28470
- const id = loose5(c).req.param("id");
28654
+ const id = loose6(c).req.param("id");
28471
28655
  if (!id || !UUID_RE2.test(id)) return c.json({ error: "Work item not found" }, 404);
28472
- const body = await readJson(loose5(c));
28656
+ const body = await readJson(loose6(c));
28473
28657
  if (body === void 0) return c.json({ error: "Invalid JSON body" }, 400);
28474
28658
  const patch = parseUpdateWorkItem(body);
28475
28659
  if (!patch) return c.json({ error: "invalid_work_item_patch" }, 400);
@@ -28484,7 +28668,7 @@ var WorkItemRoutes = class extends Route {
28484
28668
  const updated = await workItems.update({ orgId: tenant.orgId, id, userId: tenant.userId, patch });
28485
28669
  if (!updated) return c.json({ error: "Work item not found" }, 404);
28486
28670
  await this.#auditWorkItemPatch({
28487
- context: loose5(c),
28671
+ context: loose6(c),
28488
28672
  item: updated.item,
28489
28673
  previous: updated.previous,
28490
28674
  patch
@@ -28503,15 +28687,15 @@ var WorkItemRoutes = class extends Route {
28503
28687
  method: "DELETE",
28504
28688
  requiresAuth: false,
28505
28689
  handler: async (c) => {
28506
- const tenant = await this.#resolveTenant(loose5(c));
28690
+ const tenant = await this.#resolveTenant(loose6(c));
28507
28691
  if ("response" in tenant) return tenant.response;
28508
- const id = loose5(c).req.param("id");
28692
+ const id = loose6(c).req.param("id");
28509
28693
  if (!id || !UUID_RE2.test(id)) return c.json({ error: "Work item not found" }, 404);
28510
28694
  await workItems.ensureReady();
28511
28695
  const deleted = await workItems.delete({ orgId: tenant.orgId, id });
28512
28696
  if (!deleted) return c.json({ error: "Work item not found" }, 404);
28513
28697
  await audit.emit({
28514
- context: loose5(c),
28698
+ context: loose6(c),
28515
28699
  input: {
28516
28700
  action: "factory.work_item.deleted",
28517
28701
  factoryProjectId: deleted.factoryProjectId,
@@ -28732,7 +28916,14 @@ function assembleFactoryApiRoutes(deps) {
28732
28916
  });
28733
28917
  }
28734
28918
  return [
28735
- ...buildFsRoutes({ root: deps.fsRoot }),
28919
+ ...buildFsRoutes({
28920
+ root: deps.fsRoot,
28921
+ sessionFs: {
28922
+ auth: deps.auth,
28923
+ fleet: deps.fleet,
28924
+ sessions: deps.sourceControlStorage.forIntegration("github").sessions
28925
+ }
28926
+ }),
28736
28927
  ...new ConfigRoutes({
28737
28928
  auth: deps.auth,
28738
28929
  controller: deps.controller,