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/server.js
CHANGED
|
@@ -1860,36 +1860,60 @@ async function resolveAppRole(env, userId) {
|
|
|
1860
1860
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
1861
1861
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
1862
1862
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
1863
|
-
|
|
1864
|
-
|
|
1863
|
+
|
|
1864
|
+
// src/shared/sandbox.ts
|
|
1865
|
+
var SANDBOX_SCOPE_HEADER = "x-deepspace-sandbox-scope";
|
|
1866
|
+
|
|
1867
|
+
// src/server/utils/app-identity.ts
|
|
1868
|
+
function appendAppIdentity(headers, env) {
|
|
1869
|
+
headers.delete("x-app-identity-token");
|
|
1870
|
+
headers.delete("x-app-id");
|
|
1871
|
+
if (!env.APP_IDENTITY_TOKEN) return;
|
|
1872
|
+
headers.set("x-app-identity-token", env.APP_IDENTITY_TOKEN);
|
|
1873
|
+
headers.set("x-app-id", env.DEEPSPACE_APP_ID);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
// src/server/utils/ai.ts
|
|
1877
|
+
import { defaultSettingsMiddleware, wrapLanguageModel } from "ai";
|
|
1878
|
+
function resolveProxyAuthToken(env, options) {
|
|
1879
|
+
const authToken = options.authToken ?? env.APP_OWNER_JWT;
|
|
1880
|
+
if (!authToken) {
|
|
1881
|
+
throw new Error(
|
|
1882
|
+
"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`)."
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
return authToken;
|
|
1886
|
+
}
|
|
1887
|
+
function setProxyHeaders(headers, env, authToken, scope = "user") {
|
|
1888
|
+
headers.set("X-Auth-Token", authToken);
|
|
1889
|
+
appendAppIdentity(headers, {
|
|
1890
|
+
DEEPSPACE_APP_ID: env.DEEPSPACE_APP_ID ?? "",
|
|
1891
|
+
APP_IDENTITY_TOKEN: env.APP_IDENTITY_TOKEN
|
|
1892
|
+
});
|
|
1893
|
+
headers.set(SANDBOX_SCOPE_HEADER, scope);
|
|
1894
|
+
}
|
|
1895
|
+
var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 64e3;
|
|
1896
|
+
var OPENAI_TOOLS_NEED_NO_REASONING = /^gpt-(5\.6|6)-(sol|terra|luna)(-|$)/;
|
|
1897
|
+
function withOpenAIToolReasoningEffort(body) {
|
|
1865
1898
|
if (typeof body !== "string") return body;
|
|
1866
1899
|
try {
|
|
1867
1900
|
const parsed = JSON.parse(body);
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1870
|
-
return JSON.stringify({
|
|
1871
|
-
...parsed,
|
|
1872
|
-
max_tokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
|
|
1873
|
-
});
|
|
1901
|
+
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;
|
|
1902
|
+
return needsEffort ? JSON.stringify({ ...parsed, reasoning_effort: "none" }) : body;
|
|
1874
1903
|
} catch {
|
|
1875
1904
|
return body;
|
|
1876
1905
|
}
|
|
1877
1906
|
}
|
|
1878
1907
|
function createDeepSpaceAI(env, provider, options = {}) {
|
|
1879
1908
|
const transport = resolveApiTransport(env);
|
|
1880
|
-
const authToken = options
|
|
1881
|
-
if (!authToken) {
|
|
1882
|
-
throw new Error(
|
|
1883
|
-
"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`)."
|
|
1884
|
-
);
|
|
1885
|
-
}
|
|
1909
|
+
const authToken = resolveProxyAuthToken(env, options);
|
|
1886
1910
|
const proxyFetch = (input, init) => {
|
|
1887
1911
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1888
1912
|
const headers = new Headers(init?.headers);
|
|
1889
1913
|
headers.delete("authorization");
|
|
1890
1914
|
headers.delete("x-api-key");
|
|
1891
|
-
headers
|
|
1892
|
-
const body = provider === "
|
|
1915
|
+
setProxyHeaders(headers, env, authToken, options.sandboxScope);
|
|
1916
|
+
const body = provider === "openai" ? withOpenAIToolReasoningEffort(init?.body) : init?.body;
|
|
1893
1917
|
if (body !== init?.body) {
|
|
1894
1918
|
headers.delete("content-length");
|
|
1895
1919
|
}
|
|
@@ -1903,8 +1927,13 @@ function createDeepSpaceAI(env, provider, options = {}) {
|
|
|
1903
1927
|
};
|
|
1904
1928
|
const baseURL = `https://api-worker.internal/api/proxy/${provider}/v1`;
|
|
1905
1929
|
switch (provider) {
|
|
1906
|
-
case "anthropic":
|
|
1907
|
-
|
|
1930
|
+
case "anthropic": {
|
|
1931
|
+
const anthropic2 = createAnthropic({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
|
|
1932
|
+
const defaults = defaultSettingsMiddleware({
|
|
1933
|
+
settings: { maxOutputTokens: ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS }
|
|
1934
|
+
});
|
|
1935
|
+
return (modelId) => wrapLanguageModel({ model: anthropic2(modelId), middleware: defaults });
|
|
1936
|
+
}
|
|
1908
1937
|
case "openai": {
|
|
1909
1938
|
const op = createOpenAI({ baseURL, apiKey: "platform-managed", fetch: proxyFetch });
|
|
1910
1939
|
return op.chat;
|
|
@@ -1925,10 +1954,7 @@ function createDeepSpaceAI(env, provider, options = {}) {
|
|
|
1925
1954
|
}
|
|
1926
1955
|
|
|
1927
1956
|
// src/server/utils/agent.ts
|
|
1928
|
-
import {
|
|
1929
|
-
stepCountIs,
|
|
1930
|
-
streamText
|
|
1931
|
-
} from "ai";
|
|
1957
|
+
import { isStepCount, streamText } from "ai";
|
|
1932
1958
|
|
|
1933
1959
|
// src/shared/ai-models.ts
|
|
1934
1960
|
var MULTI_STEP_AGENT_PROFILES = ["application", "documentation"];
|
|
@@ -1950,16 +1976,17 @@ var openAIModel = createMultiStepModelFactory({
|
|
|
1950
1976
|
providerLabel: "OpenAI",
|
|
1951
1977
|
transport: "chat-completions"
|
|
1952
1978
|
});
|
|
1979
|
+
var DIRECT_GENERATION_NOTE = "Available for direct generation; the current proxy adapter has not passed multi-step tool-result continuation.";
|
|
1953
1980
|
var DEEPSPACE_AI_MODELS = [
|
|
1954
1981
|
anthropicModel({
|
|
1955
|
-
id: "claude-
|
|
1956
|
-
label: "Claude
|
|
1982
|
+
id: "claude-opus-5-5",
|
|
1983
|
+
label: "Claude Opus 5.5",
|
|
1957
1984
|
family: "Claude 5",
|
|
1958
1985
|
recommendation: "frontier"
|
|
1959
1986
|
}),
|
|
1960
1987
|
anthropicModel({
|
|
1961
|
-
id: "claude-
|
|
1962
|
-
label: "Claude
|
|
1988
|
+
id: "claude-fable-5-1",
|
|
1989
|
+
label: "Claude Fable 5.1",
|
|
1963
1990
|
family: "Claude 5",
|
|
1964
1991
|
recommendation: "frontier"
|
|
1965
1992
|
}),
|
|
@@ -1976,23 +2003,29 @@ var DEEPSPACE_AI_MODELS = [
|
|
|
1976
2003
|
recommendation: "fast"
|
|
1977
2004
|
}),
|
|
1978
2005
|
openAIModel({
|
|
1979
|
-
id: "gpt-
|
|
1980
|
-
label: "GPT-
|
|
1981
|
-
family: "GPT-
|
|
1982
|
-
recommendation: "frontier"
|
|
1983
|
-
}),
|
|
1984
|
-
openAIModel({
|
|
1985
|
-
id: "gpt-5.6-terra",
|
|
1986
|
-
label: "GPT-5.6 Terra",
|
|
1987
|
-
family: "GPT-5.6",
|
|
2006
|
+
id: "gpt-6-sol",
|
|
2007
|
+
label: "GPT-6 Sol",
|
|
2008
|
+
family: "GPT-6",
|
|
1988
2009
|
recommendation: "balanced"
|
|
1989
2010
|
}),
|
|
1990
2011
|
openAIModel({
|
|
1991
|
-
id: "gpt-
|
|
1992
|
-
label: "GPT-
|
|
1993
|
-
family: "GPT-
|
|
2012
|
+
id: "gpt-6-luna",
|
|
2013
|
+
label: "GPT-6 Luna",
|
|
2014
|
+
family: "GPT-6",
|
|
1994
2015
|
recommendation: "fast"
|
|
1995
2016
|
}),
|
|
2017
|
+
{
|
|
2018
|
+
id: "gpt-6-astra",
|
|
2019
|
+
label: "GPT-6 Astra",
|
|
2020
|
+
provider: "openai",
|
|
2021
|
+
providerLabel: "OpenAI",
|
|
2022
|
+
family: "GPT-6",
|
|
2023
|
+
agentSupport: "none",
|
|
2024
|
+
agentProfiles: [],
|
|
2025
|
+
transport: "chat-completions",
|
|
2026
|
+
recommendation: "limited",
|
|
2027
|
+
note: "Available for direct generation; its tool calling requires the Responses API, which the DeepSpace proxy does not serve."
|
|
2028
|
+
},
|
|
1996
2029
|
{
|
|
1997
2030
|
id: "gpt-oss-120b",
|
|
1998
2031
|
label: "GPT-OSS 120B",
|
|
@@ -2003,12 +2036,30 @@ var DEEPSPACE_AI_MODELS = [
|
|
|
2003
2036
|
agentProfiles: [],
|
|
2004
2037
|
transport: "chat-completions",
|
|
2005
2038
|
recommendation: "limited",
|
|
2006
|
-
note:
|
|
2007
|
-
}
|
|
2039
|
+
note: DIRECT_GENERATION_NOTE
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
id: "qwen-3.8-27b",
|
|
2043
|
+
label: "Qwen 3.8 27B",
|
|
2044
|
+
provider: "cerebras",
|
|
2045
|
+
providerLabel: "Cerebras",
|
|
2046
|
+
family: "Qwen 3.8",
|
|
2047
|
+
agentSupport: "single-step",
|
|
2048
|
+
agentProfiles: [],
|
|
2049
|
+
transport: "chat-completions",
|
|
2050
|
+
recommendation: "limited",
|
|
2051
|
+
note: DIRECT_GENERATION_NOTE
|
|
2052
|
+
},
|
|
2053
|
+
// Superseded models: still resolvable for apps that saved them.
|
|
2054
|
+
anthropicModel({ id: "claude-opus-5", label: "Claude Opus 5", family: "Claude 5", recommendation: "frontier", legacy: true }),
|
|
2055
|
+
anthropicModel({ id: "claude-fable-5", label: "Claude Fable 5", family: "Claude 5", recommendation: "frontier", legacy: true }),
|
|
2056
|
+
openAIModel({ id: "gpt-5.6-sol", label: "GPT-5.6 Sol", family: "GPT-5.6", recommendation: "frontier", legacy: true }),
|
|
2057
|
+
openAIModel({ id: "gpt-5.6-terra", label: "GPT-5.6 Terra", family: "GPT-5.6", recommendation: "balanced", legacy: true }),
|
|
2058
|
+
openAIModel({ id: "gpt-5.6-luna", label: "GPT-5.6 Luna", family: "GPT-5.6", recommendation: "fast", legacy: true })
|
|
2008
2059
|
];
|
|
2009
2060
|
var DEEPSPACE_AI_DEFAULTS = {
|
|
2010
|
-
agent: "claude-
|
|
2011
|
-
directGeneration: "claude-
|
|
2061
|
+
agent: "claude-opus-5-5",
|
|
2062
|
+
directGeneration: "claude-opus-5-5",
|
|
2012
2063
|
summarization: "claude-haiku-4-5"
|
|
2013
2064
|
};
|
|
2014
2065
|
var DEEPSPACE_AGENT_PROFILES = {
|
|
@@ -2063,7 +2114,7 @@ var DeepSpaceAgentProfileError = class extends Error {
|
|
|
2063
2114
|
code = "agent_profile_violation";
|
|
2064
2115
|
};
|
|
2065
2116
|
function streamDeepSpaceAgent(env, options) {
|
|
2066
|
-
const { profile, modelId, authToken, ...streamOptions } = options;
|
|
2117
|
+
const { profile, modelId, authToken, sandboxScope, ...streamOptions } = options;
|
|
2067
2118
|
const selection = resolveDeepSpaceAgentModel(modelId, profile);
|
|
2068
2119
|
if (!selection) throw new DeepSpaceAgentModelError(modelId, profile);
|
|
2069
2120
|
if (selection.profile.allowedTools !== "application-defined") {
|
|
@@ -2074,14 +2125,17 @@ function streamDeepSpaceAgent(env, options) {
|
|
|
2074
2125
|
}
|
|
2075
2126
|
}
|
|
2076
2127
|
}
|
|
2077
|
-
const provider = createDeepSpaceAI(env, selection.provider,
|
|
2128
|
+
const provider = createDeepSpaceAI(env, selection.provider, {
|
|
2129
|
+
...authToken ? { authToken } : {},
|
|
2130
|
+
...sandboxScope ? { sandboxScope } : {}
|
|
2131
|
+
});
|
|
2078
2132
|
const { prepareStep, tools, ...remainingStreamOptions } = streamOptions;
|
|
2079
2133
|
const boundedTools = limitToolExecutions(tools, selection.profile.maxToolCalls);
|
|
2080
2134
|
const result = streamText({
|
|
2081
2135
|
...remainingStreamOptions,
|
|
2082
2136
|
...boundedTools ? { tools: boundedTools } : {},
|
|
2083
2137
|
model: provider(selection.modelId),
|
|
2084
|
-
stopWhen:
|
|
2138
|
+
stopWhen: isStepCount(selection.profile.maxSteps),
|
|
2085
2139
|
prepareStep: enforceToolCallLimit(prepareStep, selection.profile.maxToolCalls),
|
|
2086
2140
|
...selection.provider === "openai" ? { providerOptions: { openai: { reasoningEffort: "none" } } } : {}
|
|
2087
2141
|
});
|
|
@@ -2193,6 +2247,115 @@ function safeLogText(value) {
|
|
|
2193
2247
|
).trim().slice(0, 300);
|
|
2194
2248
|
}
|
|
2195
2249
|
|
|
2250
|
+
// src/server/utils/sandbox.ts
|
|
2251
|
+
import { anthropic, forwardAnthropicContainerIdFromLastStep } from "@ai-sdk/anthropic";
|
|
2252
|
+
var FILES_PATH = "/api/proxy/anthropic/v1/files";
|
|
2253
|
+
var CODE_EXECUTION_CALLER = "code_execution_20260120";
|
|
2254
|
+
function codeExecutionTool() {
|
|
2255
|
+
return anthropic.tools.codeExecution_20260120();
|
|
2256
|
+
}
|
|
2257
|
+
function callableFromSandbox(tool2) {
|
|
2258
|
+
const anthropicOptions = tool2.providerOptions?.anthropic;
|
|
2259
|
+
return {
|
|
2260
|
+
...tool2,
|
|
2261
|
+
providerOptions: {
|
|
2262
|
+
...tool2.providerOptions,
|
|
2263
|
+
anthropic: { ...anthropicOptions, allowedCallers: ["direct", CODE_EXECUTION_CALLER] }
|
|
2264
|
+
}
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
var forwardSandboxContainer = forwardAnthropicContainerIdFromLastStep;
|
|
2268
|
+
function reuseSandbox(containerId) {
|
|
2269
|
+
return containerId ? { anthropic: { container: { id: containerId } } } : void 0;
|
|
2270
|
+
}
|
|
2271
|
+
function sandboxUpload(file) {
|
|
2272
|
+
return {
|
|
2273
|
+
type: "file",
|
|
2274
|
+
mediaType: file.mime_type,
|
|
2275
|
+
filename: file.filename,
|
|
2276
|
+
data: { type: "reference", reference: { anthropic: file.id } },
|
|
2277
|
+
providerOptions: { anthropic: { containerUpload: true } }
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function collectFileIds(value, out) {
|
|
2281
|
+
if (Array.isArray(value)) {
|
|
2282
|
+
for (const item of value) collectFileIds(item, out);
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
if (!value || typeof value !== "object") return;
|
|
2286
|
+
for (const [key, child] of Object.entries(value)) {
|
|
2287
|
+
if (key === "file_id" && typeof child === "string" && child) out.add(child);
|
|
2288
|
+
else collectFileIds(child, out);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
function sandboxOutputs(steps) {
|
|
2292
|
+
let containerId = null;
|
|
2293
|
+
const fileIds = /* @__PURE__ */ new Set();
|
|
2294
|
+
for (const step of steps) {
|
|
2295
|
+
const container = step.providerMetadata?.anthropic?.container;
|
|
2296
|
+
if (typeof container?.id === "string") containerId = container.id;
|
|
2297
|
+
for (const part of step.content) {
|
|
2298
|
+
const result = part;
|
|
2299
|
+
if (result.type === "tool-result" && result.providerExecuted) collectFileIds(result.output, fileIds);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return { containerId, fileIds: [...fileIds] };
|
|
2303
|
+
}
|
|
2304
|
+
var SandboxFileError = class extends Error {
|
|
2305
|
+
constructor(status, message) {
|
|
2306
|
+
super(message);
|
|
2307
|
+
this.status = status;
|
|
2308
|
+
this.name = "SandboxFileError";
|
|
2309
|
+
}
|
|
2310
|
+
};
|
|
2311
|
+
function sandboxFiles(env, options = {}) {
|
|
2312
|
+
const authToken = resolveProxyAuthToken(env, options);
|
|
2313
|
+
async function send(path, init = {}) {
|
|
2314
|
+
const headers = new Headers(init.headers);
|
|
2315
|
+
setProxyHeaders(headers, env, authToken, options.scope);
|
|
2316
|
+
const response = await apiWorkerFetch(env, `${FILES_PATH}${path}`, { ...init, headers });
|
|
2317
|
+
if (!response.ok) {
|
|
2318
|
+
const text = await response.text();
|
|
2319
|
+
let message = text || response.statusText;
|
|
2320
|
+
try {
|
|
2321
|
+
const body = JSON.parse(text);
|
|
2322
|
+
if (typeof body.error === "string") message = body.error;
|
|
2323
|
+
else if (body.error && typeof body.error === "object") message = JSON.stringify(body.error);
|
|
2324
|
+
} catch {
|
|
2325
|
+
}
|
|
2326
|
+
throw new SandboxFileError(response.status, `Sandbox file request failed (${response.status}): ${message}`);
|
|
2327
|
+
}
|
|
2328
|
+
return response;
|
|
2329
|
+
}
|
|
2330
|
+
const fileUrl = (fileId) => `/${encodeURIComponent(fileId)}`;
|
|
2331
|
+
return {
|
|
2332
|
+
async upload(file, { filename, expiresInSeconds } = {}) {
|
|
2333
|
+
const name = filename ?? (file instanceof File ? file.name : void 0);
|
|
2334
|
+
if (!name) throw new SandboxFileError(400, "sandboxFiles.upload: a Blob needs options.filename");
|
|
2335
|
+
const form = new FormData();
|
|
2336
|
+
form.set("file", file, name);
|
|
2337
|
+
if (expiresInSeconds !== void 0) form.set("expires_in_seconds", String(expiresInSeconds));
|
|
2338
|
+
return (await send("", { method: "POST", body: form })).json();
|
|
2339
|
+
},
|
|
2340
|
+
async get(fileId) {
|
|
2341
|
+
return (await send(fileUrl(fileId))).json();
|
|
2342
|
+
},
|
|
2343
|
+
download(fileId) {
|
|
2344
|
+
return send(`${fileUrl(fileId)}/content`);
|
|
2345
|
+
},
|
|
2346
|
+
async list({ limit, page } = {}) {
|
|
2347
|
+
const query = new URLSearchParams();
|
|
2348
|
+
if (limit !== void 0) query.set("limit", String(limit));
|
|
2349
|
+
if (page !== void 0) query.set("page", page);
|
|
2350
|
+
const suffix = query.size > 0 ? `?${query}` : "";
|
|
2351
|
+
return (await send(suffix)).json();
|
|
2352
|
+
},
|
|
2353
|
+
async delete(fileId) {
|
|
2354
|
+
await send(fileUrl(fileId), { method: "DELETE" });
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2196
2359
|
// src/server/utils/composio-tools.ts
|
|
2197
2360
|
import { tool, jsonSchema } from "ai";
|
|
2198
2361
|
var EMPTY_SCHEMA = { type: "object", properties: {} };
|
|
@@ -2275,15 +2438,6 @@ async function composioTools(env, options) {
|
|
|
2275
2438
|
return tools;
|
|
2276
2439
|
}
|
|
2277
2440
|
|
|
2278
|
-
// src/server/utils/app-identity.ts
|
|
2279
|
-
function appendAppIdentity(headers, env) {
|
|
2280
|
-
headers.delete("x-app-identity-token");
|
|
2281
|
-
headers.delete("x-app-id");
|
|
2282
|
-
if (!env.APP_IDENTITY_TOKEN) return;
|
|
2283
|
-
headers.set("x-app-identity-token", env.APP_IDENTITY_TOKEN);
|
|
2284
|
-
headers.set("x-app-id", env.DEEPSPACE_APP_ID);
|
|
2285
|
-
}
|
|
2286
|
-
|
|
2287
2441
|
// src/server/utils/screenshot.ts
|
|
2288
2442
|
async function captureScreenshot(env, url) {
|
|
2289
2443
|
const headers = new Headers({ "content-type": "application/json" });
|
|
@@ -2636,14 +2790,14 @@ function formatTranscript(messages) {
|
|
|
2636
2790
|
}
|
|
2637
2791
|
function makeDefaultSummarizer(env, options = {}) {
|
|
2638
2792
|
return async (messages) => {
|
|
2639
|
-
const
|
|
2793
|
+
const anthropic2 = createDeepSpaceAI(env, "anthropic", { authToken: options.authToken });
|
|
2640
2794
|
const { text } = await generateText({
|
|
2641
2795
|
// Stable alias — provider bug-fix snapshots roll forward without code change.
|
|
2642
|
-
model:
|
|
2796
|
+
model: anthropic2("claude-haiku-4-5"),
|
|
2643
2797
|
// 2500 gives the model room to finish a sentence after hitting the
|
|
2644
2798
|
// "under 2000 tokens" soft limit in the prompt.
|
|
2645
2799
|
maxOutputTokens: 2500,
|
|
2646
|
-
|
|
2800
|
+
instructions: SUMMARIZER_SYSTEM_PROMPT,
|
|
2647
2801
|
messages: [{ role: "user", content: formatTranscript(messages) }]
|
|
2648
2802
|
});
|
|
2649
2803
|
return text;
|
|
@@ -4956,6 +5110,7 @@ function getAllUserRecords(sql, schemaRegistry) {
|
|
|
4956
5110
|
}
|
|
4957
5111
|
async function registerUser(sql, userId, name, email, imageUrl, isAdmin, defaultRole = "viewer", schemaRegistry) {
|
|
4958
5112
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5113
|
+
const normalizedEmail = email.trim().toLowerCase();
|
|
4959
5114
|
const existing = getUserRecord(sql, userId, schemaRegistry);
|
|
4960
5115
|
let role;
|
|
4961
5116
|
if (isAdmin) {
|
|
@@ -4966,7 +5121,7 @@ async function registerUser(sql, userId, name, email, imageUrl, isAdmin, default
|
|
|
4966
5121
|
role = defaultRole;
|
|
4967
5122
|
}
|
|
4968
5123
|
if (existing) {
|
|
4969
|
-
const updatedEmail =
|
|
5124
|
+
const updatedEmail = normalizedEmail || existing.data.email;
|
|
4970
5125
|
const updatedName = name && name !== "Anonymous" ? name : existing.data.name;
|
|
4971
5126
|
const updatedImageUrl = imageUrl ?? existing.data.imageUrl;
|
|
4972
5127
|
sql.exec(
|
|
@@ -4995,14 +5150,14 @@ async function registerUser(sql, userId, name, email, imageUrl, isAdmin, default
|
|
|
4995
5150
|
userId,
|
|
4996
5151
|
now,
|
|
4997
5152
|
now,
|
|
4998
|
-
|
|
5153
|
+
normalizedEmail,
|
|
4999
5154
|
name,
|
|
5000
5155
|
imageUrl ?? null,
|
|
5001
5156
|
role,
|
|
5002
5157
|
now,
|
|
5003
5158
|
now
|
|
5004
5159
|
);
|
|
5005
|
-
return { id: userId, email, name, imageUrl, role, createdAt: now, lastSeenAt: now };
|
|
5160
|
+
return { id: userId, email: normalizedEmail, name, imageUrl, role, createdAt: now, lastSeenAt: now };
|
|
5006
5161
|
}
|
|
5007
5162
|
function rosterFor(ctx, schema, records, publicRoster, userId, role) {
|
|
5008
5163
|
if (!userId || isAnonymousUserId(userId)) return [];
|
|
@@ -5057,16 +5212,19 @@ function broadcastUserList(ctx, except) {
|
|
|
5057
5212
|
});
|
|
5058
5213
|
}
|
|
5059
5214
|
}
|
|
5060
|
-
function handleUserUpdate(ctx, _ws, attachment
|
|
5215
|
+
function handleUserUpdate(ctx, _ws, attachment) {
|
|
5061
5216
|
const existing = getUserRecord(ctx.sql, attachment.userId, ctx.schemaRegistry);
|
|
5062
5217
|
if (!existing) return;
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5218
|
+
putRecord(
|
|
5219
|
+
ctx,
|
|
5220
|
+
"users",
|
|
5221
|
+
attachment.userId,
|
|
5222
|
+
{ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
5223
|
+
attachment.userId,
|
|
5224
|
+
"admin",
|
|
5225
|
+
true,
|
|
5226
|
+
true
|
|
5227
|
+
);
|
|
5070
5228
|
}
|
|
5071
5229
|
async function handleSetRole(ctx, ws, attachment, payload) {
|
|
5072
5230
|
if (attachment.role !== "admin") {
|
|
@@ -5815,7 +5973,7 @@ var RecordRoom = class extends BaseRoom {
|
|
|
5815
5973
|
this.schemaRegistry
|
|
5816
5974
|
);
|
|
5817
5975
|
const regMs = Date.now() - regStart;
|
|
5818
|
-
if (!before || before.name !== registeredUser.name || before.imageUrl !== registeredUser.imageUrl || before.role !== registeredUser.role) {
|
|
5976
|
+
if (!before || before.name !== registeredUser.name || before.email !== registeredUser.email || before.imageUrl !== registeredUser.imageUrl || before.role !== registeredUser.role) {
|
|
5819
5977
|
broadcastUserList(this.createUserContext(), ws);
|
|
5820
5978
|
}
|
|
5821
5979
|
attachment = {
|
|
@@ -5942,12 +6100,7 @@ var RecordRoom = class extends BaseRoom {
|
|
|
5942
6100
|
handleUserList(userCtx, ws, attachment);
|
|
5943
6101
|
break;
|
|
5944
6102
|
case MSG.USER_UPDATE:
|
|
5945
|
-
handleUserUpdate(
|
|
5946
|
-
recordCtx,
|
|
5947
|
-
ws,
|
|
5948
|
-
attachment,
|
|
5949
|
-
payload
|
|
5950
|
-
);
|
|
6103
|
+
handleUserUpdate(recordCtx, ws, attachment);
|
|
5951
6104
|
break;
|
|
5952
6105
|
case MSG.SET_ROLE:
|
|
5953
6106
|
await handleSetRole(userCtx, ws, attachment, payload);
|
|
@@ -7179,6 +7332,7 @@ function computeNextRunAt(task, from) {
|
|
|
7179
7332
|
|
|
7180
7333
|
// src/server/rooms/job-room.ts
|
|
7181
7334
|
var RUNNING_STALE_MS = 16 * 60 * 1e3;
|
|
7335
|
+
var BACKGROUND_ALARM_MS = 3e4;
|
|
7182
7336
|
var DEFAULT_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
7183
7337
|
var DEFAULT_SNAPSHOT_LIMIT = 100;
|
|
7184
7338
|
var JobRoom = class extends BaseRoom {
|
|
@@ -7187,6 +7341,7 @@ var JobRoom = class extends BaseRoom {
|
|
|
7187
7341
|
retentionMs;
|
|
7188
7342
|
snapshotLimit;
|
|
7189
7343
|
retryBackoffMs;
|
|
7344
|
+
backgroundJobTypes;
|
|
7190
7345
|
authorizeWrite;
|
|
7191
7346
|
authorizeRead;
|
|
7192
7347
|
broadcastQueue = Promise.resolve();
|
|
@@ -7200,6 +7355,7 @@ var JobRoom = class extends BaseRoom {
|
|
|
7200
7355
|
this.retentionMs = Math.max(0, config.retentionMs ?? DEFAULT_RETENTION_MS);
|
|
7201
7356
|
this.snapshotLimit = Math.max(1, config.snapshotLimit ?? DEFAULT_SNAPSHOT_LIMIT);
|
|
7202
7357
|
this.retryBackoffMs = Math.max(0, config.retryBackoffMs ?? 1e3);
|
|
7358
|
+
this.backgroundJobTypes = new Set(config.backgroundJobTypes ?? []);
|
|
7203
7359
|
this.authorizeWrite = config.authorizeWrite ?? ((user) => user.role === ROLES.MEMBER || user.role === ROLES.ADMIN);
|
|
7204
7360
|
this.authorizeRead = config.authorizeRead ?? this.authorizeWrite;
|
|
7205
7361
|
}
|
|
@@ -7447,11 +7603,8 @@ var JobRoom = class extends BaseRoom {
|
|
|
7447
7603
|
// ==========================================================================
|
|
7448
7604
|
// Alarm / execution
|
|
7449
7605
|
// ==========================================================================
|
|
7450
|
-
/**
|
|
7451
|
-
* Drain due jobs in FIFO order. All due jobs share the alarm's
|
|
7452
|
-
* 15-min wall budget (a CF property, not a per-job allocation).
|
|
7453
|
-
*/
|
|
7454
7606
|
async drainDueJobs() {
|
|
7607
|
+
if (this.inFlight.size > 0) return;
|
|
7455
7608
|
const cutoff = (/* @__PURE__ */ new Date()).toISOString();
|
|
7456
7609
|
while (true) {
|
|
7457
7610
|
const row = this.sql.exec(
|
|
@@ -7463,7 +7616,16 @@ var JobRoom = class extends BaseRoom {
|
|
|
7463
7616
|
cutoff
|
|
7464
7617
|
).toArray()[0];
|
|
7465
7618
|
if (!row) return;
|
|
7466
|
-
|
|
7619
|
+
const execution = this.executeJob(row);
|
|
7620
|
+
if (this.backgroundJobTypes.has(row.type)) {
|
|
7621
|
+
this.state.waitUntil(
|
|
7622
|
+
execution.catch(
|
|
7623
|
+
(error) => console.error(`[jobs] background execution failed: ${loggableError(error)}`)
|
|
7624
|
+
).finally(() => this.scheduleNextAlarm())
|
|
7625
|
+
);
|
|
7626
|
+
return;
|
|
7627
|
+
}
|
|
7628
|
+
await execution;
|
|
7467
7629
|
}
|
|
7468
7630
|
}
|
|
7469
7631
|
async executeJob(row) {
|
|
@@ -7500,7 +7662,7 @@ var JobRoom = class extends BaseRoom {
|
|
|
7500
7662
|
this.inFlight.delete(row.id);
|
|
7501
7663
|
}
|
|
7502
7664
|
const currentStatus = this.readStatus(row.id);
|
|
7503
|
-
if (currentStatus
|
|
7665
|
+
if (currentStatus !== "running") {
|
|
7504
7666
|
this.continueState = null;
|
|
7505
7667
|
return;
|
|
7506
7668
|
}
|
|
@@ -7632,6 +7794,10 @@ var JobRoom = class extends BaseRoom {
|
|
|
7632
7794
|
// Scheduling / recovery / pruning
|
|
7633
7795
|
// ==========================================================================
|
|
7634
7796
|
scheduleNextAlarm() {
|
|
7797
|
+
if (this.inFlight.size > 0) {
|
|
7798
|
+
this.state.storage.setAlarm(Date.now() + BACKGROUND_ALARM_MS);
|
|
7799
|
+
return;
|
|
7800
|
+
}
|
|
7635
7801
|
const queued = this.sql.exec(
|
|
7636
7802
|
`SELECT next_run_at FROM jobs
|
|
7637
7803
|
WHERE status = 'queued'
|
|
@@ -7665,6 +7831,7 @@ var JobRoom = class extends BaseRoom {
|
|
|
7665
7831
|
cutoff
|
|
7666
7832
|
).toArray();
|
|
7667
7833
|
for (const r of stuck) {
|
|
7834
|
+
if (this.inFlight.has(r.id)) continue;
|
|
7668
7835
|
if (r.attempts >= r.max_attempts) {
|
|
7669
7836
|
this.sql.exec(
|
|
7670
7837
|
`UPDATE jobs
|
|
@@ -8619,6 +8786,7 @@ function isTestAccountTier(tier) {
|
|
|
8619
8786
|
|
|
8620
8787
|
// src/server/auth/betterAuth.ts
|
|
8621
8788
|
import { betterAuth } from "better-auth";
|
|
8789
|
+
import { getOAuthState } from "better-auth/api";
|
|
8622
8790
|
import { organization, twoFactor } from "better-auth/plugins";
|
|
8623
8791
|
import { SignJWT, importPKCS8 } from "jose";
|
|
8624
8792
|
function createDeepSpaceAuth(config) {
|
|
@@ -8662,6 +8830,7 @@ function createDeepSpaceAuth(config) {
|
|
|
8662
8830
|
emailAndPassword: {
|
|
8663
8831
|
enabled: config.emailAndPassword ?? true
|
|
8664
8832
|
},
|
|
8833
|
+
...config.userAdditionalFields ? { user: { additionalFields: config.userAdditionalFields } } : {},
|
|
8665
8834
|
socialProviders,
|
|
8666
8835
|
trustedOrigins: config.trustedOrigins ?? [
|
|
8667
8836
|
"https://deep.space",
|
|
@@ -8697,29 +8866,45 @@ function createDeepSpaceAuth(config) {
|
|
|
8697
8866
|
// keys) — that case is caught by the runtime assertions in
|
|
8698
8867
|
// __tests__/betterAuth-hooks.test.ts, which read the wired hook back
|
|
8699
8868
|
// off auth.options. Don't rename the key without running them.
|
|
8700
|
-
databaseHooks: buildDatabaseHooks(config.onUserCreated),
|
|
8869
|
+
databaseHooks: buildDatabaseHooks(config.beforeUserCreate, config.onUserCreated),
|
|
8701
8870
|
plugins: [organization(), twoFactor()]
|
|
8702
8871
|
});
|
|
8703
8872
|
}
|
|
8704
|
-
function buildDatabaseHooks(onUserCreated) {
|
|
8705
|
-
if (!onUserCreated) return void 0;
|
|
8873
|
+
function buildDatabaseHooks(beforeUserCreate, onUserCreated) {
|
|
8874
|
+
if (!beforeUserCreate && !onUserCreated) return void 0;
|
|
8706
8875
|
return {
|
|
8707
8876
|
user: {
|
|
8708
8877
|
create: {
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8878
|
+
...beforeUserCreate ? {
|
|
8879
|
+
before: async (user, ctx) => beforeUserCreate(
|
|
8880
|
+
user,
|
|
8881
|
+
ctx ? {
|
|
8882
|
+
request: ctx.request,
|
|
8883
|
+
headers: ctx.headers ?? void 0,
|
|
8884
|
+
path: ctx.path,
|
|
8885
|
+
// Read from this package's Better Auth instance. In a
|
|
8886
|
+
// pnpm graph the consuming worker can have another
|
|
8887
|
+
// physical copy whose request-state singleton differs.
|
|
8888
|
+
oauthState: await getOAuthState()
|
|
8889
|
+
} : null
|
|
8890
|
+
)
|
|
8891
|
+
} : {},
|
|
8892
|
+
...onUserCreated ? {
|
|
8893
|
+
after: async (user, ctx) => {
|
|
8894
|
+
try {
|
|
8895
|
+
await onUserCreated(
|
|
8896
|
+
{ id: user.id, email: user.email, name: user.name },
|
|
8897
|
+
// Forward BOTH request and standalone headers — some better-auth
|
|
8898
|
+
// entry points populate ctx.headers without a request.
|
|
8899
|
+
ctx ? { request: ctx.request, headers: ctx.headers ?? void 0 } : null
|
|
8900
|
+
);
|
|
8901
|
+
} catch (err) {
|
|
8902
|
+
console.error(
|
|
8903
|
+
`[deepspace] onUserCreated hook failed (signup unaffected): ${loggableError(err)}`
|
|
8904
|
+
);
|
|
8905
|
+
}
|
|
8721
8906
|
}
|
|
8722
|
-
}
|
|
8907
|
+
} : {}
|
|
8723
8908
|
}
|
|
8724
8909
|
}
|
|
8725
8910
|
};
|
|
@@ -8924,6 +9109,7 @@ export {
|
|
|
8924
9109
|
SYSTEM_COLLECTION_SCHEMAS,
|
|
8925
9110
|
SYSTEM_MAINTAINED_COLUMNS,
|
|
8926
9111
|
SYSTEM_MANAGED_COLUMNS,
|
|
9112
|
+
SandboxFileError,
|
|
8927
9113
|
SchemaRegistry,
|
|
8928
9114
|
SubscriptionAuthError,
|
|
8929
9115
|
SubscriptionRequiredError,
|
|
@@ -8946,6 +9132,7 @@ export {
|
|
|
8946
9132
|
buildTableSelect,
|
|
8947
9133
|
buildUiParts,
|
|
8948
9134
|
callRegistry,
|
|
9135
|
+
callableFromSandbox,
|
|
8949
9136
|
canCreate,
|
|
8950
9137
|
canDelete,
|
|
8951
9138
|
canRead,
|
|
@@ -8955,6 +9142,7 @@ export {
|
|
|
8955
9142
|
captureScreenshot,
|
|
8956
9143
|
checkFieldPermissions,
|
|
8957
9144
|
checkUnclaimedOwnerTransition,
|
|
9145
|
+
codeExecutionTool,
|
|
8958
9146
|
coerceValue,
|
|
8959
9147
|
collectionTableName,
|
|
8960
9148
|
columnId,
|
|
@@ -8975,6 +9163,7 @@ export {
|
|
|
8975
9163
|
executeQuery,
|
|
8976
9164
|
folderFilter,
|
|
8977
9165
|
formatBytes,
|
|
9166
|
+
forwardSandboxContainer,
|
|
8978
9167
|
getAllUsers,
|
|
8979
9168
|
getChat,
|
|
8980
9169
|
getOrCreateYjsDoc,
|
|
@@ -9033,8 +9222,12 @@ export {
|
|
|
9033
9222
|
resolveAppRole,
|
|
9034
9223
|
resolveColumn,
|
|
9035
9224
|
resolveSessionReadAuth,
|
|
9225
|
+
reuseSandbox,
|
|
9036
9226
|
rowToData,
|
|
9037
9227
|
runMigrations,
|
|
9228
|
+
sandboxFiles,
|
|
9229
|
+
sandboxOutputs,
|
|
9230
|
+
sandboxUpload,
|
|
9038
9231
|
saveYjsDoc,
|
|
9039
9232
|
storageLimitForTier,
|
|
9040
9233
|
storageQuotaMessage,
|