@protobase/server 0.52.1 → 0.53.1

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.
@@ -1,7 +1,5 @@
1
1
  import { HttpProblem } from "../problem.js";
2
- import { forcedJson } from "./result-budget.js";
3
2
  import { defineTool } from "./tools.js";
4
- import { cutRecordNote } from "./budgeted-page.js";
5
3
  import { writeText } from "./write-messages.js";
6
4
  import { describedResource } from "./write-prompts.js";
7
5
  import { fieldsParameter } from "./write-echo.js";
@@ -127,7 +125,7 @@ const batchWriteTool = (input = {}) => defineTool({
127
125
  const answer = await context.approve(approval);
128
126
  if (answer !== "approved") return `The user did not approve this batch (${answer}); nothing was written.`;
129
127
  const befores = await recordsBefore(context, operations);
130
- return context.records.batch(operations).then(async (results) => forcedJson({ results: await batchEcho(context, operations, results, befores, fields) }, cutRecordNote), refused);
128
+ return context.records.batch(operations).then(async (results) => JSON.stringify({ results: await batchEcho(context, operations, results, befores, fields) }), refused);
131
129
  }
132
130
  });
133
131
  //#endregion
@@ -1,7 +1,7 @@
1
- import { budgetedQueryResult } from "./query-result-budget.js";
2
1
  import { runReadOnlyQuery, runReadWriteQuery } from "./run-query.js";
3
2
  import { defineTool } from "./tools.js";
4
3
  //#region src/assistant/query-tools.ts
