@mastra/factory 0.1.0-alpha.3 → 0.1.0-alpha.4

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 (60) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/factory.d.ts.map +1 -1
  3. package/dist/factory.js +772 -206
  4. package/dist/factory.js.map +1 -1
  5. package/dist/index.js +772 -206
  6. package/dist/index.js.map +1 -1
  7. package/dist/integrations/github/integration.d.ts +2 -2
  8. package/dist/integrations/github/integration.js +43 -11
  9. package/dist/integrations/github/integration.js.map +1 -1
  10. package/dist/integrations/github/provenance.js.map +1 -1
  11. package/dist/integrations/github/routes.js +10 -9
  12. package/dist/integrations/github/routes.js.map +1 -1
  13. package/dist/integrations/github/sandbox.d.ts +1 -0
  14. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  15. package/dist/integrations/github/sandbox.js +2 -2
  16. package/dist/integrations/github/sandbox.js.map +1 -1
  17. package/dist/integrations/github/session-subscriptions.d.ts +3 -0
  18. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  19. package/dist/integrations/github/session-subscriptions.js +30 -0
  20. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  21. package/dist/integrations/github/token-refresh.d.ts +6 -0
  22. package/dist/integrations/github/token-refresh.d.ts.map +1 -0
  23. package/dist/integrations/github/token-refresh.js +17 -0
  24. package/dist/integrations/github/token-refresh.js.map +1 -0
  25. package/dist/integrations/platform/github/integration.js +41 -9
  26. package/dist/integrations/platform/github/integration.js.map +1 -1
  27. package/dist/routes/config.d.ts +16 -11
  28. package/dist/routes/config.d.ts.map +1 -1
  29. package/dist/routes/config.js +222 -109
  30. package/dist/routes/config.js.map +1 -1
  31. package/dist/routes/custom-provider-source.d.ts +52 -0
  32. package/dist/routes/custom-provider-source.d.ts.map +1 -0
  33. package/dist/routes/custom-provider-source.js +107 -0
  34. package/dist/routes/custom-provider-source.js.map +1 -0
  35. package/dist/routes/oauth.js +33 -1
  36. package/dist/routes/oauth.js.map +1 -1
  37. package/dist/routes/surface.d.ts +4 -0
  38. package/dist/routes/surface.d.ts.map +1 -1
  39. package/dist/routes/surface.js +266 -110
  40. package/dist/routes/surface.js.map +1 -1
  41. package/dist/sandbox/fleet.d.ts +3 -0
  42. package/dist/sandbox/fleet.d.ts.map +1 -1
  43. package/dist/sandbox/fleet.js +10 -3
  44. package/dist/sandbox/fleet.js.map +1 -1
  45. package/dist/spa-static.d.ts +4 -5
  46. package/dist/spa-static.d.ts.map +1 -1
  47. package/dist/spa-static.js +5 -2
  48. package/dist/spa-static.js.map +1 -1
  49. package/dist/storage/domains/custom-providers/base.d.ts +57 -0
  50. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
  51. package/dist/storage/domains/custom-providers/base.js +150 -0
  52. package/dist/storage/domains/custom-providers/base.js.map +1 -0
  53. package/dist/storage/domains/memory-settings/base.d.ts +62 -0
  54. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
  55. package/dist/storage/domains/memory-settings/base.js +111 -0
  56. package/dist/storage/domains/memory-settings/base.js.map +1 -0
  57. package/dist/workspace.d.ts.map +1 -1
  58. package/dist/workspace.js +17 -0
  59. package/dist/workspace.js.map +1 -1
  60. package/package.json +7 -6
package/dist/factory.js CHANGED
@@ -11163,8 +11163,10 @@ function assertIdentifier(kind, name) {
11163
11163
  }
11164
11164
  }
11165
11165
  function isUniqueViolation(error) {
11166
+ const code = typeof error === "object" && error !== null ? error.code : void 0;
11167
+ if (code === "SQLITE_CONSTRAINT_UNIQUE" || code === "SQLITE_CONSTRAINT_PRIMARYKEY") return true;
11166
11168
  const message = error instanceof Error ? error.message : String(error);
11167
- return message.includes("UNIQUE constraint failed") || message.includes("SQLITE_CONSTRAINT");
11169
+ return message.includes("UNIQUE constraint failed") || message.includes("SQLITE_CONSTRAINT_UNIQUE") || message.includes("SQLITE_CONSTRAINT_PRIMARYKEY");
11168
11170
  }
11169
11171
  function primaryKeyOf(schema) {
11170
11172
  const pks = Object.entries(schema.columns).filter(([, spec]) => spec.type === "uuid-pk" || spec.primaryKey);
@@ -24103,6 +24105,35 @@ ${unreflectedContent}` : bufferedReflection;
24103
24105
  if (spec.default !== void 0) ddl += ` DEFAULT ${serializeDefault(spec.default)}`;
24104
24106
  return ddl;
24105
24107
  }
24108
+ /**
24109
+ * A table created by an older schema may still say NOT NULL on a column the
24110
+ * current schema declares nullable — inserts of null then fail on databases
24111
+ * that predate the change. SQLite has no `ALTER COLUMN DROP NOT NULL`, so
24112
+ * relaxation swaps in a table rebuilt from the current schema (create shadow
24113
+ * → copy → drop → rename) atomically.
24114
+ */
24115
+ async #relaxDriftedNotNulls(schema) {
24116
+ const info = await this.#client.execute(`PRAGMA table_info("${schema.name}")`);
24117
+ const hasDrift = info.rows.some((row) => {
24118
+ const spec = schema.columns[String(row.name)];
24119
+ if (!spec || spec.type === "uuid-pk" || spec.primaryKey) return false;
24120
+ return spec.nullable === true && Number(row.notnull) === 1;
24121
+ });
24122
+ if (!hasDrift) return;
24123
+ const ddl = Object.entries(schema.columns).map(([name, spec]) => this.#columnDdl(name, spec));
24124
+ const shadow = `${schema.name}__nullable_rebuild`;
24125
+ const cols = Object.keys(schema.columns).map((name) => `"${name}"`).join(", ");
24126
+ await this.#client.batch(
24127
+ [
24128
+ `DROP TABLE IF EXISTS "${shadow}"`,
24129
+ `CREATE TABLE "${shadow}" (${ddl.join(", ")})`,
24130
+ `INSERT INTO "${shadow}" (${cols}) SELECT ${cols} FROM "${schema.name}"`,
24131
+ `DROP TABLE "${schema.name}"`,
24132
+ `ALTER TABLE "${shadow}" RENAME TO "${schema.name}"`
24133
+ ],
24134
+ "write"
24135
+ );
24136
+ }
24106
24137
  async #ensureCollection(schema) {
24107
24138
  assertIdentifier("collection", schema.name);
24108
24139
  primaryKeyOf(schema);
@@ -24114,6 +24145,7 @@ ${unreflectedContent}` : bufferedReflection;
24114
24145
  if (existing.has(name)) continue;
24115
24146
  await this.#client.execute(`ALTER TABLE "${schema.name}" ADD COLUMN ${this.#columnDdl(name, spec)}`);
24116
24147
  }
