deepspace 0.33.1 → 0.34.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 +35 -0
- package/README.md +5 -1
- package/dist/build.js.map +1 -1
- package/dist/chunk-KELQ7OD5.js +1957 -0
- package/dist/chunk-KELQ7OD5.js.map +1 -0
- package/dist/{chunk-PZDVKSLI.js → chunk-SINBB5BQ.js} +1 -1
- package/dist/{chunk-PZDVKSLI.js.map → chunk-SINBB5BQ.js.map} +1 -1
- package/dist/cli.js +74 -15
- 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 +73 -0
- package/dist/expo.js +233 -0
- package/dist/expo.js.map +1 -0
- package/dist/index.d.ts +105 -22
- package/dist/index.js +50 -23
- 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/worker.d.ts +312 -48
- package/dist/worker.js +499 -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 +28 -8
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,10 @@ 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
|
+
|
|
6019
6062
|
// src/server/utils/cors.ts
|
|
6020
6063
|
function matchOrigin(origin, patterns) {
|
|
6021
6064
|
if (!origin) return null;
|
|
@@ -7664,36 +7707,57 @@ async function resolveAppRole(env, userId) {
|
|
|
7664
7707
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
7665
7708
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
7666
7709
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
7667
|
-
|
|
7668
|
-
|
|
7710
|
+
|
|
7711
|
+
// src/server/utils/app-identity.ts
|
|
7712
|
+
function appendAppIdentity(headers, env) {
|
|
7713
|
+
headers.delete("x-app-identity-token");
|
|
7714
|
+
headers.delete("x-app-id");
|
|
7715
|
+
if (!env.APP_IDENTITY_TOKEN) return;
|
|
7716
|
+
headers.set("x-app-identity-token", env.APP_IDENTITY_TOKEN);
|
|
7717
|
+
headers.set("x-app-id", env.DEEPSPACE_APP_ID);
|
|
7718
|
+
}
|
|
7719
|
+
|
|
7720
|
+
// src/server/utils/ai.ts
|
|
7721
|
+
import { defaultSettingsMiddleware, wrapLanguageModel } from "ai";
|
|
7722
|
+
function resolveProxyAuthToken(env, options) {
|
|
7723
|
+
const authToken = options.authToken ?? env.APP_OWNER_JWT;
|
|
7724
|
+
if (!authToken) {
|
|
7725
|
+
throw new Error(
|
|
7726
|
+
"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`)."
|
|
7727
|
+
);
|
|
7728
|
+
}
|
|
7729
|
+
return authToken;
|
|
7730
|
+
}
|
|
7731
|
+
function setProxyHeaders(headers, env, authToken, scope = "user") {
|
|
7732
|
+
headers.set("X-Auth-Token", authToken);
|
|
7733
|
+
appendAppIdentity(headers, {
|
|
7734
|
+
DEEPSPACE_APP_ID: env.DEEPSPACE_APP_ID ?? "",
|
|
7735
|
+
APP_IDENTITY_TOKEN: env.APP_IDENTITY_TOKEN
|
|
7736
|
+
});
|
|
7737
|
+
headers.set(SANDBOX_SCOPE_HEADER, scope);
|
|
7738
|
+
}
|
|
7739
|
+
var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 64e3;
|
|
7740
|
+
var OPENAI_TOOLS_NEED_NO_REASONING = /^gpt-(5\.6|6)-(sol|terra|luna)(-|$)/;
|
|
7741
|
+
function withOpenAIToolReasoningEffort(body) {
|
|
7669
7742
|
if (typeof body !== "string") return body;
|
|
7670
7743
|
try {
|
|
7671
7744
|
const parsed = JSON.parse(body);
|
|
7672
|
-
const
|
|
7673
|
-
|
|
7674
|
-
return JSON.stringify({
|
|
7675
|
-
...parsed,
|
|
7676
|
-
max_tokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
|
|
7677
|
-
});
|
|
7745
|
+
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;
|
|
7746
|
+
return needsEffort ? JSON.stringify({ ...parsed, reasoning_effort: "none" }) : body;
|
|
7678
7747
|
} catch {
|
|
7679
7748
|
return body;
|
|
7680
7749
|
}
|
|
7681
7750
|
}
|
|
7682
7751
|
function createDeepSpaceAI(env, provider, options = {}) {
|
|
7683
7752
|
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
|
-
}
|
|
7753
|
+
const authToken = resolveProxyAuthToken(env, options);
|
|
7690
7754
|
const proxyFetch = (input, init) => {
|
|
7691
7755
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
7692
7756
|
const headers = new Headers(init?.headers);
|
|
7693
7757
|
headers.delete("authorization");
|
|
7694
7758
|
headers.delete("x-api-key");
|
|
7695
|
-
headers
|
|
7696
|
-
const body = provider === "
|
|
7759
|
+
setProxyHeaders(headers, env, authToken, options.sandboxScope);
|
|
7760
|
+
const body = provider === "openai" ? withOpenAIToolReasoningEffort(init?.body) : init?.body;
|
|
7697
7761
|
if (body !== init?.body) {
|
|
7698
7762
|
headers.delete("content-length");
|
|
7699
7763
|
}
|
|
@@ -7707,8 +7771,13 @@ function createDeepSpaceAI(env, provider, options = {}) {
|
|
|
7707
7771
|
};
|
|
7708
7772
|
const baseURL = `https://api-worker.internal/api/proxy/${provider}/v1`;
|
|
7709
7773
|
switch (provider) {
|
|
7710
|
-
case "anthropic":
|
|
7711
|
-
|
|
7774
|
+
case "anthropic": {
|
|
7775
|
+
const anthropic2 = createAnthropic({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
|
|
7776
|
+
const defaults = defaultSettingsMiddleware({
|
|
7777
|
+
settings: { maxOutputTokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS }
|
|
7778
|
+
});
|
|
7779
|
+
return (modelId) => wrapLanguageModel({ model: anthropic2(modelId), middleware: defaults });
|
|
7780
|
+
}
|
|
7712
7781
|
case "openai": {
|
|
7713
7782
|
const op = createOpenAI({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
|
|
7714
7783
|
return op.chat;
|
|
@@ -7729,10 +7798,7 @@ function createDeepSpaceAI(env, provider, options = {}) {
|
|
|
7729
7798
|
}
|
|
7730
7799
|
|
|
7731
7800
|
// src/server/utils/agent.ts
|
|
7732
|
-
import {
|
|
7733
|
-
stepCountIs,
|
|
7734
|
-
streamText
|
|
7735
|
-
} from "ai";
|
|
7801
|
+
import { isStepCount, streamText } from "ai";
|
|
7736
7802
|
var DeepSpaceAgentModelError = class extends Error {
|
|
7737
7803
|
constructor(modelId, profile) {
|
|
7738
7804
|
super(
|
|
@@ -7754,7 +7820,7 @@ var DeepSpaceAgentProfileError = class extends Error {
|
|
|
7754
7820
|
code = "agent_profile_violation";
|
|
7755
7821
|
};
|
|
7756
7822
|
function streamDeepSpaceAgent(env, options) {
|
|
7757
|
-
const { profile, modelId, authToken, ...streamOptions } = options;
|
|
7823
|
+
const { profile, modelId, authToken, sandboxScope, ...streamOptions } = options;
|
|
7758
7824
|
const selection = resolveDeepSpaceAgentModel(modelId, profile);
|
|
7759
7825
|
if (!selection) throw new DeepSpaceAgentModelError(modelId, profile);
|
|
7760
7826
|
if (selection.profile.allowedTools !== "application-defined") {
|
|
@@ -7765,14 +7831,17 @@ function streamDeepSpaceAgent(env, options) {
|
|
|
7765
7831
|
}
|
|
7766
7832
|
}
|
|
7767
7833
|
}
|
|
7768
|
-
const provider = createDeepSpaceAI(env, selection.provider,
|
|
7834
|
+
const provider = createDeepSpaceAI(env, selection.provider, {
|
|
7835
|
+
...authToken ? { authToken } : {},
|
|
7836
|
+
...sandboxScope ? { sandboxScope } : {}
|
|
7837
|
+
});
|
|
7769
7838
|
const { prepareStep, tools, ...remainingStreamOptions } = streamOptions;
|
|
7770
7839
|
const boundedTools = limitToolExecutions(tools, selection.profile.maxToolCalls);
|
|
7771
7840
|
const result = streamText({
|
|
7772
7841
|
...remainingStreamOptions,
|
|
7773
7842
|
...boundedTools ? { tools: boundedTools } : {},
|
|
7774
7843
|
model: provider(selection.modelId),
|
|
7775
|
-
stopWhen:
|
|
7844
|
+
stopWhen: isStepCount(selection.profile.maxSteps),
|
|
7776
7845
|
prepareStep: enforceToolCallLimit(prepareStep, selection.profile.maxToolCalls),
|
|
7777
7846
|
...selection.provider === "openai" ? { providerOptions: { openai: { reasoningEffort: "none" } } } : {}
|
|
7778
7847
|
});
|
|
@@ -7884,6 +7953,115 @@ function safeLogText(value) {
|
|
|
7884
7953
|
).trim().slice(0, 300);
|
|
7885
7954
|
}
|
|
7886
7955
|
|
|
7956
|
+
// src/server/utils/sandbox.ts
|
|
7957
|
+
import { anthropic, forwardAnthropicContainerIdFromLastStep } from "@ai-sdk/anthropic";
|
|
7958
|
+
var FILES_PATH = "/api/proxy/anthropic/v1/files";
|
|
7959
|
+
var CODE_EXECUTION_CALLER = "code_execution_20260120";
|
|
7960
|
+
function codeExecutionTool() {
|
|
7961
|
+
return anthropic.tools.codeExecution_20260120();
|
|
7962
|
+
}
|
|
7963
|
+
function callableFromSandbox(tool3) {
|
|
7964
|
+
const anthropicOptions = tool3.providerOptions?.anthropic;
|
|
7965
|
+
return {
|
|
7966
|
+
...tool3,
|
|
7967
|
+
providerOptions: {
|
|
7968
|
+
...tool3.providerOptions,
|
|
7969
|
+
anthropic: { ...anthropicOptions, allowedCallers: ["direct", CODE_EXECUTION_CALLER] }
|
|
7970
|
+
}
|
|
7971
|
+
};
|
|
7972
|
+
}
|
|
7973
|
+
var forwardSandboxContainer = forwardAnthropicContainerIdFromLastStep;
|
|
7974
|
+
function reuseSandbox(containerId) {
|
|
7975
|
+
return containerId ? { anthropic: { container: { id: containerId } } } : void 0;
|
|
7976
|
+
}
|
|
7977
|
+
function sandboxUpload(file) {
|
|
7978
|
+
return {
|
|
7979
|
+
type: "file",
|
|
7980
|
+
mediaType: file.mime_type,
|
|
7981
|
+
filename: file.filename,
|
|
7982
|
+
data: { type: "reference", reference: { anthropic: file.id } },
|
|
7983
|
+
providerOptions: { anthropic: { containerUpload: true } }
|
|
7984
|
+
};
|
|
7985
|
+
}
|
|
7986
|
+
function collectFileIds(value, out) {
|
|
7987
|
+
if (Array.isArray(value)) {
|
|
7988
|
+
for (const item of value) collectFileIds(item, out);
|
|
7989
|
+
return;
|
|
7990
|
+
}
|
|
7991
|
+
if (!value || typeof value !== "object") return;
|
|
7992
|
+
for (const [key, child] of Object.entries(value)) {
|
|
7993
|
+
if (key === "file_id" && typeof child === "string" && child) out.add(child);
|
|
7994
|
+
else collectFileIds(child, out);
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
function sandboxOutputs(steps) {
|
|
7998
|
+
let containerId = null;
|
|
7999
|
+
const fileIds = /* @__PURE__ */ new Set();
|
|
8000
|
+
for (const step of steps) {
|
|
8001
|
+
const container = step.providerMetadata?.anthropic?.container;
|
|
8002
|
+
if (typeof container?.id === "string") containerId = container.id;
|
|
8003
|
+
for (const part of step.content) {
|
|
8004
|
+
const result = part;
|
|
8005
|
+
if (result.type === "tool-result" && result.providerExecuted) collectFileIds(result.output, fileIds);
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
return { containerId, fileIds: [...fileIds] };
|
|
8009
|
+
}
|
|
8010
|
+
var SandboxFileError = class extends Error {
|
|
8011
|
+
constructor(status, message) {
|
|
8012
|
+
super(message);
|
|
8013
|
+
this.status = status;
|
|
8014
|
+
this.name = "SandboxFileError";
|
|
8015
|
+
}
|
|
8016
|
+
};
|
|
8017
|
+
function sandboxFiles(env, options = {}) {
|
|
8018
|
+
const authToken = resolveProxyAuthToken(env, options);
|
|
8019
|
+
async function send(path, init = {}) {
|
|
8020
|
+
const headers = new Headers(init.headers);
|
|
8021
|
+
setProxyHeaders(headers, env, authToken, options.scope);
|
|
8022
|
+
const response = await apiWorkerFetch(env, `${FILES_PATH}${path}`, { ...init, headers });
|
|
8023
|
+
if (!response.ok) {
|
|
8024
|
+
const text = await response.text();
|
|
8025
|
+
let message = text || response.statusText;
|
|
8026
|
+
try {
|
|
8027
|
+
const body = JSON.parse(text);
|
|
8028
|
+
if (typeof body.error === "string") message = body.error;
|
|
8029
|
+
else if (body.error && typeof body.error === "object") message = JSON.stringify(body.error);
|
|
8030
|
+
} catch {
|
|
8031
|
+
}
|
|
8032
|
+
throw new SandboxFileError(response.status, `Sandbox file request failed (${response.status}): ${message}`);
|
|
8033
|
+
}
|
|
8034
|
+
return response;
|
|
8035
|
+
}
|
|
8036
|
+
const fileUrl = (fileId) => `/${encodeURIComponent(fileId)}`;
|
|
8037
|
+
return {
|
|
8038
|
+
async upload(file, { filename, expiresInSeconds } = {}) {
|
|
8039
|
+
const name = filename ?? (file instanceof File ? file.name : void 0);
|
|
8040
|
+
if (!name) throw new SandboxFileError(400, "sandboxFiles.upload: a Blob needs options.filename");
|
|
8041
|
+
const form = new FormData();
|
|
8042
|
+
form.set("file", file, name);
|
|
8043
|
+
if (expiresInSeconds !== void 0) form.set("expires_in_seconds", String(expiresInSeconds));
|
|
8044
|
+
return (await send("", { method: "POST", body: form })).json();
|
|
8045
|
+
},
|
|
8046
|
+
async get(fileId) {
|
|
8047
|
+
return (await send(fileUrl(fileId))).json();
|
|
8048
|
+
},
|
|
8049
|
+
download(fileId) {
|
|
8050
|
+
return send(`${fileUrl(fileId)}/content`);
|
|
8051
|
+
},
|
|
8052
|
+
async list({ limit, page } = {}) {
|
|
8053
|
+
const query = new URLSearchParams();
|
|
8054
|
+
if (limit !== void 0) query.set("limit", String(limit));
|
|
8055
|
+
if (page !== void 0) query.set("page", page);
|
|
8056
|
+
const suffix = query.size > 0 ? `?${query}` : "";
|
|
8057
|
+
return (await send(suffix)).json();
|
|
8058
|
+
},
|
|
8059
|
+
async delete(fileId) {
|
|
8060
|
+
await send(fileUrl(fileId), { method: "DELETE" });
|
|
8061
|
+
}
|
|
8062
|
+
};
|
|
8063
|
+
}
|
|
8064
|
+
|
|
7887
8065
|
// src/server/utils/composio-tools.ts
|
|
7888
8066
|
import { tool, jsonSchema } from "ai";
|
|
7889
8067
|
var EMPTY_SCHEMA = { type: "object", properties: {} };
|
|
@@ -7966,15 +8144,6 @@ async function composioTools(env, options) {
|
|
|
7966
8144
|
return tools;
|
|
7967
8145
|
}
|
|
7968
8146
|
|
|
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
8147
|
// src/server/utils/screenshot.ts
|
|
7979
8148
|
async function captureScreenshot(env, url) {
|
|
7980
8149
|
const headers = new Headers({ "content-type": "application/json" });
|
|
@@ -8327,14 +8496,14 @@ function formatTranscript(messages) {
|
|
|
8327
8496
|
}
|
|
8328
8497
|
function makeDefaultSummarizer(env, options = {}) {
|
|
8329
8498
|
return async (messages) => {
|
|
8330
|
-
const
|
|
8499
|
+
const anthropic2 = createDeepSpaceAI(env, "anthropic", { authToken: options.authToken });
|
|
8331
8500
|
const { text } = await generateText({
|
|
8332
8501
|
// Stable alias — provider bug-fix snapshots roll forward without code change.
|
|
8333
|
-
model:
|
|
8502
|
+
model: anthropic2("claude-haiku-4-5"),
|
|
8334
8503
|
// 2500 gives the model room to finish a sentence after hitting the
|
|
8335
8504
|
// "under 2000 tokens" soft limit in the prompt.
|
|
8336
8505
|
maxOutputTokens: 2500,
|
|
8337
|
-
|
|
8506
|
+
instructions: SUMMARIZER_SYSTEM_PROMPT,
|
|
8338
8507
|
messages: [{ role: "user", content: formatTranscript(messages) }]
|
|
8339
8508
|
});
|
|
8340
8509
|
return text;
|
|
@@ -8822,6 +8991,9 @@ function splitStatements(sql) {
|
|
|
8822
8991
|
// src/documentation/worker/routes.ts
|
|
8823
8992
|
import { Hono } from "hono";
|
|
8824
8993
|
|
|
8994
|
+
// src/documentation/worker/assistant-routes.ts
|
|
8995
|
+
import { createUIMessageStreamResponse, toUIMessageStream } from "ai";
|
|
8996
|
+
|
|
8825
8997
|
// src/documentation/resource-path.ts
|
|
8826
8998
|
function canonicalNativeDocumentationRequestPath(pathname) {
|
|
8827
8999
|
try {
|
|
@@ -9420,7 +9592,7 @@ function registerDocumentationAssistantRoutes(app, resolveAuth, publicBasePath =
|
|
|
9420
9592
|
profile: "documentation",
|
|
9421
9593
|
modelId: selectedModel.modelId,
|
|
9422
9594
|
...authToken ? { authToken } : {},
|
|
9423
|
-
|
|
9595
|
+
instructions: buildDocumentationAssistantPrompt(manifest.name || c.env.APP_NAME, route),
|
|
9424
9596
|
messages: [...history, { role: "user", content: question }],
|
|
9425
9597
|
tools: buildDocumentationAssistantTools(corpus, publicBasePath),
|
|
9426
9598
|
abortSignal: c.req.raw.signal,
|
|
@@ -9436,14 +9608,17 @@ function registerDocumentationAssistantRoutes(app, resolveAuth, publicBasePath =
|
|
|
9436
9608
|
);
|
|
9437
9609
|
return c.json({ error: "Documentation assistant is not configured" }, 503);
|
|
9438
9610
|
}
|
|
9439
|
-
return
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9611
|
+
return createUIMessageStreamResponse({
|
|
9612
|
+
stream: toUIMessageStream({
|
|
9613
|
+
stream: result.stream,
|
|
9614
|
+
sendReasoning: false,
|
|
9615
|
+
onError: (error) => {
|
|
9616
|
+
console.error(
|
|
9617
|
+
`[documentation-assistant] response error: ${deepSpaceAgentErrorSummary(error, diagnosticContext)}`
|
|
9618
|
+
);
|
|
9619
|
+
return "The documentation assistant could not complete this response.";
|
|
9620
|
+
}
|
|
9621
|
+
})
|
|
9447
9622
|
});
|
|
9448
9623
|
});
|
|
9449
9624
|
}
|
|
@@ -10258,6 +10433,7 @@ function isTestAccountTier(tier) {
|
|
|
10258
10433
|
|
|
10259
10434
|
// src/server/auth/betterAuth.ts
|
|
10260
10435
|
import { betterAuth } from "better-auth";
|
|
10436
|
+
import { getOAuthState } from "better-auth/api";
|
|
10261
10437
|
import { organization, twoFactor } from "better-auth/plugins";
|
|
10262
10438
|
import { SignJWT, importPKCS8 } from "jose";
|
|
10263
10439
|
function createDeepSpaceAuth(config) {
|
|
@@ -10301,6 +10477,7 @@ function createDeepSpaceAuth(config) {
|
|
|
10301
10477
|
emailAndPassword: {
|
|
10302
10478
|
enabled: config.emailAndPassword ?? true
|
|
10303
10479
|
},
|
|
10480
|
+
...config.userAdditionalFields ? { user: { additionalFields: config.userAdditionalFields } } : {},
|
|
10304
10481
|
socialProviders,
|
|
10305
10482
|
trustedOrigins: config.trustedOrigins ?? [
|
|
10306
10483
|
"https://deep.space",
|
|
@@ -10336,29 +10513,45 @@ function createDeepSpaceAuth(config) {
|
|
|
10336
10513
|
// keys) — that case is caught by the runtime assertions in
|
|
10337
10514
|
// __tests__/betterAuth-hooks.test.ts, which read the wired hook back
|
|
10338
10515
|
// off auth.options. Don't rename the key without running them.
|
|
10339
|
-
databaseHooks: buildDatabaseHooks(config.onUserCreated),
|
|
10516
|
+
databaseHooks: buildDatabaseHooks(config.beforeUserCreate, config.onUserCreated),
|
|
10340
10517
|
plugins: [organization(), twoFactor()]
|
|
10341
10518
|
});
|
|
10342
10519
|
}
|
|
10343
|
-
function buildDatabaseHooks(onUserCreated) {
|
|
10344
|
-
if (!onUserCreated) return void 0;
|
|
10520
|
+
function buildDatabaseHooks(beforeUserCreate, onUserCreated) {
|
|
10521
|
+
if (!beforeUserCreate && !onUserCreated) return void 0;
|
|
10345
10522
|
return {
|
|
10346
10523
|
user: {
|
|
10347
10524
|
create: {
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10525
|
+
...beforeUserCreate ? {
|
|
10526
|
+
before: async (user, ctx) => beforeUserCreate(
|
|
10527
|
+
user,
|
|
10528
|
+
ctx ? {
|
|
10529
|
+
request: ctx.request,
|
|
10530
|
+
headers: ctx.headers ?? void 0,
|
|
10531
|
+
path: ctx.path,
|
|
10532
|
+
// Read from this package's Better Auth instance. In a
|
|
10533
|
+
// pnpm graph the consuming worker can have another
|
|
10534
|
+
// physical copy whose request-state singleton differs.
|
|
10535
|
+
oauthState: await getOAuthState()
|
|
10536
|
+
} : null
|
|
10537
|
+
)
|
|
10538
|
+
} : {},
|
|
10539
|
+
...onUserCreated ? {
|
|
10540
|
+
after: async (user, ctx) => {
|
|
10541
|
+
try {
|
|
10542
|
+
await onUserCreated(
|
|
10543
|
+
{ id: user.id, email: user.email, name: user.name },
|
|
10544
|
+
// Forward BOTH request and standalone headers — some better-auth
|
|
10545
|
+
// entry points populate ctx.headers without a request.
|
|
10546
|
+
ctx ? { request: ctx.request, headers: ctx.headers ?? void 0 } : null
|
|
10547
|
+
);
|
|
10548
|
+
} catch (err) {
|
|
10549
|
+
console.error(
|
|
10550
|
+
`[deepspace] onUserCreated hook failed (signup unaffected): ${loggableError(err)}`
|
|
10551
|
+
);
|
|
10552
|
+
}
|
|
10360
10553
|
}
|
|
10361
|
-
}
|
|
10554
|
+
} : {}
|
|
10362
10555
|
}
|
|
10363
10556
|
}
|
|
10364
10557
|
};
|
|
@@ -10592,7 +10785,8 @@ function registerAgentToolRoutes(app, options) {
|
|
|
10592
10785
|
output = await tool3.execute(input, {
|
|
10593
10786
|
toolCallId: crypto.randomUUID(),
|
|
10594
10787
|
messages: [],
|
|
10595
|
-
abortSignal: c.req.raw.signal
|
|
10788
|
+
abortSignal: c.req.raw.signal,
|
|
10789
|
+
context: void 0
|
|
10596
10790
|
});
|
|
10597
10791
|
} catch {
|
|
10598
10792
|
return errorResponse(500, "tool_execution_failed");
|
|
@@ -10651,6 +10845,184 @@ function normalizeAgentTargetOrigin(value, policy) {
|
|
|
10651
10845
|
return !label.includes(".") && validateAppName(label).valid ? url.origin : null;
|
|
10652
10846
|
}
|
|
10653
10847
|
|
|
10848
|
+
// src/server/expo-auth.ts
|
|
10849
|
+
var CODE_CHALLENGE_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
10850
|
+
var CODE_VERIFIER_RE = /^[A-Za-z0-9._~-]{43,128}$/;
|
|
10851
|
+
function allowedRedirect(value, options = {}) {
|
|
10852
|
+
try {
|
|
10853
|
+
new URL(value);
|
|
10854
|
+
return options.allowedRedirectUris?.includes(value) ?? false;
|
|
10855
|
+
} catch {
|
|
10856
|
+
return false;
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
function validState(value) {
|
|
10860
|
+
return value === null || value.length >= 1 && value.length <= 1024;
|
|
10861
|
+
}
|
|
10862
|
+
function validPkce(challenge, method, required = false) {
|
|
10863
|
+
if (!challenge && !method) return !required;
|
|
10864
|
+
return method === "S256" && !!challenge && CODE_CHALLENGE_RE.test(challenge);
|
|
10865
|
+
}
|
|
10866
|
+
function authWorkerUnavailable() {
|
|
10867
|
+
return Response.json({ error: "The authentication service is not configured." }, { status: 503 });
|
|
10868
|
+
}
|
|
10869
|
+
function nativeAuthStart(request2, env, options) {
|
|
10870
|
+
const url = new URL(request2.url);
|
|
10871
|
+
const provider = url.searchParams.get("provider");
|
|
10872
|
+
const redirectUri = url.searchParams.get("redirect_uri");
|
|
10873
|
+
const state = url.searchParams.get("state");
|
|
10874
|
+
const codeChallenge = url.searchParams.get("code_challenge");
|
|
10875
|
+
const codeChallengeMethod = url.searchParams.get("code_challenge_method");
|
|
10876
|
+
if (!provider || !redirectUri || !validState(state) || !allowedRedirect(redirectUri, options) || !validPkce(codeChallenge, codeChallengeMethod, true)) {
|
|
10877
|
+
return Response.json({ error: "A supported provider, PKCE challenge, and registered redirect URI are required." }, { status: 400 });
|
|
10878
|
+
}
|
|
10879
|
+
if (!env.AUTH_WORKER_URL) return authWorkerUnavailable();
|
|
10880
|
+
const callback = new URL("/api/auth/oauth-complete", url.origin);
|
|
10881
|
+
callback.searchParams.set("redirect_uri", redirectUri);
|
|
10882
|
+
if (state) callback.searchParams.set("state", state);
|
|
10883
|
+
if (codeChallenge) {
|
|
10884
|
+
callback.searchParams.set("code_challenge", codeChallenge);
|
|
10885
|
+
callback.searchParams.set("code_challenge_method", "S256");
|
|
10886
|
+
}
|
|
10887
|
+
const authUrl = new URL("/login/social", env.AUTH_WORKER_URL);
|
|
10888
|
+
authUrl.searchParams.set("provider", provider);
|
|
10889
|
+
authUrl.searchParams.set("returnTo", callback.toString());
|
|
10890
|
+
return Response.redirect(authUrl.toString(), 302);
|
|
10891
|
+
}
|
|
10892
|
+
function nativeAuthCallback(request2, _env, options) {
|
|
10893
|
+
const url = new URL(request2.url);
|
|
10894
|
+
const redirectUri = url.searchParams.get("redirect_uri");
|
|
10895
|
+
const code = url.searchParams.get("code");
|
|
10896
|
+
const state = url.searchParams.get("state");
|
|
10897
|
+
const codeChallenge = url.searchParams.get("code_challenge");
|
|
10898
|
+
const codeChallengeMethod = url.searchParams.get("code_challenge_method");
|
|
10899
|
+
if (!redirectUri || !code || code.length > 4096 || !validState(state) || !validPkce(codeChallenge, codeChallengeMethod, true) || !allowedRedirect(redirectUri, options)) {
|
|
10900
|
+
return Response.json({ error: "The sign-in callback was incomplete." }, { status: 400 });
|
|
10901
|
+
}
|
|
10902
|
+
const destination = new URL(redirectUri);
|
|
10903
|
+
destination.searchParams.set("code", code);
|
|
10904
|
+
if (state) destination.searchParams.set("state", state);
|
|
10905
|
+
return Response.redirect(destination.toString(), 302);
|
|
10906
|
+
}
|
|
10907
|
+
async function nativeAuthExchange(request2, env) {
|
|
10908
|
+
const body = await request2.json().catch(() => null);
|
|
10909
|
+
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)) {
|
|
10910
|
+
return Response.json({ error: "A valid one-time sign-in code and PKCE verifier are required." }, { status: 400 });
|
|
10911
|
+
}
|
|
10912
|
+
let exchange2;
|
|
10913
|
+
try {
|
|
10914
|
+
exchange2 = await authWorkerFetch(env, "/api/auth/exchange-code", {
|
|
10915
|
+
method: "POST",
|
|
10916
|
+
headers: { "Content-Type": "application/json" },
|
|
10917
|
+
body: JSON.stringify({ code: body.code, code_verifier: body.code_verifier })
|
|
10918
|
+
});
|
|
10919
|
+
} catch {
|
|
10920
|
+
return Response.json({ error: "The authentication service is unavailable." }, { status: 502 });
|
|
10921
|
+
}
|
|
10922
|
+
if (!exchange2.ok) {
|
|
10923
|
+
const definitiveFailure = exchange2.status === 400 || exchange2.status === 401;
|
|
10924
|
+
return Response.json(
|
|
10925
|
+
{ error: definitiveFailure ? "The sign-in code expired. Please try again." : "The authentication service is unavailable." },
|
|
10926
|
+
{ status: definitiveFailure ? 401 : 502 }
|
|
10927
|
+
);
|
|
10928
|
+
}
|
|
10929
|
+
const exchanged = await exchange2.json().catch(() => null);
|
|
10930
|
+
if (!exchanged || typeof exchanged.sessionToken !== "string") {
|
|
10931
|
+
return Response.json({ error: "The auth service did not issue a session." }, { status: 502 });
|
|
10932
|
+
}
|
|
10933
|
+
let tokenResponse = null;
|
|
10934
|
+
let token = null;
|
|
10935
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
10936
|
+
try {
|
|
10937
|
+
tokenResponse = await authWorkerFetch(env, "/api/auth/token", {
|
|
10938
|
+
method: "POST",
|
|
10939
|
+
headers: { Cookie: `${SESSION_COOKIE}=${encodeURIComponent(exchanged.sessionToken)}` }
|
|
10940
|
+
});
|
|
10941
|
+
} catch {
|
|
10942
|
+
tokenResponse = null;
|
|
10943
|
+
}
|
|
10944
|
+
if (tokenResponse?.ok) {
|
|
10945
|
+
token = await tokenResponse.json().catch(() => null);
|
|
10946
|
+
if (token && typeof token.token === "string") break;
|
|
10947
|
+
}
|
|
10948
|
+
if (tokenResponse?.status === 401) break;
|
|
10949
|
+
}
|
|
10950
|
+
if (!tokenResponse) {
|
|
10951
|
+
return Response.json({ error: "The authentication service is unavailable." }, { status: 502 });
|
|
10952
|
+
}
|
|
10953
|
+
if (!tokenResponse.ok) {
|
|
10954
|
+
return Response.json(
|
|
10955
|
+
tokenResponse.status === 401 ? { error: "The sign-in session expired. Please try again." } : { error: "The authentication service is unavailable." },
|
|
10956
|
+
{ status: tokenResponse.status === 401 ? 401 : 502 }
|
|
10957
|
+
);
|
|
10958
|
+
}
|
|
10959
|
+
if (!token || typeof token.token !== "string") {
|
|
10960
|
+
return Response.json({ error: "The auth service did not issue an access token." }, { status: 502 });
|
|
10961
|
+
}
|
|
10962
|
+
return Response.json(
|
|
10963
|
+
{ sessionToken: exchanged.sessionToken, accessToken: token.token },
|
|
10964
|
+
{ headers: { "Cache-Control": "no-store" } }
|
|
10965
|
+
);
|
|
10966
|
+
}
|
|
10967
|
+
async function nativeAuthToken(request2, env) {
|
|
10968
|
+
const input = await request2.json().catch(() => null);
|
|
10969
|
+
if (!input || typeof input.sessionToken !== "string" || input.sessionToken.length < 16 || input.sessionToken.length > 4096) {
|
|
10970
|
+
return Response.json({ error: "A valid DeepSpace session is required." }, { status: 401 });
|
|
10971
|
+
}
|
|
10972
|
+
let response;
|
|
10973
|
+
try {
|
|
10974
|
+
response = await authWorkerFetch(env, "/api/auth/token", {
|
|
10975
|
+
method: "POST",
|
|
10976
|
+
headers: { Cookie: `${SESSION_COOKIE}=${encodeURIComponent(input.sessionToken)}` }
|
|
10977
|
+
});
|
|
10978
|
+
} catch {
|
|
10979
|
+
return Response.json({ error: "The authentication service is unavailable." }, { status: 502 });
|
|
10980
|
+
}
|
|
10981
|
+
if (!response.ok) {
|
|
10982
|
+
return Response.json(
|
|
10983
|
+
{ error: response.status === 401 ? "The DeepSpace session expired." : "The authentication service is unavailable." },
|
|
10984
|
+
{ status: response.status === 401 ? 401 : 502 }
|
|
10985
|
+
);
|
|
10986
|
+
}
|
|
10987
|
+
const body = await response.json().catch(() => null);
|
|
10988
|
+
if (!body || typeof body.token !== "string") {
|
|
10989
|
+
return Response.json({ error: "The auth service did not issue an access token." }, { status: 502 });
|
|
10990
|
+
}
|
|
10991
|
+
return Response.json(
|
|
10992
|
+
{ accessToken: body.token },
|
|
10993
|
+
{ headers: { "Cache-Control": "no-store" } }
|
|
10994
|
+
);
|
|
10995
|
+
}
|
|
10996
|
+
async function nativeAuthMe(request2, env) {
|
|
10997
|
+
if (!env.AUTH_JWT_PUBLIC_KEY || !env.AUTH_JWT_ISSUER) {
|
|
10998
|
+
return Response.json({ error: "JWT verification is not configured." }, { status: 500 });
|
|
10999
|
+
}
|
|
11000
|
+
const header = request2.headers.get("Authorization");
|
|
11001
|
+
const token = header?.startsWith("Bearer ") ? header.slice(7) : null;
|
|
11002
|
+
const verified = await verifyJwt(
|
|
11003
|
+
{ publicKey: env.AUTH_JWT_PUBLIC_KEY, issuer: env.AUTH_JWT_ISSUER },
|
|
11004
|
+
token
|
|
11005
|
+
);
|
|
11006
|
+
if (!verified.result) return Response.json({ error: "Sign in required." }, { status: 401 });
|
|
11007
|
+
return Response.json(
|
|
11008
|
+
{ userId: verified.result.userId, claims: verified.result.claims },
|
|
11009
|
+
{ headers: { "Cache-Control": "no-store" } }
|
|
11010
|
+
);
|
|
11011
|
+
}
|
|
11012
|
+
async function nativeAuthSignOut(request2, env) {
|
|
11013
|
+
const body = await request2.json().catch(() => null);
|
|
11014
|
+
if (body && typeof body.sessionToken === "string" && body.sessionToken.length >= 16 && body.sessionToken.length <= 4096) {
|
|
11015
|
+
try {
|
|
11016
|
+
await authWorkerFetch(env, "/api/auth/sign-out", {
|
|
11017
|
+
method: "POST",
|
|
11018
|
+
headers: { Cookie: `${SESSION_COOKIE}=${encodeURIComponent(body.sessionToken)}` }
|
|
11019
|
+
});
|
|
11020
|
+
} catch {
|
|
11021
|
+
}
|
|
11022
|
+
}
|
|
11023
|
+
return Response.json({ ok: true });
|
|
11024
|
+
}
|
|
11025
|
+
|
|
10654
11026
|
// src/shared/api-error.ts
|
|
10655
11027
|
var SLUG_RE = /^[a-z0-9]+(?:[_.-][a-z0-9]+)*$/;
|
|
10656
11028
|
var HUMAN_MESSAGES = {
|
|
@@ -10716,6 +11088,7 @@ export {
|
|
|
10716
11088
|
AGENT_TOOL_RESPONSE_BODY_CAP,
|
|
10717
11089
|
AI_CHATS_SCHEMA,
|
|
10718
11090
|
AI_MESSAGES_SCHEMA,
|
|
11091
|
+
AI_SDK_7_MIGRATION_ID,
|
|
10719
11092
|
ALLOWED_BINDING_TYPES,
|
|
10720
11093
|
ANONYMOUS_USER_ID_PREFIX,
|
|
10721
11094
|
APP_ID_RE,
|
|
@@ -10785,6 +11158,8 @@ export {
|
|
|
10785
11158
|
ROLE_DEFAULT,
|
|
10786
11159
|
RecordRoom,
|
|
10787
11160
|
RegistryClientError,
|
|
11161
|
+
SANDBOX_SCOPES,
|
|
11162
|
+
SANDBOX_SCOPE_HEADER,
|
|
10788
11163
|
SDK_RUN_WORKER_FIRST,
|
|
10789
11164
|
SECRET_NAME_RE,
|
|
10790
11165
|
SECURE_ROOM_BOUNDARIES_MIGRATION_ID,
|
|
@@ -10794,6 +11169,7 @@ export {
|
|
|
10794
11169
|
SYSTEM_COLLECTIONS,
|
|
10795
11170
|
SYSTEM_MAINTAINED_COLUMNS,
|
|
10796
11171
|
SYSTEM_MANAGED_COLUMNS,
|
|
11172
|
+
SandboxFileError,
|
|
10797
11173
|
SchemaRegistry,
|
|
10798
11174
|
TEST_ACCOUNT_EMAIL_SUFFIX,
|
|
10799
11175
|
UNDECLARED_ASSET_NOT_FOUND_HANDLING,
|
|
@@ -10814,6 +11190,7 @@ export {
|
|
|
10814
11190
|
buildTableSelect,
|
|
10815
11191
|
buildUiParts,
|
|
10816
11192
|
callRegistry,
|
|
11193
|
+
callableFromSandbox,
|
|
10817
11194
|
canCreate,
|
|
10818
11195
|
canDelete,
|
|
10819
11196
|
canRead,
|
|
@@ -10823,6 +11200,7 @@ export {
|
|
|
10823
11200
|
checkFieldPermissions,
|
|
10824
11201
|
checkUnclaimedOwnerTransition,
|
|
10825
11202
|
clientBuild,
|
|
11203
|
+
codeExecutionTool,
|
|
10826
11204
|
coerceValue,
|
|
10827
11205
|
collectionTableName,
|
|
10828
11206
|
columnId,
|
|
@@ -10850,6 +11228,7 @@ export {
|
|
|
10850
11228
|
epochSeconds,
|
|
10851
11229
|
folderFilter,
|
|
10852
11230
|
formatBytes,
|
|
11231
|
+
forwardSandboxContainer,
|
|
10853
11232
|
getChat,
|
|
10854
11233
|
getDeepSpaceAIModel,
|
|
10855
11234
|
getMessageType,
|
|
@@ -10881,6 +11260,12 @@ export {
|
|
|
10881
11260
|
meterVectorize,
|
|
10882
11261
|
mintAppId,
|
|
10883
11262
|
mintUlid,
|
|
11263
|
+
nativeAuthCallback,
|
|
11264
|
+
nativeAuthExchange,
|
|
11265
|
+
nativeAuthMe,
|
|
11266
|
+
nativeAuthSignOut,
|
|
11267
|
+
nativeAuthStart,
|
|
11268
|
+
nativeAuthToken,
|
|
10884
11269
|
noopPermissionContext,
|
|
10885
11270
|
normalizeAgentTargetOrigin,
|
|
10886
11271
|
normalizeApiError,
|
|
@@ -10904,8 +11289,12 @@ export {
|
|
|
10904
11289
|
resolveCompatibilityFlags,
|
|
10905
11290
|
resolveDeepSpaceAgentModel,
|
|
10906
11291
|
resolveSessionReadAuth,
|
|
11292
|
+
reuseSandbox,
|
|
10907
11293
|
rowToData,
|
|
10908
11294
|
runMigrations,
|
|
11295
|
+
sandboxFiles,
|
|
11296
|
+
sandboxOutputs,
|
|
11297
|
+
sandboxUpload,
|
|
10909
11298
|
serverBuild,
|
|
10910
11299
|
storageLimitForTier,
|
|
10911
11300
|
storageQuotaMessage,
|