deepspace 0.33.1 → 0.35.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 (38) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +5 -1
  3. package/dist/{chunk-PZDVKSLI.js → chunk-ESEI3MMY.js} +2 -2
  4. package/dist/{chunk-PZDVKSLI.js.map → chunk-ESEI3MMY.js.map} +1 -1
  5. package/dist/chunk-ESYXTIV2.js +1957 -0
  6. package/dist/chunk-ESYXTIV2.js.map +1 -0
  7. package/dist/cli.js +151 -53
  8. package/dist/cli.js.map +1 -1
  9. package/dist/documentation-client-core.js +1 -1
  10. package/dist/documentation-default-renderer.cjs +1 -1
  11. package/dist/documentation-react.js +1 -1
  12. package/dist/documentation-runtime.js +10 -10
  13. package/dist/documentation-server-core.js +1 -1
  14. package/dist/documentation.js +47 -22
  15. package/dist/documentation.js.map +1 -1
  16. package/dist/expo.d.ts +83 -0
  17. package/dist/expo.js +242 -0
  18. package/dist/expo.js.map +1 -0
  19. package/dist/index.d.ts +110 -23
  20. package/dist/index.js +134 -53
  21. package/dist/index.js.map +1 -1
  22. package/dist/server.d.ts +205 -37
  23. package/dist/server.js +290 -97
  24. package/dist/server.js.map +1 -1
  25. package/dist/testing.js +45 -3
  26. package/dist/testing.js.map +1 -1
  27. package/dist/worker.d.ts +348 -48
  28. package/dist/worker.js +547 -110
  29. package/dist/worker.js.map +1 -1
  30. package/features/documents/feature.json +15 -0
  31. package/features/documents/src/DocumentsListPage.tsx +22 -8
  32. package/features/documents/src/InviteDialog.tsx +63 -42
  33. package/features/documents/src/__tests__/documents-find-invitee-action.test.ts +131 -0
  34. package/features/documents/src/__tests__/documents-invitee-lookup.test.ts +67 -0
  35. package/features/documents/src/documents-find-invitee-action.ts +56 -0
  36. package/features/documents/src/documents-invitee-lookup.ts +38 -0
  37. package/features/documents/src/documents-ui.css +10 -0
  38. package/package.json +29 -9
package/dist/worker.js CHANGED
@@ -2374,6 +2374,7 @@ function getAllUserRecords(sql, schemaRegistry) {
2374
2374
  }
