@agent-native/core 0.208.0 → 0.209.0-nightly-20261014070126

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.
Files changed (74) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/dist/agent/approval-key.d.ts +2 -0
  3. package/dist/agent/approval-key.js +12 -0
  4. package/dist/agent/context-xray/tokenize.js +12 -3
  5. package/dist/agent/engine/translate-anthropic.js +4 -0
  6. package/dist/agent/engine/types.d.ts +2 -0
  7. package/dist/agent/production-agent.d.ts +20 -5
  8. package/dist/agent/production-agent.js +227 -34
  9. package/dist/agent/run-manager.js +57 -11
  10. package/dist/agent/run-store.d.ts +10 -0
  11. package/dist/agent/run-store.js +49 -4
  12. package/dist/agent/thread-data-builder.d.ts +2 -0
  13. package/dist/agent/thread-data-builder.js +288 -14
  14. package/dist/agent/tool-approval-store.d.ts +3 -1
  15. package/dist/agent/tool-approval-store.js +26 -12
  16. package/dist/agent/tool-call-journal.d.ts +1 -1
  17. package/dist/agent/tool-call-journal.js +5 -3
  18. package/dist/agent/types.d.ts +7 -0
  19. package/dist/client/chat/agentkit-agent-native.js +18 -0
  20. package/dist/client/chat/agentkit-protocol.js +56 -5
  21. package/dist/client/chat/runtime.d.ts +10 -0
  22. package/dist/client/chat/runtime.js +257 -26
  23. package/dist/client/sse-event-processor.d.ts +22 -0
  24. package/dist/client/sse-event-processor.js +91 -11
  25. package/dist/collab/struct-routes.d.ts +1 -1
  26. package/dist/connections/catalog.d.ts +24 -1
  27. package/dist/connections/catalog.js +12 -0
  28. package/dist/data-programs/schema.d.ts +1 -0
  29. package/dist/data-programs/schema.js +3 -0
  30. package/dist/data-programs/store.js +2 -1
  31. package/dist/deploy/build.js +16 -14
  32. package/dist/eject/workspace-connections.js +1 -0
  33. package/dist/extensions/fetch-tool.js +114 -23
  34. package/dist/extensions/schema.d.ts +1 -0
  35. package/dist/extensions/schema.js +3 -0
  36. package/dist/extensions/store.js +2 -1
  37. package/dist/extensions/web-content.d.ts +5 -1
  38. package/dist/extensions/web-content.js +24 -6
  39. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  40. package/dist/notifications/routes.d.ts +1 -1
  41. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  42. package/dist/provider-api/actions/provider-api.d.ts +9 -9
  43. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  44. package/dist/provider-api/index.d.ts +2 -0
  45. package/dist/provider-api/index.js +95 -12
  46. package/dist/resources/handlers.d.ts +1 -1
  47. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  48. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  49. package/dist/server/auth.js +1 -0
  50. package/dist/server/core-routes-plugin.js +1 -0
  51. package/dist/server/index.d.ts +2 -1
  52. package/dist/server/index.js +2 -1
  53. package/dist/server/realtime-token.d.ts +1 -1
  54. package/dist/server/transcribe-voice.d.ts +1 -1
  55. package/dist/server/workspace-provider-oauth.d.ts +1 -1
  56. package/dist/server/workspace-provider-oauth.js +2 -0
  57. package/dist/shared/context-status.js +1 -1
  58. package/dist/shared/optional-peer-ssr.d.ts +2 -0
  59. package/dist/shared/optional-peer-ssr.js +123 -0
  60. package/dist/shared/tool-inputs.d.ts +2 -0
  61. package/dist/shared/tool-inputs.js +56 -0
  62. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  63. package/dist/tracking/index.d.ts +1 -0
  64. package/dist/tracking/index.js +1 -0
  65. package/dist/vite/client.js +46 -3
  66. package/dist/workspace-connections/store.d.ts +3 -1
  67. package/dist/workspace-connections/store.js +18 -8
  68. package/dist/workspace-files/constants.d.ts +3 -0
  69. package/dist/workspace-files/constants.js +3 -0
  70. package/dist/workspace-files/store.d.ts +2 -3
  71. package/dist/workspace-files/store.js +2 -3
  72. package/docs/content/automations.mdx +19 -13
  73. package/package.json +3 -3
  74. package/src/eject/workspace-connections.ts +1 -0
@@ -10,10 +10,12 @@
10
10
  * the raw secret never enters the model's context.
