deepspace 0.15.0 → 0.16.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/dist/server.d.ts CHANGED
@@ -219,28 +219,24 @@ declare const UPLOAD_PART_BYTES: number;
219
219
  */
220
220
  declare const MAX_UPLOAD_PARTS: number;
221
221
  /**
222
- * Per-tier storage schedule, in bytes. Product semantics, not a deployment
223
- * dial — the one table both storage allocations admit against:
222
+ * The customer-visible storage schedule, in bytes, PER ACCOUNT — the total an
223
+ * owner's uploaded files may occupy across every app they own, not a budget
224
+ * each app gets separately. This is the number the billing page advertises.
224
225
  *
225
- * - the repo store (Git packs + retained bundles + deploy assets), admitted
226
- * by the app's Repo DO (`platform/deploy-worker/src/vc/storage-quota.ts`)
227
- * - the app-files allocation, admitted by the shared files handler
228
- * (`server/utils/scoped-r2-files.ts`)
226
+ * Admitted by the shared files handler (`server/utils/scoped-r2-files.ts`),
227
+ * which sums the owner's apps rather than the one being written to.
229
228
  *
230
- * Each allocation gets the full amount independently; they are different
231
- * buckets with different writers (docs/platform/r2-storage.md). The
232
- * customer-visible "storage" number is the app-files allocation — the repo
233
- * store is platform bookkeeping that degrades (drops rollback retention)
234
- * rather than surfacing here.
229
+ * It deliberately does NOT govern the repo store — see
230
+ * `REPO_STORE_LIMIT_BYTES` below, and docs/platform/r2-storage.md.
235
231
  */