24148
+ await this.#relaxDriftedNotNulls(schema);
24117
24149
  for (const index of schema.uniqueIndexes ?? []) {
24118
24150
  assertIdentifier("index", index.name);
24119
24151
  index.columns.forEach((column) => assertIdentifier("column", column));
@@ -24798,6 +24830,19 @@ function retirePullRequestSubscription(id, status, storage) {
24798
24830
  return storage.subscriptions.updateStatus(id, status);
24799
24831
  }
24800
24832
 
24833
+ // src/integrations/github/token-refresh.ts
24834
+ var GITHUB_TOKEN_INJECTOR_CONTEXT_KEY = "factoryGithubTokenInjector";
24835
+ function registerGithubTokenInjector(requestContext, injector) {
24836
+ requestContext.set(GITHUB_TOKEN_INJECTOR_CONTEXT_KEY, injector);
24837
+ }
24838
+ function injectGithubToken(requestContext, token) {
24839
+ const injector = requestContext.get(GITHUB_TOKEN_INJECTOR_CONTEXT_KEY);
24840
+ if (!injector) {
24841
+ throw new Error("GitHub token refresh requires an active Factory sandbox workspace.");
24842
+ }
24843
+ injector(token);
24844
+ }
24845
+
24801
24846
  // src/integrations/github/session-subscriptions.ts
24802
24847
  function sessionUserId(user) {
24803
24848
  return user?.workosId ?? user?.id;
@@ -24885,11 +24930,30 @@ async function unsubscribeCurrentSessionFromPullRequest(requestContext, pullRequ
24885
24930
  await unsubscribeFromPullRequest(await subscriptionInput(target, number3), github.integrationStorage);
24886
24931
  return number3;
24887
24932
  }
24933
+ async function refreshGithubToken(requestContext, github) {
24934
+ const target = await resolveSessionTarget(requestContext, github);
24935
+ const access = await github.versionControl.getRepositoryAccess({
24936
+ orgId: target.orgId,
24937
+ repositoryId: target.repository.id
24938
+ });
24939
+ const token = access.authorization?.token;
24940
+ if (!token) throw new Error("Repository access did not include a bearer token for the Factory session.");
24941
+ injectGithubToken(requestContext, token);
24942
+ }
24888
24943
  function createGithubSubscriptionTools(requestContext, github) {
24889
24944
  const context = requestContext.get("controller");
24890
24945
  const user = requestContext.get("user");
24891
24946
  if (!context?.getState().projectRepositoryId || !sessionOrgId(user) || !sessionUserId(user)) return {};
24892
24947
  return {
24948
+ github_refresh_token: createTool({
24949
+ id: "github_refresh_token",
24950
+ description: "Refresh GitHub CLI authentication in the active Factory sandbox. Use this after a gh command fails because authentication is expired, invalid, or missing. It installs a fresh GH_TOKEN for subsequent sandbox commands. After this tool succeeds, retry the failed gh command. Takes no arguments and never returns the token.",
24951
+ inputSchema: z.object({}),
24952
+ execute: async () => {
24953
+ await refreshGithubToken(requestContext, github);
24954
+ return { refreshed: true };
24955
+ }
24956
+ }),
24893
24957
  github_subscribe_pr: createTool({
24894
24958
  id: "github_subscribe_pr",
24895
24959
  description: "Subscribe this thread to GitHub pull request activity. You usually do not need this tool: successful gh pr create commands subscribe automatically. Use it for an existing PR or to recover when automatic subscription did not occur. Closed or merged PRs are unsubscribed automatically. Accepts a PR number or canonical URL for the active project.",
@@ -25033,20 +25097,27 @@ var SandboxBudgetError = class extends Error {
25033
25097
  max;
25034
25098
  code = "sandbox-budget-exceeded";
25035
25099
  };
25036
- function toMaterializationSandbox(sandbox) {
25100
+ function toMaterializationSandbox(sandbox, initialEnvironment = {}) {
25037
25101
  if (typeof sandbox.executeCommand !== "function") {
25038
25102
  throw new Error(
25039
25103
  `Sandbox provider '${sandbox.provider}' does not implement executeCommand() \u2014 cannot materialize repos.`
25040
25104
  );
25041
25105
  }
25042
25106
  const lifecycle = sandbox;
25107
+ const environment = { ...initialEnvironment };
25043
25108
  return {
25044
25109
  id: sandbox.id,
25045
25110
  start: async () => {
25046
25111
  await (lifecycle._start ?? sandbox.start)?.call(sandbox);
25047
25112
  },
25048
25113
  getInfo: async () => await sandbox.getInfo?.() ?? {},
25049
- executeCommand: (command, args, options) => sandbox.executeCommand(command, args, options),
25114
+ executeCommand: (command, args, options) => sandbox.executeCommand(command, args, {
25115
+ ...options,
25116
+ env: { ...environment, ...options?.env }
25117
+ }),
25118
+ setEnvironmentVariable: (name, value) => {
25119
+ environment[name] = value;
25120
+ },
25050
25121
  stop: async () => {
25051
25122
  await (lifecycle._stop ?? sandbox.stop)?.call(sandbox);
25052
25123
  }
@@ -25186,7 +25257,7 @@ var SandboxFleet = class {
25186
25257
  ...opts.idleTimeoutMinutes !== void 0 ? { idleTimeoutMinutes: opts.idleTimeoutMinutes } : {},
25187
25258
  ...opts.checkpointName ? { checkpointName: opts.checkpointName } : {}
25188
25259
  });
25189
- return toMaterializationSandbox(clone);
25260
+ return toMaterializationSandbox(clone, opts.env);
25190
25261
  }
25191
25262
  async ensureSandbox(store, envOrProgress, progressOrOptions, maybeOptions = {}) {
25192
25263
  const env = typeof envOrProgress === "function" ? void 0 : envOrProgress;
@@ -25356,8 +25427,8 @@ function bindingStore(row, storage) {
25356
25427
  };
25357
25428
  }
25358
25429
  async function ensureProjectSandbox(options) {
25359
- const { fleet, row, storage, onProgress } = options;
25360
- return fleet.ensureSandbox(bindingStore(row, storage), onProgress);
25430
+ const { fleet, row, storage, token, onProgress } = options;
25431
+ return fleet.ensureSandbox(bindingStore(row, storage), { GH_TOKEN: token }, onProgress);
25361
25432
  }
25362
25433
  async function teardownProjectSandbox(options) {
25363
25434
  const { fleet, row, storage, sandbox } = options;
@@ -26623,13 +26694,6 @@ async function loadOrCreateSandboxRow(github, project, userId) {
26623
26694
  async function prepareProject(options) {
26624
26695
  const { github, fleet, project, userId, onProgress } = options;
26625
26696
  const sandboxRow = await loadOrCreateSandboxRow(github, project, userId);
26626
- const sandbox = await ensureProjectSandbox({
26627
- fleet,
26628
- row: sandboxRow,
26629
- storage: github.sourceControlStorage.sandboxes,
26630
- onProgress
26631
- });
26632
- const fresh = await github.sourceControlStorage.sandboxes.getById({ id: sandboxRow.id });
26633
26697
  const access = await github.versionControl.getRepositoryAccess({
26634
26698
  orgId: project.installation.orgId,
26635
26699
  repositoryId: project.repository.id
@@ -26637,6 +26701,14 @@ async function prepareProject(options) {
26637
26701
  if (!access.authorization) {
26638
26702
  throw new MaterializeError("Repository access did not include a bearer token.", "clone-failed");
26639
26703
  }
26704
+ const sandbox = await ensureProjectSandbox({
26705
+ fleet,
26706
+ row: sandboxRow,
26707
+ storage: github.sourceControlStorage.sandboxes,
26708
+ token: access.authorization.token,
26709
+ onProgress
26710
+ });
26711
+ const fresh = await github.sourceControlStorage.sandboxes.getById({ id: sandboxRow.id });
26640
26712
  const finalRow = fresh ?? sandboxRow;
26641
26713
  await materializeRepo({
26642
26714
  row: finalRow,
@@ -29076,6 +29148,158 @@ function isNotFound2(error) {
29076
29148
  return error instanceof PlatformApiError && error.status === 404;
29077
29149
  }
29078
29150
 
29151
+ // src/routes/custom-provider-source.ts
29152
+ import { setCustomProvidersSource } from "@mastra/code-sdk/agents/custom-provider-source";
29153
+
29154
+ // src/routes/provider-credentials.ts
29155
+ function getAuthProviderId(provider) {
29156
+ return provider === "openai" ? "openai-codex" : provider;
29157
+ }
29158
+ var WEB_OAUTH_FLOW_KINDS = {
29159
+ anthropic: "paste-code",
29160
+ openai: "device-code",
29161
+ "github-copilot": "device-code",
29162
+ xai: "device-code"
29163
+ };
29164
+ async function getTenantCredentialsStorage(credentials) {
29165
+ if (!credentials) return void 0;
29166
+ try {
29167
+ await credentials.ensureReady();
29168
+ } catch {
29169
+ return void 0;
29170
+ }
29171
+ return credentials;
29172
+ }
29173
+ function tenantOrgId(tenant) {
29174
+ return tenant.orgId ?? `user:${tenant.userId}`;
29175
+ }
29176
+ async function resolveCredentialContext({
29177
+ c,
29178
+ auth,
29179
+ credentials
29180
+ }) {
29181
+ await auth.ensureUser(c);
29182
+ const tenant = auth.tenant(c);
29183
+ if (!tenant) {
29184
+ if (auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
29185
+ return { mode: "local" };
29186
+ }
29187
+ const storage = await getTenantCredentialsStorage(credentials);
29188
+ if (!storage) {
29189
+ return {
29190
+ response: c.json(
29191
+ {
29192
+ error: "credentials_unavailable",
29193
+ message: "Tenant credential storage is unavailable \u2014 the app database is not configured or failed to start."
29194
+ },
29195
+ 503
29196
+ )
29197
+ };
29198
+ }
29199
+ return { mode: "tenant", storage, orgId: tenantOrgId(tenant), userId: tenant.userId };
29200
+ }
29201
+ async function listTenantCredentialsForRequest({
29202
+ c,
29203
+ auth,
29204
+ credentials
29205
+ }) {
29206
+ await auth.ensureUser(c);
29207
+ const tenant = auth.tenant(c);
29208
+ if (!tenant) return auth.enabled() ? [] : void 0;
29209
+ const storage = await getTenantCredentialsStorage(credentials);
29210
+ if (!storage) return [];
29211
+ return storage.listCredentials(tenantOrgId(tenant), tenant.userId);
29212
+ }
29213
+
29214
+ // src/routes/custom-provider-source.ts
29215
+ var SNAPSHOT_TTL_MS = 15e3;
29216
+ var MAX_CACHED_ORGS = 1e3;
29217
+ var LOCAL_ORG = "local";
29218
+ var OrgCustomProvidersSnapshot = class {
29219
+ #orgId;
29220
+ #storage;
29221
+ #snapshot = [];
29222
+ #fetchedAt = 0;
29223
+ #hydrating;
29224
+ constructor(orgId, storage) {
29225
+ this.#orgId = orgId;
29226
+ this.#storage = storage;
29227
+ }
29228
+ /** Hydrate the snapshot when stale; coalesces concurrent callers. */
29229
+ async ensureFresh(now = Date.now()) {
29230
+ if (now - this.#fetchedAt < SNAPSHOT_TTL_MS) return;
29231
+ this.#hydrating ??= this.#hydrate().finally(() => {
29232
+ this.#hydrating = void 0;
29233
+ });
29234
+ await this.#hydrating;
29235
+ }
29236
+ async #hydrate() {
29237
+ await this.#storage.ensureReady();
29238
+ const records = await this.#storage.list({ orgId: this.#orgId });
29239
+ this.#snapshot = records.map((record) => ({
29240
+ name: record.name,
29241
+ url: record.url,
29242
+ apiKey: record.apiKey ?? void 0,
29243
+ models: record.models
29244
+ }));
29245
+ this.#fetchedAt = Date.now();
29246
+ }
29247
+ /** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
29248
+ get() {
29249
+ if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS) {
29250
+ void this.ensureFresh().catch(() => {
29251
+ });
29252
+ }
29253
+ return this.#snapshot;
29254
+ }
29255
+ };
29256
+ var orgSnapshots = /* @__PURE__ */ new Map();
29257
+ function snapshotFor(orgId, storage) {
29258
+ let snapshot = orgSnapshots.get(orgId);
29259
+ if (!snapshot) {
29260
+ if (orgSnapshots.size >= MAX_CACHED_ORGS) {
29261
+ const oldest = orgSnapshots.keys().next().value;
29262
+ if (oldest !== void 0) orgSnapshots.delete(oldest);
29263
+ }
29264
+ snapshot = new OrgCustomProvidersSnapshot(orgId, storage);
29265
+ orgSnapshots.set(orgId, snapshot);
29266
+ }
29267
+ return snapshot;
29268
+ }
29269
+ function orgForTenant(tenant, authEnabled) {
29270
+ if (!authEnabled) return LOCAL_ORG;
29271
+ return tenant ? tenantOrgId(tenant) : void 0;
29272
+ }
29273
+ function registerCustomProvidersSource({
29274
+ storage,
29275
+ authEnabled
29276
+ }) {
29277
+ setCustomProvidersSource((tenant) => {
29278
+ const orgId = orgForTenant(tenant, authEnabled);
29279
+ if (!orgId) return [];
29280
+ return snapshotFor(orgId, storage).get();
29281
+ });
29282
+ }
29283
+ function invalidateCustomProvidersSnapshots(tenant) {
29284
+ orgSnapshots.delete(tenant.orgId);
29285
+ }
29286
+ function createCustomProvidersPrimer({
29287
+ auth,
29288
+ storage,
29289
+ authEnabled
29290
+ }) {
29291
+ return async (c, next) => {
29292
+ const orgId = orgForTenant(auth.tenant(c), authEnabled);
29293
+ if (orgId) {
29294
+ try {
29295
+ await snapshotFor(orgId, storage).ensureFresh();
29296
+ } catch {
29297
+ }
29298
+ }
29299
+ await next();
29300
+ };
29301
+ }
29302
+
29079
29303
  // src/routes/projects.ts
29080
29304
  import { registerApiRoute as registerApiRoute6 } from "@mastra/core/server";
29081
29305
 
@@ -30503,82 +30727,10 @@ var FactoryTransitionService = class {
30503
30727
  };
30504
30728
 
30505
30729
  // src/routes/config.ts
30506
- import { removeCustomPackFromSettings } from "@mastra/code-sdk/onboarding/custom-packs";
30507
- import {
30508
- removeCustomProviderFromSettings,
30509
- upsertCustomProviderInSettings
30510
- } from "@mastra/code-sdk/onboarding/custom-providers";
30511
- import { applyOmRoleOverride, persistOmObserveAttachments } from "@mastra/code-sdk/onboarding/om-settings";
30730
+ import { DEFAULT_OM_MODEL_ID } from "@mastra/code-sdk/constants";
30512
30731
  import { getAvailableModePacks } from "@mastra/code-sdk/onboarding/packs";
30513
- import {
30514
- getCustomProviderId,
30515
- loadSettings,
30516
- saveSettings,
30517
- THREAD_ACTIVE_MODEL_PACK_ID_KEY
30518
- } from "@mastra/code-sdk/onboarding/settings";
30732
+ import { getCustomProviderId, THREAD_ACTIVE_MODEL_PACK_ID_KEY } from "@mastra/code-sdk/onboarding/settings";
30519
30733
  import { registerApiRoute as registerApiRoute7 } from "@mastra/core/server";
30520
-
30521
- // src/routes/provider-credentials.ts
30522
- function getAuthProviderId(provider) {
30523
- return provider === "openai" ? "openai-codex" : provider;
30524
- }
30525
- var WEB_OAUTH_FLOW_KINDS = {
30526
- anthropic: "paste-code",
30527
- openai: "device-code",
30528
- "github-copilot": "device-code",
30529
- xai: "device-code"
30530
- };
30531
- async function getTenantCredentialsStorage(credentials) {
30532
- if (!credentials) return void 0;
30533
- try {
30534
- await credentials.ensureReady();
30535
- } catch {
30536
- return void 0;
30537
- }
30538
- return credentials;
30539
- }
30540
- function tenantOrgId(tenant) {
30541
- return tenant.orgId ?? `user:${tenant.userId}`;
30542
- }
30543
- async function resolveCredentialContext({
30544
- c,
30545
- auth,
30546
- credentials
30547
- }) {
30548
- await auth.ensureUser(c);
30549
- const tenant = auth.tenant(c);
30550
- if (!tenant) {
30551
- if (auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
30552
- return { mode: "local" };
30553
- }
30554
- const storage = await getTenantCredentialsStorage(credentials);
30555
- if (!storage) {
30556
- return {
30557
- response: c.json(
30558
- {
30559
- error: "credentials_unavailable",
30560
- message: "Tenant credential storage is unavailable \u2014 the app database is not configured or failed to start."
30561
- },
30562
- 503
30563
- )
30564
- };
30565
- }
30566
- return { mode: "tenant", storage, orgId: tenantOrgId(tenant), userId: tenant.userId };
30567
- }
30568
- async function listTenantCredentialsForRequest({
30569
- c,
30570
- auth,
30571
- credentials
30572
- }) {
30573
- await auth.ensureUser(c);
30574
- const tenant = auth.tenant(c);
30575
- if (!tenant) return auth.enabled() ? [] : void 0;
30576
- const storage = await getTenantCredentialsStorage(credentials);
30577
- if (!storage) return [];
30578
- return storage.listCredentials(tenantOrgId(tenant), tenant.userId);
30579
- }
30580
-
30581
- // src/routes/config.ts
30582
30734
  function loose6(c) {
30583
30735
  return c;
30584
30736
  }
@@ -30622,15 +30774,39 @@ async function listProviders({
30622
30774
  }
30623
30775
  return Array.from(seen.values()).sort((a, b) => a.provider.localeCompare(b.provider));
30624
30776
  }
30625
- function listCustomProviders() {
30626
- const settings = loadSettings();
30627
- return settings.customProviders.map((p) => ({
30628
- id: getCustomProviderId(p.name),
30629
- name: p.name,
30630
- url: p.url,
30631
- hasApiKey: Boolean(p.apiKey),
30632
- models: p.models
30633
- }));
30777
+ function toCustomProviderInfo(record) {
30778
+ return {
30779
+ id: record.providerId,
30780
+ name: record.name,
30781
+ url: record.url,
30782
+ hasApiKey: Boolean(record.apiKey),
30783
+ models: record.models
30784
+ };
30785
+ }
30786
+ async function resolveCustomProvidersContext({
30787
+ c,
30788
+ auth,
30789
+ customProviders
30790
+ }) {
30791
+ await auth.ensureUser(c);
30792
+ const tenant = auth.tenant(c);
30793
+ if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
30794
+ if (customProviders) {
30795
+ try {
30796
+ await customProviders.ensureReady();
30797
+ return tenant ? { storage: customProviders, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: customProviders, orgId: "local", userId: "local" };
30798
+ } catch {
30799
+ }
30800
+ }
30801
+ return {
30802
+ response: c.json(
30803
+ {
30804
+ error: "custom_providers_unavailable",
30805
+ message: "Custom provider storage is unavailable \u2014 the app database is not configured or failed to start."
30806
+ },
30807
+ 503
30808
+ )
30809
+ };
30634
30810
  }
30635
30811
  function parseCustomProviderBody(body) {
30636
30812
  if (!body || typeof body !== "object") return { error: "Invalid JSON body" };
@@ -30695,15 +30871,6 @@ async function buildProviderAccess({
30695
30871
  function canUseModelProvider(access, provider) {
30696
30872
  return Boolean(access[provider]);
30697
30873
  }
30698
- async function getTenantModelPacksStorage(modelPacks) {
30699
- if (!modelPacks) return void 0;
30700
- try {
30701
- await modelPacks.ensureReady();
30702
- } catch {
30703
- return void 0;
30704
- }
30705
- return modelPacks;
30706
- }
30707
30874
  async function resolvePackContext({
30708
30875
  c,
30709
30876
  auth,
@@ -30711,23 +30878,23 @@ async function resolvePackContext({
30711
30878
  }) {
30712
30879
  await auth.ensureUser(c);
30713
30880
  const tenant = auth.tenant(c);
30714
- if (!tenant) {
30715
- if (auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
30716
- return { mode: "local" };
30717
- }
30718
- const storage = await getTenantModelPacksStorage(modelPacks);
30719
- if (!storage) {
30720
- return {
30721
- response: c.json(
30722
- {
30723
- error: "model_packs_unavailable",
30724
- message: "Model pack storage is unavailable \u2014 the app database is not configured or failed to start."
30725
- },
30726
- 503
30727
- )
30728
- };
30881
+ if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
30882
+ if (modelPacks) {
30883
+ try {
30884
+ await modelPacks.ensureReady();
30885
+ return tenant ? { storage: modelPacks, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: modelPacks, orgId: "local", userId: "local" };
30886
+ } catch {
30887
+ }
30729
30888
  }
30730
- return { mode: "tenant", storage, orgId: tenantOrgId(tenant), userId: tenant.userId };
30889
+ return {
30890
+ response: c.json(
30891
+ {
30892
+ error: "model_packs_unavailable",
30893
+ message: "Model pack storage is unavailable \u2014 the app database is not configured or failed to start."
30894
+ },
30895
+ 503
30896
+ )
30897
+ };
30731
30898
  }
30732
30899
  function recordToModePack(record) {
30733
30900
  return { id: `custom:${record.id}`, name: record.name, description: "Saved custom pack", models: record.models };
@@ -30740,7 +30907,7 @@ async function listModelPacks({
30740
30907
  activePackId
30741
30908
  }) {
30742
30909
  const access = await buildProviderAccess({ controller, authStorage, tenantCredentials });
30743
- const packs = packContext.mode === "local" ? getAvailableModePacks(access, loadSettings().customModelPacks) : [
30910
+ const packs = [
30744
30911
  ...getAvailableModePacks(access),
30745
30912
  ...(await packContext.storage.list({ orgId: packContext.orgId })).map(recordToModePack)
30746
30913
  ];
@@ -30798,20 +30965,57 @@ function readOMConfig(session) {
30798
30965
  observeAttachments: observeAttachments === true || observeAttachments === false ? observeAttachments : "auto"
30799
30966
  };
30800
30967
  }
30801
- function persistOmThreshold({ role, value }) {
30802
- const settings = loadSettings();
30803
- if (role === "observation") settings.models.omObservationThreshold = value;
30804
- else settings.models.omReflectionThreshold = value;
30805
- saveSettings(settings);
30806
- }
30807
- function persistOmRoleOverride({
30808
- role,
30809
- modelId,
30810
- otherRoleCurrentModelId
30968
+ async function resolveMemorySettingsContext({
30969
+ c,
30970
+ auth,
30971
+ memorySettings
30811
30972
  }) {
30812
- const settings = loadSettings();
30813
- applyOmRoleOverride(settings, role, modelId, otherRoleCurrentModelId);
30814
- saveSettings(settings);
30973
+ await auth.ensureUser(c);
30974
+ const tenant = auth.tenant(c);
30975
+ if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
30976
+ if (memorySettings) {
30977
+ try {
30978
+ await memorySettings.ensureReady();
30979
+ return tenant ? { storage: memorySettings, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: memorySettings, orgId: "local", userId: "local" };
30980
+ } catch {
30981
+ }
30982
+ }
30983
+ return {
30984
+ response: c.json(
30985
+ {
30986
+ error: "memory_settings_unavailable",
30987
+ message: "Memory settings storage is unavailable \u2014 the app database is not configured or failed to start."
30988
+ },
30989
+ 503
30990
+ )
30991
+ };
30992
+ }
30993
+ async function persistMemorySettings(context, patch, fillIfUnset) {
30994
+ await context.storage.patch({ orgId: context.orgId, userId: context.userId, patch, fillIfUnset });
30995
+ }
30996
+ async function hydrateSessionMemorySettings(session, record) {
30997
+ for (const role of ["observer", "reflector"]) {
30998
+ const stored = role === "observer" ? record?.observerModelId : record?.reflectorModelId;
30999
+ const target = stored ?? DEFAULT_OM_MODEL_ID;
31000
+ if (session.om[role].modelId() !== target) {
31001
+ await session.om[role].switchModel({ modelId: target });
31002
+ }
31003
+ }
31004
+ const state = session.state.get() ?? {};
31005
+ const updates = {};
31006
+ const observationThreshold = record?.observationThreshold ?? DEFAULT_OBSERVATION_THRESHOLD;
31007
+ if (state.observationThreshold !== observationThreshold) {
31008
+ updates.observationThreshold = observationThreshold;
31009
+ }
31010
+ const reflectionThreshold = record?.reflectionThreshold ?? DEFAULT_REFLECTION_THRESHOLD;
31011
+ if (state.reflectionThreshold !== reflectionThreshold) {
31012
+ updates.reflectionThreshold = reflectionThreshold;
31013
+ }
31014
+ const observeAttachments = record?.observeAttachments ?? "auto";
31015
+ if ((state.observeAttachments ?? "auto") !== observeAttachments) {
31016
+ updates.observeAttachments = observeAttachments;
31017
+ }
31018
+ if (Object.keys(updates).length > 0) await session.state.set(updates);
30815
31019
  }
30816
31020
  var ConfigRoutes = class extends Route {
30817
31021
  routes() {
@@ -30819,6 +31023,8 @@ var ConfigRoutes = class extends Route {
30819
31023
  const { controller, authStorage, auth } = options;
30820
31024
  const onCredentialsChanged = options.onCredentialsChanged ?? (() => {
30821
31025
  });
31026
+ const onCustomProvidersChanged = options.onCustomProvidersChanged ?? (() => {
31027
+ });
30822
31028
  return [
30823
31029
  registerApiRoute7("/web/config/providers", {
30824
31030
  method: "GET",
@@ -30910,14 +31116,22 @@ var ConfigRoutes = class extends Route {
30910
31116
  }
30911
31117
  }),
30912
31118
  // ── Custom providers (OpenAI-compatible endpoints) ──────────────────────
30913
- // Mirrors the TUI's /custom-providers command. Backed by GlobalSettings
30914
- // (settings.json), not session state these are user-global definitions.
31119
+ // Mirrors the TUI's /custom-providers command, but backed by the
31120
+ // `custom-providers` domain (org rows in tenant mode, a sentinel `local`
31121
+ // org in no-auth mode) — the server never reads settings.json for these.
30915
31122
  registerApiRoute7("/web/config/custom-providers", {
30916
31123
  method: "GET",
30917
31124
  requiresAuth: false,
30918
- handler: (c) => {
31125
+ handler: async (c) => {
31126
+ const ctx = await resolveCustomProvidersContext({
31127
+ c: loose6(c),
31128
+ auth,
31129
+ customProviders: options.customProviders
31130
+ });
31131
+ if ("response" in ctx) return ctx.response;
30919
31132
  try {
30920
- return c.json({ providers: listCustomProviders() });
31133
+ const records = await ctx.storage.list({ orgId: ctx.orgId });
31134
+ return c.json({ providers: records.map(toCustomProviderInfo) });
30921
31135
  } catch (error) {
30922
31136
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
30923
31137
  }
@@ -30927,6 +31141,12 @@ var ConfigRoutes = class extends Route {
30927
31141
  method: "POST",
30928
31142
  requiresAuth: false,
30929
31143
  handler: async (c) => {
31144
+ const ctx = await resolveCustomProvidersContext({
31145
+ c: loose6(c),
31146
+ auth,
31147
+ customProviders: options.customProviders
31148
+ });
31149
+ if ("response" in ctx) return ctx.response;
30930
31150
  let body;
30931
31151
  try {
30932
31152
  body = await c.req.json();
@@ -30937,11 +31157,20 @@ var ConfigRoutes = class extends Route {
30937
31157
  if ("error" in parsed) return c.json({ error: parsed.error }, 400);
30938
31158
  const previousId = body && typeof body === "object" && typeof body.previousId === "string" ? body.previousId : void 0;
30939
31159
  try {
30940
- const settings = loadSettings();
30941
- upsertCustomProviderInSettings(settings, parsed, previousId);
30942
- saveSettings(settings);
30943
- const id = getCustomProviderId(parsed.name);
30944
- return c.json({ ok: true, provider: listCustomProviders().find((p) => p.id === id) });
31160
+ const record = await ctx.storage.upsert({
31161
+ orgId: ctx.orgId,
31162
+ userId: ctx.userId,
31163
+ input: {
31164
+ providerId: getCustomProviderId(parsed.name),
31165
+ name: parsed.name,
31166
+ url: parsed.url,
31167
+ apiKey: parsed.apiKey,
31168
+ models: parsed.models
31169
+ },
31170
+ previousProviderId: previousId
31171
+ });
31172
+ onCustomProvidersChanged({ orgId: ctx.orgId });
31173
+ return c.json({ ok: true, provider: toCustomProviderInfo(record) });
30945
31174
  } catch (error) {
30946
31175
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
30947
31176
  }
@@ -30950,12 +31179,17 @@ var ConfigRoutes = class extends Route {
30950
31179
  registerApiRoute7("/web/config/custom-providers/:id", {
30951
31180
  method: "DELETE",
30952
31181
  requiresAuth: false,
30953
- handler: (c) => {
31182
+ handler: async (c) => {
31183
+ const ctx = await resolveCustomProvidersContext({
31184
+ c: loose6(c),
31185
+ auth,
31186
+ customProviders: options.customProviders
31187
+ });
31188
+ if ("response" in ctx) return ctx.response;
30954
31189
  const id = c.req.param("id");
30955
31190
  try {
30956
- const settings = loadSettings();
30957
- removeCustomProviderFromSettings(settings, id);
30958
- saveSettings(settings);
31191
+ await ctx.storage.delete({ orgId: ctx.orgId, providerId: id });
31192
+ onCustomProvidersChanged({ orgId: ctx.orgId });
30959
31193
  return c.json({ ok: true });
30960
31194
  } catch (error) {
30961
31195
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -30984,8 +31218,30 @@ var ConfigRoutes = class extends Route {
30984
31218
  tenantCredentials
30985
31219
  })
30986
31220
  ]);
31221
+ const catalog = models.filter((m) => canUseModelProvider(access, m.provider) && typeof m.id === "string").map((m) => ({ id: m.id, provider: m.provider, modelName: m.modelName, hasApiKey: true }));
31222
+ if (options.customProviders) {
31223
+ try {
31224
+ const ctx = await resolveCustomProvidersContext({
31225
+ c: loose6(c),
31226
+ auth,
31227
+ customProviders: options.customProviders
31228
+ });
31229
+ if (!("response" in ctx)) {
31230
+ const known = new Set(catalog.map((m) => m.id));
31231
+ for (const record of await ctx.storage.list({ orgId: ctx.orgId })) {
31232
+ for (const model of record.models) {
31233
+ const id = `${record.providerId}/${model}`;
31234
+ if (known.has(id)) continue;
31235
+ known.add(id);
31236
+ catalog.push({ id, provider: record.providerId, modelName: model, hasApiKey: true });
31237
+ }
31238
+ }
31239
+ }
31240
+ } catch {
31241
+ }
31242
+ }
30987
31243
  return c.json({
30988
- models: models.filter((m) => canUseModelProvider(access, m.provider) && typeof m.id === "string").map((m) => ({ id: m.id, provider: m.provider, modelName: m.modelName, hasApiKey: true })).sort(
31244
+ models: catalog.sort(
30989
31245
  (a, b) => a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider)
30990
31246
  )
30991
31247
  });
@@ -30995,10 +31251,10 @@ var ConfigRoutes = class extends Route {
30995
31251
  }
30996
31252
  }),
30997
31253
  // ── Model packs ─────────────────────────────────────────────────────────
30998
- // Mirrors the TUI's /models-pack command. Custom-pack CRUD is DB-backed in
30999
- // tenant mode (model-packs storage domain, org-scoped) and settings.json in
31000
- // local mode; activation is session-scoped and resolves the session from
31001
- // the controller registry by resourceId.
31254
+ // Mirrors the TUI's /models-pack command. Custom-pack CRUD lives in the
31255
+ // model-packs storage domain (org-scoped, sentinel `local` org in no-auth
31256
+ // mode — never settings.json); activation is session-scoped and resolves
31257
+ // the session from the controller registry by resourceId.
31002
31258
  registerApiRoute7("/web/config/model-packs", {
31003
31259
  method: "GET",
31004
31260
  requiresAuth: false,
@@ -31052,21 +31308,12 @@ var ConfigRoutes = class extends Route {
31052
31308
  return c.json({ error: "models.build, models.plan and models.fast are required" }, 400);
31053
31309
  }
31054
31310
  try {
31055
- if (packContext.mode === "tenant") {
31056
- const record = await packContext.storage.upsert({
31057
- orgId: packContext.orgId,
31058
- userId: packContext.userId,
31059
- input: { name, models: { build, plan, fast } }
31060
- });
31061
- return c.json({ ok: true, pack: recordToModePack(record) });
31062
- }
31063
- const settings = loadSettings();
31064
- const entry = { name, models: { build, plan, fast }, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
31065
- const idx = settings.customModelPacks.findIndex((p) => p.name === name);
31066
- if (idx >= 0) settings.customModelPacks[idx] = entry;
31067
- else settings.customModelPacks.push(entry);
31068
- saveSettings(settings);
31069
- return c.json({ ok: true, pack: { id: `custom:${name}`, name, models: { build, plan, fast } } });
31311
+ const record = await packContext.storage.upsert({
31312
+ orgId: packContext.orgId,
31313
+ userId: packContext.userId,
31314
+ input: { name, models: { build, plan, fast } }
31315
+ });
31316
+ return c.json({ ok: true, pack: recordToModePack(record) });
31070
31317
  } catch (error) {
31071
31318
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
31072
31319
  }
@@ -31080,15 +31327,9 @@ var ConfigRoutes = class extends Route {
31080
31327
  if ("response" in packContext) return packContext.response;
31081
31328
  const id = decodeURIComponent(c.req.param("id"));
31082
31329
  try {
31083
- if (packContext.mode === "tenant") {
31084
- const recordId = id.startsWith("custom:") ? id.slice("custom:".length) : id;
31085
- const deleted = await packContext.storage.delete({ orgId: packContext.orgId, id: recordId });
31086
- return deleted ? c.json({ ok: true }) : c.json({ error: `Unknown pack "${id}"` }, 404);
31087
- }
31088
- const settings = loadSettings();
31089
- removeCustomPackFromSettings(settings, id);
31090
- saveSettings(settings);
31091
- return c.json({ ok: true });
31330
+ const recordId = id.startsWith("custom:") ? id.slice("custom:".length) : id;
31331
+ const deleted = await packContext.storage.delete({ orgId: packContext.orgId, id: recordId });
31332
+ return deleted ? c.json({ ok: true }) : c.json({ error: `Unknown pack "${id}"` }, 404);
31092
31333
  } catch (error) {
31093
31334
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
31094
31335
  }
@@ -31135,8 +31376,10 @@ var ConfigRoutes = class extends Route {
31135
31376
  }),
31136
31377
  // ── Observational memory ──────────────────────────────────────────────────
31137
31378
  // Mirrors the TUI's /om command. All five knobs are session-scoped (resolved
31138
- // from the session, persisted to its state + thread setting) plus written to
31139
- // GlobalSettings so the choice survives restarts and stays in sync with the TUI.
31379
+ // from the session, persisted to its state + thread setting) and durably
31380
+ // stored in the per-(org, user) `memory-settings` app table never
31381
+ // settings.json. GET hydrates the session from the stored row first so the
31382
+ // DB, not the SDK's boot-time seed, is the source of truth.
31140
31383
  registerApiRoute7("/web/config/om", {
31141
31384
  method: "GET",
31142
31385
  requiresAuth: false,
@@ -31144,9 +31387,17 @@ var ConfigRoutes = class extends Route {
31144
31387
  const resourceId = c.req.query("resourceId");
31145
31388
  const scope = c.req.query("scope") || void 0;
31146
31389
  if (!resourceId) return c.json({ error: "Missing required query param: resourceId" }, 400);
31390
+ const context = await resolveMemorySettingsContext({
31391
+ c: loose6(c),
31392
+ auth,
31393
+ memorySettings: options.memorySettings
31394
+ });
31395
+ if ("response" in context) return context.response;
31147
31396
  try {
31148
31397
  const session = await controller.getSessionByResource?.(resourceId, scope);
31149
31398
  if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
31399
+ const record = await context.storage.get({ orgId: context.orgId, userId: context.userId });
31400
+ await hydrateSessionMemorySettings(session, record);
31150
31401
  return c.json({ config: readOMConfig(session) });
31151
31402
  } catch (error) {
31152
31403
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -31172,13 +31423,24 @@ var ConfigRoutes = class extends Route {
31172
31423
  const modelId = typeof body.modelId === "string" ? body.modelId.trim() : "";
31173
31424
  if (!resourceId) return c.json({ error: "Missing required field: resourceId" }, 400);
31174
31425
  if (!modelId) return c.json({ error: "Missing required field: modelId" }, 400);
31426
+ const context = await resolveMemorySettingsContext({
31427
+ c: loose6(c),
31428
+ auth,
31429
+ memorySettings: options.memorySettings
31430
+ });
31431
+ if ("response" in context) return context.response;
31175
31432
  try {
31176
31433
  const session = await controller.getSessionByResource?.(resourceId, scope);
31177
31434
  if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
31178
31435
  const otherRole = role === "observer" ? session.om.reflector : session.om.observer;
31179
31436
  const otherRoleCurrentModelId = otherRole.modelId() ?? null;
31180
31437
  await session.om[role].switchModel({ modelId });
31181
- persistOmRoleOverride({ role, modelId, otherRoleCurrentModelId });
31438
+ const otherKey = role === "observer" ? "reflectorModelId" : "observerModelId";
31439
+ await persistMemorySettings(
31440
+ context,
31441
+ { [role === "observer" ? "observerModelId" : "reflectorModelId"]: modelId },
31442
+ otherRoleCurrentModelId ? { [otherKey]: otherRoleCurrentModelId } : void 0
31443
+ );
31182
31444
  return c.json({ ok: true, config: readOMConfig(session) });
31183
31445
  } catch (error) {
31184
31446
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -31203,19 +31465,27 @@ var ConfigRoutes = class extends Route {
31203
31465
  if (observation === void 0 && reflection === void 0) {
31204
31466
  return c.json({ error: "Provide observationThreshold and/or reflectionThreshold (positive numbers)" }, 400);
31205
31467
  }
31468
+ const context = await resolveMemorySettingsContext({
31469
+ c: loose6(c),
31470
+ auth,
31471
+ memorySettings: options.memorySettings
31472
+ });
31473
+ if ("response" in context) return context.response;
31206
31474
  try {
31207
31475
  const session = await controller.getSessionByResource?.(resourceId, scope);
31208
31476
  if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
31209
31477
  if (observation !== void 0) {
31210
31478
  await session.state.set({ observationThreshold: observation });
31211
31479
  await session.thread.setSetting({ key: "observationThreshold", value: observation });
31212
- persistOmThreshold({ role: "observation", value: observation });
31213
31480
  }
31214
31481
  if (reflection !== void 0) {
31215
31482
  await session.state.set({ reflectionThreshold: reflection });
31216
31483
  await session.thread.setSetting({ key: "reflectionThreshold", value: reflection });
31217
- persistOmThreshold({ role: "reflection", value: reflection });
31218
31484
  }
31485
+ await persistMemorySettings(context, {
31486
+ ...observation !== void 0 ? { observationThreshold: observation } : {},
31487
+ ...reflection !== void 0 ? { reflectionThreshold: reflection } : {}
31488
+ });
31219
31489
  return c.json({ ok: true, config: readOMConfig(session) });
31220
31490
  } catch (error) {
31221
31491
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -31240,12 +31510,18 @@ var ConfigRoutes = class extends Route {
31240
31510
  if (raw !== "auto" && raw !== true && raw !== false) {
31241
31511
  return c.json({ error: "value must be 'auto', true, or false" }, 400);
31242
31512
  }
31513
+ const context = await resolveMemorySettingsContext({
31514
+ c: loose6(c),
31515
+ auth,
31516
+ memorySettings: options.memorySettings
31517
+ });
31518
+ if ("response" in context) return context.response;
31243
31519
  try {
31244
31520
  const session = await controller.getSessionByResource?.(resourceId, scope);
31245
31521
  if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
31246
31522
  await session.state.set({ observeAttachments: value });
31247
31523
  await session.thread.setSetting({ key: "observeAttachments", value });
31248
- persistOmObserveAttachments(value);
31524
+ await persistMemorySettings(context, { observeAttachments: value });
31249
31525
  return c.json({ ok: true, config: readOMConfig(session) });
31250
31526
  } catch (error) {
31251
31527
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -32463,7 +32739,7 @@ var SkillRoutes = class extends Route {
32463
32739
  // src/routes/tenant-credentials.ts
32464
32740
  import { setCredentialStoreProvider } from "@mastra/code-sdk/agents/credential-resolver";
32465
32741
  import { getOAuthProvider } from "@mastra/code-sdk/auth/storage";
32466
- var SNAPSHOT_TTL_MS = 15e3;
32742
+ var SNAPSHOT_TTL_MS2 = 15e3;
32467
32743
  var MAX_CACHED_TENANTS = 1e3;
32468
32744
  var TenantCredentialStore = class {
32469
32745
  allowEnvironmentFallback = false;
@@ -32480,7 +32756,7 @@ var TenantCredentialStore = class {
32480
32756
  }
32481
32757
  /** Hydrate the snapshot when stale; coalesces concurrent callers. */
32482
32758
  async ensureFresh(now = Date.now()) {
32483
- if (now - this.#fetchedAt < SNAPSHOT_TTL_MS) return;
32759
+ if (now - this.#fetchedAt < SNAPSHOT_TTL_MS2) return;
32484
32760
  this.#hydrating ??= this.#hydrate().finally(() => {
32485
32761
  this.#hydrating = void 0;
32486
32762
  });
@@ -32502,7 +32778,7 @@ var TenantCredentialStore = class {
32502
32778
  }
32503
32779
  /** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
32504
32780
  reload() {
32505
- if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS) {
32781
+ if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS2) {
32506
32782
  void this.ensureFresh().catch(() => {
32507
32783
  });
32508
32784
  }
@@ -34886,7 +35162,10 @@ function assembleFactoryApiRoutes(deps) {
34886
35162
  authStorage: deps.authStorage,
34887
35163
  modelCredentials: deps.domains.modelCredentials,
34888
35164
  modelPacks: deps.domains.modelPacks,
34889
- onCredentialsChanged: invalidateTenantCredentialSnapshots
35165
+ memorySettings: deps.domains.memorySettings,
35166
+ customProviders: deps.domains.customProviders,
35167
+ onCredentialsChanged: invalidateTenantCredentialSnapshots,
35168
+ onCustomProvidersChanged: invalidateCustomProvidersSnapshots
34890
35169
  }).routes(),
34891
35170
  ...new OAuthRoutes({
34892
35171
  auth: deps.auth,
@@ -36051,10 +36330,13 @@ var MIME = {
36051
36330
  };
36052
36331
  var SERVER_PREFIXES = ["/api", "/web", "/auth"];
36053
36332
  function resolveUiDistDir() {
36333
+ const projectRoot = process.env.MASTRA_PROJECT_ROOT?.trim();
36054
36334
  const candidates = [
36055
36335
  process.env.MASTRACODE_UI_DIST,
36056
- join2(dirname(fileURLToPath(import.meta.url)), "ui"),
36057
- resolve2(process.cwd(), "src/mastra/public/ui")
36336
+ resolve2(process.cwd(), "factory"),
36337
+ join2(dirname(fileURLToPath(import.meta.url)), "factory"),
36338
+ projectRoot && resolve2(projectRoot, "src/mastra/public/factory"),
36339
+ resolve2(process.cwd(), "src/mastra/public/factory")
36058
36340
  ];
36059
36341
  for (const candidate of candidates) {
36060
36342
  if (candidate && existsSync(join2(candidate, "index.html"))) return resolve2(candidate);
@@ -36480,8 +36762,154 @@ var AuditDomain = class {
36480
36762
  }
36481
36763
  };
36482
36764
 
36483
- // src/storage/domains/intake/base.ts
36765
+ // src/storage/domains/custom-providers/base.ts
36484
36766
  import { FactoryStorageDomain as FactoryStorageDomain5, UniqueViolationError as UniqueViolationError5 } from "@mastra/core/storage";
36767
+ var CUSTOM_PROVIDERS_SCHEMA = {
36768
+ name: "custom_providers",
36769
+ columns: {
36770
+ id: { type: "uuid-pk" },
36771
+ org_id: { type: "text" },
36772
+ created_by: { type: "text" },
36773
+ provider_id: { type: "text" },
36774
+ name: { type: "text" },
36775
+ url: { type: "text" },
36776
+ api_key: { type: "text", nullable: true },
36777
+ models: { type: "json" },
36778
+ created_at: { type: "timestamp" },
36779
+ updated_at: { type: "timestamp" }
36780
+ },
36781
+ uniqueIndexes: [{ name: "custom_providers_org_provider_key", columns: ["org_id", "provider_id"] }]
36782
+ };
36783
+ function toRecord(row) {
36784
+ return {
36785
+ id: row.id,
36786
+ orgId: row.org_id,
36787
+ createdBy: row.created_by,
36788
+ providerId: row.provider_id,
36789
+ name: row.name,
36790
+ url: row.url,
36791
+ apiKey: row.api_key,
36792
+ models: Array.isArray(row.models) ? row.models : [],
36793
+ createdAt: row.created_at,
36794
+ updatedAt: row.updated_at
36795
+ };
36796
+ }
36797
+ var CustomProvidersStorage = class extends FactoryStorageDomain5 {
36798
+ constructor() {
36799
+ super("custom-providers");
36800
+ }
36801
+ async init() {
36802
+ await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);
36803
+ }
36804
+ async dangerouslyClearAll() {
36805
+ await this.ops.deleteMany("custom_providers", {});
36806
+ }
36807
+ get #db() {
36808
+ return this.ops;
36809
+ }
36810
+ /**
36811
+ * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors
36812
+ * the settings.json upsert semantics (no key retention: an absent `apiKey`
36813
+ * clears the stored key). `previousProviderId` renames the provider: the
36814
+ * common path is a single atomic in-place update of the old row; renaming
36815
+ * onto an id that already exists overwrites the target first and removes the
36816
+ * old row last, so no interleaving or failure can lose the provider.
36817
+ */
36818
+ async upsert({
36819
+ orgId,
36820
+ userId,
36821
+ input,
36822
+ previousProviderId
36823
+ }) {
36824
+ const now = /* @__PURE__ */ new Date();
36825
+ const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : void 0;
36826
+ if (renameFrom) {
36827
+ const target = await this.#db.findOne("custom_providers", {
36828
+ org_id: orgId,
36829
+ provider_id: input.providerId
36830
+ });
36831
+ if (!target) {
36832
+ const renamed = await this.#db.updateAtomic(
36833
+ "custom_providers",
36834
+ { org_id: orgId, provider_id: renameFrom },
36835
+ () => ({
36836
+ provider_id: input.providerId,
36837
+ name: input.name,
36838
+ url: input.url,
36839
+ api_key: input.apiKey ?? null,
36840
+ models: input.models,
36841
+ updated_at: now
36842
+ })
36843
+ );
36844
+ if (renamed) return toRecord(renamed);
36845
+ }
36846
+ }
36847
+ const record = await this.#write({ orgId, userId, input, now });
36848
+ if (renameFrom) {
36849
+ await this.#db.deleteMany("custom_providers", { org_id: orgId, provider_id: renameFrom });
36850
+ }
36851
+ return record;
36852
+ }
36853
+ /**
36854
+ * Update-first, then insert-and-catch-unique-violation (see `queue_health`):
36855
+ * concurrent creates of the same provider both succeed (last write wins on
36856
+ * the single row) instead of one failing on the unique index, and a row
36857
+ * deleted mid-flight is recreated rather than reported as a stale success.
36858
+ */
36859
+ async #write({
36860
+ orgId,
36861
+ userId,
36862
+ input,
36863
+ now
36864
+ }) {
36865
+ const updateExisting = () => this.#db.updateAtomic(
36866
+ "custom_providers",
36867
+ { org_id: orgId, provider_id: input.providerId },
36868
+ () => ({
36869
+ name: input.name,
36870
+ url: input.url,
36871
+ api_key: input.apiKey ?? null,
36872
+ models: input.models,
36873
+ updated_at: now
36874
+ })
36875
+ );
36876
+ const updated = await updateExisting();
36877
+ if (updated) return toRecord(updated);
36878
+ try {
36879
+ const row = await this.#db.insertOne("custom_providers", {
36880
+ org_id: orgId,
36881
+ created_by: userId,
36882
+ provider_id: input.providerId,
36883
+ name: input.name,
36884
+ url: input.url,
36885
+ api_key: input.apiKey ?? null,
36886
+ models: input.models,
36887
+ created_at: now,
36888
+ updated_at: now
36889
+ });
36890
+ return toRecord(row);
36891
+ } catch (error) {
36892
+ if (!(error instanceof UniqueViolationError5)) throw error;
36893
+ const row = await updateExisting();
36894
+ if (!row) throw error;
36895
+ return toRecord(row);
36896
+ }
36897
+ }
36898
+ async list({ orgId }) {
36899
+ const rows = await this.#db.findMany(
36900
+ "custom_providers",
36901
+ { org_id: orgId },
36902
+ { orderBy: [["name", "asc"]] }
36903
+ );
36904
+ return rows.map(toRecord);
36905
+ }
36906
+ async delete({ orgId, providerId }) {
36907
+ return await this.#db.deleteMany("custom_providers", { org_id: orgId, provider_id: providerId }) > 0;
36908
+ }
36909
+ };
36910
+
36911
+ // src/storage/domains/intake/base.ts
36912
+ import { FactoryStorageDomain as FactoryStorageDomain6, UniqueViolationError as UniqueViolationError6 } from "@mastra/core/storage";
36485
36913
  var DEFAULT_INTAKE_CONFIG = {};
36486
36914
  var INTAKE_SETTINGS_SCHEMA = {
36487
36915
  name: "intake_settings",
@@ -36495,7 +36923,7 @@ var INTAKE_SETTINGS_SCHEMA = {
36495
36923
  },
36496
36924
  uniqueIndexes: [{ name: "intake_settings_org_user_unique", columns: ["org_id", "user_id"] }]
36497
36925
  };
36498
- var IntakeStorage = class extends FactoryStorageDomain5 {
36926
+ var IntakeStorage = class extends FactoryStorageDomain6 {
36499
36927
  constructor() {
36500
36928
  super("intake");
36501
36929
  }
@@ -36531,14 +36959,14 @@ var IntakeStorage = class extends FactoryStorageDomain5 {
36531
36959
  try {
36532
36960
  await this.#db.insertOne("intake_settings", { ...where, config, created_at: now, updated_at: now });
36533
36961
  } catch (error) {
36534
- if (!(error instanceof UniqueViolationError5)) throw error;
36962
+ if (!(error instanceof UniqueViolationError6)) throw error;
36535
36963
  await this.#db.updateMany("intake_settings", where, { config, updated_at: now });
36536
36964
  }
36537
36965
  }
36538
36966
  };
36539
36967
 
36540
36968
  // src/storage/domains/integrations/base.ts
36541
- import { FactoryStorageDomain as FactoryStorageDomain6, UniqueViolationError as UniqueViolationError6 } from "@mastra/core/storage";
36969
+ import { FactoryStorageDomain as FactoryStorageDomain7, UniqueViolationError as UniqueViolationError7 } from "@mastra/core/storage";
36542
36970
  var INTEGRATION_CONNECTIONS_SCHEMA = {
36543
36971
  name: "integration_connections",
36544
36972
  columns: {
@@ -36591,7 +37019,7 @@ var INTEGRATION_SETTINGS_SCHEMA = {
36591
37019
  { name: "integration_settings_integration_org_user_unique", columns: ["integration_id", "org_id", "user_id"] }
36592
37020
  ]
36593
37021
  };
36594
- var IntegrationStorage = class extends FactoryStorageDomain6 {
37022
+ var IntegrationStorage = class extends FactoryStorageDomain7 {
36595
37023
  constructor() {
36596
37024
  super("integrations");
36597
37025
  }
@@ -36667,7 +37095,7 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
36667
37095
  });
36668
37096
  return;
36669
37097
  } catch (error) {
36670
- if (!(error instanceof UniqueViolationError6)) throw error;
37098
+ if (!(error instanceof UniqueViolationError7)) throw error;
36671
37099
  lastError = error;
36672
37100
  }
36673
37101
  }
@@ -36772,7 +37200,7 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
36772
37200
  });
36773
37201
  return;
36774
37202
  } catch (error) {
36775
- if (!(error instanceof UniqueViolationError6)) throw error;
37203
+ if (!(error instanceof UniqueViolationError7)) throw error;
36776
37204
  lastError = error;
36777
37205
  }
36778
37206
  }
@@ -36783,8 +37211,115 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
36783
37211
  }
36784
37212
  };
36785
37213
 
37214
+ // src/storage/domains/memory-settings/base.ts
37215
+ import { FactoryStorageDomain as FactoryStorageDomain8, UniqueViolationError as UniqueViolationError8 } from "@mastra/core/storage";
37216
+ var MEMORY_SETTINGS_SCHEMA = {
37217
+ name: "memory_settings",
37218
+ columns: {
37219
+ id: { type: "uuid-pk" },
37220
+ org_id: { type: "text" },
37221
+ user_id: { type: "text" },
37222
+ observer_model_id: { type: "text", nullable: true },
37223
+ reflector_model_id: { type: "text", nullable: true },
37224
+ observation_threshold: { type: "integer", nullable: true },
37225
+ reflection_threshold: { type: "integer", nullable: true },
37226
+ // 'auto' | true | false — json keeps the tri-state without string encoding.
37227
+ observe_attachments: { type: "json", nullable: true },
37228
+ created_at: { type: "timestamp" },
37229
+ updated_at: { type: "timestamp" }
37230
+ },
37231
+ uniqueIndexes: [{ name: "memory_settings_org_user_key", columns: ["org_id", "user_id"] }]
37232
+ };
37233
+ function toRecord2(row) {
37234
+ return {
37235
+ orgId: row.org_id,
37236
+ userId: row.user_id,
37237
+ observerModelId: row.observer_model_id,
37238
+ reflectorModelId: row.reflector_model_id,
37239
+ observationThreshold: row.observation_threshold,
37240
+ reflectionThreshold: row.reflection_threshold,
37241
+ observeAttachments: row.observe_attachments,
37242
+ createdAt: row.created_at,
37243
+ updatedAt: row.updated_at
37244
+ };
37245
+ }
37246
+ function patchToColumns(patch) {
37247
+ const columns = {};
37248
+ if (patch.observerModelId !== void 0) columns.observer_model_id = patch.observerModelId;
37249
+ if (patch.reflectorModelId !== void 0) columns.reflector_model_id = patch.reflectorModelId;
37250
+ if (patch.observationThreshold !== void 0) columns.observation_threshold = patch.observationThreshold;
37251
+ if (patch.reflectionThreshold !== void 0) columns.reflection_threshold = patch.reflectionThreshold;
37252
+ if (patch.observeAttachments !== void 0) columns.observe_attachments = patch.observeAttachments;
37253
+ return columns;
37254
+ }
37255
+ var MemorySettingsStorage = class extends FactoryStorageDomain8 {
37256
+ constructor() {
37257
+ super("memory-settings");
37258
+ }
37259
+ async init() {
37260
+ await this.ensureCollections([MEMORY_SETTINGS_SCHEMA]);
37261
+ }
37262
+ async dangerouslyClearAll() {
37263
+ await this.ops.deleteMany("memory_settings", {});
37264
+ }
37265
+ get #db() {
37266
+ return this.ops;
37267
+ }
37268
+ async get({ orgId, userId }) {
37269
+ const row = await this.#db.findOne("memory_settings", { org_id: orgId, user_id: userId });
37270
+ return row ? toRecord2(row) : null;
37271
+ }
37272
+ /**
37273
+ * Upsert the user's row, writing only the knobs present in `patch`.
37274
+ * `fillIfUnset` knobs are written only where the stored value is still
37275
+ * `NULL`, decided inside the atomic update so concurrent explicit writes
37276
+ * win over fills. Concurrent first writes are resolved via the shared
37277
+ * insert-then-catch-unique-violation pattern (see `queue_health`).
37278
+ */
37279
+ async patch({
37280
+ orgId,
37281
+ userId,
37282
+ patch,
37283
+ fillIfUnset
37284
+ }) {
37285
+ const now = /* @__PURE__ */ new Date();
37286
+ const updateExisting = () => this.#db.updateAtomic("memory_settings", { org_id: orgId, user_id: userId }, (row) => {
37287
+ const columns = { ...patchToColumns(patch), updated_at: now };
37288
+ if (fillIfUnset?.observerModelId !== void 0 && row.observer_model_id == null) {
37289
+ columns.observer_model_id = patch.observerModelId ?? fillIfUnset.observerModelId;
37290
+ }
37291
+ if (fillIfUnset?.reflectorModelId !== void 0 && row.reflector_model_id == null) {
37292
+ columns.reflector_model_id = patch.reflectorModelId ?? fillIfUnset.reflectorModelId;
37293
+ }
37294
+ return columns;
37295
+ });
37296
+ const updated = await updateExisting();
37297
+ if (updated) return toRecord2(updated);
37298
+ try {
37299
+ const row = await this.#db.insertOne("memory_settings", {
37300
+ org_id: orgId,
37301
+ user_id: userId,
37302
+ observer_model_id: fillIfUnset?.observerModelId ?? null,
37303
+ reflector_model_id: fillIfUnset?.reflectorModelId ?? null,
37304
+ observation_threshold: null,
37305
+ reflection_threshold: null,
37306
+ observe_attachments: null,
37307
+ ...patchToColumns(patch),
37308
+ created_at: now,
37309
+ updated_at: now
37310
+ });
37311
+ return toRecord2(row);
37312
+ } catch (error) {
37313
+ if (!(error instanceof UniqueViolationError8)) throw error;
37314
+ const row = await updateExisting();
37315
+ if (!row) throw error;
37316
+ return toRecord2(row);
37317
+ }
37318
+ }
37319
+ };
37320
+
36786
37321
  // src/storage/domains/model-packs/base.ts
36787
- import { FactoryStorageDomain as FactoryStorageDomain7 } from "@mastra/core/storage";
37322
+ import { FactoryStorageDomain as FactoryStorageDomain9 } from "@mastra/core/storage";
36788
37323
  var MODEL_PACKS_SCHEMA = {
36789
37324
  name: "model_packs",
36790
37325
  columns: {
@@ -36811,7 +37346,7 @@ function toModelPack(row) {
36811
37346
  updatedAt: row.updated_at
36812
37347
  };
36813
37348
  }
36814
- var ModelPacksStorage = class extends FactoryStorageDomain7 {
37349
+ var ModelPacksStorage = class extends FactoryStorageDomain9 {
36815
37350
  constructor() {
36816
37351
  super("model-packs");
36817
37352
  }
@@ -36875,7 +37410,7 @@ var ModelPacksStorage = class extends FactoryStorageDomain7 {
36875
37410
  };
36876
37411
 
36877
37412
  // src/storage/domains/projects/base.ts
36878
- import { FactoryStorageDomain as FactoryStorageDomain8 } from "@mastra/core/storage";
37413
+ import { FactoryStorageDomain as FactoryStorageDomain10 } from "@mastra/core/storage";
36879
37414
  var FACTORY_PROJECTS_SCHEMA = {
36880
37415
  name: "factory_projects",
36881
37416
  columns: {
@@ -36902,7 +37437,7 @@ function toFactoryProject(row) {
36902
37437
  updatedAt: row.updated_at
36903
37438
  };
36904
37439
  }
36905
- var FactoryProjectsStorage = class extends FactoryStorageDomain8 {
37440
+ var FactoryProjectsStorage = class extends FactoryStorageDomain10 {
36906
37441
  constructor() {
36907
37442
  super("projects");
36908
37443
  }
@@ -36970,7 +37505,7 @@ var FactoryProjectsStorage = class extends FactoryStorageDomain8 {
36970
37505
  };
36971
37506
 
36972
37507
  // src/storage/domains/source-control/base.ts
36973
- import { FactoryStorageDomain as FactoryStorageDomain9, UniqueViolationError as UniqueViolationError7 } from "@mastra/core/storage";
37508
+ import { FactoryStorageDomain as FactoryStorageDomain11, UniqueViolationError as UniqueViolationError9 } from "@mastra/core/storage";
36974
37509
  var FACTORY_PROJECTS = "factory_projects";
36975
37510
  var INSTALLATIONS = "source_control_installations";
36976
37511
  var REPOSITORIES = "source_control_repositories";
@@ -37223,7 +37758,7 @@ function toSession(row) {
37223
37758
  updatedAt: row.updated_at
37224
37759
  };
37225
37760
  }
37226
- var SourceControlStorage = class extends FactoryStorageDomain9 {
37761
+ var SourceControlStorage = class extends FactoryStorageDomain11 {
37227
37762
  constructor() {
37228
37763
  super("source-control");
37229
37764
  }
@@ -37417,7 +37952,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
37417
37952
  });
37418
37953
  return toConnection(row);
37419
37954
  } catch (error) {
37420
- if (!(error instanceof UniqueViolationError7)) throw error;
37955
+ if (!(error instanceof UniqueViolationError9)) throw error;
37421
37956
  const row = await db().findOne(CONNECTIONS, {
37422
37957
  factory_project_id: input.factoryProjectId,
37423
37958
  integration_id: integrationId,
@@ -37546,7 +38081,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
37546
38081
  });
37547
38082
  return toSandbox(row);
37548
38083
  } catch (error) {
37549
- if (!(error instanceof UniqueViolationError7)) throw error;
38084
+ if (!(error instanceof UniqueViolationError9)) throw error;
37550
38085
  const row = await db().findOne(SANDBOXES, where);
37551
38086
  if (!row) throw error;
37552
38087
  return toSandbox(row);
@@ -37659,7 +38194,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
37659
38194
  });
37660
38195
  return toSession(row);
37661
38196
  } catch (error) {
37662
- if (!(error instanceof UniqueViolationError7)) throw error;
38197
+ if (!(error instanceof UniqueViolationError9)) throw error;
37663
38198
  const row = await db().findOne(SESSIONS, {
37664
38199
  project_repository_id: input.projectRepositoryId,
37665
38200
  user_id: input.userId,
@@ -37762,6 +38297,7 @@ var factorySkillExtension = {
37762
38297
  function createWorkspaceFactory(options = {}) {
37763
38298
  const { sandbox: sandboxConfig, github, fleet } = options;
37764
38299
  const isLocalSandbox = sandboxConfig?.machine instanceof LocalSandbox;
38300
+ const githubTokenInjectors = /* @__PURE__ */ new Map();
37765
38301
  return async ({ requestContext, mastra, skillExtension }) => {
37766
38302
  const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
37767
38303
  const ctx = requestContext.get("controller");
@@ -37813,6 +38349,8 @@ function createWorkspaceFactory(options = {}) {
37813
38349
  const existing = mastra?.getWorkspaceById(workspaceId);
37814
38350
  if (existing) {
37815
38351
  existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);
38352
+ const injectGithubToken3 = githubTokenInjectors.get(workspaceId);
38353
+ if (injectGithubToken3) registerGithubTokenInjector(requestContext, injectGithubToken3);
37816
38354
  return existing;
37817
38355
  }
37818
38356
  } catch {
@@ -37843,6 +38381,14 @@ function createWorkspaceFactory(options = {}) {
37843
38381
  repoFullName
37844
38382
  });
37845
38383
  if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
38384
+ const injectGithubToken2 = (freshToken) => {
38385
+ if (!sandbox.setEnvironmentVariable) {
38386
+ throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
38387
+ }
38388
+ sandbox.setEnvironmentVariable("GH_TOKEN", freshToken);
38389
+ };
38390
+ githubTokenInjectors.set(workspaceId, injectGithubToken2);
38391
+ registerGithubTokenInjector(requestContext, injectGithubToken2);
37846
38392
  const filesystem = new SandboxFilesystem({ sandbox, workdir });
37847
38393
  const projectSkillPaths = [path2.join(configDir, "skills"), ".claude/skills", ".agents/skills"];
37848
38394
  const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
@@ -37950,6 +38496,8 @@ var MastraFactory = class {
37950
38496
  const workItemsStorage = storage.registerDomain(new WorkItemsStorage());
37951
38497
  const modelCredentialsStorage = storage.registerDomain(new ModelCredentialsStorage());
37952
38498
  const modelPacksStorage = storage.registerDomain(new ModelPacksStorage());
38499
+ const memorySettingsStorage = storage.registerDomain(new MemorySettingsStorage());
38500
+ const customProvidersStorage = storage.registerDomain(new CustomProvidersStorage());
37953
38501
  const queueHealthStorage = storage.registerDomain(new QueueHealthStorage());
37954
38502
  const integrationStorage = storage.registerDomain(new IntegrationStorage());
37955
38503
  const factoryProjectsStorage = storage.registerDomain(new FactoryProjectsStorage());
@@ -37958,6 +38506,8 @@ var MastraFactory = class {
37958
38506
  intake: intakeStorage,
37959
38507
  modelCredentials: modelCredentialsStorage,
37960
38508
  modelPacks: modelPacksStorage,
38509
+ memorySettings: memorySettingsStorage,
38510
+ customProviders: customProvidersStorage,
37961
38511
  projects: factoryProjectsStorage,
37962
38512
  queueHealth: queueHealthStorage,
37963
38513
  workItems: workItemsStorage
@@ -38006,6 +38556,7 @@ var MastraFactory = class {
38006
38556
  if (auth) {
38007
38557
  registerTenantCredentialResolver(modelCredentialsStorage);
38008
38558
  }
38559
+ registerCustomProvidersSource({ storage: customProvidersStorage, authEnabled: Boolean(auth) });
38009
38560
  for (const integration of integrations) {
38010
38561
  integration.initialize?.({
38011
38562
  storage: integrationStorage.forIntegration(integration.id),
@@ -38070,6 +38621,9 @@ var MastraFactory = class {
38070
38621
  fleet
38071
38622
  }),
38072
38623
  disableGithubSignals: true,
38624
+ // Memory settings live in the factory's `memory-settings` app table (per
38625
+ // org/user), so the host machine's TUI settings.json must not seed them.
38626
+ disableSettingsOmSeed: true,
38073
38627
  storage: storage.getMastraStorage(),
38074
38628
  ...factoryProcessor ? { inputProcessors: [factoryProcessor] } : {},
38075
38629
  ...vector ? { vector } : {},
@@ -38178,13 +38732,25 @@ var MastraFactory = class {
38178
38732
  const uiDist = resolveUiDistDir();
38179
38733
  const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];
38180
38734
  if (!auth) {
38181
- return { ...spa.length ? { middleware: spa } : {}, ...cors, ...onError };
38735
+ return {
38736
+ middleware: [
38737
+ createCustomProvidersPrimer({ auth: routeAuth, storage: customProvidersStorage, authEnabled: false }),
38738
+ ...spa
38739
+ ],
38740
+ ...cors,
38741
+ ...onError
38742
+ };
38182
38743
  }
38183
38744
  return {
38184
38745
  auth,
38185
38746
  middleware: [
38186
38747
  createFactoryAuthGate(auth),
38187
38748
  createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),
38749
+ createCustomProvidersPrimer({
38750
+ auth: routeAuth,
38751
+ storage: customProvidersStorage,
38752
+ authEnabled: Boolean(auth)
38753
+ }),
38188
38754
  ...spa
38189
38755
  ],
38190
38756
  ...cors,