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.
- package/CHANGELOG.md +46 -0
- package/README.md +5 -1
- package/dist/{chunk-PZDVKSLI.js → chunk-ESEI3MMY.js} +2 -2
- package/dist/{chunk-PZDVKSLI.js.map → chunk-ESEI3MMY.js.map} +1 -1
- package/dist/chunk-ESYXTIV2.js +1957 -0
- package/dist/chunk-ESYXTIV2.js.map +1 -0
- package/dist/cli.js +151 -53
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +10 -10
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +47 -22
- package/dist/documentation.js.map +1 -1
- package/dist/expo.d.ts +83 -0
- package/dist/expo.js +242 -0
- package/dist/expo.js.map +1 -0
- package/dist/index.d.ts +110 -23
- package/dist/index.js +134 -53
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +205 -37
- package/dist/server.js +290 -97
- package/dist/server.js.map +1 -1
- package/dist/testing.js +45 -3
- package/dist/testing.js.map +1 -1
- package/dist/worker.d.ts +348 -48
- package/dist/worker.js +547 -110
- package/dist/worker.js.map +1 -1
- package/features/documents/feature.json +15 -0
- package/features/documents/src/DocumentsListPage.tsx +22 -8
- package/features/documents/src/InviteDialog.tsx +63 -42
- package/features/documents/src/__tests__/documents-find-invitee-action.test.ts +131 -0
- package/features/documents/src/__tests__/documents-invitee-lookup.test.ts +67 -0
- package/features/documents/src/documents-find-invitee-action.ts +56 -0
- package/features/documents/src/documents-invitee-lookup.ts +38 -0
- package/features/documents/src/documents-ui.css +10 -0
- 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 =
|
|
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
|
-
|
|
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
|
|
2479
|
+
function handleUserUpdate(ctx, _ws, attachment) {
|
|
2479
2480
|
const existing = getUserRecord(ctx.sql, attachment.userId, ctx.schemaRegistry);
|
|
2480
2481
|
if (!existing) return;
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
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
|
-
|
|
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
|
|
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-
|
|
5826
|
-
verifiedAt: "2026-
|
|
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-
|
|
5855
|
-
label: "Claude
|
|
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-
|
|
5861
|
-
label: "Claude
|
|
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-
|
|
5879
|
-
label: "GPT-
|
|
5880
|
-
family: "GPT-
|
|
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-
|
|
5891
|
-
label: "GPT-
|
|
5892
|
-
family: "GPT-
|
|
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:
|
|
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-
|
|
5910
|
-
directGeneration: "claude-
|
|
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
|
-
|
|
7668
|
-
|
|
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
|
|
7673
|
-
|
|
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
|
|
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
|
|
7696
|
-
const body = provider === "
|
|
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
|
-
|
|
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,
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
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
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
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,
|