4
+ const maxResultLength = 2e4;
5
5
  const sqlParameter = (description) => ({
6
6
  type: "string",
7
7
  description
@@ -11,7 +11,7 @@ const refused = (error) => {
11
11
  if (isDatabaseError(error)) return `Postgres refused the statement: ${error.message}`;
12
12
  throw error;
13
13
  };
14
- const shown = (result, statement, context, more) => {
14
+ const shown = (result, statement, context) => {
15
15
  context.show({
16
16
  type: "table",
17
17
  id: crypto.randomUUID(),
@@ -20,9 +20,9 @@ const shown = (result, statement, context, more) => {
20
20
  caption: statement,
21
21
  ...result.truncated && { truncated: true }
22
22
  });
23
- return budgetedQueryResult(result, more);
23
+ const json = JSON.stringify(result);
24
+ return json.length > maxResultLength ? `${json.slice(0, maxResultLength)}... (cut off)` : json;
24
25
  };
25
- const readOn = (count) => `; add OFFSET ${count} to the statement to read on`;
26
26
  const statementOf = (args) => typeof args.sql === "string" && args.sql.trim() ? args.sql : void 0;
27
27
  /**
28
28
  * Lets the model read the app's database: one SELECT per call, in a read-only transaction (see `runReadOnlyQuery`).
@@ -31,7 +31,7 @@ const statementOf = (args) => typeof args.sql === "string" && args.sql.trim() ?
31
31
  */
32
32
  const readOnlyQueryTool = (db, options = {}) => defineTool({
33
33
  name: "run_read_only_query",
34
- description: `Runs one PostgreSQL SELECT on the app's database in a read-only transaction and returns up to ${options.rowLimit ?? 200} rows as JSON ({ columns, rows, truncated }); a result too large for one answer holds fewer rows, with long values cut and named in cut_fields, and a note on reading the rest. The user sees the rows as a table. Use it to answer questions about the data. It reads the app's resource tables only. It cannot change anything.`,
34
+ description: `Runs one PostgreSQL SELECT on the app's database in a read-only transaction and returns up to ${options.rowLimit ?? 200} rows as JSON ({ columns, rows, truncated }). The user sees the rows as a table. Use it to answer questions about the data. It reads the app's resource tables only. It cannot change anything.`,
35
35
  parameters: {
36
36
  type: "object",
37
37
  properties: { sql: sqlParameter("One SELECT statement, without a trailing semicolon.") },
@@ -44,7 +44,7 @@ const readOnlyQueryTool = (db, options = {}) => defineTool({
44
44
  run: async (args, context) => {
45
45
  const statement = statementOf(args);
46
46
  if (!statement) return "Pass the statement as { \"sql\": \"select ...\" }.";
47
- return runReadOnlyQuery(db, statement, options).then((result) => shown(result, statement, context, readOn), refused);
47
+ return runReadOnlyQuery(db, statement, options).then((result) => shown(result, statement, context), refused);
48
48
  }
49
49
  });
50
50
  /**
@@ -79,7 +79,7 @@ const readWriteQueryTool = (db, options = {}) => defineTool({
79
79
  code: statement
80
80
  });
81
81
  if (answer !== "approved") return `The user did not approve the statement (${answer}); nothing ran.`;
82
- return runReadWriteQuery(db, statement, options).then((result) => shown(result, statement, context, () => ""), refused);
82
+ return runReadWriteQuery(db, statement, options).then((result) => shown(result, statement, context), refused);
83
83
  }
84
84
  });
85
85
  //#endregion
@@ -1,14 +1,16 @@
1
1
  import { AssistantTool } from "./tools.js";
2
2
  //#region src/assistant/record-tools.d.ts
3
- /** Lists the resources the caller may read, with their fields, labels and which fields filters and sorts take. */
3
+ /** Lists the resources the caller may read, small enough for a first call: names, labels and a line of help each, without fields. */
4
4
  export declare const listResourcesTool: () => AssistantTool;
5
+ /** Describes one resource the caller may read, with its fields, their labels, help and values, and which fields filters and sorts take. */
6
+ export declare const describeResourceTool: () => AssistantTool;
5
7
  /** Finds records of a resource with the REST API's filters, sorting and pages, under the caller's access rules. */
6
8
  export declare const listRecordsTool: () => AssistantTool;
7
9
  /** Reads one record by its key, under the caller's access rules. */
8
10
  export declare const getRecordTool: () => AssistantTool;
9
11
  /**
10
- * `list_resources`, `list_records`, `count_records` and `get_record`: reading and counting the app's records as the
11
- * caller, through the REST API's rules.
12
+ * `list_resources`, `describe_resource`, `list_records`, `count_records` and `get_record`: reading and counting the
13
+ * app's records as the caller, through the REST API's rules.
12
14
  */
13
15
  export declare const readRecordTools: () => AssistantTool[];
14
16
  //#endregion
@@ -1,8 +1,6 @@
1
- import { forcedJson } from "./result-budget.js";
1
+ import { notFound } from "../problem.js";
2
2
  import { defineTool } from "./tools.js";
3
3
  import { invalidShowDeleted, keyOf, keyParameter, refused, resourceOf, resourceParameter, showDeletedOf, showDeletedParameter } from "./record-tool-args.js";
4
- import { budgetedPage, cutRecordNote } from "./budgeted-page.js";
5
- import { fieldPiece, pieceParameters, readsPiece } from "./record-field-piece.js";
6
4
  import { countRecordsTool } from "./count-records-tool.js";
7
5
  //#region src/assistant/record-tools.ts
8
6
  const readOnly = {
@@ -26,30 +24,58 @@ const fieldEntry = (field, view) => {
26
24
  sortable: field.sortable
27
25
  };
28
26
  };
29
- const resourceEntry = (model, view) => ({
27
+ /** The first line of a view's help: list_resources stays a line per resource however long the help is. */
28
+ const firstLine = (text) => text.split("\n", 1)[0].trim();
29
+ const resourceSummary = (model, view) => ({
30
30
  name: model.name,
31
31
  ...view?.names && { label: view.names.plural },
32
- ...view?.help && { help: view.help },
32
+ ...view?.help && { help: firstLine(view.help) },
33
33
  key: model.primaryKey,
34
- soft_delete: model.softDelete !== void 0,
34
+ soft_delete: model.softDelete !== void 0
35
+ });
36
+ const resourceDetail = (model, view) => ({
37
+ ...resourceSummary(model, view),
38
+ ...view?.help && { help: view.help },
35
39
  fields: Object.values(model.fields).map((field) => fieldEntry(field, view))
36
40
  });
37
- const described = ({ models, views }) => models.map((model) => resourceEntry(model, views.find((view) => view.resource === model.name)));
38
- /** Lists the resources the caller may read, with their fields, labels and which fields filters and sorts take. */
41
+ const viewOf = ({ views }, model) => views.find((view) => view.resource === model.name);
42
+ /** Lists the resources the caller may read, small enough for a first call: names, labels and a line of help each, without fields. */
39
43
  const listResourcesTool = () => defineTool({
40
44
  name: "list_resources",
41
- description: "Lists the resources you may read, as JSON: each with its name, label, key fields, whether it soft deletes (soft_delete) and fields (name, type, label, enum values, the resource a relation links to, and whether it is optional, read-only, filterable and sortable). Filters and order_by in list_records take only fields marked filterable or sortable.",
45
+ description: "Lists the resources you may read, as JSON: each with its name, label, the first line of its help, key fields and whether it soft deletes (soft_delete). describe_resource gives a resource's fields.",
42
46
  parameters: {
43
47
  type: "object",
44
48
  properties: {}
45
49
  },
46
50
  annotations: readOnly,
47
- run: async (_args, context) => JSON.stringify(described(await context.resources()))
51
+ run: async (_args, context) => {
52
+ const resources = await context.resources();
53
+ return JSON.stringify(resources.models.map((model) => resourceSummary(model, viewOf(resources, model))));
54
+ }
55
+ });
56
+ /** Describes one resource the caller may read, with its fields, their labels, help and values, and which fields filters and sorts take. */
57
+ const describeResourceTool = () => defineTool({
58
+ name: "describe_resource",
59
+ description: "Describes one resource you may read, as JSON: its name, label, help, key fields, whether it soft deletes (soft_delete) and fields (name, type, label, help, enum values, the resource a relation links to, and whether it is optional, read-only, filterable and sortable). Filters and order_by in list_records take only fields marked filterable or sortable.",
60
+ parameters: {
61
+ type: "object",
62
+ properties: { resource: resourceParameter },
63
+ required: ["resource"]
64
+ },
65
+ annotations: readOnly,
66
+ run: async (args, context) => {
67
+ const name = resourceOf(args);
68
+ if (!name) return "Pass the resource as { \"resource\": \"<name>\" }.";
69
+ const resources = await context.resources();
70
+ const model = resources.models.find((candidate) => candidate.name === name);
71
+ if (!model) return refused(notFound(`No resource ${name} you may read; list_resources lists them.`));
72
+ return JSON.stringify(resourceDetail(model, viewOf(resources, model)));
73
+ }
48
74
  });
49
75
  /** Finds records of a resource with the REST API's filters, sorting and pages, under the caller's access rules. */
50
76
  const listRecordsTool = () => defineTool({
51
77
  name: "list_records",
52
- description: `Finds records of a resource and returns a page of them as JSON ({ items, next_page_token, total_size_estimate }). filter uses AIP-160 syntax, such as status = "open" AND amount > 10, and order_by a comma-separated list of fields, each optionally followed by desc. Pass next_page_token back as page_token for the next page; it is empty on the last. A page too large for one result holds fewer records, with long values cut and named in cut_fields, and a note on reading the rest. show_deleted: true also returns soft-deleted records.`,
78
+ description: `Finds records of a resource and returns a page of them as JSON ({ items, next_page_token, total_size_estimate }). filter uses AIP-160 syntax, such as status = "open" AND amount > 10, and order_by a comma-separated list of fields, each optionally followed by desc. Pass next_page_token back as page_token for the next page; it is empty on the last. show_deleted: true also returns soft-deleted records.`,
53
79
  parameters: {
54
80
  type: "object",
55
81
  properties: {
@@ -89,21 +115,19 @@ const listRecordsTool = () => defineTool({
89
115
  page_size: typeof args.page_size === "number" ? Math.min(args.page_size, maxPageSize) : defaultPageSize,
90
116
  ...showDeleted && { show_deleted: true }
91
117
  };
92
- const list = (paged) => context.records.list(resource, paged);
93
- return list(params).then((page) => budgetedPage(page, params, list)).catch(refused);
118
+ return context.records.list(resource, params).then((page) => JSON.stringify(page), refused);
94
119
  }
95
120
  });
96
121
  /** Reads one record by its key, under the caller's access rules. */
97
122
  const getRecordTool = () => defineTool({
98
123
  name: "get_record",
99
- description: "Reads one record of a resource by its key and returns it as JSON ({ record, etag }), with long values cut and named in cut_fields when it is too large for one result. field reads one field alone, in pieces from offset. show_deleted: true also finds a soft-deleted record.",
124
+ description: "Reads one record of a resource by its key and returns it as JSON ({ record, etag }). show_deleted: true also finds a soft-deleted record.",
100
125
  parameters: {
101
126
  type: "object",
102
127
  properties: {
103
128
  resource: resourceParameter,
104
129
  key: keyParameter,
105
- show_deleted: showDeletedParameter,
106
- ...pieceParameters
130
+ show_deleted: showDeletedParameter
107
131
  },
108
132
  required: ["resource", "key"]
109
133
  },
@@ -114,19 +138,19 @@ const getRecordTool = () => defineTool({
114
138
  if (!resource || key === void 0) return "Pass the resource and key as { \"resource\": \"<name>\", \"key\": \"<key>\" }.";
115
139
  const showDeleted = showDeletedOf(args);
116
140
  if (showDeleted === void 0) return invalidShowDeleted;
117
- const shown = (found) => readsPiece(args) ? fieldPiece(found, args) : forcedJson(found, cutRecordNote);
118
- return context.records.get(resource, key, { show_deleted: showDeleted }).then(shown, refused);
141
+ return context.records.get(resource, key, { show_deleted: showDeleted }).then((record) => JSON.stringify(record), refused);
119
142
  }
120
143
  });
121
144
  /**
122
- * `list_resources`, `list_records`, `count_records` and `get_record`: reading and counting the app's records as the
123
- * caller, through the REST API's rules.
145
+ * `list_resources`, `describe_resource`, `list_records`, `count_records` and `get_record`: reading and counting the
146
+ * app's records as the caller, through the REST API's rules.
124
147
  */
125
148
  const readRecordTools = () => [
126
149
  listResourcesTool(),
150
+ describeResourceTool(),
127
151
  listRecordsTool(),
128
152
  countRecordsTool(),
129
153
  getRecordTool()
130
154
  ];
131
155
  //#endregion
132
- export { getRecordTool, listRecordsTool, listResourcesTool, readRecordTools };
156
+ export { describeResourceTool, getRecordTool, listRecordsTool, listResourcesTool, readRecordTools };
@@ -1,7 +1,5 @@
1
- import { forcedJson } from "./result-budget.js";
2
1
  import { defineTool } from "./tools.js";
3
2
  import { keyOf, keyParameter, refused, resourceOf } from "./record-tool-args.js";
4
- import { cutRecordNote } from "./budgeted-page.js";
5
3
  import { createApproval, updateApproval } from "./write-prompts.js";
6
4
  import { approvalTexts, recordBefore } from "./record-before.js";
7
5
  import { forbiddenText, notApproved, onlyNote, outside, resourceParameterFor } from "./write-tool-limits.js";
@@ -15,7 +13,7 @@ const writes = {
15
13
  };
16
14
  const dataParameter = {
17
15
  type: "object",
18
- description: "Field names to values, as list_resources gives the fields."
16
+ description: "Field names to values, as describe_resource gives the fields."
19
17
  };
20
18
  const dataOf = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
21
19
  /** Creates a record of a resource once the user approves it, under the caller's access rules. */
@@ -43,7 +41,7 @@ const createRecordTool = (input = {}) => defineTool({
43
41
  const { text, described } = await approvalTexts(context, resource);
44
42
  const answer = await context.approve(createApproval(text, described, data));
45
43
  if (answer !== "approved") return notApproved(answer);
46
- return context.records.create(resource, data).then((written) => forcedJson(writeEcho(described.model, written, data, void 0, fields), cutRecordNote), refused);
44
+ return context.records.create(resource, data).then((written) => JSON.stringify(writeEcho(described.model, written, data, void 0, fields)), refused);
47
45
  }
48
46
  });
49
47
  /** Updates one record by its key once the user approves it, under the caller's access rules. */
@@ -57,7 +55,7 @@ const updateRecordTool = (input = {}) => defineTool({
57
55
  key: keyParameter,
58
56
  data: {
59
57
  ...dataParameter,
60
- description: "The fields to change, names to new values, as list_resources gives the fields."
58
+ description: "The fields to change, names to new values, as describe_resource gives the fields."
61
59
  },
62
60
  etag: {
63
61
  type: "string",
@@ -90,7 +88,7 @@ const updateRecordTool = (input = {}) => defineTool({
90
88
  const { text, described } = await approvalTexts(context, resource);
91
89
  const answer = await context.approve(updateApproval(text, described, key, before?.record ?? {}, data));
92
90
  if (answer !== "approved") return notApproved(answer);
93
- return context.records.update(resource, key, data, { etag }).then((written) => forcedJson(writeEcho(described.model, written, data, before?.record, fields), cutRecordNote), refused);
91
+ return context.records.update(resource, key, data, { etag }).then((written) => JSON.stringify(writeEcho(described.model, written, data, before?.record, fields)), refused);
94
92
  }
95
93
  });
96
94
  /** `create_record`, `update_record` and `delete_record`, on the resources `input` allows. */
package/dist/index.d.ts CHANGED
@@ -120,7 +120,7 @@ import { readableResources } from "./assistant/readable-resources.js";
120
120
  import { WidgetOptions, maxWidgetPropsBytes, widget } from "./assistant/widget.js";
121
121
  import { QueryOptions, QueryResult, runReadOnlyQuery, runReadWriteQuery } from "./assistant/run-query.js";
122
122
  import { readOnlyQueryTool, readWriteQueryTool } from "./assistant/query-tools.js";
123
- import { getRecordTool, listRecordsTool, listResourcesTool, readRecordTools } from "./assistant/record-tools.js";
123
+ import { describeResourceTool, getRecordTool, listRecordsTool, listResourcesTool, readRecordTools } from "./assistant/record-tools.js";
124
124
  import { countRecordsTool } from "./assistant/count-records-tool.js";
125
125
  import { WriteRecordToolsInput } from "./assistant/write-tool-limits.js";
126
126
  import { createRecordTool, updateRecordTool, writeRecordTools } from "./assistant/write-record-tools.js";
@@ -170,4 +170,4 @@ import { MobileRelease, ReleaseFile, ServedReleaseFile, composeRelease, releaseM
170
170
  import { MobileReleaseAppInput, OfferedRelease, mobileReleaseApp } from "./mobile-release/release-app.js";
171
171
  import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
172
172
  import { AdminAuth, CreateAuthOptions, UserCreatedHook, normalizeRoles } from "./better-auth/create-auth.js";
173
- export { type Admin, type AdminAuth, type AdminOptions, type AggregateParams, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type ArgumentsPayload, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type AuthHooks, type AuthSettings, type AuthenticateSettings, type Authenticator, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentToolsInput, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueJob, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileProviderSettings, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSocket, type FunctionSource, type HistoryColumn, type HistoryOptions, type HistoryPosition, type HistoryStore, HistoryStoreError, type HookContext, HttpProblem, type IssueTokenOptions, type JobContext, type JobDeclaration, type JobDefinition, type JobName, type JobOptions, type JobPayload, type JobRun, type JobsRuntime, type JsonObject, type JsonValue, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpApprovals, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, type Membership, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewDevice, type NewNotification, type NewOrganization, type NewUser, type NotificationRecipients, NotificationSendError, type NotificationsOptions, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type PushAnswer, type PushMessage, PushMessageError, type PushSender, type QueryOptions, type QueryResult, type ReadableResources, type RealtimeOptions, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordGroups, type RecordKeyInput, type RecordPage, type ReleaseFile, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type SendNotification, type ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredNotification, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolComment, type ToolCommentPage, type ToolComments, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type Urgency, type User, type UserCreatedHook, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, relayed, releaseManifest, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
173
+ export { type Admin, type AdminAuth, type AdminOptions, type AggregateParams, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type ArgumentsPayload, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type AuthHooks, type AuthSettings, type AuthenticateSettings, type Authenticator, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentToolsInput, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueJob, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileProviderSettings, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSocket, type FunctionSource, type HistoryColumn, type HistoryOptions, type HistoryPosition, type HistoryStore, HistoryStoreError, type HookContext, HttpProblem, type IssueTokenOptions, type JobContext, type JobDeclaration, type JobDefinition, type JobName, type JobOptions, type JobPayload, type JobRun, type JobsRuntime, type JsonObject, type JsonValue, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpApprovals, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, type Membership, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewDevice, type NewNotification, type NewOrganization, type NewUser, type NotificationRecipients, NotificationSendError, type NotificationsOptions, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type PushAnswer, type PushMessage, PushMessageError, type PushSender, type QueryOptions, type QueryResult, type ReadableResources, type RealtimeOptions, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordGroups, type RecordKeyInput, type RecordPage, type ReleaseFile, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type SendNotification, type ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredNotification, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolComment, type ToolCommentPage, type ToolComments, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type Urgency, type User, type UserCreatedHook, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, relayed, releaseManifest, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ import { mcpHandler } from "./mcp/mcp-handler.js";
46
46
  import { mcpRoutes } from "./mcp/mcp-routes.js";
47
47
  import { mcpServer } from "./mcp/mcp-server.js";
48
48
  import { countRecordsTool } from "./assistant/count-records-tool.js";
49
- import { getRecordTool, listRecordsTool, listResourcesTool, readRecordTools } from "./assistant/record-tools.js";
49
+ import { describeResourceTool, getRecordTool, listRecordsTool, listResourcesTool, readRecordTools } from "./assistant/record-tools.js";
50
50
  import { deleteRecordTool } from "./assistant/delete-record-tool.js";
51
51
  import { createRecordTool, updateRecordTool, writeRecordTools } from "./assistant/write-record-tools.js";
52
52
  import { batchWriteTool } from "./assistant/batch-write-tool.js";
@@ -128,4 +128,4 @@ import { jobsDrainApp } from "./jobs/drain-route.js";
128
128
  import { composeRelease, releaseManifest } from "./mobile-release/release.js";
129
129
  import { mobileReleaseApp } from "./mobile-release/release-app.js";
130
130
  import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
131
- export { ConnectionsBusy, DeviceStoreError, FatalJobError, HistoryStoreError, HttpProblem, MissingCredentials, ModelError, NotificationSendError, PushMessageError, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, relayed, releaseManifest, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
131
+ export { ConnectionsBusy, DeviceStoreError, FatalJobError, HistoryStoreError, HttpProblem, MissingCredentials, ModelError, NotificationSendError, PushMessageError, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, relayed, releaseManifest, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.52.1";
2
+ var version = "0.53.1";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.52.1",
3
+ "version": "0.53.1",
4
4
  "description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -40,9 +40,9 @@
40
40
  "@better-auth/passkey": "^1.7.12",
41
41
  "@better-auth/sso": "^1.7.12",
42
42
  "@modelcontextprotocol/server": "^2.3.1",
43
- "@protobase/layout": "0.52.1",
44
- "@protobase/query": "0.52.1",
45
- "@protobase/schema": "0.52.1",
43
+ "@protobase/layout": "0.53.1",
44
+ "@protobase/query": "0.53.1",
45
+ "@protobase/schema": "0.53.1",
46
46
  "@react-email/render": "^2.1.0",
47
47
  "@scalar/hono-api-reference": "^0.12.10",
48
48
  "@sentry/core": "^11.7.0",
@@ -1,30 +0,0 @@
1
- import { budgetedJson, forcedJson, leadingFit } from "./result-budget.js";
2
- //#region src/assistant/budgeted-page.ts
3
- /** How a record tool's result says to read a field it cut. */
4
- const cutRecordNote = "The fields in cut_fields were cut to fit the size limit, each with its full length; read the rest of one with get_record and its field, offset and max_chars arguments.";
5
- const fewerNote = (count) => `Only ${count} records fit the size limit; pass next_page_token as page_token for the next ones. ${cutRecordNote}`;
6
- /**
7
- * A page of records as JSON within the size budget: its strings trimmed, then, when that does not fit, the page
8
- * listed again with as many records as fit, so its next_page_token goes on from the last one returned.
9
- */
10
- const budgetedPage = async (page, params, list) => {
11
- const json = budgetedJson(page, cutRecordNote);
12
- if (json) return json;
13
- if (page.items.length <= 1) return forcedJson(page, cutRecordNote);
14
- const fits = (count) => budgetedJson({
15
- ...page,
16
- items: page.items.slice(0, count),
17
- note: fewerNote(count)
18
- }, fewerNote(count)) !== void 0;
19
- const count = leadingFit(page.items.length - 1, fits);
20
- const fewer = await list({
21
- ...params,
22
- page_size: count
23
- });
24
- return forcedJson({
25
- ...fewer,
26
- note: fewerNote(fewer.items.length)
27
- }, fewerNote(fewer.items.length));
28
- };
29
- //#endregion
30
- export { budgetedPage, cutRecordNote };
@@ -1,32 +0,0 @@
1
- import { budgetedJson, forcedJson, leadingFit } from "./result-budget.js";
2
- //#region src/assistant/query-result-budget.ts
3
- const cutCellNote = "The values in cut_fields were cut to fit the size limit, each with its full length (rows[r][c] is row r, column c); read the rest of one with substring(<column> from <start>), counting characters from 1.";
4
- const fewerRowsNote = (count, more) => `Only the first ${count} rows fit the size limit${more}. ${cutCellNote}`;
5
- /**
6
- * A query's result as JSON within the size budget: its strings trimmed, then, when that does not fit, only as many
7
- * leading rows as fit, marked truncated. `more` ends the note on the rows left out, such as how to read on.
8
- */
9
- const budgetedQueryResult = (result, more) => {
10
- const json = budgetedJson(result, cutCellNote);
11
- if (json) return json;
12
- if (result.rows.length <= 1) return forcedJson(result, cutCellNote);
13
- const leading = (count) => {
14
- const note = fewerRowsNote(count, more(count));
15
- return {
16
- result: {
17
- ...result,
18
- rows: result.rows.slice(0, count),
19
- truncated: true,
20
- note
21
- },
22
- note
23
- };
24
- };
25
- const { result: fewer, note } = leading(leadingFit(result.rows.length - 1, (count) => {
26
- const { result, note } = leading(count);
27
- return budgetedJson(result, note) !== void 0;
28
- }));
29
- return forcedJson(fewer, note);
30
- };
31
- //#endregion
32
- export { budgetedQueryResult };
@@ -1,54 +0,0 @@
1
- import { forcedJson } from "./result-budget.js";
2
- //#region src/assistant/record-field-piece.ts
3
- /** get_record's arguments that read one field in pieces, such as a field a result cut to fit its size limit. */
4
- const pieceParameters = {
5
- field: {
6
- type: "string",
7
- description: "One field to read alone, in pieces: max_chars characters of its value from offset. A value that is not text reads as its JSON."
8
- },
9
- offset: {
10
- type: "integer",
11
- minimum: 0,
12
- description: "With field: the character to start at, 0 by default."
13
- },
14
- max_chars: {
15
- type: "integer",
16
- minimum: 1,
17
- maximum: 3e4,
18
- description: "With field: the characters to read, 20000 by default."
19
- }
20
- };
21
- const defaultChars = 2e4;
22
- const maxChars = 3e4;
23
- const invalid = (message) => JSON.stringify({
24
- error: "invalid-parameter",
25
- message
26
- });
27
- const countOf = (value, fallback) => value === void 0 ? fallback : typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : void 0;
28
- /** Whether the call reads one field in pieces rather than the whole record. */
29
- const readsPiece = (args) => args.field !== void 0;
30
- /**
31
- * The piece of `args.field` of `found` the call asks for, as JSON ({ field, offset, length, value, next_offset }), where
32
- * length is the whole value's and next_offset is absent once the piece reaches its end.
33
- */
34
- const fieldPiece = (found, args) => {
35
- const field = args.field;
36
- if (typeof field !== "string" || !Object.hasOwn(found.record, field)) return invalid(`Invalid parameter field: pass one of ${Object.keys(found.record).join(", ")}`);
37
- const offset = countOf(args.offset, 0);
38
- if (offset === void 0) return invalid("Invalid parameter offset: pass a whole number from 0");
39
- const chars = countOf(args.max_chars, defaultChars);
40
- if (!chars) return invalid(`Invalid parameter max_chars: pass a whole number from 1 to ${maxChars}`);
41
- const raw = found.record[field];
42
- const text = typeof raw === "string" ? raw : JSON.stringify(raw) ?? "null";
43
- const end = offset + Math.min(chars, maxChars);
44
- const piece = {
45
- field,
46
- offset,
47
- length: text.length,
48
- value: text.slice(offset, end),
49
- ...end < text.length && { next_offset: end }
50
- };
51
- return forcedJson(piece, "The value was cut to fit the size limit; pass a smaller max_chars.");
52
- };
53
- //#endregion
54
- export { fieldPiece, pieceParameters, readsPiece };
@@ -1,72 +0,0 @@
1
- //#region src/assistant/result-budget.ts
2
- /**
3
- * Keeps a tool result within what a model's client takes: over the budget, its longest string values are trimmed, each
4
- * named in `cut_fields` with its full length, and a `note` says how to read the rest. A tool with a list returns fewer
5
- * items when trimming alone does not fit (see `leadingFit`).
6
- */
7
- /** The characters of JSON one tool result may hold. */
8
- const resultBudget = 4e4;
9
- const stringFloor = 500;
10
- const neverCut = /* @__PURE__ */ new Set(["next_page_token", "etag"]);
11
- const pathOf = (path, key) => path ? `${path}.${key}` : key;
12
- const trimmed = (value, cap, path, cuts) => {
13
- if (typeof value === "string") {
14
- if (value.length <= cap) return value;
15
- cuts.push({
16
- field: path,
17
- length: value.length
18
- });
19
- return `${value.slice(0, cap)}…`;
20
- }
21
- if (Array.isArray(value)) return value.map((item, index) => trimmed(item, cap, `${path}[${index}]`, cuts));
22
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, !path && neverCut.has(key) ? item : trimmed(item, cap, pathOf(path, key), cuts)]));
23
- return value;
24
- };
25
- const longestString = (value) => {
26
- if (typeof value === "string") return value.length;
27
- if (value !== null && typeof value === "object") return Math.max(0, ...Object.values(value).map(longestString));
28
- return 0;
29
- };
30
- const cutAt = (result, cap, note) => {
31
- const cuts = [];
32
- const value = trimmed(result, cap, "", cuts);
33
- return JSON.stringify(cuts.length ? {
34
- ...value,
35
- cut_fields: cuts,
36
- note
37
- } : value);
38
- };
39
- /**
40
- * `result` as JSON within `budget`, its strings trimmed as little as fits but to no fewer than `floor` characters;
41
- * undefined when even that does not fit. `note` says how to read a cut field's rest.
42
- */
43
- const budgetedJson = (result, note, budget = resultBudget, floor = stringFloor) => {
44
- const json = JSON.stringify(result);
45
- if (json.length <= budget) return json;
46
- const plain = JSON.parse(json);
47
- let low = floor;
48
- let high = longestString(plain);
49
- if (low >= high) return void 0;
50
- if (cutAt(plain, low, note).length > budget) return void 0;
51
- while (high - low > 1) {
52
- const cap = Math.floor((low + high) / 2);
53
- if (cutAt(plain, cap, note).length <= budget) low = cap;
54
- else high = cap;
55
- }
56
- return cutAt(plain, low, note);
57
- };
58
- /** `result` as JSON within `budget` if it can be, its strings trimmed down to nothing when they must; never undefined. */
59
- const forcedJson = (result, note, budget = resultBudget) => budgetedJson(result, note, budget) ?? budgetedJson(result, note, budget, 0) ?? cutAt(JSON.parse(JSON.stringify(result)), 0, note);
60
- /** The most leading items of `count`, at least 1, for which `fits` holds, `fits` holding for fewer when it does for more. */
61
- const leadingFit = (count, fits) => {
62
- let low = 1;
63
- let high = count;
64
- while (low < high) {
65
- const middle = Math.ceil((low + high) / 2);
66
- if (fits(middle)) low = middle;
67
- else high = middle - 1;
68
- }
69
- return low;
70
- };
71
- //#endregion
72
- export { budgetedJson, forcedJson, leadingFit, resultBudget };