11
11
  */
12
12
  import { fail, isActionContractError } from "../action.js";
13
+ import { SAVE_TO_FILE_MAX_BYTES } from "../workspace-files/constants.js";
13
14
  import { collectSecretValues, MAX_EXTENSION_PROXY_RESPONSE_SIZE, normalizeExtensionProxyMethod, readResponseBytesWithLimit, readResponseTextWithLimit, redactSecrets, redactString, sanitizeOutboundHeaders, } from "./proxy-security.js";
14
15
  import { createSsrfSafeDispatcher, isBlockedExtensionUrlWithDns, } from "./url-safety.js";
15
- import { formatWebContentResult, parseWebContentSearchOptions, processWebContent, } from "./web-content.js";
16
+ import { formatWebContentResult, hasWebContentSearch, parseWebContentSearchOptions, processWebContent, } from "./web-content.js";
16
17
  const DEFAULT_TIMEOUT_MS = 15_000;
18
+ const MAX_WEB_REQUEST_HTML_BYTES = SAVE_TO_FILE_MAX_BYTES;
17
19
  const VISION_IMAGE_CONTENT_TYPES = new Set([
18
20
  "image/jpeg",
19
21
  "image/png",
@@ -21,6 +23,20 @@ const VISION_IMAGE_CONTENT_TYPES = new Set([
21
23
  "image/webp",
22
24
  ]);
23
25
  const CREDENTIAL_NAME_RE = /(?:^|[-_])(access[-_]?token|api[-_]?key|auth(?:orization)?|credential|expires?|key[-_]?pair[-_]?id|key|password|passwd|policy|secret|session|signature|sig|token|jwt|assertion)(?:$|[-_])/i;
26
+ function isHtmlResponseContentType(contentType) {
27
+ const mediaType = contentType.toLowerCase();
28
+ return (mediaType === "text/html" ||
29
+ mediaType === "application/xhtml+xml" ||
30
+ mediaType.endsWith("+html"));
31
+ }
32
+ function truncateToolText(value, maxChars) {
33
+ if (value.length <= maxChars)
34
+ return value;
35
+ const marker = "\n...[truncated]";
36
+ if (maxChars <= marker.length)
37
+ return marker.slice(0, maxChars);
38
+ return `${value.slice(0, maxChars - marker.length)}${marker}`;
39
+ }
24
40
  function uint8ArrayToBase64(bytes) {
25
41
  if (typeof Buffer !== "undefined")
26
42
  return Buffer.from(bytes).toString("base64");
@@ -85,7 +101,7 @@ export function createFetchToolEntry(opts = {}) {
85
101
  return {
86
102
  "web-request": {
87
103
  tool: {
88
- description: `Make an outbound HTTP request to any EXTERNAL URL — APIs, webhooks, and arbitrary web pages (HTML, RSS, JSON, etc.). Use this to fetch the contents of a URL the user pastes in chat. Sends realistic Chrome-on-macOS headers by default (User-Agent, Accept, Sec-Fetch-*) so most sites that block obvious bots will respond normally; pass an explicit header to override any default. Supports \${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered actions directly (e.g. \`search-records\`, \`provider-api-request\`, \`update-resource\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,
104
+ description: `Make an outbound HTTP request to an external URL: APIs, webhooks, or web pages. HTML/XHTML responses up to 20 MB are extracted before maxChars limits the response body; optional metadata and links can add characters. Raw and other inline responses remain capped at 1 MB. saveToFile can stage responses up to 20 MB. For HTML, it saves extracted Markdown or text unless responseMode is raw; with search, it returns bounded matches instead of a preview. Sends realistic Chrome-on-macOS headers by default; explicit headers override them. Supports \${keys.NAME} placeholders in url, headers, and body, resolved server-side so raw secrets never enter context. Never use this for internal /_agent-native/ endpoints or localhost action URLs; call registered actions directly.`,
89
105
  parameters: {
90
106
  type: "object",
91
107
  properties: {
@@ -112,7 +128,7 @@ export function createFetchToolEntry(opts = {}) {
112
128
  },
113
129
  maxChars: {
114
130
  type: "number",
115
- description: "Maximum response body characters to return. Default: 32000. Max: 200000. Increase when you need to read a large document, API response, or dataset.",
131
+ description: "Maximum response-body characters returned to context. Default: 32000; max: 200000. Metadata and optional links are additional. This limits extracted content, not the HTML download. Use saveToFile for a longer extracted document.",
116
132
  },
117
133
  responseMode: {
118
134
  type: "string",
@@ -134,11 +150,11 @@ export function createFetchToolEntry(opts = {}) {
134
150
  },
135
151
  includeLinks: {
136
152
  type: "boolean",
137
- description: "Whether extracted HTML responses should include a compact links list. Default: true for extracted pages.",
153
+ description: "Whether extracted HTML responses should include a compact links list. Default: true for inline responses and false when saveToFile is set.",
138
154
  },
139
155
  search: {
140
156
  type: "object",
141
- description: "Optional post-fetch search over extracted content by default. Supports {query, queries, terms, regex, regexFlags, source:'extracted'|'raw', maxMatches, contextChars, caseSensitive}. Regex is safety-checked and bounded; prefer query/terms for simple grep-like searches.",
157
+ description: "Optional post-fetch search over extracted content by default. Searches at most the first 500,000 characters of the selected source; saved results report searchSourceTruncated when this limit is reached. Supports {query, queries, terms, regex, regexFlags, source:'extracted'|'raw', maxMatches, contextChars, caseSensitive}. Regex is safety-checked and bounded; prefer query/terms for simple grep-like searches.",
142
158
  properties: {
143
159
  query: { type: "string" },
144
160
  queries: { type: "array", items: { type: "string" } },
@@ -153,7 +169,7 @@ export function createFetchToolEntry(opts = {}) {
153
169
  },
154
170
  saveToFile: {
155
171
  type: "string",
156
- description: "Workspace file path to save the full response body to instead of returning it in context (e.g. 'analysis/page.html'). When set, returns only a compact summary {savedTo, status, bytes, preview}. Useful for large web pages or API responses that would overflow context.",
172
+ description: "Workspace path to stage a response up to 20 MB (e.g. 'scratch/research/page.md'). HTML saves full extracted Markdown/text unless responseMode is raw; non-HTML and raw mode save the response body. With search, returns matches bounded by maxChars and reports when source scanning was capped; without search, returns a preview.",
157
173
  },
158
174
  },
159
175
  required: ["url"],
@@ -295,6 +311,9 @@ export function createFetchToolEntry(opts = {}) {
295
311
  }
296
312
  const contentType = response.headers.get("content-type")?.split(";")[0].trim() ??
297
313
  "text/plain";
314
+ const requestedResponseMode = String(args.responseMode ?? "auto");
315
+ const normalizedResponseMode = requestedResponseMode.toLowerCase();
316
+ const isHtmlContentType = isHtmlResponseContentType(contentType);
298
317
  const httpErrorCode = response.ok
299
318
  ? undefined
300
319
  : `http_${response.status}`;
@@ -335,10 +354,11 @@ export function createFetchToolEntry(opts = {}) {
335
354
  }
336
355
  let body;
337
356
  let bodyResult;
357
+ const readLimit = saveToFilePath ||
358
+ (isHtmlContentType && normalizedResponseMode !== "raw")
359
+ ? MAX_WEB_REQUEST_HTML_BYTES
360
+ : MAX_EXTENSION_PROXY_RESPONSE_SIZE;
338
361
  try {
339
- const readLimit = saveToFilePath
340
- ? 20 * 1024 * 1024
341
- : MAX_EXTENSION_PROXY_RESPONSE_SIZE;
342
362
  bodyResult = await readResponseTextWithLimit(response, readLimit);
343
363
  }
344
364
  catch {
@@ -347,8 +367,15 @@ export function createFetchToolEntry(opts = {}) {
347
367
  });
348
368
  }
349
369
  if (bodyResult.truncated) {
350
- fail(`HTTP ${response.status} ${response.statusText}\n\n${bodyResult.text}`, {
351
- errorCode: httpErrorCode ?? "web_request_response_too_large",
370
+ const declaredBytes = Number(response.headers.get("content-length"));
371
+ const exceedsSaveLimit = readLimit === MAX_WEB_REQUEST_HTML_BYTES ||
372
+ (Number.isFinite(declaredBytes) &&
373
+ declaredBytes > MAX_WEB_REQUEST_HTML_BYTES);
374
+ const recovery = exceedsSaveLimit
375
+ ? `The response exceeds web-request's ${MAX_WEB_REQUEST_HTML_BYTES}-byte limit.`
376
+ : `Retry with saveToFile to stage responses up to ${MAX_WEB_REQUEST_HTML_BYTES} bytes.`;
377
+ fail(`HTTP ${response.status} ${response.statusText}\n\nResponse too large: at least ${bodyResult.size} bytes observed; maximum ${readLimit} bytes. ${recovery}`, {
378
+ errorCode: "web_request_response_too_large",
352
379
  });
353
380
  }
354
381
  body = bodyResult.text;
@@ -361,21 +388,69 @@ export function createFetchToolEntry(opts = {}) {
361
388
  }
362
389
  let displayBody;
363
390
  let processedMode = "raw";
391
+ let searchResultsTruncated = false;
392
+ let searchSourceTruncated = false;
393
+ let fileContent = body;
394
+ let fileContentType = contentType;
395
+ const search = parseWebContentSearchOptions(args.search);
396
+ const hasSearch = hasWebContentSearch(search);
397
+ const includeLinks = args.includeLinks === undefined
398
+ ? !saveToFilePath
399
+ : parseBooleanArg(args.includeLinks);
400
+ const saveContentFormat = saveToFilePath &&
401
+ isHtmlContentType &&
402
+ normalizedResponseMode !== "raw"
403
+ ? normalizedResponseMode === "text"
404
+ ? "text"
405
+ : "markdown"
406
+ : undefined;
364
407
  try {
365
408
  const processed = await processWebContent({
366
409
  url: resolvedUrl,
367
410
  body,
368
411
  contentType,
369
- responseMode: String(args.responseMode ?? "auto"),
412
+ responseMode: saveToFilePath && hasSearch ? "matches" : requestedResponseMode,
370
413
  extract: String(args.extract ?? "readability"),
371
- includeLinks: args.includeLinks === undefined
372
- ? true
373
- : parseBooleanArg(args.includeLinks),
374
- search: parseWebContentSearchOptions(args.search),
375
- maxChars,
414
+ includeLinks,
415
+ search,
416
+ maxChars: saveContentFormat ? null : maxChars,
417
+ saveContentFormat,
376
418
  });
377
419
  processedMode = processed.mode;
420
+ searchSourceTruncated = processed.searchTruncated ?? false;
378
421
  displayBody = formatWebContentResult(processed);
422
+ if (saveToFilePath && hasSearch && displayBody.length > maxChars) {
423
+ searchResultsTruncated = true;
424
+ displayBody = truncateToolText(displayBody, maxChars);
425
+ }
426
+ if (saveContentFormat) {
427
+ if (processed.fullContent === undefined) {
428
+ throw new Error("HTML extraction did not produce saved content");
429
+ }
430
+ fileContent = formatWebContentResult({
431
+ ...processed,
432
+ mode: saveContentFormat,
433
+ content: processed.fullContent,
434
+ fullContent: undefined,
435
+ matches: undefined,
436
+ totalMatches: undefined,
437
+ omittedMatches: undefined,
438
+ searchSource: undefined,
439
+ searchTruncated: undefined,
440
+ truncated: false,
441
+ });
442
+ fileContentType =
443
+ saveContentFormat === "markdown"
444
+ ? "text/markdown"
445
+ : "text/plain";
446
+ }
447
+ if (!saveToFilePath &&
448
+ isHtmlContentType &&
449
+ normalizedResponseMode !== "raw" &&
450
+ processed.truncated &&
451
+ processed.contentLength !== undefined) {
452
+ displayBody += `\n\nFull extracted content: ${processed.contentLength} characters. Retry with saveToFile (up to ${MAX_WEB_REQUEST_HTML_BYTES} bytes), then use workspace-files grep or read with offsets.`;
453
+ }
379
454
  }
380
455
  catch (err) {
381
456
  fail(`web-request post-processing error: ${err?.message ?? String(err)}`, { errorCode: httpErrorCode ?? "web_request_processing_failed" });
@@ -383,7 +458,7 @@ export function createFetchToolEntry(opts = {}) {
383
458
  console.log(`[fetch-tool] ${method} ${rawUrl} → ${response.status} (${elapsed}ms, keys: ${allUsedKeys.join(",") || "none"})`);
384
459
  if (saveToFilePath) {
385
460
  try {
386
- const { writeWorkspaceFile, SAVE_TO_FILE_MAX_BYTES, isScratchWorkspacePath, toWorkspaceFileCard, } = await import("../workspace-files/store.js");
461
+ const { writeWorkspaceFile, isScratchWorkspacePath, toWorkspaceFileCard, } = await import("../workspace-files/store.js");
387
462
  const { getRequestOrgId, getRequestUserEmail } = await import("../server/request-context.js");
388
463
  const orgId = getRequestOrgId();
389
464
  const email = getRequestUserEmail();
@@ -398,21 +473,37 @@ export function createFetchToolEntry(opts = {}) {
398
473
  if (!response.ok && !scratchPath) {
399
474
  throw new Error(`Refusing to save a failed response to durable workspace file "${saveToFilePath}" (status ${response.status}). Use a scratch/... path for diagnostics.`);
400
475
  }
401
- const meta = await writeWorkspaceFile(scope, saveToFilePath, body, contentType, {
476
+ const savedFormat = saveContentFormat ?? "raw";
477
+ const meta = await writeWorkspaceFile(scope, saveToFilePath, fileContent, fileContentType, {
402
478
  maxFileBytes: SAVE_TO_FILE_MAX_BYTES,
403
479
  });
404
- const bytes = Buffer.byteLength(body, "utf8");
405
- const preview = displayBody.slice(0, 2000);
480
+ const bytes = Buffer.byteLength(fileContent, "utf8");
481
+ const previewSource = saveContentFormat
482
+ ? fileContent
483
+ : displayBody;
484
+ const preview = previewSource.slice(0, 2000);
406
485
  if (!response.ok)
407
- fail(`HTTP ${response.status} ${response.statusText}\n\n${displayBody}`, { errorCode: `http_${response.status}` });
486
+ fail(`HTTP ${response.status} ${response.statusText}\n\n${truncateToolText(displayBody, maxChars)}`, { errorCode: `http_${response.status}` });
408
487
  return JSON.stringify({
409
488
  savedToFile: true,
410
489
  savedTo: saveToFilePath,
411
490
  status: response.status,
412
491
  bytes,
492
+ chars: fileContent.length,
413
493
  contentType,
494
+ savedFormat,
414
495
  responseMode: processedMode,
415
- preview: preview.length < displayBody.length ? `${preview}…` : preview,
496
+ ...(hasSearch
497
+ ? {
498
+ searchResults: displayBody,
499
+ searchResultsTruncated,
500
+ searchSourceTruncated,
501
+ }
502
+ : {
503
+ preview: preview.length < previewSource.length
504
+ ? `${preview}…`
505
+ : preview,
506
+ }),
416
507
  ...(scratchPath ? {} : { file: toWorkspaceFileCard(meta) }),
417
508
  });
418
509
  }
@@ -914,6 +914,7 @@ export declare const EXTENSIONS_HIDDEN_AT_COLUMN_SQL = "ALTER TABLE tools ADD CO
914
914
  export declare const EXTENSIONS_HIDDEN_BY_COLUMN_SQL = "ALTER TABLE tools ADD COLUMN IF NOT EXISTS hidden_by TEXT";
915
915
  export declare const EXTENSIONS_HIDDEN_AT_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tools_hidden_at_idx ON tools (hidden_at)";
916
916
  export declare const EXTENSION_SHARES_RESOURCE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_shares_resource_idx ON tool_shares (resource_id)";
917
+ export declare const EXTENSION_SHARES_NOTIFIED_AT_COLUMN_SQL = "ALTER TABLE tool_shares ADD COLUMN IF NOT EXISTS notified_at TEXT";
917
918
  export declare const EXTENSION_HIDES_CREATE_SQL = "CREATE TABLE IF NOT EXISTS tool_hidden_extensions (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT now()\n)";
918
919
  export declare const EXTENSION_HIDES_UNIQUE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS tool_hidden_extensions_user_tool_idx\n ON tool_hidden_extensions (owner_email, tool_id)";
919
920
  export declare const EXTENSION_HIDES_OWNER_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_hidden_extensions_owner_idx\n ON tool_hidden_extensions (owner_email)";
@@ -118,6 +118,9 @@ export const EXTENSIONS_HIDDEN_AT_COLUMN_SQL = `ALTER TABLE tools ADD COLUMN IF
118
118
  export const EXTENSIONS_HIDDEN_BY_COLUMN_SQL = `ALTER TABLE tools ADD COLUMN IF NOT EXISTS hidden_by TEXT`;
119
119
  export const EXTENSIONS_HIDDEN_AT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tools_hidden_at_idx ON tools (hidden_at)`;
120
120
  export const EXTENSION_SHARES_RESOURCE_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_shares_resource_idx ON tool_shares (resource_id)`;
121
+ // tool_shares tables created before notified_at existed never got it from the
122
+ // CREATE TABLE IF NOT EXISTS above, and every share insert names the column.
123
+ export const EXTENSION_SHARES_NOTIFIED_AT_COLUMN_SQL = `ALTER TABLE tool_shares ADD COLUMN IF NOT EXISTS notified_at TEXT`;
121
124
  export const EXTENSION_HIDES_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_hidden_extensions (
122
125
  id TEXT PRIMARY KEY,
123
126
  tool_id TEXT NOT NULL,
@@ -11,7 +11,7 @@ import { accessFilter, assertAccess, resolveAccess, ForbiddenError, } from "../s
11
11
  import { registerShareableResource } from "../sharing/registry.js";
12
12
  import { EXTENSION_CHANGE_MARKER_KEY, extensionChangeMarkerSession, extensionChangeMarkerValue, } from "./change-marker.js";
13
13
  import { applyExtensionContentUpdate, ExtensionContentEditError, } from "./content-patch.js";
14
- import { extensions, extensionHides, extensionShares, extensionHistory, EXTENSIONS_CREATE_SQL, EXTENSION_SHARES_CREATE_SQL, EXTENSION_DATA_CREATE_SQL, EXTENSION_DATA_ITEM_INDEX_SQL, EXTENSION_DATA_DROP_OLD_INDEX_SQL, EXTENSIONS_OWNER_INDEX_SQL, EXTENSIONS_ORG_INDEX_SQL, EXTENSIONS_UPDATED_INDEX_SQL, EXTENSIONS_ARCHIVED_AT_COLUMN_SQL, EXTENSIONS_ARCHIVED_AT_INDEX_SQL, EXTENSIONS_HIDDEN_AT_COLUMN_SQL, EXTENSIONS_HIDDEN_BY_COLUMN_SQL, EXTENSIONS_HIDDEN_AT_INDEX_SQL, EXTENSION_SHARES_RESOURCE_INDEX_SQL, EXTENSION_HIDES_CREATE_SQL, EXTENSION_HIDES_UNIQUE_INDEX_SQL, EXTENSION_HIDES_OWNER_INDEX_SQL, EXTENSION_HISTORY_CREATE_SQL, EXTENSION_HISTORY_VERSION_INDEX_SQL, EXTENSION_HISTORY_CREATED_INDEX_SQL, EXTENSION_CONSENTS_CREATE_SQL, EXTENSION_CONSENTS_VIEWER_INDEX_SQL, } from "./schema.js";
14
+ import { extensions, extensionHides, extensionShares, extensionHistory, EXTENSIONS_CREATE_SQL, EXTENSION_SHARES_CREATE_SQL, EXTENSION_DATA_CREATE_SQL, EXTENSION_DATA_ITEM_INDEX_SQL, EXTENSION_DATA_DROP_OLD_INDEX_SQL, EXTENSIONS_OWNER_INDEX_SQL, EXTENSIONS_ORG_INDEX_SQL, EXTENSIONS_UPDATED_INDEX_SQL, EXTENSIONS_ARCHIVED_AT_COLUMN_SQL, EXTENSIONS_ARCHIVED_AT_INDEX_SQL, EXTENSIONS_HIDDEN_AT_COLUMN_SQL, EXTENSIONS_HIDDEN_BY_COLUMN_SQL, EXTENSIONS_HIDDEN_AT_INDEX_SQL, EXTENSION_SHARES_RESOURCE_INDEX_SQL, EXTENSION_SHARES_NOTIFIED_AT_COLUMN_SQL, EXTENSION_HIDES_CREATE_SQL, EXTENSION_HIDES_UNIQUE_INDEX_SQL, EXTENSION_HIDES_OWNER_INDEX_SQL, EXTENSION_HISTORY_CREATE_SQL, EXTENSION_HISTORY_VERSION_INDEX_SQL, EXTENSION_HISTORY_CREATED_INDEX_SQL, EXTENSION_CONSENTS_CREATE_SQL, EXTENSION_CONSENTS_VIEWER_INDEX_SQL, } from "./schema.js";
15
15
  const getDb = createGetDb({
16
16
  extensions,
17
17
  extensionShares,
@@ -27,6 +27,7 @@ export async function ensureExtensionsTables() {
27
27
  await ensureTableExists("tools", EXTENSIONS_CREATE_SQL);
28
28
  await migrateMisnamedExtensionsTable(client);
29
29
  await ensureTableExists("tool_shares", EXTENSION_SHARES_CREATE_SQL);
30
+ await ensureColumnExists("tool_shares", "notified_at", EXTENSION_SHARES_NOTIFIED_AT_COLUMN_SQL);
30
31
  await ensureTableExists("tool_data", EXTENSION_DATA_CREATE_SQL);
31
32
  await ensureExtensionDataItemId();
32
33
  await ensureExtensionDataScope();
@@ -20,7 +20,9 @@ export interface WebContentProcessOptions {
20
20
  extract?: string;
21
21
  includeLinks?: boolean;
22
22
  search?: WebContentSearchOptions | null;
23
- maxChars?: number;
23
+ /** `null` keeps the complete content available for a saved workspace file. */
24
+ maxChars?: number | null;
25
+ saveContentFormat?: "markdown" | "text";
24
26
  }
25
27
  export interface WebContentLink {
26
28
  text: string;
@@ -45,6 +47,8 @@ export interface WebContentResult {
45
47
  publishedTime?: string;
46
48
  agentMetadata?: unknown[];
47
49
  content?: string;
50
+ fullContent?: string;
51
+ contentLength?: number;
48
52
  links?: WebContentLink[];
49
53
  matches?: WebContentMatch[];
50
54
  totalMatches?: number;
@@ -68,16 +68,33 @@ export async function processWebContent(options) {
68
68
  const html = isHtmlResponse(contentType, options.body);
69
69
  const mode = resolveMode(requestedMode, html, search);
70
70
  const emptyHtmlBody = html && !options.body.trim();
71
- const maxChars = normalizeBoundedNumber(options.maxChars, DEFAULT_MAX_CONTENT_CHARS, 1, 200_000);
72
- const extracted = html && !emptyHtmlBody && (mode !== "raw" || hasWebContentSearch(search))
73
- ? await extractHtml(options.body, options.url, extract, mode === "markdown" || mode === "matches")
71
+ const maxChars = options.maxChars === null
72
+ ? Number.POSITIVE_INFINITY
73
+ : normalizeBoundedNumber(options.maxChars, DEFAULT_MAX_CONTENT_CHARS, 1, 200_000);
74
+ const extracted = html &&
75
+ !emptyHtmlBody &&
76
+ (mode !== "raw" || hasWebContentSearch(search) || options.saveContentFormat)
77
+ ? await extractHtml(options.body, options.url, extract, mode === "markdown" ||
78
+ mode === "matches" ||
79
+ options.saveContentFormat === "markdown")
74
80
  : null;
75
81
  const baseContent = contentForMode(mode, options.body, extracted);
82
+ const fullContent = options.saveContentFormat
83
+ ? options.saveContentFormat === "text"
84
+ ? (extracted?.text ?? (extracted ? "" : options.body))
85
+ : extracted?.markdown ||
86
+ extracted?.text ||
87
+ (extracted ? "" : options.body)
88
+ : undefined;
76
89
  const result = {
77
90
  mode,
78
91
  extract,
79
92
  contentType,
80
93
  ...metadataFromExtraction(extracted),
94
+ ...(fullContent !== undefined ? { fullContent } : {}),
95
+ ...(mode !== "links" && mode !== "metadata"
96
+ ? { contentLength: baseContent.length }
97
+ : {}),
81
98
  };
82
99
  if (emptyHtmlBody &&
83
100
  mode !== "raw" &&
@@ -183,9 +200,10 @@ function resolveMode(requestedMode, html, search) {
183
200
  }
184
201
  function isHtmlResponse(contentType, body) {
185
202
  if (contentType) {
186
- return (contentType === "text/html" ||
187
- contentType === "application/xhtml+xml" ||
188
- contentType.endsWith("+html"));
203
+ const mediaType = contentType.toLowerCase();
204
+ return (mediaType === "text/html" ||
205
+ mediaType === "application/xhtml+xml" ||
206
+ mediaType.endsWith("+html"));
189
207
  }
190
208
  return /<!doctype html|<html[\s>]|<body[\s>]|<article[\s>]/i.test(body.slice(0, 2_000));
191
209
  }
@@ -42,8 +42,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
42
42
  url: string;
43
43
  id: string;
44
44
  provider: string;
45
- error?: undefined;
46
45
  configured?: undefined;
46
+ error?: undefined;
47
47
  } | {
48
48
  url?: undefined;
49
49
  id?: undefined;
@@ -16,6 +16,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
16
16
  } | {
17
17
  count?: undefined;
18
18
  updated?: undefined;
19
- ok: boolean;
20
19
  error?: undefined;
20
+ ok: boolean;
21
21
  }>>;
@@ -75,10 +75,10 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- message?: undefined;
79
- id?: undefined;
80
78
  provider?: undefined;
81
79
  deleted?: undefined;
80
+ id?: undefined;
81
+ found?: undefined;
82
82
  providers: {
83
83
  id: string;
84
84
  label: string;
@@ -89,45 +89,45 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
89
89
  updatedAt: number;
90
90
  }[];
91
91
  count: number;
92
- found?: undefined;
93
92
  registered?: undefined;
94
93
  label?: undefined;
95
- } | {
96
94
  message?: undefined;
97
- id?: undefined;
95
+ } | {
98
96
  deleted?: undefined;
99
- providers?: undefined;
97
+ id?: undefined;
100
98
  count?: undefined;
99
+ providers?: undefined;
101
100
  found: boolean;
102
101
  provider: import("../custom-registry.js").CustomProviderConfig;
103
102
  registered?: undefined;
104
103
  label?: undefined;
105
- } | {
106
104
  message?: undefined;
105
+ } | {
107
106
  provider?: undefined;
108
107
  deleted?: undefined;
109
- providers?: undefined;
110
108
  count?: undefined;
109
+ providers?: undefined;
111
110
  found: boolean;
112
111
  id: string;
113
112
  registered?: undefined;
114
113
  label?: undefined;
115
- } | {
116
114
  message?: undefined;
115
+ } | {
117
116
  provider?: undefined;
118
- providers?: undefined;
119
117
  count?: undefined;
120
118
  found?: undefined;
119
+ providers?: undefined;
121
120
  deleted: boolean;
122
121
  id: string;
123
122
  registered?: undefined;
124
123
  label?: undefined;
124
+ message?: undefined;
125
125
  } | {
126
126
  provider?: undefined;
127
127
  deleted?: undefined;
128
- providers?: undefined;
129
128
  count?: undefined;
130
129
  found?: undefined;
130
+ providers?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -311,10 +311,10 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
- provider?: undefined;
315
- deleted?: undefined;
316
314
  id?: undefined;
315
+ deleted?: undefined;
317
316
  message?: undefined;
317
+ found?: undefined;
318
318
  providers: {
319
319
  id: string;
320
320
  label: string;
@@ -325,12 +325,12 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
325
325
  updatedAt: number;
326
326
  }[];
327
327
  count: number;
328
- found?: undefined;
328
+ provider?: undefined;
329
329
  registered?: undefined;
330
330
  label?: undefined;
331
331
  } | {
332
- deleted?: undefined;
333
332
  id?: undefined;
333
+ deleted?: undefined;
334
334
  count?: undefined;
335
335
  message?: undefined;
336
336
  providers?: undefined;
@@ -339,31 +339,31 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
339
339
  registered?: undefined;
340
340
  label?: undefined;
341
341
  } | {
342
- provider?: undefined;
343
342
  deleted?: undefined;
344
343
  count?: undefined;
345
344
  message?: undefined;
346
345
  providers?: undefined;
346
+ provider?: undefined;
347
347
  found: boolean;
348
348
  id: string;
349
349
  registered?: undefined;
350
350
  label?: undefined;
351
351
  } | {
352
- provider?: undefined;
353
352
  count?: undefined;
354
353
  message?: undefined;
355
- providers?: undefined;
356
354
  found?: undefined;
355
+ providers?: undefined;
356
+ provider?: undefined;
357
357
  deleted: boolean;
358
358
  id: string;
359
359
  registered?: undefined;
360
360
  label?: undefined;
361
361
  } | {
362
- provider?: undefined;
363
362
  deleted?: undefined;
364
363
  count?: undefined;
365
- providers?: undefined;
366
364
  found?: undefined;
365
+ providers?: undefined;
366
+ provider?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
+ deleted?: undefined;
76
77
  jobs: {
77
78
  id: string;
78
79
  name: string;
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
83
84
  updatedAt: string;
84
85
  }[];
85
86
  total: number;
86
- deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
+ deleted?: undefined;
94
95
  jobs?: undefined;
95
96
  total?: undefined;
96
- deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
@@ -207,6 +207,8 @@ export type ProviderApiAuthKind = {
207
207
  } | {
208
208
  type: "google-service-account";
209
209
  scopes: readonly string[];
210
+ oauthProvider?: string;
211
+ workspaceProvider?: string;
210
212
  } | {
211
213
  type: "oauth-bearer";
212
214
  oauthProvider: string;