@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
package/dist/factory.js CHANGED
@@ -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-${randomUUID5()}`,
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-${randomUUID5()}`,
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;
@@ -25423,6 +25509,34 @@ function getGithubFeatureDiagnostics(options) {
25423
25509
  // src/integrations/github/project-lock.ts
25424
25510
  import { createHash } from "crypto";
25425
25511
  var inProcessLocks = /* @__PURE__ */ new Map();
25512
+ var DEFAULT_PROJECT_LOCK_TIMEOUT_MS = 6e4;
25513
+ var ProjectLockTimeoutError = class extends Error {
25514
+ key;
25515
+ timeoutMs;
25516
+ constructor(key, timeoutMs) {
25517
+ super(`Project lock critical section for "${key}" exceeded ${timeoutMs}ms`);
25518
+ this.name = "ProjectLockTimeoutError";
25519
+ this.key = key;
25520
+ this.timeoutMs = timeoutMs;
25521
+ }
25522
+ };
25523
+ async function runWithTimeout(key, timeoutMs, fn) {
25524
+ const controller = new AbortController();
25525
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
25526
+ const abortError = new Promise((_, reject) => {
25527
+ controller.signal.addEventListener("abort", () => reject(new ProjectLockTimeoutError(key, timeoutMs)), {
25528
+ once: true
25529
+ });
25530
+ });
25531
+ const work = fn(controller.signal);
25532
+ work.catch(() => {
25533
+ });
25534
+ try {
25535
+ return await Promise.race([work, abortError]);
25536
+ } finally {
25537
+ clearTimeout(timer);
25538
+ }
25539
+ }
25426
25540
  function hashKey(key) {
25427
25541
  const digest = createHash("sha256").update(key).digest();
25428
25542
  const a = digest.readInt32BE(0);
@@ -25430,9 +25544,9 @@ function hashKey(key) {
25430
25544
  return [a, b];
25431
25545
  }
25432
25546
  function withProjectLock(options) {
25433
- const { key, storage, fn, pool } = options;
25547
+ const { key, storage, fn, pool, timeoutMs = DEFAULT_PROJECT_LOCK_TIMEOUT_MS } = options;
25434
25548
  const prev = inProcessLocks.get(key) ?? Promise.resolve();
25435
- const run = () => withDbAdvisoryLock({ key, storage, fn, pool });
25549
+ const run = () => withDbAdvisoryLock({ key, storage, fn, pool, timeoutMs });
25436
25550
  const next = prev.then(run, run);
25437
25551
  const tail = next.then(
25438
25552
  () => void 0,
@@ -25447,28 +25561,31 @@ function withProjectLock(options) {
25447
25561
  return next;
25448
25562
  }
25449
25563
  async function withDbAdvisoryLock(options) {
25450
- const { key, storage, fn, pool } = options;
25564
+ const { key, storage, fn, pool, timeoutMs = DEFAULT_PROJECT_LOCK_TIMEOUT_MS } = options;
25451
25565
  if (process.env.MASTRACODE_DISTRIBUTED_LOCK === "0") {
25452
- return fn();
25566
+ return runWithTimeout(key, timeoutMs, fn);
25453
25567
  }
25454
- if (pool) return advisoryLockOver(pool, key, fn);
25568
+ if (pool) return advisoryLockOver(pool, key, timeoutMs, fn);
25455
25569
  if (typeof storage?.withDistributedLock !== "function") {
25456
- return fn();
25570
+ return runWithTimeout(key, timeoutMs, fn);
25457
25571
  }
25458
- return storage.withDistributedLock(key, fn);
25572
+ return storage.withDistributedLock(key, () => runWithTimeout(key, timeoutMs, fn));
25459
25573
  }
25460
- async function advisoryLockOver(pool, key, fn) {
25574
+ async function advisoryLockOver(pool, key, timeoutMs, fn) {
25461
25575
  const [k1, k2] = hashKey(key);
25462
25576
  const client = await pool.connect();
25463
25577
  try {
25464
25578
  await client.query("BEGIN");
25465
25579
  await client.query("SELECT pg_advisory_xact_lock($1, $2)", [k1, k2]);
25466
25580
  try {
25467
- const result = await fn();
25581
+ const result = await runWithTimeout(key, timeoutMs, fn);
25468
25582
  await client.query("COMMIT");
25469
25583
  return result;
25470
25584
  } catch (err) {
25471
- await client.query("ROLLBACK");
25585
+ try {
25586
+ await client.query("ROLLBACK");
25587
+ } catch {
25588
+ }
25472
25589
  throw err;
25473
25590
  }
25474
25591
  } finally {
@@ -26444,7 +26561,7 @@ function buildGithubRoutes(options) {
26444
26561
  list = await github.listInstallationRepos(Number(inst.externalId));
26445
26562
  } catch (err) {
26446
26563
  if (err.status !== 404) throw err;
26447
- console.error(`[MastraCode Web] pruning stale GitHub installation ${inst.externalId} (404 from GitHub)`);
26564
+ console.error(`[Mastra Factory] pruning stale GitHub installation ${inst.externalId} (404 from GitHub)`);
26448
26565
  await github.sourceControlStorage.installations.delete({ orgId: resolved.tenant.orgId, id: inst.id });
26449
26566
  continue;
26450
26567
  }
@@ -27387,7 +27504,7 @@ function logPlatformError(message, fields) {
27387
27504
  }
27388
27505
  function writePlatformLog(level, message, fields) {
27389
27506
  const metadata = fields ? ` ${JSON.stringify(stripUndefined(fields))}` : "";
27390
- process.stderr.write(`[MastraCode Web] ${level.toUpperCase()} ${message}${metadata}
27507
+ process.stderr.write(`[Mastra Factory] ${level.toUpperCase()} ${message}${metadata}
27391
27508
  `);
27392
27509
  }
27393
27510
  function stripUndefined(fields) {
@@ -27404,7 +27521,7 @@ import { randomUUID as randomUUID2 } from "crypto";
27404
27521
  import { isLeaseProvider, NoopLeaseProvider } from "@mastra/core/events";
27405
27522
  import { MastraWorker } from "@mastra/core/worker";
27406
27523
  var API_PREFIX = "/v1/server/github-app";
27407
- var DEFAULT_POLL_INTERVAL_MS = 5e3;
27524
+ var DEFAULT_POLL_INTERVAL_MS = 2e4;
27408
27525
  var EVENT_PAGE_SIZE = 500;
27409
27526
  var MIN_LEASE_TTL_MS = 3e4;
27410
27527
  var CURSOR_ORG_ID = "__platform_github_event_worker__";
@@ -27594,10 +27711,16 @@ var PlatformGithubEventWorker = class extends MastraWorker {
27594
27711
  const query = new URLSearchParams({ limit: String(EVENT_PAGE_SIZE) });
27595
27712
  if ("afterEventId" in cursor) query.set("afterEventId", cursor.afterEventId);
27596
27713
  else query.set("afterTimestamp", String(cursor.afterTimestamp));
27714
+ const pollStartedAt = performance.now();
27597
27715
  const page = await this.#client.request(
27598
27716
  "GET",
27599
27717
  `${API_PREFIX}/repositories/${repositoryId}/events?${query}`
27600
27718
  );
27719
+ this.deps?.logger.info("Platform GitHub repository event poll completed", {
27720
+ repositoryId,
27721
+ eventCount: page.events.length,
27722
+ latencyMs: Math.round(performance.now() - pollStartedAt)
27723
+ });
27601
27724
  if (page.events.length === 0 || !page.nextCursor) return;
27602
27725
  for (const event of page.events) {
27603
27726
  if (!this.#running || !this.#hasLease) return;
@@ -27720,6 +27843,7 @@ var PlatformGithubIntegration = class {
27720
27843
  #storage;
27721
27844
  #integrationStorage;
27722
27845
  intake = {
27846
+ resolveIntakeDispatch: (input) => this.#resolveIntakeDispatch(input),
27723
27847
  listSources: async ({ orgId, userId }) => {
27724
27848
  const installations = await this.#client.request("GET", `${API_PREFIX2}/github-app/installations`);
27725
27849
  const usable = installations.installations.filter(
@@ -27818,7 +27942,8 @@ var PlatformGithubIntegration = class {
27818
27942
  return this.#listIssues(input.connection, sourceId, parsePositiveCursor(input.cursor), input.labels);
27819
27943
  },
27820
27944
  getIssue: (input) => this.#getIssue(input.connection, input.sourceId, input.issueId),
27821
- createComment: (input) => this.#createIssueComment(input)
27945
+ createComment: (input) => this.#createIssueComment(input),
27946
+ updateIssue: (input) => this.#updateIntakeIssue(input)
27822
27947
  };
27823
27948
  versionControl = {
27824
27949
  initialize: ({ storage }) => {
@@ -27910,6 +28035,21 @@ var PlatformGithubIntegration = class {
27910
28035
  }
27911
28036
  return this.#integrationStorage;
27912
28037
  }
28038
+ /** Resolve a stored GitHub locator without scanning installations or repositories. */
28039
+ async #resolveIntakeDispatch({
28040
+ orgId,
28041
+ externalSource
28042
+ }) {
28043
+ const target = parseGithubExternalTarget(externalSource.externalId);
28044
+ if (!target) return null;
28045
+ const repository = target.repository.includes("/") ? target.repository : (await this.storage.repositories.findByExternalId({ orgId, externalId: target.repository }))?.slug;
28046
+ if (!repository) return null;
28047
+ return {
28048
+ connection: { type: "app-installation", installationId: 1 },
28049
+ sourceId: repository,
28050
+ issueId: target.issueId
28051
+ };
28052
+ }
27913
28053
  initialize({ storage }) {
27914
28054
  this.#integrationStorage = storage;
27915
28055
  logPlatformInfo("Platform GitHub integration initialized", {
@@ -28208,6 +28348,39 @@ var PlatformGithubIntegration = class {
28208
28348
  throw error;
28209
28349
  }
28210
28350
  }
28351
+ async #updateIntakeIssue(input) {
28352
+ requireGithubConnection(input.connection);
28353
+ const repository = requireSource(input.sourceId, "GitHub Intake requires a repository source.");
28354
+ const issueNumber = requirePositiveId(input.issueId, "issue");
28355
+ if (input.state.kind === "byName") {
28356
+ logPlatformWarn(`Platform GitHub: updateIssue byName is not supported (name=${input.state.name}); ignoring.`);
28357
+ return null;
28358
+ }
28359
+ const targetState = input.state.stateType === "unstarted" || input.state.stateType === "started" ? "open" : "closed";
28360
+ const stateReason = targetState === "closed" ? input.state.stateType === "canceled" ? "not_planned" : "completed" : null;
28361
+ try {
28362
+ await this.#client.request("GET", repositoryPath(repository, `pulls/${issueNumber}`));
28363
+ logPlatformWarn(`Platform GitHub: updateIssue rejected \u2014 target ${repository}#${issueNumber} is a pull request.`);
28364
+ return null;
28365
+ } catch (error) {
28366
+ if (!isNotFound(error)) throw error;
28367
+ }
28368
+ try {
28369
+ const issue = await this.#client.request(
28370
+ "PATCH",
28371
+ repositoryPath(repository, `issues/${issueNumber}`),
28372
+ {
28373
+ state: targetState,
28374
+ ...stateReason ? { state_reason: stateReason } : {}
28375
+ },
28376
+ { actingUserId: input.actingUserId }
28377
+ );
28378
+ return parseIntakeIssue(repository, issue);
28379
+ } catch (error) {
28380
+ if (isNotFound(error)) return null;
28381
+ throw error;
28382
+ }
28383
+ }
28211
28384
  async #createIssueComment(input) {
28212
28385
  requireGithubConnection(input.connection);
28213
28386
  const repository = requireSource(input.sourceId, "GitHub Intake requires a repository source.");
@@ -28592,6 +28765,11 @@ function parsePositiveInteger(value) {
28592
28765
  const parsed = Number(value);
28593
28766
  return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
28594
28767
  }
28768
+ function parseGithubExternalTarget(externalId) {
28769
+ const match = externalId.match(/^(.+\/.+):(\d+)$/) ?? externalId.match(/^github:(\d+):(?:issue|pull-request):(\d+)$/) ?? externalId.match(/^(\d+):(\d+)$/);
28770
+ if (!match?.[1] || !match[2] || parsePositiveInteger(match[2]) === null) return null;
28771
+ return { repository: match[1], issueId: match[2] };
28772
+ }
28595
28773
  function optionalPositiveIntegerEnv(name) {
28596
28774
  const value = process.env[name]?.trim();
28597
28775
  if (!value) return void 0;
@@ -28962,7 +29140,14 @@ var PlatformLinearIntegration = class {
28962
29140
  #endpointHost;
28963
29141
  #projects;
28964
29142
  #auth;
29143
+ /**
29144
+ * Per-instance workflow-state cache keyed by `${workspaceId}:${teamId}`. Scoped to the process
29145
+ * lifetime; not shared across requests intentionally to keep failure modes simple (stale states
29146
+ * clear on process restart). Populated lazily on first `updateIssue` per team.
29147
+ */
29148
+ #workflowStatesByTeam = /* @__PURE__ */ new Map();
28965
29149
  intake = {
29150
+ resolveIntakeDispatch: (input) => this.#resolveIntakeDispatch(input),
28966
29151
  listSources: async () => {
28967
29152
  const sources = await this.#listProjectSources();
28968
29153
  return sources.map(({ workspace, project }) => ({
@@ -29033,6 +29218,59 @@ var PlatformLinearIntegration = class {
29033
29218
  if (isNotFound2(error)) return null;
29034
29219
  throw error;
29035
29220
  }
29221
+ },
29222
+ updateIssue: async (input) => {
29223
+ requireLinearConnection(input.connection);
29224
+ const located = await this.#findIssue(input.sourceId, input.issueId);
29225
+ if (!located) return null;
29226
+ const { workspaceId, issue } = located;
29227
+ const target = input.state;
29228
+ if (target.kind === "byType" && issue.state.type === target.stateType) {
29229
+ return parseIssue(issue);
29230
+ }
29231
+ if (target.kind === "byName" && issue.state.name.toLowerCase() === target.name.toLowerCase()) {
29232
+ return parseIssue(issue);
29233
+ }
29234
+ let states;
29235
+ try {
29236
+ states = await this.#listWorkflowStates(workspaceId, issue.team.id);
29237
+ } catch (error) {
29238
+ if (isNotFound2(error)) {
29239
+ logPlatformWarn("Platform Linear: workflow-states endpoint not available; skipping updateIssue.", {
29240
+ workspaceId,
29241
+ teamId: issue.team.id
29242
+ });
29243
+ return null;
29244
+ }
29245
+ throw error;
29246
+ }
29247
+ let matched;
29248
+ if (target.kind === "byType") {
29249
+ matched = states.slice().sort((a, b) => a.position - b.position).find((s) => s.type === target.stateType);
29250
+ } else {
29251
+ const wanted = target.name.toLowerCase();
29252
+ matched = states.find((s) => s.name.toLowerCase() === wanted);
29253
+ }
29254
+ if (!matched) {
29255
+ logPlatformWarn("Platform Linear: no workflow state matched target; skipping updateIssue.", {
29256
+ workspaceId,
29257
+ teamId: issue.team.id,
29258
+ target
29259
+ });
29260
+ return null;
29261
+ }
29262
+ try {
29263
+ await this.#client.request(
29264
+ "PATCH",
29265
+ `${API_PREFIX3}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(input.issueId)}`,
29266
+ { stateId: matched.id }
29267
+ );
29268
+ } catch (error) {
29269
+ if (isNotFound2(error)) return null;
29270
+ throw error;
29271
+ }
29272
+ const refreshed = await this.#findIssue(input.sourceId, input.issueId);
29273
+ return refreshed ? parseIssue(refreshed.issue) : null;
29036
29274
  }
29037
29275
  };
29038
29276
  constructor() {
@@ -29105,6 +29343,23 @@ var PlatformLinearIntegration = class {
29105
29343
  async getFreshAccessToken(_connection) {
29106
29344
  return PLATFORM_MANAGED_CONNECTION_TOKEN;
29107
29345
  }
29346
+ /**
29347
+ * Background-dispatch context: platform-managed connection token when the
29348
+ * org has a connected workspace. Linear work items store the issue UUID
29349
+ * directly as `externalId`.
29350
+ */
29351
+ async #resolveIntakeDispatch({
29352
+ orgId,
29353
+ externalSource
29354
+ }) {
29355
+ if (externalSource.type !== "issue") return null;
29356
+ const connection = await this.loadConnection(orgId);
29357
+ if (!connection) return null;
29358
+ return {
29359
+ connection: { type: "oauth", accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN },
29360
+ issueId: externalSource.externalId
29361
+ };
29362
+ }
29108
29363
  canPostComments(connection) {
29109
29364
  const scopes = (connection.scope ?? "").split(/[\s,]+/).filter(Boolean);
29110
29365
  return scopes.some((scope) => scope === "comments:create" || scope === "write" || scope === "admin");
@@ -29264,6 +29519,17 @@ var PlatformLinearIntegration = class {
29264
29519
  }
29265
29520
  return null;
29266
29521
  }
29522
+ async #listWorkflowStates(workspaceId, teamId) {
29523
+ const cacheKey = `${workspaceId}:${teamId}`;
29524
+ const cached = this.#workflowStatesByTeam.get(cacheKey);
29525
+ if (cached) return cached;
29526
+ const result = await this.#client.request(
29527
+ "GET",
29528
+ `${API_PREFIX3}/workspaces/${encodeURIComponent(workspaceId)}/workflow-states?teamId=${encodeURIComponent(teamId)}&first=100`
29529
+ );
29530
+ this.#workflowStatesByTeam.set(cacheKey, result.workflowStates);
29531
+ return result.workflowStates;
29532
+ }
29267
29533
  async #candidateWorkspaceIds(sourceId) {
29268
29534
  if (sourceId) return [decodeSourceId(sourceId).workspaceId];
29269
29535
  return (await this.#listWorkspaces()).map((workspace) => workspace.linearWorkspaceId);
@@ -29769,6 +30035,7 @@ var ProjectRoutes = class extends Route {
29769
30035
  orgId: tenant.orgId,
29770
30036
  id: connection.installationId
29771
30037
  });
30038
+ if (!installation) continue;
29772
30039
  const links = await handle.projectRepositories.list({ orgId: tenant.orgId, connectionId: connection.id });
29773
30040
  connections.push({
29774
30041
  ...connection,
@@ -31851,12 +32118,16 @@ var ConfigRoutes = class extends Route {
31851
32118
  // src/routes/fs.ts
31852
32119
  import { lstat, open, readdir, realpath, stat } from "fs/promises";
31853
32120
  import { homedir } from "os";
31854
- import { isAbsolute, join, resolve, sep } from "path";
32121
+ import { isAbsolute, join, posix as posixPath, resolve, sep } from "path";
32122
+ import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
31855
32123
  import { detectProject, getResourceIdOverride } from "@mastra/code-sdk/utils/project";
31856
32124
  import { registerApiRoute as registerApiRoute8 } from "@mastra/core/server";
31857
32125
  var MAX_TEXT_FILE_BYTES = 512 * 1024;
31858
32126
  var TEXT_DECODER = new TextDecoder("utf-8", { fatal: true });
31859
32127
  var APPROVED_RENDERED_ROOTS = /* @__PURE__ */ new Set([".artifacts"]);
32128
+ function loose7(c) {
32129
+ return c;
32130
+ }
31860
32131
  function resolveFsRoot(root) {
31861
32132
  return resolve(root && root.trim() ? root : homedir());
31862
32133
  }
@@ -32037,6 +32308,91 @@ async function listArtifacts(root, workspacePath) {
32037
32308
  entries: listing.entries
32038
32309
  };
32039
32310
  }
32311
+ async function resolveAuthorizedSession(c, deps, workspacePath) {
32312
+ if (!deps) return null;
32313
+ const session = await deps.sessions.getBySessionId(workspacePath);
32314
+ if (!session) return null;
32315
+ if (deps.auth.enabled()) {
32316
+ await deps.auth.ensureUser(c);
32317
+ const tenant = deps.auth.tenant(c);
32318
+ if (!tenant || tenant.orgId !== session.orgId || tenant.userId !== session.userId) {
32319
+ throw new Error("Session is not available to the current user");
32320
+ }
32321
+ }
32322
+ return session;
32323
+ }
32324
+ async function sessionSandbox(fleet, session) {
32325
+ if (!fleet.enabled || !session.sandboxId || !session.sandboxWorkdir) return null;
32326
+ let sandbox;
32327
+ try {
32328
+ sandbox = await fleet.reattachSandbox(session.sandboxId, { workingDirectory: session.sandboxWorkdir });
32329
+ } catch {
32330
+ return null;
32331
+ }
32332
+ return {
32333
+ sandbox,
32334
+ filesystem: new SandboxFilesystem({ sandbox, workdir: session.sandboxWorkdir }),
32335
+ workdir: session.sandboxWorkdir
32336
+ };
32337
+ }
32338
+ async function listSessionRenderedPath(fleet, session, renderedRoot) {
32339
+ const safeRoot = assertApprovedRenderedRoot(renderedRoot);
32340
+ const rootPath = posixPath.join(session.sandboxWorkdir ?? "", safeRoot);
32341
+ const empty = { workspacePath: session.sessionId, root: safeRoot, rootPath, entries: [] };
32342
+ const handle = await sessionSandbox(fleet, session);
32343
+ if (!handle) return empty;
32344
+ const quotedRoot = `'${rootPath.replace(/'/g, `'\\''`)}'`;
32345
+ const result = await handle.sandbox.executeCommand(
32346
+ "sh",
32347
+ [
32348
+ "-c",
32349
+ `test -d ${quotedRoot} && find ${quotedRoot} -mindepth 1 -printf '%y\\t%s\\t%T@\\t%p\\n' 2>/dev/null || true`
32350
+ ],
32351
+ { timeout: 3e4 }
32352
+ );
32353
+ if (result.exitCode !== 0) return empty;
32354
+ const entries = [];
32355
+ for (const line of result.stdout.split("\n")) {
32356
+ if (!line) continue;
32357
+ const [type, sizeStr, mtimeStr, ...pathParts] = line.split(" ");
32358
+ const fullPath = pathParts.join(" ");
32359
+ if (!fullPath || !fullPath.startsWith(`${rootPath}/`)) continue;
32360
+ const relativePath = fullPath.slice(rootPath.length + 1);
32361
+ entries.push({
32362
+ name: posixPath.basename(relativePath),
32363
+ path: relativePath,
32364
+ type: type === "d" ? "directory" : "file",
32365
+ size: type === "d" ? 0 : Number(sizeStr) || 0,
32366
+ updatedAt: new Date((Number(mtimeStr) || 0) * 1e3).toISOString()
32367
+ });
32368
+ }
32369
+ entries.sort((a, b) => a.path.localeCompare(b.path));
32370
+ return { workspacePath: session.sessionId, root: safeRoot, rootPath, entries };
32371
+ }
32372
+ async function readSessionWorkspaceFile(fleet, session, path3) {
32373
+ const safePath = assertRelativePath(path3, "path");
32374
+ assertApprovedRenderedRoot(safePath.split("/")[0] ?? "");
32375
+ const handle = await sessionSandbox(fleet, session);
32376
+ if (!handle) throw new Error("Session workspace is not available");
32377
+ const { filesystem } = handle;
32378
+ const info = await filesystem.stat(safePath);
32379
+ if (info.type === "directory") throw new Error("Path is a directory");
32380
+ const buffer = await filesystem.readFile(safePath);
32381
+ const truncated = buffer.length > MAX_TEXT_FILE_BYTES;
32382
+ const base = {
32383
+ workspacePath: session.sessionId,
32384
+ path: safePath,
32385
+ name: posixPath.basename(safePath),
32386
+ size: buffer.length,
32387
+ updatedAt: info.modifiedAt.toISOString()
32388
+ };
32389
+ try {
32390
+ const content = TEXT_DECODER.decode(truncated ? buffer.subarray(0, MAX_TEXT_FILE_BYTES) : buffer);
32391
+ return { ...base, contentType: "text", content, truncated };
32392
+ } catch {
32393
+ return { ...base, contentType: "unsupported" };
32394
+ }
32395
+ }
32040
32396
  function resolveCodebase(projectPath) {
32041
32397
  const info = detectProject(projectPath);
32042
32398
  const override = getResourceIdOverride(info.rootPath);
@@ -32050,6 +32406,7 @@ function resolveCodebase(projectPath) {
32050
32406
  }
32051
32407
  function buildFsRoutes(options = {}) {
32052
32408
  const root = resolveFsRoot(options.root);
32409
+ const sessionFs = options.sessionFs;
32053
32410
  return [
32054
32411
  registerApiRoute8("/web/fs/list", {
32055
32412
  method: "GET",
@@ -32089,10 +32446,14 @@ function buildFsRoutes(options = {}) {
32089
32446
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
32090
32447
  if (!renderedRoot) return c.json({ error: "Missing required query param: root" }, 400);
32091
32448
  try {
32449
+ const session = await resolveAuthorizedSession(loose7(c), sessionFs, workspacePath);
32450
+ if (session && sessionFs) {
32451
+ return c.json(await listSessionRenderedPath(sessionFs.fleet, session, renderedRoot));
32452
+ }
32092
32453
  return c.json(await listWorkspaceRenderedPath(root, workspacePath, renderedRoot));
32093
32454
  } catch (error) {
32094
32455
  const message = error instanceof Error ? error.message : String(error);
32095
- const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") ? 403 : 500;
32456
+ const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") || message.includes("not available") ? 403 : 500;
32096
32457
  return c.json({ error: message }, status);
32097
32458
  }
32098
32459
  }
@@ -32106,10 +32467,14 @@ function buildFsRoutes(options = {}) {
32106
32467
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
32107
32468
  if (!path3) return c.json({ error: "Missing required query param: path" }, 400);
32108
32469
  try {
32470
+ const session = await resolveAuthorizedSession(loose7(c), sessionFs, workspacePath);
32471
+ if (session && sessionFs) {
32472
+ return c.json(await readSessionWorkspaceFile(sessionFs.fleet, session, path3));
32473
+ }
32109
32474
  return c.json(await readWorkspaceFile(root, workspacePath, path3));
32110
32475
  } catch (error) {
32111
32476
  const message = error instanceof Error ? error.message : String(error);
32112
- const status = message.includes("outside") || message.includes("relative") || message.includes("escapes") || message.includes("not approved") ? 403 : message.includes("directory") ? 400 : 500;
32477
+ 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;
32113
32478
  return c.json({ error: message }, status);
32114
32479
  }
32115
32480
  }
@@ -32135,7 +32500,7 @@ function buildFsRoutes(options = {}) {
32135
32500
 
32136
32501
  // src/routes/intake.ts
32137
32502
  import { registerApiRoute as registerApiRoute9 } from "@mastra/core/server";
32138
- function loose7(c) {
32503
+ function loose8(c) {
32139
32504
  return c;
32140
32505
  }
32141
32506
  function sanitizeIdList(value) {
@@ -32199,7 +32564,7 @@ var IntakeRoutes = class extends Route {
32199
32564
  method: "GET",
32200
32565
  requiresAuth: false,
32201
32566
  handler: async (c) => {
32202
- const tenant = await this.#resolveTenant(loose7(c));
32567
+ const tenant = await this.#resolveTenant(loose8(c));
32203
32568
  if ("response" in tenant) return tenant.response;
32204
32569
  await intake.ensureReady();
32205
32570
  const config = await intake.getConfig({ ...tenant, integrationIds });
@@ -32210,7 +32575,7 @@ var IntakeRoutes = class extends Route {
32210
32575
  method: "PUT",
32211
32576
  requiresAuth: false,
32212
32577
  handler: async (c) => {
32213
- const tenant = await this.#resolveTenant(loose7(c));
32578
+ const tenant = await this.#resolveTenant(loose8(c));
32214
32579
  if ("response" in tenant) return tenant.response;
32215
32580
  let body;
32216
32581
  try {
@@ -32225,7 +32590,7 @@ var IntakeRoutes = class extends Route {
32225
32590
  await intake.ensureReady();
32226
32591
  await intake.saveConfig({ ...tenant, config });
32227
32592
  await audit.emit({
32228
- context: loose7(c),
32593
+ context: loose8(c),
32229
32594
  input: {
32230
32595
  action: "factory.intake.config_updated",
32231
32596
  targets: [{ type: "intake_config", id: tenant.orgId }],
@@ -32244,7 +32609,7 @@ var IntakeRoutes = class extends Route {
32244
32609
  method: "GET",
32245
32610
  requiresAuth: false,
32246
32611
  handler: async (c) => {
32247
- const tenant = await this.#resolveTenant(loose7(c));
32612
+ const tenant = await this.#resolveTenant(loose8(c));
32248
32613
  if ("response" in tenant) return tenant.response;
32249
32614
  const pages = await Promise.all(
32250
32615
  integrations.map(async (integration) => ({
@@ -32263,7 +32628,7 @@ var IntakeRoutes = class extends Route {
32263
32628
  method: "GET",
32264
32629
  requiresAuth: false,
32265
32630
  handler: async (c) => {
32266
- const tenant = await this.#resolveTenant(loose7(c));
32631
+ const tenant = await this.#resolveTenant(loose8(c));
32267
32632
  if ("response" in tenant) return tenant.response;
32268
32633
  const cursors = decodeCursor2(c.req.query("cursor"));
32269
32634
  if (!cursors) return c.json({ error: "invalid_cursor" }, 400);
@@ -32642,7 +33007,7 @@ var OAUTH_FLOWS = {
32642
33007
  }
32643
33008
  }
32644
33009
  };
32645
- function loose8(c) {
33010
+ function loose9(c) {
32646
33011
  return c;
32647
33012
  }
32648
33013
  var localSessionsPromise;
@@ -32712,14 +33077,14 @@ var OAuthRoutes = class extends Route {
32712
33077
  method: "POST",
32713
33078
  requiresAuth: false,
32714
33079
  handler: async (c) => {
32715
- const ctx = await resolveCredentialContext({ c: loose8(c), auth, credentials: modelCredentials });
33080
+ const ctx = await resolveCredentialContext({ c: loose9(c), auth, credentials: modelCredentials });
32716
33081
  if ("response" in ctx) return ctx.response;
32717
33082
  const provider = c.req.param("provider");
32718
33083
  const flow = OAUTH_FLOWS[provider];
32719
33084
  if (!flow) {
32720
33085
  return c.json({ error: "oauth_not_supported", message: `Provider does not support web sign-in` }, 404);
32721
33086
  }
32722
- const body = await readJsonBody(loose8(c));
33087
+ const body = await readJsonBody(loose9(c));
32723
33088
  if (typeof body.mode === "string" && body.mode !== flow.kind) {
32724
33089
  return c.json({ error: "invalid_mode", message: `Unsupported mode for ${provider}: ${body.mode}` }, 400);
32725
33090
  }
@@ -32756,12 +33121,12 @@ var OAuthRoutes = class extends Route {
32756
33121
  method: "POST",
32757
33122
  requiresAuth: false,
32758
33123
  handler: async (c) => {
32759
- const ctx = await resolveCredentialContext({ c: loose8(c), auth, credentials: modelCredentials });
33124
+ const ctx = await resolveCredentialContext({ c: loose9(c), auth, credentials: modelCredentials });
32760
33125
  if ("response" in ctx) return ctx.response;
32761
33126
  const provider = c.req.param("provider");
32762
33127
  const flow = OAUTH_FLOWS[provider];
32763
33128
  if (!flow?.complete) return c.json({ error: "oauth_not_supported" }, 404);
32764
- const body = await readJsonBody(loose8(c));
33129
+ const body = await readJsonBody(loose9(c));
32765
33130
  const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
32766
33131
  const code = typeof body.code === "string" ? body.code.trim() : "";
32767
33132
  if (!sessionId || !code) return c.json({ error: "Missing required fields: sessionId, code" }, 400);
@@ -32791,12 +33156,12 @@ var OAuthRoutes = class extends Route {
32791
33156
  method: "POST",
32792
33157
  requiresAuth: false,
32793
33158
  handler: async (c) => {
32794
- const ctx = await resolveCredentialContext({ c: loose8(c), auth, credentials: modelCredentials });
33159
+ const ctx = await resolveCredentialContext({ c: loose9(c), auth, credentials: modelCredentials });
32795
33160
  if ("response" in ctx) return ctx.response;
32796
33161
  const provider = c.req.param("provider");
32797
33162
  const flow = OAUTH_FLOWS[provider];
32798
33163
  if (!flow?.poll) return c.json({ error: "oauth_not_supported" }, 404);
32799
- const body = await readJsonBody(loose8(c));
33164
+ const body = await readJsonBody(loose9(c));
32800
33165
  const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
32801
33166
  if (!sessionId) return c.json({ error: "Missing required field: sessionId" }, 400);
32802
33167
  const session = await loadOwnedSession({ ctx, provider, sessionId });
@@ -32847,7 +33212,7 @@ var OAuthRoutes = class extends Route {
32847
33212
  method: "DELETE",
32848
33213
  requiresAuth: false,
32849
33214
  handler: async (c) => {
32850
- const ctx = await resolveCredentialContext({ c: loose8(c), auth, credentials: modelCredentials });
33215
+ const ctx = await resolveCredentialContext({ c: loose9(c), auth, credentials: modelCredentials });
32851
33216
  if ("response" in ctx) return ctx.response;
32852
33217
  const provider = c.req.param("provider");
32853
33218
  const sessionId = c.req.param("sessionId");
@@ -32860,7 +33225,7 @@ var OAuthRoutes = class extends Route {
32860
33225
  method: "DELETE",
32861
33226
  requiresAuth: false,
32862
33227
  handler: async (c) => {
32863
- const ctx = await resolveCredentialContext({ c: loose8(c), auth, credentials: modelCredentials });
33228
+ const ctx = await resolveCredentialContext({ c: loose9(c), auth, credentials: modelCredentials });
32864
33229
  if ("response" in ctx) return ctx.response;
32865
33230
  const provider = c.req.param("provider");
32866
33231
  const authProviderId = getAuthProviderId(provider);
@@ -32926,7 +33291,7 @@ var MAX_SCOPE_LENGTH = 2048;
32926
33291
  var MAX_ARGUMENTS_LENGTH2 = 16384;
32927
33292
  var SKILL_NAME_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
32928
33293
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
32929
- function loose9(context) {
33294
+ function loose10(context) {
32930
33295
  return context;
32931
33296
  }
32932
33297
  function parseBody(value) {
@@ -33000,7 +33365,7 @@ var SkillRoutes = class extends Route {
33000
33365
  const { controllerId, controller, authorizeSessionAddress: customAuthorize } = this.deps;
33001
33366
  const authorize = customAuthorize ?? ((context, address) => this.#authorizeSessionAddress(context, address));
33002
33367
  const handleSkillRequest = async (context, dispatch) => {
33003
- const c = loose9(context);
33368
+ const c = loose10(context);
33004
33369
  if (c.req.param("controllerId") !== controllerId) {
33005
33370
  return c.json({ error: "controller_not_found", message: "Agent controller not found." }, 404);
33006
33371
  }
@@ -34627,7 +34992,7 @@ function computeFactoryMetrics(items, opts) {
34627
34992
  }
34628
34993
 
34629
34994
  // src/routes/work-items.ts
34630
- function loose10(c) {
34995
+ function loose11(c) {
34631
34996
  return c;
34632
34997
  }
34633
34998
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -34945,7 +35310,7 @@ var WorkItemRoutes = class extends Route {
34945
35310
  method: "GET",
34946
35311
  requiresAuth: false,
34947
35312
  handler: async (c) => {
34948
- const resolved = await this.#resolveProject(loose10(c));
35313
+ const resolved = await this.#resolveProject(loose11(c));
34949
35314
  if ("response" in resolved) return resolved.response;
34950
35315
  await workItems.ensureReady();
34951
35316
  const items = await workItems.list({
@@ -34960,11 +35325,11 @@ var WorkItemRoutes = class extends Route {
34960
35325
  method: "GET",
34961
35326
  requiresAuth: false,
34962
35327
  handler: async (c) => {
34963
- const resolved = await this.#resolveProject(loose10(c));
35328
+ const resolved = await this.#resolveProject(loose11(c));
34964
35329
  if ("response" in resolved) return resolved.response;
34965
35330
  const { windowStart, windowEnd } = parseMetricsRange(
34966
- loose10(c).req.query("from"),
34967
- loose10(c).req.query("to"),
35331
+ loose11(c).req.query("from"),
35332
+ loose11(c).req.query("to"),
34968
35333
  /* @__PURE__ */ new Date()
34969
35334
  );
34970
35335
  await workItems.ensureReady();
@@ -34980,7 +35345,7 @@ var WorkItemRoutes = class extends Route {
34980
35345
  method: "GET",
34981
35346
  requiresAuth: false,
34982
35347
  handler: async (c) => {
34983
- const resolved = await this.#resolveProject(loose10(c));
35348
+ const resolved = await this.#resolveProject(loose11(c));
34984
35349
  if ("response" in resolved) return resolved.response;
34985
35350
  await queueHealth.ensureReady();
34986
35351
  const stored = await queueHealth.getConfig(resolved.orgId, resolved.factoryProjectId);
@@ -34992,7 +35357,7 @@ var WorkItemRoutes = class extends Route {
34992
35357
  method: "GET",
34993
35358
  requiresAuth: false,
34994
35359
  handler: async (c) => {
34995
- const context = loose10(c);
35360
+ const context = loose11(c);
34996
35361
  const resolved = await this.#resolveProject(context);
34997
35362
  if ("response" in resolved) return resolved.response;
34998
35363
  const cursorRaw = context.req.query("before");
@@ -35017,7 +35382,7 @@ var WorkItemRoutes = class extends Route {
35017
35382
  method: "POST",
35018
35383
  requiresAuth: false,
35019
35384
  handler: async (c) => {
35020
- const context = loose10(c);
35385
+ const context = loose11(c);
35021
35386
  const resolved = await this.#resolveProject(context);
35022
35387
  if ("response" in resolved) return resolved.response;
35023
35388
  const decisionId = context.req.param("decisionId");
@@ -35038,9 +35403,9 @@ var WorkItemRoutes = class extends Route {
35038
35403
  method: "POST",
35039
35404
  requiresAuth: false,
35040
35405
  handler: async (c) => {
35041
- const resolved = await this.#resolveProject(loose10(c));
35406
+ const resolved = await this.#resolveProject(loose11(c));
35042
35407
  if ("response" in resolved) return resolved.response;
35043
- const body = await readJson2(loose10(c));
35408
+ const body = await readJson2(loose11(c));
35044
35409
  if (body === void 0) return c.json({ error: "Invalid JSON body" }, 400);
35045
35410
  const input = parseCreateWorkItem(body);
35046
35411
  if (!input) return c.json({ error: "invalid_work_item" }, 400);
@@ -35083,7 +35448,7 @@ var WorkItemRoutes = class extends Route {
35083
35448
  }
35084
35449
  item = await workItems.getForProject(resolved.orgId, resolved.factoryProjectId, item.id) ?? item;
35085
35450
  await audit.emit({
35086
- context: loose10(c),
35451
+ context: loose11(c),
35087
35452
  input: {
35088
35453
  action: "factory.work_item.created",
35089
35454
  factoryProjectId: resolved.factoryProjectId,
@@ -35094,7 +35459,7 @@ var WorkItemRoutes = class extends Route {
35094
35459
  } else {
35095
35460
  const { stages: _stages, sessions: _sessions, ...boundedPatch } = input;
35096
35461
  await this.#auditWorkItemPatch({
35097
- context: loose10(c),
35462
+ context: loose11(c),
35098
35463
  item,
35099
35464
  previous: result.previous,
35100
35465
  patch: boundedPatch
@@ -35114,11 +35479,11 @@ var WorkItemRoutes = class extends Route {
35114
35479
  method: "POST",
35115
35480
  requiresAuth: false,
35116
35481
  handler: async (c) => {
35117
- const resolved = await this.#resolveProject(loose10(c));
35482
+ const resolved = await this.#resolveProject(loose11(c));
35118
35483
  if ("response" in resolved) return resolved.response;
35119
- const workItemId = loose10(c).req.param("workItemId");
35484
+ const workItemId = loose11(c).req.param("workItemId");
35120
35485
  if (!workItemId || !UUID_RE4.test(workItemId)) return c.json({ error: "Work item not found" }, 404);
35121
- const parsed = parseTransitionBody(await readJson2(loose10(c)));
35486
+ const parsed = parseTransitionBody(await readJson2(loose11(c)));
35122
35487
  if (!parsed) return c.json({ error: "invalid_transition_request" }, 400);
35123
35488
  if (!transitionService) {
35124
35489
  return c.json({ error: "factory_transition_unavailable" }, 503);
@@ -35136,7 +35501,7 @@ var WorkItemRoutes = class extends Route {
35136
35501
  }
35137
35502
  });
35138
35503
  await audit.emit({
35139
- context: loose10(c),
35504
+ context: loose11(c),
35140
35505
  input: {
35141
35506
  action: result.status === "accepted" ? "factory.work_item.stage_moved" : "factory.work_item.transition_rejected",
35142
35507
  factoryProjectId: resolved.factoryProjectId,
@@ -35158,14 +35523,14 @@ var WorkItemRoutes = class extends Route {
35158
35523
  method: "POST",
35159
35524
  requiresAuth: false,
35160
35525
  handler: async (c) => {
35161
- const resolved = await this.#resolveProject(loose10(c));
35526
+ const resolved = await this.#resolveProject(loose11(c));
35162
35527
  if ("response" in resolved) return resolved.response;
35163
35528
  if (!startCoordinator) {
35164
35529
  return c.json({ error: "factory_start_unavailable" }, 503);
35165
35530
  }
35166
- const input = parseStartBody(await readJson2(loose10(c)), resolved, resolved.factoryProjectId);
35531
+ const input = parseStartBody(await readJson2(loose11(c)), resolved, resolved.factoryProjectId);
35167
35532
  if (!input) return c.json({ error: "invalid_factory_start" }, 400);
35168
- input.requestContext = loose10(c).get("requestContext");
35533
+ input.requestContext = loose11(c).get("requestContext");
35169
35534
  input.defaultModelId = resolved.defaultModelId ?? void 0;
35170
35535
  if (!input.workItem.id && ((input.workItem.input.stages ?? ["intake"]).length !== 1 || (input.workItem.input.stages ?? ["intake"])[0] !== "intake")) {
35171
35536
  return c.json(
@@ -35184,7 +35549,7 @@ var WorkItemRoutes = class extends Route {
35184
35549
  throw error;
35185
35550
  }
35186
35551
  await audit.emit({
35187
- context: loose10(c),
35552
+ context: loose11(c),
35188
35553
  input: {
35189
35554
  action: "factory.run.started",
35190
35555
  factoryProjectId: resolved.factoryProjectId,
@@ -35206,11 +35571,11 @@ var WorkItemRoutes = class extends Route {
35206
35571
  method: "PATCH",
35207
35572
  requiresAuth: false,
35208
35573
  handler: async (c) => {
35209
- const tenant = await this.#resolveTenant(loose10(c));
35574
+ const tenant = await this.#resolveTenant(loose11(c));
35210
35575
  if ("response" in tenant) return tenant.response;
35211
- const id = loose10(c).req.param("id");
35576
+ const id = loose11(c).req.param("id");
35212
35577
  if (!id || !UUID_RE4.test(id)) return c.json({ error: "Work item not found" }, 404);
35213
- const body = await readJson2(loose10(c));
35578
+ const body = await readJson2(loose11(c));
35214
35579
  if (body === void 0) return c.json({ error: "Invalid JSON body" }, 400);
35215
35580
  const patch = parseUpdateWorkItem(body);
35216
35581
  if (!patch) return c.json({ error: "invalid_work_item_patch" }, 400);
@@ -35225,7 +35590,7 @@ var WorkItemRoutes = class extends Route {
35225
35590
  const updated = await workItems.update({ orgId: tenant.orgId, id, userId: tenant.userId, patch });
35226
35591
  if (!updated) return c.json({ error: "Work item not found" }, 404);
35227
35592
  await this.#auditWorkItemPatch({
35228
- context: loose10(c),
35593
+ context: loose11(c),
35229
35594
  item: updated.item,
35230
35595
  previous: updated.previous,
35231
35596
  patch
@@ -35244,15 +35609,15 @@ var WorkItemRoutes = class extends Route {
35244
35609
  method: "DELETE",
35245
35610
  requiresAuth: false,
35246
35611
  handler: async (c) => {
35247
- const tenant = await this.#resolveTenant(loose10(c));
35612
+ const tenant = await this.#resolveTenant(loose11(c));
35248
35613
  if ("response" in tenant) return tenant.response;
35249
- const id = loose10(c).req.param("id");
35614
+ const id = loose11(c).req.param("id");
35250
35615
  if (!id || !UUID_RE4.test(id)) return c.json({ error: "Work item not found" }, 404);
35251
35616
  await workItems.ensureReady();
35252
35617
  const deleted = await workItems.delete({ orgId: tenant.orgId, id });
35253
35618
  if (!deleted) return c.json({ error: "Work item not found" }, 404);
35254
35619
  await audit.emit({
35255
- context: loose10(c),
35620
+ context: loose11(c),
35256
35621
  input: {
35257
35622
  action: "factory.work_item.deleted",
35258
35623
  factoryProjectId: deleted.factoryProjectId,
@@ -35473,7 +35838,14 @@ function assembleFactoryApiRoutes(deps) {
35473
35838
  });
35474
35839
  }
35475
35840
  return [
35476
- ...buildFsRoutes({ root: deps.fsRoot }),
35841
+ ...buildFsRoutes({
35842
+ root: deps.fsRoot,
35843
+ sessionFs: {
35844
+ auth: deps.auth,
35845
+ fleet: deps.fleet,
35846
+ sessions: deps.sourceControlStorage.forIntegration("github").sessions
35847
+ }
35848
+ }),
35477
35849
  ...new ConfigRoutes({
35478
35850
  auth: deps.auth,
35479
35851
  controller: deps.controller,
@@ -36627,7 +36999,7 @@ function handleServerError(err, c) {
36627
36999
  return c.json({ error: err.message }, err.status);
36628
37000
  }
36629
37001
  const detail = err instanceof Error ? err.stack ?? err.message : String(err);
36630
- console.error(`[MastraCode Web] ${c.req.method} ${c.req.path} failed: ${detail}`);
37002
+ console.error(`[Mastra Factory] ${c.req.method} ${c.req.path} failed: ${detail}`);
36631
37003
  return c.json(
36632
37004
  {
36633
37005
  error: "internal_error",
@@ -36939,7 +37311,7 @@ var AuditStorage = class extends FactoryStorageDomain4 {
36939
37311
  import { registerApiRoute as registerApiRoute14 } from "@mastra/core/server";
36940
37312
  var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
36941
37313
  var MAX_ACTION_FILTERS = 16;
36942
- function loose11(c) {
37314
+ function loose12(c) {
36943
37315
  return c;
36944
37316
  }
36945
37317
  function parseActionsParam(raw) {
@@ -37062,7 +37434,7 @@ var AuditDomain = class {
37062
37434
  registerApiRoute14("/web/factory/projects/:id/audit", {
37063
37435
  method: "GET",
37064
37436
  handler: async (cc) => {
37065
- const c = loose11(cc);
37437
+ const c = loose12(cc);
37066
37438
  const tenant = await this.#resolveTenant(c);
37067
37439
  if ("response" in tenant) return tenant.response;
37068
37440
  const projectId = c.req.param("id");
@@ -38228,13 +38600,12 @@ var SourceControlStorage = class extends FactoryStorageDomain11 {
38228
38600
  );
38229
38601
  },
38230
38602
  get: getRepository,
38231
- findByExternalId: async ({ orgId, installationId, externalId }) => {
38232
- await requireInstallation({ orgId, id: installationId });
38233
- const row = await db().findOne(REPOSITORIES, {
38234
- installation_id: installationId,
38235
- external_id: externalId
38236
- });
38237
- return row ? toRepository(row) : null;
38603
+ findByExternalId: async ({ orgId, externalId }) => {
38604
+ const rows = await db().findMany(REPOSITORIES, { external_id: externalId });
38605
+ for (const row of rows) {
38606
+ if (await getInstallation({ orgId, id: row.installation_id })) return toRepository(row);
38607
+ }
38608
+ return null;
38238
38609
  },
38239
38610
  findBySlug: async ({ orgId, installationId, slug }) => {
38240
38611
  await requireInstallation({ orgId, id: installationId });
@@ -38560,7 +38931,7 @@ var SourceControlStorage = class extends FactoryStorageDomain11 {
38560
38931
  import { existsSync as existsSync2 } from "fs";
38561
38932
  import path2, { dirname as dirname2, join as join3 } from "path";
38562
38933
  import { fileURLToPath as fileURLToPath2 } from "url";
38563
- import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
38934
+ import { SandboxFilesystem as SandboxFilesystem2 } from "@mastra/code-sdk/agents/sandbox-filesystem";
38564
38935
  import { MASTRACODE_WORKSPACE_TOOLS } from "@mastra/code-sdk/agents/tool-availability";
38565
38936
  import { getDynamicWorkspace } from "@mastra/code-sdk/agents/workspace";
38566
38937
  import { DEFAULT_CONFIG_DIR } from "@mastra/code-sdk/constants";
@@ -38746,7 +39117,7 @@ function createWorkspaceFactory(options = {}) {
38746
39117
  githubTokenInjectors.set(workspaceId, { inject: injectGithubToken2, patKind, ghToken: ghCliToken });
38747
39118
  registerGithubTokenInjector(requestContext, injectGithubToken2);
38748
39119
  registerGithubPatKind(requestContext, patKind);
38749
- const filesystem = new SandboxFilesystem({ sandbox, workdir });
39120
+ const filesystem = new SandboxFilesystem2({ sandbox, workdir });
38750
39121
  const projectSkillPaths = [path2.join(configDir, "skills"), ".claude/skills", ".agents/skills"];
38751
39122
  const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
38752
39123
  return new Workspace({