2375
2375
  async function registerUser(sql, userId, name, email, imageUrl, isAdmin, defaultRole = "viewer", schemaRegistry) {
2376
2376
  const now = (/* @__PURE__ */ new Date()).toISOString();
2377
+ const normalizedEmail = email.trim().toLowerCase();
2377
2378
  const existing = getUserRecord(sql, userId, schemaRegistry);
2378
2379
  let role;
2379
2380
  if (isAdmin) {
@@ -2384,7 +2385,7 @@ async function registerUser(sql, userId, name, email, imageUrl, isAdmin, default
2384
2385
  role = defaultRole;
2385
2386
  }
2386
2387
  if (existing) {
2387
- const updatedEmail = email || existing.data.email;
2388
+ const updatedEmail = normalizedEmail || existing.data.email;
2388
2389
  const updatedName = name && name !== "Anonymous" ? name : existing.data.name;
2389
2390
  const updatedImageUrl = imageUrl ?? existing.data.imageUrl;
2390
2391
  sql.exec(
@@ -2413,14 +2414,14 @@ async function registerUser(sql, userId, name, email, imageUrl, isAdmin, default
2413
2414
  userId,
2414
2415
  now,
2415
2416
  now,
2416
- email,
2417
+ normalizedEmail,
2417
2418
  name,
2418
2419
  imageUrl ?? null,
2419
2420
  role,
2420
2421
  now,
2421
2422
  now
2422
2423
  );
2423
- return { id: userId, email, name, imageUrl, role, createdAt: now, lastSeenAt: now };
2424
+ return { id: userId, email: normalizedEmail, name, imageUrl, role, createdAt: now, lastSeenAt: now };
2424
2425
  }
2425
2426
  function rosterFor(ctx, schema, records, publicRoster, userId, role) {
2426
2427
  if (!userId || isAnonymousUserId(userId)) return [];
@@ -2475,16 +2476,19 @@ function broadcastUserList(ctx, except) {
2475
2476
  });
2476
2477
  }
2477
2478
  }
2478
- function handleUserUpdate(ctx, _ws, attachment, payload) {
2479
+ function handleUserUpdate(ctx, _ws, attachment) {
2479
2480
  const existing = getUserRecord(ctx.sql, attachment.userId, ctx.schemaRegistry);
2480
2481
  if (!existing) return;
2481
- const data = { lastSeenAt: (/* @__PURE__ */ new Date()).toISOString() };
2482
- if (payload.name && payload.name !== "Anonymous") data.name = payload.name;
2483
- if (payload.email) data.email = payload.email;
2484
- if (payload.imageUrl !== void 0) data.imageUrl = payload.imageUrl || void 0;
2485
- putRecord(ctx, "users", attachment.userId, data, attachment.userId, "admin", true, true);
2486
- const rosterChanged = data.name !== void 0 && data.name !== existing.data.name || data.imageUrl !== void 0 && data.imageUrl !== existing.data.imageUrl;
2487
- if (rosterChanged) broadcastUserList(ctx);
2482
+ putRecord(
2483
+ ctx,
2484
+ "users",
2485
+ attachment.userId,
2486
+ { lastSeenAt: (/* @__PURE__ */ new Date()).toISOString() },
2487
+ attachment.userId,
2488
+ "admin",
2489
+ true,
2490
+ true
2491
+ );
2488
2492
  }
2489
2493
  async function handleSetRole(ctx, ws, attachment, payload) {
2490
2494
  if (attachment.role !== "admin") {
@@ -3233,7 +3237,7 @@ var RecordRoom = class extends BaseRoom {
3233
3237
  this.schemaRegistry
3234
3238
  );
3235
3239
  const regMs = Date.now() - regStart;
3236
- if (!before || before.name !== registeredUser.name || before.imageUrl !== registeredUser.imageUrl || before.role !== registeredUser.role) {
3240
+ if (!before || before.name !== registeredUser.name || before.email !== registeredUser.email || before.imageUrl !== registeredUser.imageUrl || before.role !== registeredUser.role) {
3237
3241
  broadcastUserList(this.createUserContext(), ws);
3238
3242
  }
3239
3243
  attachment = {
@@ -3360,12 +3364,7 @@ var RecordRoom = class extends BaseRoom {
3360
3364
  handleUserList(userCtx, ws, attachment);
3361
3365
  break;
3362
3366
  case MSG.USER_UPDATE:
3363
- handleUserUpdate(
3364
- recordCtx,
3365
- ws,
3366
- attachment,
3367
- payload
3368
- );
3367
+ handleUserUpdate(recordCtx, ws, attachment);
3369
3368
  break;
3370
3369
  case MSG.SET_ROLE:
3371
3370
  await handleSetRole(userCtx, ws, attachment, payload);
@@ -4609,6 +4608,7 @@ function computeNextRunAt(task, from) {
4609
4608
 
4610
4609
  // src/server/rooms/job-room.ts
4611
4610
  var RUNNING_STALE_MS = 16 * 60 * 1e3;
4611
+ var BACKGROUND_ALARM_MS = 3e4;
4612
4612
  var DEFAULT_RETENTION_MS = 24 * 60 * 60 * 1e3;
4613
4613
  var DEFAULT_SNAPSHOT_LIMIT = 100;
4614
4614
  var JobRoom = class extends BaseRoom {
@@ -4617,6 +4617,7 @@ var JobRoom = class extends BaseRoom {
4617
4617
  retentionMs;
4618
4618
  snapshotLimit;
4619
4619
  retryBackoffMs;
4620
+ backgroundJobTypes;
4620
4621
  authorizeWrite;
4621
4622
  authorizeRead;
4622
4623
  broadcastQueue = Promise.resolve();
@@ -4630,6 +4631,7 @@ var JobRoom = class extends BaseRoom {
4630
4631
  this.retentionMs = Math.max(0, config.retentionMs ?? DEFAULT_RETENTION_MS);
4631
4632
  this.snapshotLimit = Math.max(1, config.snapshotLimit ?? DEFAULT_SNAPSHOT_LIMIT);
4632
4633
  this.retryBackoffMs = Math.max(0, config.retryBackoffMs ?? 1e3);
4634
+ this.backgroundJobTypes = new Set(config.backgroundJobTypes ?? []);
4633
4635
  this.authorizeWrite = config.authorizeWrite ?? ((user) => user.role === ROLES.MEMBER || user.role === ROLES.ADMIN);
4634
4636
  this.authorizeRead = config.authorizeRead ?? this.authorizeWrite;
4635
4637
  }
@@ -4877,11 +4879,8 @@ var JobRoom = class extends BaseRoom {
4877
4879
  // ==========================================================================
4878
4880
  // Alarm / execution
4879
4881
  // ==========================================================================
4880
- /**
4881
- * Drain due jobs in FIFO order. All due jobs share the alarm's
4882
- * 15-min wall budget (a CF property, not a per-job allocation).
4883
- */
4884
4882
  async drainDueJobs() {
4883
+ if (this.inFlight.size > 0) return;
4885
4884
  const cutoff = (/* @__PURE__ */ new Date()).toISOString();
4886
4885
  while (true) {
4887
4886
  const row = this.sql.exec(
@@ -4893,7 +4892,16 @@ var JobRoom = class extends BaseRoom {
4893
4892
  cutoff
4894
4893
  ).toArray()[0];
4895
4894
  if (!row) return;
4896
- await this.executeJob(row);
4895
+ const execution = this.executeJob(row);
4896
+ if (this.backgroundJobTypes.has(row.type)) {
4897
+ this.state.waitUntil(
4898
+ execution.catch(
4899
+ (error) => console.error(`[jobs] background execution failed: ${loggableError(error)}`)
4900
+ ).finally(() => this.scheduleNextAlarm())
4901
+ );
4902
+ return;
4903
+ }
4904
+ await execution;
4897
4905
  }
4898
4906
  }
4899
4907
  async executeJob(row) {
@@ -4930,7 +4938,7 @@ var JobRoom = class extends BaseRoom {
4930
4938
  this.inFlight.delete(row.id);
4931
4939
  }
4932
4940
  const currentStatus = this.readStatus(row.id);
4933
- if (currentStatus === "canceled") {
4941
+ if (currentStatus !== "running") {
4934
4942
  this.continueState = null;
4935
4943
  return;
4936
4944
  }
@@ -5062,6 +5070,10 @@ var JobRoom = class extends BaseRoom {
5062
5070
  // Scheduling / recovery / pruning
5063
5071
  // ==========================================================================
5064
5072
  scheduleNextAlarm() {
5073
+ if (this.inFlight.size > 0) {
5074
+ this.state.storage.setAlarm(Date.now() + BACKGROUND_ALARM_MS);
5075
+ return;
5076
+ }
5065
5077
  const queued = this.sql.exec(
5066
5078
  `SELECT next_run_at FROM jobs
5067
5079
  WHERE status = 'queued'
@@ -5095,6 +5107,7 @@ var JobRoom = class extends BaseRoom {
5095
5107
  cutoff
5096
5108
  ).toArray();
5097
5109
  for (const r of stuck) {
5110
+ if (this.inFlight.has(r.id)) continue;
5098
5111
  if (r.attempts >= r.max_attempts) {
5099
5112
  this.sql.exec(
5100
5113
  `UPDATE jobs
@@ -5795,6 +5808,7 @@ var ACTION_TOOLS_DELETE_WHERE_MIGRATION_ID = "2026-08-action-tools-delete-where"
5795
5808
  var BUILD_INJECTED_APP_ID_MIGRATION_ID = "2026-08-build-injected-app-id";
5796
5809
  var ACTION_ROUTES_BEARER_GUARD_MIGRATION_ID = "2026-08-action-routes-bearer-guard";
5797
5810
  var FILES_SESSION_COOKIE_READS_MIGRATION_ID = "2026-09-files-session-cookie-reads";
5811
+ var AI_SDK_7_MIGRATION_ID = "2026-09-ai-sdk-7";
5798
5812
  function validateAppMigrationIds(value) {
5799
5813
  if (!Array.isArray(value)) {
5800
5814
  return { valid: false, reason: "app migrations must be a JSON string array" };
@@ -5822,8 +5836,8 @@ function validateAppMigrationIds(value) {
5822
5836
 
5823
5837
  // src/shared/ai-models.ts
5824
5838
  var DEEPSPACE_MODEL_CATALOG_PROVENANCE = {
5825
- version: "2026-08-04",
5826
- verifiedAt: "2026-08-04",
5839
+ version: "2026-09-24",
5840
+ verifiedAt: "2026-09-24",
5827
5841
  sources: {
5828
5842
  anthropic: "https://platform.claude.com/docs/en/api/beta/models/list",
5829
5843
  openai: "https://developers.openai.com/api/docs/models/all",
@@ -5849,16 +5863,17 @@ var openAIModel = createMultiStepModelFactory({
5849
5863
  providerLabel: "OpenAI",
5850
5864
  transport: "chat-completions"
5851
5865
  });
5866
+ var DIRECT_GENERATION_NOTE = "Available for direct generation; the current proxy adapter has not passed multi-step tool-result continuation.";
5852
5867
  var DEEPSPACE_AI_MODELS = [
5853
5868
  anthropicModel({
5854
- id: "claude-fable-5",
5855
- label: "Claude Fable 5",
5869
+ id: "claude-opus-5-5",
5870
+ label: "Claude Opus 5.5",
5856
5871
  family: "Claude 5",
5857
5872
  recommendation: "frontier"
5858
5873
  }),
5859
5874
  anthropicModel({
5860
- id: "claude-opus-5",
5861
- label: "Claude Opus 5",
5875
+ id: "claude-fable-5-1",
5876
+ label: "Claude Fable 5.1",
5862
5877
  family: "Claude 5",
5863
5878
  recommendation: "frontier"
5864
5879
  }),
@@ -5875,23 +5890,29 @@ var DEEPSPACE_AI_MODELS = [
5875
5890
  recommendation: "fast"
5876
5891
  }),
5877
5892
  openAIModel({
5878
- id: "gpt-5.6-sol",
5879
- label: "GPT-5.6 Sol",
5880
- family: "GPT-5.6",
5881
- recommendation: "frontier"
5882
- }),
5883
- openAIModel({
5884
- id: "gpt-5.6-terra",
5885
- label: "GPT-5.6 Terra",
5886
- family: "GPT-5.6",
5893
+ id: "gpt-6-sol",
5894
+ label: "GPT-6 Sol",
5895
+ family: "GPT-6",
5887
5896
  recommendation: "balanced"
5888
5897
  }),
5889
5898
  openAIModel({
5890
- id: "gpt-5.6-luna",
5891
- label: "GPT-5.6 Luna",
5892
- family: "GPT-5.6",
5899
+ id: "gpt-6-luna",
5900
+ label: "GPT-6 Luna",
5901
+ family: "GPT-6",
5893
5902
  recommendation: "fast"
5894
5903
  }),
5904
+ {
5905
+ id: "gpt-6-astra",
5906
+ label: "GPT-6 Astra",
5907
+ provider: "openai",
5908
+ providerLabel: "OpenAI",
5909
+ family: "GPT-6",
5910
+ agentSupport: "none",
5911
+ agentProfiles: [],
5912
+ transport: "chat-completions",
5913
+ recommendation: "limited",
5914
+ note: "Available for direct generation; its tool calling requires the Responses API, which the DeepSpace proxy does not serve."
5915
+ },
5895
5916
  {
5896
5917
  id: "gpt-oss-120b",
5897
5918
  label: "GPT-OSS 120B",
@@ -5902,12 +5923,30 @@ var DEEPSPACE_AI_MODELS = [
5902
5923
  agentProfiles: [],
5903
5924
  transport: "chat-completions",
5904
5925
  recommendation: "limited",
5905
- note: "Available for direct generation; the current proxy adapter has not passed multi-step tool-result continuation."
5906
- }
5926
+ note: DIRECT_GENERATION_NOTE
5927
+ },
5928
+ {
5929
+ id: "qwen-3.8-27b",
5930
+ label: "Qwen 3.8 27B",
5931
+ provider: "cerebras",
5932
+ providerLabel: "Cerebras",
5933
+ family: "Qwen 3.8",
5934
+ agentSupport: "single-step",
5935
+ agentProfiles: [],
5936
+ transport: "chat-completions",
5937
+ recommendation: "limited",
5938
+ note: DIRECT_GENERATION_NOTE
5939
+ },
5940
+ // Superseded models: still resolvable for apps that saved them.
5941
+ anthropicModel({ id: "claude-opus-5", label: "Claude Opus 5", family: "Claude 5", recommendation: "frontier", legacy: true }),
5942
+ anthropicModel({ id: "claude-fable-5", label: "Claude Fable 5", family: "Claude 5", recommendation: "frontier", legacy: true }),
5943
+ openAIModel({ id: "gpt-5.6-sol", label: "GPT-5.6 Sol", family: "GPT-5.6", recommendation: "frontier", legacy: true }),
5944
+ openAIModel({ id: "gpt-5.6-terra", label: "GPT-5.6 Terra", family: "GPT-5.6", recommendation: "balanced", legacy: true }),
5945
+ openAIModel({ id: "gpt-5.6-luna", label: "GPT-5.6 Luna", family: "GPT-5.6", recommendation: "fast", legacy: true })
5907
5946
  ];
5908
5947
  var DEEPSPACE_AI_DEFAULTS = {
5909
- agent: "claude-sonnet-5",
5910
- directGeneration: "claude-sonnet-5",
5948
+ agent: "claude-opus-5-5",
5949
+ directGeneration: "claude-opus-5-5",
5911
5950
  summarization: "claude-haiku-4-5"
5912
5951
  };
5913
5952
  var DEEPSPACE_AGENT_PROFILES = {
@@ -5935,7 +5974,7 @@ function getDeepSpaceAIModel(modelId) {
5935
5974
  function listDeepSpaceAgentModels(profileId = "application") {
5936
5975
  const defaultModel = DEEPSPACE_AGENT_PROFILES[profileId].defaultModel;
5937
5976
  return DEEPSPACE_AI_MODELS.filter(
5938
- (model) => model.agentProfiles.includes(profileId)
5977
+ (model) => !model.legacy && model.agentProfiles.includes(profileId)
5939
5978
  ).sort((left, right) => Number(right.id === defaultModel) - Number(left.id === defaultModel));
5940
5979
  }
5941
5980
  function resolveDeepSpaceAgentModel(modelId, profileId = "application") {
@@ -6016,6 +6055,56 @@ var BROWSER_PROXY_ROUTES = [
6016
6055
  { method: "GET", path: "/_deepspace/charges/me" }
6017
6056
  ];
6018
6057
 
6058
+ // src/shared/sandbox.ts
6059
+ var SANDBOX_SCOPES = ["user", "app"];
6060
+ var SANDBOX_SCOPE_HEADER = "x-deepspace-sandbox-scope";
6061
+
6062
+ // src/shared/planes.ts
6063
+ var PLANES = {
6064
+ production: {
6065
+ auth: "https://auth.deep.space",
6066
+ api: "https://api-worker.deep.space",
6067
+ platform: "https://platform-worker.deep.space",
6068
+ deploy: "https://deploy-worker.deep.space",
6069
+ appDomain: "app.space",
6070
+ platformDomain: "deep.space",
6071
+ home: "https://deep.space",
6072
+ dashboard: "https://dashboard.deep.space",
6073
+ privacyPolicy: "https://deep.space/privacy"
6074
+ },
6075
+ // Services on deepspacesites.com, apps on spacestest.com: production's
6076
+ // two-zone split, because a Worker cannot fetch one in its own zone.
6077
+ staging: {
6078
+ auth: "https://auth.deepspacesites.com",
6079
+ api: "https://api.deepspacesites.com",
6080
+ platform: "https://platform.deepspacesites.com",
6081
+ deploy: "https://deploy.deepspacesites.com",
6082
+ appDomain: "spacestest.com",
6083
+ platformDomain: "deepspacesites.com",
6084
+ home: "https://spacestest.com",
6085
+ dashboard: "https://dashboard.deepspacesites.com",
6086
+ privacyPolicy: "https://deep.space/privacy"
6087
+ },
6088
+ // Services on deepspacemedical.com, apps on deepspacemedical.app: two zones
6089
+ // in the medical Cloudflare account. The plane has no dashboard.
6090
+ medical: {
6091
+ auth: "https://auth.deepspacemedical.com",
6092
+ api: "https://api.deepspacemedical.com",
6093
+ platform: "https://platform.deepspacemedical.com",
6094
+ deploy: "https://deploy.deepspacemedical.com",
6095
+ appDomain: "deepspacemedical.app",
6096
+ platformDomain: "deepspacemedical.com",
6097
+ home: "https://deepspacemedical.com",
6098
+ dashboard: null,
6099
+ privacyPolicy: "https://deepspacemedical.com/privacy"
6100
+ }
6101
+ };
6102
+ var OPEN_PLANES = /* @__PURE__ */ new Set(["production", "staging"]);
6103
+ function isRestrictedPlane(env) {
6104
+ const plane = env.DEEPSPACE_PLANE;
6105
+ return plane !== void 0 && plane !== "" && !OPEN_PLANES.has(plane);
6106
+ }
6107
+
6019
6108
  // src/server/utils/cors.ts
6020
6109
  function matchOrigin(origin, patterns) {
6021
6110
  if (!origin) return null;
@@ -7664,36 +7753,57 @@ async function resolveAppRole(env, userId) {
7664
7753
  import { createAnthropic } from "@ai-sdk/anthropic";
7665
7754
  import { createOpenAI } from "@ai-sdk/openai";
7666
7755
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
7667
- var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
7668
- function withDefaultAnthropicMaxTokens(body) {
7756
+
7757
+ // src/server/utils/app-identity.ts
7758
+ function appendAppIdentity(headers, env) {
7759
+ headers.delete("x-app-identity-token");
7760
+ headers.delete("x-app-id");
7761
+ if (!env.APP_IDENTITY_TOKEN) return;
7762
+ headers.set("x-app-identity-token", env.APP_IDENTITY_TOKEN);
7763
+ headers.set("x-app-id", env.DEEPSPACE_APP_ID);
7764
+ }
7765
+
7766
+ // src/server/utils/ai.ts
7767
+ import { defaultSettingsMiddleware, wrapLanguageModel } from "ai";
7768
+ function resolveProxyAuthToken(env, options) {
7769
+ const authToken = options.authToken ?? env.APP_OWNER_JWT;
7770
+ if (!authToken) {
7771
+ throw new Error(
7772
+ "DeepSpace AI proxy: no auth token available. Either pass `options.authToken` explicitly (for user-initiated calls), or ensure `env.APP_OWNER_JWT` is set (injected at deploy time or by `deepspace dev start`)."
7773
+ );
7774
+ }
7775
+ return authToken;
7776
+ }
7777
+ function setProxyHeaders(headers, env, authToken, scope = "user") {
7778
+ headers.set("X-Auth-Token", authToken);
7779
+ appendAppIdentity(headers, {
7780
+ DEEPSPACE_APP_ID: env.DEEPSPACE_APP_ID ?? "",
7781
+ APP_IDENTITY_TOKEN: env.APP_IDENTITY_TOKEN
7782
+ });
7783
+ headers.set(SANDBOX_SCOPE_HEADER, scope);
7784
+ }
7785
+ var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 64e3;
7786
+ var OPENAI_TOOLS_NEED_NO_REASONING = /^gpt-(5\.6|6)-(sol|terra|luna)(-|$)/;
7787
+ function withOpenAIToolReasoningEffort(body) {
7669
7788
  if (typeof body !== "string") return body;
7670
7789
  try {
7671
7790
  const parsed = JSON.parse(body);
7672
- const hasExplicitLimit = typeof parsed.max_tokens === "number" || typeof parsed.max_completion_tokens === "number" || typeof parsed.max_output_tokens === "number";
7673
- if (hasExplicitLimit) return body;
7674
- return JSON.stringify({
7675
- ...parsed,
7676
- max_tokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
7677
- });
7791
+ const needsEffort = typeof parsed.model === "string" && OPENAI_TOOLS_NEED_NO_REASONING.test(parsed.model) && Array.isArray(parsed.tools) && parsed.tools.length > 0 && parsed.reasoning_effort === void 0;
7792
+ return needsEffort ? JSON.stringify({ ...parsed, reasoning_effort: "none" }) : body;
7678
7793
  } catch {
7679
7794
  return body;
7680
7795
  }
7681
7796
  }
7682
7797
  function createDeepSpaceAI(env, provider, options = {}) {
7683
7798
  const transport = resolveApiTransport(env);
7684
- const authToken = options.authToken ?? env.APP_OWNER_JWT;
7685
- if (!authToken) {
7686
- throw new Error(
7687
- "createDeepSpaceAI: no auth token available. Either pass `options.authToken` explicitly (for user-initiated calls), or ensure `env.APP_OWNER_JWT` is set (injected at deploy time or by `deepspace dev start`)."
7688
- );
7689
- }
7799
+ const authToken = resolveProxyAuthToken(env, options);
7690
7800
  const proxyFetch = (input, init) => {
7691
7801
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
7692
7802
  const headers = new Headers(init?.headers);
7693
7803
  headers.delete("authorization");
7694
7804
  headers.delete("x-api-key");
7695
- headers.set("X-Auth-Token", authToken);
7696
- const body = provider === "anthropic" ? withDefaultAnthropicMaxTokens(init?.body) : init?.body;
7805
+ setProxyHeaders(headers, env, authToken, options.sandboxScope);
7806
+ const body = provider === "openai" ? withOpenAIToolReasoningEffort(init?.body) : init?.body;
7697
7807
  if (body !== init?.body) {
7698
7808
  headers.delete("content-length");
7699
7809
  }
@@ -7707,8 +7817,13 @@ function createDeepSpaceAI(env, provider, options = {}) {
7707
7817
  };
7708
7818
  const baseURL = `https://api-worker.internal/api/proxy/${provider}/v1`;
7709
7819
  switch (provider) {
7710
- case "anthropic":
7711
- return createAnthropic({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
7820
+ case "anthropic": {
7821
+ const anthropic2 = createAnthropic({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
7822
+ const defaults = defaultSettingsMiddleware({
7823
+ settings: { maxOutputTokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS }
7824
+ });
7825
+ return (modelId) => wrapLanguageModel({ model: anthropic2(modelId), middleware: defaults });
7826
+ }
7712
7827
  case "openai": {
7713
7828
  const op = createOpenAI({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
7714
7829
  return op.chat;
@@ -7729,10 +7844,7 @@ function createDeepSpaceAI(env, provider, options = {}) {
7729
7844
  }
7730
7845
 
7731
7846
  // src/server/utils/agent.ts
7732
- import {
7733
- stepCountIs,
7734
- streamText
7735
- } from "ai";
7847
+ import { isStepCount, streamText } from "ai";
7736
7848
  var DeepSpaceAgentModelError = class extends Error {
7737
7849
  constructor(modelId, profile) {
7738
7850
  super(
@@ -7754,7 +7866,7 @@ var DeepSpaceAgentProfileError = class extends Error {
7754
7866
  code = "agent_profile_violation";
7755
7867
  };
7756
7868
  function streamDeepSpaceAgent(env, options) {
7757
- const { profile, modelId, authToken, ...streamOptions } = options;
7869
+ const { profile, modelId, authToken, sandboxScope, ...streamOptions } = options;
7758
7870
  const selection = resolveDeepSpaceAgentModel(modelId, profile);
7759
7871
  if (!selection) throw new DeepSpaceAgentModelError(modelId, profile);
7760
7872
  if (selection.profile.allowedTools !== "application-defined") {
@@ -7765,14 +7877,17 @@ function streamDeepSpaceAgent(env, options) {
7765
7877
  }
7766
7878
  }
7767
7879
  }
7768
- const provider = createDeepSpaceAI(env, selection.provider, authToken ? { authToken } : {});
7880
+ const provider = createDeepSpaceAI(env, selection.provider, {
7881
+ ...authToken ? { authToken } : {},
7882
+ ...sandboxScope ? { sandboxScope } : {}
7883
+ });
7769
7884
  const { prepareStep, tools, ...remainingStreamOptions } = streamOptions;
7770
7885
  const boundedTools = limitToolExecutions(tools, selection.profile.maxToolCalls);
7771
7886
  const result = streamText({
7772
7887
  ...remainingStreamOptions,
7773
7888
  ...boundedTools ? { tools: boundedTools } : {},
7774
7889
  model: provider(selection.modelId),
7775
- stopWhen: stepCountIs(selection.profile.maxSteps),
7890
+ stopWhen: isStepCount(selection.profile.maxSteps),
7776
7891
  prepareStep: enforceToolCallLimit(prepareStep, selection.profile.maxToolCalls),
7777
7892
  ...selection.provider === "openai" ? { providerOptions: { openai: { reasoningEffort: "none" } } } : {}
7778
7893
  });
@@ -7884,6 +7999,115 @@ function safeLogText(value) {
7884
7999
  ).trim().slice(0, 300);
7885
8000
  }
7886
8001
 
8002
+ // src/server/utils/sandbox.ts
8003
+ import { anthropic, forwardAnthropicContainerIdFromLastStep } from "@ai-sdk/anthropic";
8004
+ var FILES_PATH = "/api/proxy/anthropic/v1/files";
8005
+ var CODE_EXECUTION_CALLER = "code_execution_20260120";
8006
+ function codeExecutionTool() {
8007
+ return anthropic.tools.codeExecution_20260120();
8008
+ }
8009
+ function callableFromSandbox(tool3) {
8010
+ const anthropicOptions = tool3.providerOptions?.anthropic;
8011
+ return {
8012
+ ...tool3,
8013
+ providerOptions: {
8014
+ ...tool3.providerOptions,
8015
+ anthropic: { ...anthropicOptions, allowedCallers: ["direct", CODE_EXECUTION_CALLER] }
8016
+ }
8017
+ };
8018
+ }
8019
+ var forwardSandboxContainer = forwardAnthropicContainerIdFromLastStep;
8020
+ function reuseSandbox(containerId) {
8021
+ return containerId ? { anthropic: { container: { id: containerId } } } : void 0;
8022
+ }
8023
+ function sandboxUpload(file) {
8024
+ return {
8025
+ type: "file",
8026
+ mediaType: file.mime_type,
8027
+ filename: file.filename,
8028
+ data: { type: "reference", reference: { anthropic: file.id } },
8029
+ providerOptions: { anthropic: { containerUpload: true } }
8030
+ };
8031
+ }
8032
+ function collectFileIds(value, out) {
8033
+ if (Array.isArray(value)) {
8034
+ for (const item of value) collectFileIds(item, out);
8035
+ return;
8036
+ }
8037
+ if (!value || typeof value !== "object") return;
8038
+ for (const [key, child] of Object.entries(value)) {
8039
+ if (key === "file_id" && typeof child === "string" && child) out.add(child);
8040
+ else collectFileIds(child, out);
8041
+ }
8042
+ }
8043
+ function sandboxOutputs(steps) {
8044
+ let containerId = null;
8045
+ const fileIds = /* @__PURE__ */ new Set();
8046
+ for (const step of steps) {
8047
+ const container = step.providerMetadata?.anthropic?.container;
8048
+ if (typeof container?.id === "string") containerId = container.id;
8049
+ for (const part of step.content) {
8050
+ const result = part;
8051
+ if (result.type === "tool-result" && result.providerExecuted) collectFileIds(result.output, fileIds);
8052
+ }
8053
+ }
8054
+ return { containerId, fileIds: [...fileIds] };
8055
+ }
8056
+ var SandboxFileError = class extends Error {
8057
+ constructor(status, message) {
8058
+ super(message);
8059
+ this.status = status;
8060
+ this.name = "SandboxFileError";
8061
+ }
8062
+ };
8063
+ function sandboxFiles(env, options = {}) {
8064
+ const authToken = resolveProxyAuthToken(env, options);
8065
+ async function send(path, init = {}) {
8066
+ const headers = new Headers(init.headers);
8067
+ setProxyHeaders(headers, env, authToken, options.scope);
8068
+ const response = await apiWorkerFetch(env, `${FILES_PATH}${path}`, { ...init, headers });
8069
+ if (!response.ok) {
8070
+ const text = await response.text();
8071
+ let message = text || response.statusText;
8072
+ try {
8073
+ const body = JSON.parse(text);
8074
+ if (typeof body.error === "string") message = body.error;
8075
+ else if (body.error && typeof body.error === "object") message = JSON.stringify(body.error);
8076
+ } catch {
8077
+ }
8078
+ throw new SandboxFileError(response.status, `Sandbox file request failed (${response.status}): ${message}`);
8079
+ }
8080
+ return response;
8081
+ }
8082
+ const fileUrl = (fileId) => `/${encodeURIComponent(fileId)}`;
8083
+ return {
8084
+ async upload(file, { filename, expiresInSeconds } = {}) {
8085
+ const name = filename ?? (file instanceof File ? file.name : void 0);
8086
+ if (!name) throw new SandboxFileError(400, "sandboxFiles.upload: a Blob needs options.filename");
8087
+ const form = new FormData();
8088
+ form.set("file", file, name);
8089
+ if (expiresInSeconds !== void 0) form.set("expires_in_seconds", String(expiresInSeconds));
8090
+ return (await send("", { method: "POST", body: form })).json();
8091
+ },
8092
+ async get(fileId) {
8093
+ return (await send(fileUrl(fileId))).json();
8094
+ },
8095
+ download(fileId) {
8096
+ return send(`${fileUrl(fileId)}/content`);
8097
+ },
8098
+ async list({ limit, page } = {}) {
8099
+ const query = new URLSearchParams();
8100
+ if (limit !== void 0) query.set("limit", String(limit));
8101
+ if (page !== void 0) query.set("page", page);
8102
+ const suffix = query.size > 0 ? `?${query}` : "";
8103
+ return (await send(suffix)).json();
8104
+ },
8105
+ async delete(fileId) {
8106
+ await send(fileUrl(fileId), { method: "DELETE" });
8107
+ }
8108
+ };
8109
+ }
8110
+
7887
8111
  // src/server/utils/composio-tools.ts
7888
8112
  import { tool, jsonSchema } from "ai";
7889
8113
  var EMPTY_SCHEMA = { type: "object", properties: {} };
@@ -7966,15 +8190,6 @@ async function composioTools(env, options) {
7966
8190
  return tools;
7967
8191
  }
7968
8192
 
7969
- // src/server/utils/app-identity.ts
7970
- function appendAppIdentity(headers, env) {
7971
- headers.delete("x-app-identity-token");
7972
- headers.delete("x-app-id");
7973
- if (!env.APP_IDENTITY_TOKEN) return;
7974
- headers.set("x-app-identity-token", env.APP_IDENTITY_TOKEN);
7975
- headers.set("x-app-id", env.DEEPSPACE_APP_ID);
7976
- }
7977
-
7978
8193
  // src/server/utils/screenshot.ts
7979
8194
  async function captureScreenshot(env, url) {
7980
8195
  const headers = new Headers({ "content-type": "application/json" });
@@ -8327,14 +8542,14 @@ function formatTranscript(messages) {
8327
8542
  }
8328
8543
  function makeDefaultSummarizer(env, options = {}) {
8329
8544
  return async (messages) => {
8330
- const anthropic = createDeepSpaceAI(env, "anthropic", { authToken: options.authToken });
8545
+ const anthropic2 = createDeepSpaceAI(env, "anthropic", { authToken: options.authToken });
8331
8546
  const { text } = await generateText({
8332
8547
  // Stable alias — provider bug-fix snapshots roll forward without code change.
8333
- model: anthropic("claude-haiku-4-5"),
8548
+ model: anthropic2("claude-haiku-4-5"),
8334
8549
  // 2500 gives the model room to finish a sentence after hitting the
8335
8550
  // "under 2000 tokens" soft limit in the prompt.
8336
8551
  maxOutputTokens: 2500,
8337
- system: SUMMARIZER_SYSTEM_PROMPT,
8552
+ instructions: SUMMARIZER_SYSTEM_PROMPT,
8338
8553
  messages: [{ role: "user", content: formatTranscript(messages) }]
8339
8554
  });
8340
8555
  return text;
@@ -8822,6 +9037,9 @@ function splitStatements(sql) {
8822
9037
  // src/documentation/worker/routes.ts
8823
9038
  import { Hono } from "hono";
8824
9039
 
9040
+ // src/documentation/worker/assistant-routes.ts
9041
+ import { createUIMessageStreamResponse, toUIMessageStream } from "ai";
9042
+
8825
9043
  // src/documentation/resource-path.ts
8826
9044
  function canonicalNativeDocumentationRequestPath(pathname) {
8827
9045
  try {
@@ -9420,7 +9638,7 @@ function registerDocumentationAssistantRoutes(app, resolveAuth, publicBasePath =
9420
9638
  profile: "documentation",
9421
9639
  modelId: selectedModel.modelId,
9422
9640
  ...authToken ? { authToken } : {},
9423
- system: buildDocumentationAssistantPrompt(manifest.name || c.env.APP_NAME, route),
9641
+ instructions: buildDocumentationAssistantPrompt(manifest.name || c.env.APP_NAME, route),
9424
9642
  messages: [...history, { role: "user", content: question }],
9425
9643
  tools: buildDocumentationAssistantTools(corpus, publicBasePath),
9426
9644
  abortSignal: c.req.raw.signal,
@@ -9436,14 +9654,17 @@ function registerDocumentationAssistantRoutes(app, resolveAuth, publicBasePath =
9436
9654
  );
9437
9655
  return c.json({ error: "Documentation assistant is not configured" }, 503);
9438
9656
  }
9439
- return result.toUIMessageStreamResponse({
9440
- sendReasoning: false,
9441
- onError: (error) => {
9442
- console.error(
9443
- `[documentation-assistant] response error: ${deepSpaceAgentErrorSummary(error, diagnosticContext)}`
9444
- );
9445
- return "The documentation assistant could not complete this response.";
9446
- }
9657
+ return createUIMessageStreamResponse({
9658
+ stream: toUIMessageStream({
9659
+ stream: result.stream,
9660
+ sendReasoning: false,
9661
+ onError: (error) => {
9662
+ console.error(
9663
+ `[documentation-assistant] response error: ${deepSpaceAgentErrorSummary(error, diagnosticContext)}`
9664
+ );
9665
+ return "The documentation assistant could not complete this response.";
9666
+ }
9667
+ })
9447
9668
  });
9448
9669
  });
9449
9670
  }
@@ -10258,6 +10479,7 @@ function isTestAccountTier(tier) {
10258
10479
 
10259
10480
  // src/server/auth/betterAuth.ts
10260
10481
  import { betterAuth } from "better-auth";
10482
+ import { getOAuthState } from "better-auth/api";
10261
10483
  import { organization, twoFactor } from "better-auth/plugins";
10262
10484
  import { SignJWT, importPKCS8 } from "jose";
10263
10485
  function createDeepSpaceAuth(config) {
@@ -10301,6 +10523,7 @@ function createDeepSpaceAuth(config) {
10301
10523
  emailAndPassword: {
10302
10524
  enabled: config.emailAndPassword ?? true
10303
10525
  },
10526
+ ...config.userAdditionalFields ? { user: { additionalFields: config.userAdditionalFields } } : {},
10304
10527
  socialProviders,
10305
10528
  trustedOrigins: config.trustedOrigins ?? [
10306
10529
  "https://deep.space",
@@ -10336,29 +10559,45 @@ function createDeepSpaceAuth(config) {
10336
10559
  // keys) — that case is caught by the runtime assertions in
10337
10560
  // __tests__/betterAuth-hooks.test.ts, which read the wired hook back
10338
10561
  // off auth.options. Don't rename the key without running them.
10339
- databaseHooks: buildDatabaseHooks(config.onUserCreated),
10562
+ databaseHooks: buildDatabaseHooks(config.beforeUserCreate, config.onUserCreated),
10340
10563
  plugins: [organization(), twoFactor()]
10341
10564
  });
10342
10565
  }
10343
- function buildDatabaseHooks(onUserCreated) {
10344
- if (!onUserCreated) return void 0;
10566
+ function buildDatabaseHooks(beforeUserCreate, onUserCreated) {
10567
+ if (!beforeUserCreate && !onUserCreated) return void 0;
10345
10568
  return {
10346
10569
  user: {
10347
10570
  create: {
10348
- after: async (user, ctx) => {
10349
- try {
10350
- await onUserCreated(
10351
- { id: user.id, email: user.email, name: user.name },
10352
- // Forward BOTH request and standalone headers — some better-auth
10353
- // entry points populate ctx.headers without a request.
10354
- ctx ? { request: ctx.request, headers: ctx.headers ?? void 0 } : null
10355
- );
10356
- } catch (err) {
10357
- console.error(
10358
- `[deepspace] onUserCreated hook failed (signup unaffected): ${loggableError(err)}`
10359
- );
10571
+ ...beforeUserCreate ? {
10572
+ before: async (user, ctx) => beforeUserCreate(
10573
+ user,
10574
+ ctx ? {
10575
+ request: ctx.request,
10576
+ headers: ctx.headers ?? void 0,
10577
+ path: ctx.path,
10578
+ // Read from this package's Better Auth instance. In a
10579
+ // pnpm graph the consuming worker can have another
10580
+ // physical copy whose request-state singleton differs.
10581
+ oauthState: await getOAuthState()
10582
+ } : null
10583
+ )
10584
+ } : {},
10585
+ ...onUserCreated ? {
10586
+ after: async (user, ctx) => {
10587
+ try {
10588
+ await onUserCreated(
10589
+ { id: user.id, email: user.email, name: user.name },
10590
+ // Forward BOTH request and standalone headers — some better-auth
10591
+ // entry points populate ctx.headers without a request.
10592
+ ctx ? { request: ctx.request, headers: ctx.headers ?? void 0 } : null
10593
+ );
10594
+ } catch (err) {
10595
+ console.error(
10596
+ `[deepspace] onUserCreated hook failed (signup unaffected): ${loggableError(err)}`
10597
+ );
10598
+ }
10360
10599
  }
10361
- }
10600
+ } : {}
10362
10601
  }
10363
10602
  }
10364
10603
  };
@@ -10592,7 +10831,8 @@ function registerAgentToolRoutes(app, options) {
10592
10831
  output = await tool3.execute(input, {
10593
10832
  toolCallId: crypto.randomUUID(),
10594
10833
  messages: [],
10595
- abortSignal: c.req.raw.signal
10834
+ abortSignal: c.req.raw.signal,
10835
+ context: void 0
10596
10836
  });
10597
10837
  } catch {
10598
10838
  return errorResponse(500, "tool_execution_failed");
@@ -10651,6 +10891,184 @@ function normalizeAgentTargetOrigin(value, policy) {
10651
10891
  return !label.includes(".") && validateAppName(label).valid ? url.origin : null;
10652
10892
  }
10653
10893
 
10894
+ // src/server/expo-auth.ts
10895
+ var CODE_CHALLENGE_RE = /^[A-Za-z0-9_-]{43}$/;
10896
+ var CODE_VERIFIER_RE = /^[A-Za-z0-9._~-]{43,128}$/;
10897
+ function allowedRedirect(value, options = {}) {
10898
+ try {
10899
+ new URL(value);
10900
+ return options.allowedRedirectUris?.includes(value) ?? false;
10901
+ } catch {
10902
+ return false;
10903
+ }
10904
+ }
10905
+ function validState(value) {
10906
+ return value === null || value.length >= 1 && value.length <= 1024;
10907
+ }
10908
+ function validPkce(challenge, method, required = false) {
10909
+ if (!challenge && !method) return !required;
10910
+ return method === "S256" && !!challenge && CODE_CHALLENGE_RE.test(challenge);
10911
+ }
10912
+ function authWorkerUnavailable() {
10913
+ return Response.json({ error: "The authentication service is not configured." }, { status: 503 });
10914
+ }
10915
+ function nativeAuthStart(request2, env, options) {
10916
+ const url = new URL(request2.url);
10917
+ const provider = url.searchParams.get("provider");
10918
+ const redirectUri = url.searchParams.get("redirect_uri");
10919
+ const state = url.searchParams.get("state");
10920
+ const codeChallenge = url.searchParams.get("code_challenge");
10921
+ const codeChallengeMethod = url.searchParams.get("code_challenge_method");
10922
+ if (!provider || !redirectUri || !validState(state) || !allowedRedirect(redirectUri, options) || !validPkce(codeChallenge, codeChallengeMethod, true)) {
10923
+ return Response.json({ error: "A supported provider, PKCE challenge, and registered redirect URI are required." }, { status: 400 });
10924
+ }
10925
+ if (!env.AUTH_WORKER_URL) return authWorkerUnavailable();
10926
+ const callback = new URL("/api/auth/oauth-complete", url.origin);
10927
+ callback.searchParams.set("redirect_uri", redirectUri);
10928
+ if (state) callback.searchParams.set("state", state);
10929
+ if (codeChallenge) {
10930
+ callback.searchParams.set("code_challenge", codeChallenge);
10931
+ callback.searchParams.set("code_challenge_method", "S256");
10932
+ }
10933
+ const authUrl = new URL("/login/social", env.AUTH_WORKER_URL);
10934
+ authUrl.searchParams.set("provider", provider);
10935
+ authUrl.searchParams.set("returnTo", callback.toString());
10936
+ return Response.redirect(authUrl.toString(), 302);
10937
+ }
10938
+ function nativeAuthCallback(request2, _env, options) {
10939
+ const url = new URL(request2.url);
10940
+ const redirectUri = url.searchParams.get("redirect_uri");
10941
+ const code = url.searchParams.get("code");
10942
+ const state = url.searchParams.get("state");
10943
+ const codeChallenge = url.searchParams.get("code_challenge");
10944
+ const codeChallengeMethod = url.searchParams.get("code_challenge_method");
10945
+ if (!redirectUri || !code || code.length > 4096 || !validState(state) || !validPkce(codeChallenge, codeChallengeMethod, true) || !allowedRedirect(redirectUri, options)) {
10946
+ return Response.json({ error: "The sign-in callback was incomplete." }, { status: 400 });
10947
+ }
10948
+ const destination = new URL(redirectUri);
10949
+ destination.searchParams.set("code", code);
10950
+ if (state) destination.searchParams.set("state", state);
10951
+ return Response.redirect(destination.toString(), 302);
10952
+ }
10953
+ async function nativeAuthExchange(request2, env) {
10954
+ const body = await request2.json().catch(() => null);
10955
+ if (!body || typeof body.code !== "string" || body.code.length < 8 || body.code.length > 4096 || typeof body.code_verifier !== "string" || !CODE_VERIFIER_RE.test(body.code_verifier)) {
10956
+ return Response.json({ error: "A valid one-time sign-in code and PKCE verifier are required." }, { status: 400 });
10957
+ }
10958
+ let exchange2;
10959
+ try {
10960
+ exchange2 = await authWorkerFetch(env, "/api/auth/exchange-code", {
10961
+ method: "POST",
10962
+ headers: { "Content-Type": "application/json" },
10963
+ body: JSON.stringify({ code: body.code, code_verifier: body.code_verifier })
10964
+ });
10965
+ } catch {
10966
+ return Response.json({ error: "The authentication service is unavailable." }, { status: 502 });
10967
+ }
10968
+ if (!exchange2.ok) {
10969
+ const definitiveFailure = exchange2.status === 400 || exchange2.status === 401;
10970
+ return Response.json(
10971
+ { error: definitiveFailure ? "The sign-in code expired. Please try again." : "The authentication service is unavailable." },
10972
+ { status: definitiveFailure ? 401 : 502 }
10973
+ );
10974
+ }
10975
+ const exchanged = await exchange2.json().catch(() => null);
10976
+ if (!exchanged || typeof exchanged.sessionToken !== "string") {
10977
+ return Response.json({ error: "The auth service did not issue a session." }, { status: 502 });
10978
+ }
10979
+ let tokenResponse = null;
10980
+ let token = null;
10981
+ for (let attempt = 0; attempt < 2; attempt += 1) {
10982
+ try {
10983
+ tokenResponse = await authWorkerFetch(env, "/api/auth/token", {
10984
+ method: "POST",
10985
+ headers: { Cookie: `${SESSION_COOKIE}=${encodeURIComponent(exchanged.sessionToken)}` }
10986
+ });
10987
+ } catch {
10988
+ tokenResponse = null;
10989
+ }
10990
+ if (tokenResponse?.ok) {
10991
+ token = await tokenResponse.json().catch(() => null);
10992
+ if (token && typeof token.token === "string") break;
10993
+ }
10994
+ if (tokenResponse?.status === 401) break;
10995
+ }
10996
+ if (!tokenResponse) {
10997
+ return Response.json({ error: "The authentication service is unavailable." }, { status: 502 });
10998
+ }
10999
+ if (!tokenResponse.ok) {
11000
+ return Response.json(
11001
+ tokenResponse.status === 401 ? { error: "The sign-in session expired. Please try again." } : { error: "The authentication service is unavailable." },
11002
+ { status: tokenResponse.status === 401 ? 401 : 502 }
11003
+ );
11004
+ }
11005
+ if (!token || typeof token.token !== "string") {
11006
+ return Response.json({ error: "The auth service did not issue an access token." }, { status: 502 });
11007
+ }
11008
+ return Response.json(
11009
+ { sessionToken: exchanged.sessionToken, accessToken: token.token },
11010
+ { headers: { "Cache-Control": "no-store" } }
11011
+ );
11012
+ }
11013
+ async function nativeAuthToken(request2, env) {
11014
+ const input = await request2.json().catch(() => null);
11015
+ if (!input || typeof input.sessionToken !== "string" || input.sessionToken.length < 16 || input.sessionToken.length > 4096) {
11016
+ return Response.json({ error: "A valid DeepSpace session is required." }, { status: 401 });
11017
+ }
11018
+ let response;
11019
+ try {
11020
+ response = await authWorkerFetch(env, "/api/auth/token", {
11021
+ method: "POST",
11022
+ headers: { Cookie: `${SESSION_COOKIE}=${encodeURIComponent(input.sessionToken)}` }
11023
+ });
11024
+ } catch {
11025
+ return Response.json({ error: "The authentication service is unavailable." }, { status: 502 });
11026
+ }
11027
+ if (!response.ok) {
11028
+ return Response.json(
11029
+ { error: response.status === 401 ? "The DeepSpace session expired." : "The authentication service is unavailable." },
11030
+ { status: response.status === 401 ? 401 : 502 }
11031
+ );
11032
+ }
11033
+ const body = await response.json().catch(() => null);
11034
+ if (!body || typeof body.token !== "string") {
11035
+ return Response.json({ error: "The auth service did not issue an access token." }, { status: 502 });
11036
+ }
11037
+ return Response.json(
11038
+ { accessToken: body.token },
11039
+ { headers: { "Cache-Control": "no-store" } }
11040
+ );
11041
+ }
11042
+ async function nativeAuthMe(request2, env) {
11043
+ if (!env.AUTH_JWT_PUBLIC_KEY || !env.AUTH_JWT_ISSUER) {
11044
+ return Response.json({ error: "JWT verification is not configured." }, { status: 500 });
11045
+ }
11046
+ const header = request2.headers.get("Authorization");
11047
+ const token = header?.startsWith("Bearer ") ? header.slice(7) : null;
11048
+ const verified = await verifyJwt(
11049
+ { publicKey: env.AUTH_JWT_PUBLIC_KEY, issuer: env.AUTH_JWT_ISSUER },
11050
+ token
11051
+ );
11052
+ if (!verified.result) return Response.json({ error: "Sign in required." }, { status: 401 });
11053
+ return Response.json(
11054
+ { userId: verified.result.userId, claims: verified.result.claims },
11055
+ { headers: { "Cache-Control": "no-store" } }
11056
+ );
11057
+ }
11058
+ async function nativeAuthSignOut(request2, env) {
11059
+ const body = await request2.json().catch(() => null);
11060
+ if (body && typeof body.sessionToken === "string" && body.sessionToken.length >= 16 && body.sessionToken.length <= 4096) {
11061
+ try {
11062
+ await authWorkerFetch(env, "/api/auth/sign-out", {
11063
+ method: "POST",
11064
+ headers: { Cookie: `${SESSION_COOKIE}=${encodeURIComponent(body.sessionToken)}` }
11065
+ });
11066
+ } catch {
11067
+ }
11068
+ }
11069
+ return Response.json({ ok: true });
11070
+ }
11071
+
10654
11072
  // src/shared/api-error.ts
10655
11073
  var SLUG_RE = /^[a-z0-9]+(?:[_.-][a-z0-9]+)*$/;
10656
11074
  var HUMAN_MESSAGES = {
@@ -10716,6 +11134,7 @@ export {
10716
11134
  AGENT_TOOL_RESPONSE_BODY_CAP,
10717
11135
  AI_CHATS_SCHEMA,
10718
11136
  AI_MESSAGES_SCHEMA,
11137
+ AI_SDK_7_MIGRATION_ID,
10719
11138
  ALLOWED_BINDING_TYPES,
10720
11139
  ANONYMOUS_USER_ID_PREFIX,
10721
11140
  APP_ID_RE,
@@ -10768,6 +11187,7 @@ export {
10768
11187
  MSG_SYNC_UPDATE,
10769
11188
  MSG_YJS_AWARENESS,
10770
11189
  MSG_YJS_SYNC,
11190
+ PLANES,
10771
11191
  PRODUCTION_OFFICIAL_AGENT_ORIGINS,
10772
11192
  PresenceRoom,
10773
11193
  REACTIONS_SCHEMA,
@@ -10785,6 +11205,8 @@ export {
10785
11205
  ROLE_DEFAULT,
10786
11206
  RecordRoom,
10787
11207
  RegistryClientError,
11208
+ SANDBOX_SCOPES,
11209
+ SANDBOX_SCOPE_HEADER,
10788
11210
  SDK_RUN_WORKER_FIRST,
10789
11211
  SECRET_NAME_RE,
10790
11212
  SECURE_ROOM_BOUNDARIES_MIGRATION_ID,
@@ -10794,6 +11216,7 @@ export {
10794
11216
  SYSTEM_COLLECTIONS,
10795
11217
  SYSTEM_MAINTAINED_COLUMNS,
10796
11218
  SYSTEM_MANAGED_COLUMNS,
11219
+ SandboxFileError,
10797
11220
  SchemaRegistry,
10798
11221
  TEST_ACCOUNT_EMAIL_SUFFIX,
10799
11222
  UNDECLARED_ASSET_NOT_FOUND_HANDLING,
@@ -10814,6 +11237,7 @@ export {
10814
11237
  buildTableSelect,
10815
11238
  buildUiParts,
10816
11239
  callRegistry,
11240
+ callableFromSandbox,
10817
11241
  canCreate,
10818
11242
  canDelete,
10819
11243
  canRead,
@@ -10823,6 +11247,7 @@ export {
10823
11247
  checkFieldPermissions,
10824
11248
  checkUnclaimedOwnerTransition,
10825
11249
  clientBuild,
11250
+ codeExecutionTool,
10826
11251
  coerceValue,
10827
11252
  collectionTableName,
10828
11253
  columnId,
@@ -10850,6 +11275,7 @@ export {
10850
11275
  epochSeconds,
10851
11276
  folderFilter,
10852
11277
  formatBytes,
11278
+ forwardSandboxContainer,
10853
11279
  getChat,
10854
11280
  getDeepSpaceAIModel,
10855
11281
  getMessageType,
@@ -10863,6 +11289,7 @@ export {
10863
11289
  isFilesVerbPath,
10864
11290
  isOwner,
10865
11291
  isPlatformReservedPath,
11292
+ isRestrictedPlane,
10866
11293
  isTestAccountClaims,
10867
11294
  isTestAccountEmail,
10868
11295
  isTestAccountTier,
@@ -10881,6 +11308,12 @@ export {
10881
11308
  meterVectorize,
10882
11309
  mintAppId,
10883
11310
  mintUlid,
11311
+ nativeAuthCallback,
11312
+ nativeAuthExchange,
11313
+ nativeAuthMe,
11314
+ nativeAuthSignOut,
11315
+ nativeAuthStart,
11316
+ nativeAuthToken,
10884
11317
  noopPermissionContext,
10885
11318
  normalizeAgentTargetOrigin,
10886
11319
  normalizeApiError,
@@ -10904,8 +11337,12 @@ export {
10904
11337
  resolveCompatibilityFlags,
10905
11338
  resolveDeepSpaceAgentModel,
10906
11339
  resolveSessionReadAuth,
11340
+ reuseSandbox,
10907
11341
  rowToData,
10908
11342
  runMigrations,
11343
+ sandboxFiles,
11344
+ sandboxOutputs,
11345
+ sandboxUpload,
10909
11346
  serverBuild,
10910
11347
  storageLimitForTier,
10911
11348
  storageQuotaMessage,