236
- declare const APP_STORAGE_LIMIT_BYTES: {
232
+ declare const ACCOUNT_STORAGE_LIMIT_BYTES: {
237
233
  readonly test: 0;
238
234
  readonly free: number;
239
235
  readonly starter: number;
240
236
  readonly premium: number;
241
237
  readonly admin: number;
242
238
  };
243
- /** The storage limit for a billing tier; unknown or missing tiers get free. */
239
+ /** The account storage limit for a billing tier; unknown or missing tiers get free. */
244
240
  declare function storageLimitForTier(tier: string | null | undefined): number;
245
241
  /** The one storage-quota refusal sentence. `usedBytes` is net of any object
246
242
  * the upload replaces, so an upsert is charged only for its growth. */
@@ -323,15 +319,26 @@ interface ScopedR2Config {
323
319
  * applies.
324
320
  */
325
321
  interface StorageAdmission {
326
- /** The WHOLE allocation the quota governs — the app prefix, not the
327
- * (possibly deeper) scope prefix a request resolved to. */
322
+ /** This app's own prefix — the one the incoming write lands under. */
328
323
  prefix: string;
329
324
  /**
330
325
  * Resolve the owner's limit in bytes. `null` means the lookup failed;
331
- * writes then fail closed (503) rather than admitting unmetered storage,
332
- * mirroring the repo store's `StorageBillingUnavailableError`.
326
+ * writes then fail closed (503) rather than admitting unmetered storage.
333
327
  */
334
328
  limitBytes: () => Promise<number | null>;
329
+ /**
330
+ * The OTHER app prefixes this owner's storage spans, if any.
331
+ *
332
+ * The limit is per ACCOUNT, not per app: an owner's allocation is the total
333
+ * across everything they own, so admission has to see the siblings too. `[]`
334
+ * for an owner with one app, which is the common case and costs exactly what
335
+ * it did when this was a per-app limit.
336
+ *
337
+ * Returning `null` means the owner's apps could not be enumerated. Like a
338
+ * failed limit lookup that fails the write closed rather than admitting a
339
+ * write against an allocation we cannot measure.
340
+ */
341
+ siblingPrefixes?: () => Promise<string[] | null>;
335
342
  }
336
343
  interface ScopedR2Auth {
337
344
  userId: string | null;
@@ -4659,4 +4666,4 @@ declare class RefundError extends Error {
4659
4666
  constructor(message: string, status: number);
4660
4667
  }
4661
4668
 
4662
- export { AI_CHATS_SCHEMA, AI_MESSAGES_SCHEMA, ALLOWED_BINDING_TYPES, APP_ID_RE, APP_NAME_RULES, APP_STORAGE_LIMIT_BYTES, AUTO_PROVISIONABLE_TYPES, AUTO_PROVISION_SENTINEL, type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AppNameResolution, type AppNameValidation, type AuthWorkerEnv, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, COST_RATES, CancelSubscriptionError, type CancelSubscriptionOpts, type CancelSubscriptionResult, CanvasRoom, type CanvasShape, type ChatContextConfig, type ChatMessageRow, type ChatRow, type ChatTurn, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ComposioToolsOptions, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, type CustomBinding, type CustomBindingManifest, DEFAULT_CONTEXT_CONFIG, DEFAULT_DO_MANIFEST, DEFAULT_QUERY_LIMIT, DEPLOYED_APPS_DEFAULTS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DebugApiContext, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAgentDiagnosticContext, DeepSpaceAgentModelError, DeepSpaceAgentProfileError, type DeepSpaceAgentStream, type DeepSpaceAgentStreamOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, type DoMigrationDirective, type DoMigrationPlan, type ExistingDOBinding, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GameInput, GameRoom, type GameRoomConfig, type GetActionData, type GlobalDOType, type Job, type JobContext, JobRoom, type JobRoomConfig, type JobStatus, type JwtClaims, type JwtVerifierConfig, MAX_APP_FILE_BYTES, MAX_DEPLOY_ASSET_FILE_BYTES, MAX_UPLOAD_PARTS, MAX_UPLOAD_REQUEST_BYTES, MESSAGES_SCHEMA, type MutateActionData, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlanInfo, type PlatformWorkerEnv, type Player, type PrefixResult, type PresencePeer, PresenceRoom, type QueryActionData, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, REGISTRY_INSTANCE, RESERVED_BINDING_NAMES, RESERVED_COLLECTION_NAMES, RESOURCE_ID_RE, type RecordContext, RecordRoom, type RecordRoomConfig, RefundError, type RefundOpts, type RefundResult, RegistryClientError, type ResolvedColumn, type ResolvedPermission, type RolePermissions, type RunMigrationsResult, STRICT_APP_ID_RE, SYSTEM_COLLECTION_SCHEMAS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ScreenshotEnv, type ScreenshotResult, type SharedConnection, type StorageAdmission, type StoredRecord, SubscriptionAuthError, type SubscriptionContext, type SubscriptionRead, SubscriptionRequiredError, type SubscriptionStatus, type Summarizer, TEST_ACCOUNT_EMAIL_SUFFIX, type TokenDebugInfo, type ToolResult, type ToolSchema, type ToolsApiContext, UPLOAD_PART_BYTES, USERS_COLUMNS, type User, type UserAttachment, type UserContext, VOTING_SCHEMAS, type ValidationError, type VerifiedAuth, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, type YjsContext, YjsRoom, analyzePermissions, apiWorkerFetch, appendMessage, applyAiToolDefaults, applySlidingWindow, authWorkerFetch, bindingManifestFromOutputConfig, broadcastChange, broadcastYjsUpdate, buildCronContext, buildTableSelect, buildUiParts, callRegistry, canCreate, canDelete, canRead, canUpdate, cancelSubscription, capToolResultSize, captureScreenshot, checkFieldPermissions, coerceValue, collectionTableName, columnId, composioTools, computeDoMigration, computeHmacHex, createChat, createDeepSpaceAI, createDeepSpaceAuth, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, deepSpaceAgentErrorSummary, deleteChatCascade, deleteRecord, enqueueJob, executeQuery, formatBytes, getAllUsers, getChat, getGlobalDOSchemas, getGlobalDOType, getOrCreateYjsDoc, getRecord, getRolePermissions, getSubscription, getUser, getYjsDocKey, handleApiRequest, handleDelete, handlePut, handleSetRole, handleSubscribe, handleToolsRequest, handleUnsubscribe, handleUserList, handleUserUpdate, handleYjsBinaryMessage, handleYjsJoin, handleYjsLeave, isAutoProvision, isFilesVerbPath, isOwner, isTestAccountClaims, isTestAccountEmail, isTestAccountTier, lintSchema, loadMessages, makeDefaultSummarizer, matchOrigin, meterAi, meterUsage, meterVectorize, noopPermissionContext, parseQuotaLimit, platformCorsPatterns, platformWorkerFetch, prepareMessagesWithCompaction, priceBindingUsageEvent, putRecord, readRecord, recordMatchesSubscription, refundInvoice, registerUser, registryErrorJson, registryStub, requireSubscription, resolveAppName, resolveColumn, rowToData, runMigrations, saveYjsDoc, storageLimitForTier, storageQuotaMessage, streamDeepSpaceAgent, timingSafeEqualHex, timingSafeEqualStrings, totalChars, truncateOldToolResults, turnsToCoreMessages, unwrapToolOutput, updateChat, validateAppName, validateBindingManifest, validateDoManifest, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema };
4669
+ export { ACCOUNT_STORAGE_LIMIT_BYTES, AI_CHATS_SCHEMA, AI_MESSAGES_SCHEMA, ALLOWED_BINDING_TYPES, APP_ID_RE, APP_NAME_RULES, AUTO_PROVISIONABLE_TYPES, AUTO_PROVISION_SENTINEL, type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AppNameResolution, type AppNameValidation, type AuthWorkerEnv, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, COST_RATES, CancelSubscriptionError, type CancelSubscriptionOpts, type CancelSubscriptionResult, CanvasRoom, type CanvasShape, type ChatContextConfig, type ChatMessageRow, type ChatRow, type ChatTurn, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ComposioToolsOptions, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, type CustomBinding, type CustomBindingManifest, DEFAULT_CONTEXT_CONFIG, DEFAULT_DO_MANIFEST, DEFAULT_QUERY_LIMIT, DEPLOYED_APPS_DEFAULTS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DebugApiContext, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAgentDiagnosticContext, DeepSpaceAgentModelError, DeepSpaceAgentProfileError, type DeepSpaceAgentStream, type DeepSpaceAgentStreamOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, type DoMigrationDirective, type DoMigrationPlan, type ExistingDOBinding, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GameInput, GameRoom, type GameRoomConfig, type GetActionData, type GlobalDOType, type Job, type JobContext, JobRoom, type JobRoomConfig, type JobStatus, type JwtClaims, type JwtVerifierConfig, MAX_APP_FILE_BYTES, MAX_DEPLOY_ASSET_FILE_BYTES, MAX_UPLOAD_PARTS, MAX_UPLOAD_REQUEST_BYTES, MESSAGES_SCHEMA, type MutateActionData, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlanInfo, type PlatformWorkerEnv, type Player, type PrefixResult, type PresencePeer, PresenceRoom, type QueryActionData, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, REGISTRY_INSTANCE, RESERVED_BINDING_NAMES, RESERVED_COLLECTION_NAMES, RESOURCE_ID_RE, type RecordContext, RecordRoom, type RecordRoomConfig, RefundError, type RefundOpts, type RefundResult, RegistryClientError, type ResolvedColumn, type ResolvedPermission, type RolePermissions, type RunMigrationsResult, STRICT_APP_ID_RE, SYSTEM_COLLECTION_SCHEMAS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ScreenshotEnv, type ScreenshotResult, type SharedConnection, type StorageAdmission, type StoredRecord, SubscriptionAuthError, type SubscriptionContext, type SubscriptionRead, SubscriptionRequiredError, type SubscriptionStatus, type Summarizer, TEST_ACCOUNT_EMAIL_SUFFIX, type TokenDebugInfo, type ToolResult, type ToolSchema, type ToolsApiContext, UPLOAD_PART_BYTES, USERS_COLUMNS, type User, type UserAttachment, type UserContext, VOTING_SCHEMAS, type ValidationError, type VerifiedAuth, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, type YjsContext, YjsRoom, analyzePermissions, apiWorkerFetch, appendMessage, applyAiToolDefaults, applySlidingWindow, authWorkerFetch, bindingManifestFromOutputConfig, broadcastChange, broadcastYjsUpdate, buildCronContext, buildTableSelect, buildUiParts, callRegistry, canCreate, canDelete, canRead, canUpdate, cancelSubscription, capToolResultSize, captureScreenshot, checkFieldPermissions, coerceValue, collectionTableName, columnId, composioTools, computeDoMigration, computeHmacHex, createChat, createDeepSpaceAI, createDeepSpaceAuth, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, deepSpaceAgentErrorSummary, deleteChatCascade, deleteRecord, enqueueJob, executeQuery, formatBytes, getAllUsers, getChat, getGlobalDOSchemas, getGlobalDOType, getOrCreateYjsDoc, getRecord, getRolePermissions, getSubscription, getUser, getYjsDocKey, handleApiRequest, handleDelete, handlePut, handleSetRole, handleSubscribe, handleToolsRequest, handleUnsubscribe, handleUserList, handleUserUpdate, handleYjsBinaryMessage, handleYjsJoin, handleYjsLeave, isAutoProvision, isFilesVerbPath, isOwner, isTestAccountClaims, isTestAccountEmail, isTestAccountTier, lintSchema, loadMessages, makeDefaultSummarizer, matchOrigin, meterAi, meterUsage, meterVectorize, noopPermissionContext, parseQuotaLimit, platformCorsPatterns, platformWorkerFetch, prepareMessagesWithCompaction, priceBindingUsageEvent, putRecord, readRecord, recordMatchesSubscription, refundInvoice, registerUser, registryErrorJson, registryStub, requireSubscription, resolveAppName, resolveColumn, rowToData, runMigrations, saveYjsDoc, storageLimitForTier, storageQuotaMessage, streamDeepSpaceAgent, timingSafeEqualHex, timingSafeEqualStrings, totalChars, truncateOldToolResults, turnsToCoreMessages, unwrapToolOutput, updateChat, validateAppName, validateBindingManifest, validateDoManifest, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema };
package/dist/server.js CHANGED
@@ -180,19 +180,21 @@ var MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024;
180
180
  var UPLOAD_PART_BYTES = 20 * 1024 * 1024;
181
181
  var MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES);
182
182
  var MAX_BASE64_UPLOAD_BYTES = Math.floor(MAX_UPLOAD_REQUEST_BYTES * 3 / 4);
183
- var APP_STORAGE_LIMIT_BYTES = {
183
+ var GIB = 1024 * 1024 * 1024;
184
+ var ACCOUNT_STORAGE_LIMIT_BYTES = {
184
185
  test: 0,
185
- free: 128 * 1024 * 1024,
186
- starter: 512 * 1024 * 1024,
187
- premium: 2 * 1024 * 1024 * 1024,
188
- admin: 10 * 1024 * 1024 * 1024
186
+ free: 1 * GIB,
187
+ starter: 10 * GIB,
188
+ premium: 30 * GIB,
189
+ admin: 100 * GIB
189
190
  };
190
191
  function storageLimitForTier(tier) {
191
- if (tier && Object.hasOwn(APP_STORAGE_LIMIT_BYTES, tier)) {
192
- return APP_STORAGE_LIMIT_BYTES[tier];
192
+ if (tier && Object.hasOwn(ACCOUNT_STORAGE_LIMIT_BYTES, tier)) {
193
+ return ACCOUNT_STORAGE_LIMIT_BYTES[tier];
193
194
  }
194
- return APP_STORAGE_LIMIT_BYTES.free;
195
+ return ACCOUNT_STORAGE_LIMIT_BYTES.free;
195
196
  }
197
+ var REPO_STORE_LIMIT_BYTES = 256 * 1024 * 1024;
196
198
  function storageQuotaMessage(incomingBytes, usedBytes, limitBytes) {
197
199
  const free = Math.max(0, limitBytes - usedBytes);
198
200
  return `This upload needs ${formatBytes(incomingBytes)} but the app's file storage has ${formatBytes(free)} free (${formatBytes(usedBytes)} of ${formatBytes(limitBytes)} used). Delete files with \`deepspace app files rm <key>\`, or upgrade the owner's plan.`;
@@ -283,7 +285,7 @@ function declaredLength(request, what) {
283
285
  function isKeyWithinPrefix(key, prefix) {
284
286
  return key.startsWith(prefix);
285
287
  }
286
- async function allocationUsage(bucket, prefix) {
288
+ async function prefixUsage(bucket, prefix) {
287
289
  let total = 0;
288
290
  let cursor;
289
291
  do {
@@ -293,6 +295,13 @@ async function allocationUsage(bucket, prefix) {
293
295
  } while (cursor !== void 0);
294
296
  return total;
295
297
  }
298
+ async function allocationUsage(bucket, storage) {
299
+ const siblings = storage.siblingPrefixes ? await storage.siblingPrefixes() : [];
300
+ if (siblings === null) return null;
301
+ const prefixes = [storage.prefix, ...siblings.filter((p) => p !== storage.prefix)];
302
+ const totals = await Promise.all(prefixes.map((prefix) => prefixUsage(bucket, prefix)));
303
+ return totals.reduce((sum, bytes) => sum + bytes, 0);
304
+ }
296
305
  async function admitStorage(bucket, storage, incomingBytes, replacesKey) {
297
306
  if (!storage) return null;
298
307
  const limit = await storage.limitBytes();
@@ -303,7 +312,14 @@ async function admitStorage(bucket, storage, incomingBytes, replacesKey) {
303
312
  "storage_limit_unavailable"
304
313
  );
305
314
  }
306
- const stored = await allocationUsage(bucket, storage.prefix);
315
+ const stored = await allocationUsage(bucket, storage);
316
+ if (stored === null) {
317
+ return fail(
318
+ 503,
319
+ "The storage already used by this account could not be measured; nothing was uploaded. Retry shortly.",
320
+ "storage_limit_unavailable"
321
+ );
322
+ }
307
323
  const replaced = replacesKey === null ? 0 : (await bucket.head(replacesKey))?.size ?? 0;
308
324
  const used = stored - replaced;
309
325
  if (used + incomingBytes <= limit) return null;
@@ -319,7 +335,11 @@ async function admitAssembledObject(bucket, storage, key, objectBytes) {
319
335
  console.error("[files:quota] limit unavailable at complete; keeping", key);
320
336
  return null;
321
337
  }
322
- const stored = await allocationUsage(bucket, storage.prefix);
338
+ const stored = await allocationUsage(bucket, storage);
339
+ if (stored === null) {
340
+ console.error("[files:quota] account usage unmeasurable at complete; keeping", key);
341
+ return null;
342
+ }
323
343
  if (stored <= limit) return null;
324
344
  await bucket.delete(key).catch(() => void 0);
325
345
  return fail(
@@ -716,9 +736,10 @@ async function handleList(bucket, prefix, url, scope, auth) {
716
736
  });
717
737
  let storage;
718
738
  if (auth.storage) {
719
- storage = { usedBytes: await allocationUsage(bucket, auth.storage.prefix) };
739
+ const usedBytes = await allocationUsage(bucket, auth.storage);
740
+ if (usedBytes !== null) storage = { usedBytes };
720
741
  const limitBytes = await auth.storage.limitBytes();
721
- if (limitBytes !== null) storage.limitBytes = limitBytes;
742
+ if (storage && limitBytes !== null) storage.limitBytes = limitBytes;
722
743
  }
723
744
  return Response.json(
724
745
  { files, truncated: listed.truncated, ...storage ? { storage } : {} },
@@ -7984,12 +8005,12 @@ var RefundError = class extends Error {
7984
8005
  }
7985
8006
  };
7986
8007
  export {
8008
+ ACCOUNT_STORAGE_LIMIT_BYTES,
7987
8009
  AI_CHATS_SCHEMA,
7988
8010
  AI_MESSAGES_SCHEMA,
7989
8011
  ALLOWED_BINDING_TYPES,
7990
8012
  APP_ID_RE,
7991
8013
  APP_NAME_RULES,
7992
- APP_STORAGE_LIMIT_BYTES,
7993
8014
  AUTO_PROVISIONABLE_TYPES,
7994
8015
  AUTO_PROVISION_SENTINEL,
7995
8016
  BASE_USERS_SCHEMA,