@absolutejs/ai 0.5.0 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ This file is generated by `absolute-changelog` from the entries in
6
6
  `changelog/`. Edit an entry, not this file — and add new ones under
7
7
  `changelog/unreleased/`.
8
8
 
9
+ ## 0.6.1 — 2026-10-20
10
+
11
+ ### Added
12
+
13
+ - **Add typed forms and shared task next actions for top-up continuation** (`FormField`, `formCard`, `parseFormSpec`, `prepareFormSubmission`, `FormSubmission`, `BackgroundWorkCard`, `backgroundWorkNextAction`)
14
+
15
+ ## 0.6.0 — 2026-10-20
16
+
17
+ ### Added
18
+
19
+ - **Load tools on demand (deferLoading, through Anthropic tool search) and clear old tool results past a threshold (toolResultClearing); both cross the provider proxy** (`AIToolResultClearing`, `AIToolDefinition`, `AIProviderToolDefinition`, `AIProviderStreamParams`, `StreamAIOptions`)
20
+
9
21
  ## 0.5.0 — 2026-10-19
10
22
 
11
23
  ### Breaking
package/README.md CHANGED
@@ -575,3 +575,18 @@ can prevent `done`, and no adapter can reconstruct usage the provider never sent
575
575
  `onUsage` still reports only complete usage, once per instrumented stream. Keep
576
576
  credit reservations separate from charges and reconcile unknown outcomes in the
577
577
  host application. The package does not invent token counts or decide billing policy.
578
+
579
+ ### Typed forms and background work
580
+
581
+ Form fields support `valueType` (`string`, `number`, `integer`, `boolean`). For a
582
+ numeric dropdown use `type: "select", options: ["3", "10"], valueType: "integer"`.
583
+ All hosts should call `prepareFormSubmission(form, values)` before invoking the
584
+ bound tool: it returns typed input or field errors without discarding answers.
585
+ The host must still validate the complete tool schema and apply authorization.
586
+ Legacy dropdowns retain string values; migrate numeric fields explicitly.
587
+
588
+ `BackgroundWorkCard` and `backgroundWorkNextAction` provide framework-neutral
589
+ progress/CTA semantics. A funding pause offers Add credits; only confirmed
590
+ funding changes it to Continue research. Completed work offers View results;
591
+ uncertain work offers Check status, never replay. The host persists the work ID,
592
+ polls or subscribes to actual progress, and binds actions to authenticated routes.
package/changelog.json CHANGED
@@ -2,6 +2,42 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/ai",
4
4
  "releases": [
5
+ {
6
+ "changes": [
7
+ {
8
+ "kind": "added",
9
+ "summary": "Add typed forms and shared task next actions for top-up continuation",
10
+ "symbols": [
11
+ "FormField",
12
+ "formCard",
13
+ "parseFormSpec",
14
+ "prepareFormSubmission",
15
+ "FormSubmission",
16
+ "BackgroundWorkCard",
17
+ "backgroundWorkNextAction"
18
+ ]
19
+ }
20
+ ],
21
+ "date": "2026-10-20",
22
+ "version": "0.6.1"
23
+ },
24
+ {
25
+ "changes": [
26
+ {
27
+ "kind": "added",
28
+ "summary": "Load tools on demand (deferLoading, through Anthropic tool search) and clear old tool results past a threshold (toolResultClearing); both cross the provider proxy",
29
+ "symbols": [
30
+ "AIToolResultClearing",
31
+ "AIToolDefinition",
32
+ "AIProviderToolDefinition",
33
+ "AIProviderStreamParams",
34
+ "StreamAIOptions"
35
+ ]
36
+ }
37
+ ],
38
+ "date": "2026-10-20",
39
+ "version": "0.6.0"
40
+ },
5
41
  {
6
42
  "changes": [
7
43
  {
package/dist/ai/index.js CHANGED
@@ -478,6 +478,7 @@ var sendMessage = async (socket, message) => {
478
478
  return true;
479
479
  };
480
480
  var buildToolDefinitions = (tools) => Object.entries(tools).map(([name, def]) => ({
481
+ ...def.deferLoading ? { deferLoading: true } : {},
481
482
  description: def.description,
482
483
  input_schema: def.input,
483
484
  name
@@ -670,6 +671,7 @@ var processToolTurn = async (socket, options, state, messageId, conversationId,
670
671
  model: options.model,
671
672
  promptCaching: options.promptCaching,
672
673
  providerOptions: options.providerOptions,
674
+ toolResultClearing: options.toolResultClearing,
673
675
  reasoning: options.reasoning,
674
676
  signal,
675
677
  systemPrompt: options.systemPrompt,
@@ -760,6 +762,7 @@ var processStream = async (socket, options, messages, messageId, conversationId,
760
762
  model: options.model,
761
763
  promptCaching: options.promptCaching,
762
764
  providerOptions: options.providerOptions,
765
+ toolResultClearing: options.toolResultClearing,
763
766
  reasoning: options.reasoning,
764
767
  signal,
765
768
  systemPrompt: options.systemPrompt,
@@ -929,6 +932,7 @@ var withHeartbeat = async function* (source, signal, intervalMs) {
929
932
  }
930
933
  };
931
934
  var buildToolDefinitions2 = (tools) => Object.entries(tools).map(([name, def]) => ({
935
+ ...def.deferLoading ? { deferLoading: true } : {},
932
936
  description: def.description,
933
937
  input_schema: def.input,
934
938
  name
@@ -1193,6 +1197,7 @@ var streamTurns = async function* (options, renderers, messages, signal, startTi
1193
1197
  reasoning: options.reasoning,
1194
1198
  signal,
1195
1199
  systemPrompt: options.systemPrompt,
1200
+ toolResultClearing: options.toolResultClearing,
1196
1201
  tools: toolDefs
1197
1202
  }, options.contextPolicy, (prepared) => {
1198
1203
  turnState.currentMessages = prepared.messages;
@@ -1805,6 +1810,7 @@ var mergeUsage = (left, right) => {
1805
1810
  };
1806
1811
  };
1807
1812
  var toProviderTools = (tools) => Object.entries(tools).map(([name, definition]) => ({
1813
+ ...definition.deferLoading ? { deferLoading: true } : {},
1808
1814
  description: definition.description,
1809
1815
  input_schema: definition.input,
1810
1816
  name
@@ -2296,6 +2302,7 @@ var wireParams = (params) => ({
2296
2302
  ...params.systemPrompt === undefined ? {} : { systemPrompt: params.systemPrompt },
2297
2303
  ...params.temperature === undefined ? {} : { temperature: params.temperature },
2298
2304
  ...params.toolChoice === undefined ? {} : { toolChoice: params.toolChoice },
2305
+ ...params.toolResultClearing === undefined ? {} : { toolResultClearing: params.toolResultClearing },
2299
2306
  ...params.tools === undefined ? {} : { tools: params.tools },
2300
2307
  ...params.topP === undefined ? {} : { topP: params.topP }
2301
2308
  });
@@ -2744,6 +2751,11 @@ var parseFormField = (raw) => {
2744
2751
  if (!name || !label || !type)
2745
2752
  return null;
2746
2753
  const field = { label, name, type };
2754
+ if (raw.valueType !== undefined) {
2755
+ if (!["string", "number", "integer", "boolean"].includes(String(raw.valueType)))
2756
+ return null;
2757
+ field.valueType = raw.valueType;
2758
+ }
2747
2759
  const placeholder = cleanString(raw.placeholder, LABEL_MAX_CHARS);
2748
2760
  if (placeholder)
2749
2761
  field.placeholder = placeholder;
@@ -3115,6 +3127,11 @@ var formCard = {
3115
3127
  items: { type: "string" },
3116
3128
  type: "array"
3117
3129
  },
3130
+ valueType: {
3131
+ enum: ["string", "number", "integer", "boolean"],
3132
+ type: "string",
3133
+ description: "Submitted scalar type, especially integer for numeric select options"
3134
+ },
3118
3135
  placeholder: { type: "string" },
3119
3136
  required: { type: "boolean" },
3120
3137
  type: { enum: [...FORM_FIELD_TYPES], type: "string" },
@@ -3621,6 +3638,68 @@ var renderChartSvg = (spec, options = {}) => {
3621
3638
  const body = spec.type === "donut" ? donutSvg(spec, frame) : cartesianSvg(spec, frame);
3622
3639
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(spec.title)}" font-family="${FONT}"><rect width="${width}" height="${height}" fill="${theme.surface}" rx="12"/>${spec.type === "donut" ? headerSvg({ ...spec, series: [] }, frame) : headerSvg(spec, frame)}${body}</svg>`;
3623
3640
  };
3641
+ // src/ai/ui/formSubmission.ts
3642
+ var prepareFormSubmission = (form, values) => {
3643
+ const input = { ...form.submit.input };
3644
+ const errors = {};
3645
+ for (const field of form.fields) {
3646
+ if (["__proto__", "constructor", "prototype"].includes(field.name)) {
3647
+ errors.form = "Invalid field name";
3648
+ continue;
3649
+ }
3650
+ const raw = values[field.name];
3651
+ if (raw === undefined || raw === null || typeof raw === "string" && !raw.trim()) {
3652
+ if (field.required)
3653
+ errors[field.name] = "This field is required";
3654
+ continue;
3655
+ }
3656
+ if (field.type === "select" && !field.options?.includes(String(raw))) {
3657
+ errors[field.name] = "Choose one of the available options";
3658
+ continue;
3659
+ }
3660
+ const type = field.valueType ?? (field.type === "number" ? "number" : field.type === "checkbox" ? "boolean" : "string");
3661
+ let value = raw;
3662
+ if (type === "number" || type === "integer") {
3663
+ value = typeof raw === "number" ? raw : typeof raw === "string" && /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/iu.test(raw.trim()) ? Number(raw) : NaN;
3664
+ if (typeof value !== "number" || !Number.isFinite(value) || type === "integer" && !Number.isSafeInteger(value)) {
3665
+ errors[field.name] = type === "integer" ? "Enter a whole number" : "Enter a number";
3666
+ continue;
3667
+ }
3668
+ } else if (type === "boolean") {
3669
+ if (![true, false, "true", "false"].includes(raw)) {
3670
+ errors[field.name] = "Choose yes or no";
3671
+ continue;
3672
+ }
3673
+ value = raw === true || raw === "true";
3674
+ if (field.type === "checkbox" && field.required && !value) {
3675
+ errors[field.name] = "This field is required";
3676
+ continue;
3677
+ }
3678
+ } else {
3679
+ if (typeof raw !== "string") {
3680
+ errors[field.name] = "Enter text";
3681
+ continue;
3682
+ }
3683
+ value = field.type === "password" ? raw : raw.trim();
3684
+ }
3685
+ input[field.name] = value;
3686
+ }
3687
+ return Object.keys(errors).length ? { ok: false, errors } : { ok: true, input };
3688
+ };
3689
+ // src/ai/ui/backgroundWork.ts
3690
+ var backgroundWorkNextAction = (work) => {
3691
+ if (!work.workId || !Number.isSafeInteger(work.totalSteps) || work.totalSteps < 1 || !Number.isSafeInteger(work.completedSteps) || work.completedSteps < 0 || work.completedSteps > work.totalSteps)
3692
+ throw new Error("Invalid background work progress");
3693
+ if (work.status === "completed")
3694
+ return { kind: "view_results", label: "View results" };
3695
+ if (work.status === "paused" && work.pauseReason === "funding")
3696
+ return work.fundingConfirmed ? { kind: "continue", label: "Continue research" } : { kind: "top_up", label: "Add credits" };
3697
+ if (work.status === "unknown")
3698
+ return { kind: "check_status", label: "Check status" };
3699
+ if (work.hasResults)
3700
+ return { kind: "view_results", label: "View saved results" };
3701
+ return { kind: "check_status", label: "Check status" };
3702
+ };
3624
3703
  // src/ai/conversationManager.ts
3625
3704
  var NOT_FOUND2 = -1;
3626
3705
  var TITLE_MAX_LENGTH2 = 80;
@@ -6716,10 +6795,32 @@ var mapMessage = (msg) => ({
6716
6795
  role: msg.role === "system" ? "user" : msg.role
6717
6796
  });
6718
6797
  var mapToolDefinition2 = (tool) => ({
6798
+ ...tool.deferLoading ? { defer_loading: true } : {},
6719
6799
  description: tool.description,
6720
6800
  input_schema: tool.input_schema,
6721
6801
  name: tool.name
6722
6802
  });
6803
+ var TOOL_SEARCH_TOOL = {
6804
+ name: "tool_search_tool_bm25",
6805
+ type: "tool_search_tool_bm25_20251119"
6806
+ };
6807
+ var CONTEXT_MANAGEMENT_BETA = "context-management-2025-06-27";
6808
+ var contextManagementFor = (clearing) => ({
6809
+ edits: [
6810
+ {
6811
+ ...clearing.clearAtLeastInputTokens === undefined ? {} : {
6812
+ clear_at_least: {
6813
+ type: "input_tokens",
6814
+ value: clearing.clearAtLeastInputTokens
6815
+ }
6816
+ },
6817
+ ...clearing.excludeTools?.length ? { exclude_tools: [...clearing.excludeTools] } : {},
6818
+ ...clearing.keepToolUses === undefined ? {} : { keep: { type: "tool_uses", value: clearing.keepToolUses } },
6819
+ trigger: { type: "input_tokens", value: clearing.triggerInputTokens },
6820
+ type: "clear_tool_uses_20250919"
6821
+ }
6822
+ ]
6823
+ });
6723
6824
  var requestOptionsFor = (params) => {
6724
6825
  const raw = params.providerOptions?.anthropic;
6725
6826
  if (raw === undefined)
@@ -6828,16 +6929,20 @@ var buildRequestBody3 = (params, configuredMax, configCaching) => {
6828
6929
  ] : params.systemPrompt;
6829
6930
  }
6830
6931
  const clientTools = params.tools?.map(mapToolDefinition2) ?? [];
6831
- if (clientTools.length > 0) {
6832
- if (caching) {
6833
- clientTools[clientTools.length - 1] = {
6834
- ...clientTools[clientTools.length - 1],
6835
- cache_control: { type: "ephemeral" }
6836
- };
6837
- }
6932
+ const deferring = clientTools.some((tool) => tool.defer_loading === true);
6933
+ const lastLoaded = clientTools.findLastIndex((tool) => tool.defer_loading !== true);
6934
+ if (caching && lastLoaded >= 0) {
6935
+ clientTools[lastLoaded] = {
6936
+ ...clientTools[lastLoaded],
6937
+ cache_control: { type: "ephemeral" }
6938
+ };
6838
6939
  }
6839
6940
  const serverTools = (options.serverTools ?? []).map(mapServerTool);
6840
- const tools = [...clientTools, ...serverTools];
6941
+ const tools = [
6942
+ ...clientTools,
6943
+ ...deferring ? [TOOL_SEARCH_TOOL] : [],
6944
+ ...serverTools
6945
+ ];
6841
6946
  if (tools.length > 0) {
6842
6947
  body.tools = tools;
6843
6948
  if (params.toolChoice === "auto" || params.toolChoice === "none") {
@@ -6851,6 +6956,9 @@ var buildRequestBody3 = (params, configuredMax, configCaching) => {
6851
6956
  if (params.stopSequences && params.stopSequences.length > 0) {
6852
6957
  body.stop_sequences = params.stopSequences;
6853
6958
  }
6959
+ if (params.toolResultClearing) {
6960
+ body.context_management = contextManagementFor(params.toolResultClearing);
6961
+ }
6854
6962
  const mode = params.reasoning && params.reasoning.effort !== "none" ? anthropicReasoningMode(params.model) : "none";
6855
6963
  if (params.reasoning?.effort === "none")
6856
6964
  body.thinking = { type: "disabled" };
@@ -7297,6 +7405,11 @@ var fetchAndStream = async function* (baseUrl, config, params, configuredMax, pr
7297
7405
  const suppliedHeaders = typeof config.headers === "function" ? await config.headers(params) : config.headers ?? {};
7298
7406
  const requestHeaders = new Headers(suppliedHeaders);
7299
7407
  requestHeaders.set("Content-Type", "application/json");
7408
+ if (body.context_management !== undefined) {
7409
+ const betas = (requestHeaders.get("anthropic-beta") ?? "").split(",").map((beta) => beta.trim()).filter(Boolean);
7410
+ if (!betas.includes(CONTEXT_MANAGEMENT_BETA))
7411
+ requestHeaders.set("anthropic-beta", [...betas, CONTEXT_MANAGEMENT_BETA].join(","));
7412
+ }
7300
7413
  if (config.authStyle === "bearer") {
7301
7414
  requestHeaders.set("Authorization", `Bearer ${token}`);
7302
7415
  } else {
@@ -7365,7 +7478,12 @@ var anthropic = (config) => {
7365
7478
  const hosted = tools.filter((tool) => isRecord5(tool) && typeof tool.type === "string" && /^(web_search_|web_fetch_|code_execution_|bash_code_execution_|text_editor_code_execution_|tool_search_tool_)/u.test(tool.type));
7366
7479
  const scope = hosted.length > 0 || body.mcp_servers !== undefined ? "caller-input" : "request";
7367
7480
  if (hosted.length > 0) {
7368
- const clientTools = tools.filter((tool) => !hosted.includes(tool));
7481
+ const clientTools = tools.filter((tool) => !hosted.includes(tool)).map((tool) => {
7482
+ if (!isRecord5(tool) || tool.defer_loading === undefined)
7483
+ return tool;
7484
+ const { defer_loading: _deferred, ...loaded } = tool;
7485
+ return loaded;
7486
+ });
7369
7487
  if (clientTools.length)
7370
7488
  counted.tools = clientTools;
7371
7489
  else
@@ -9114,6 +9232,7 @@ export {
9114
9232
  aiChat,
9115
9233
  alibaba,
9116
9234
  anthropic,
9235
+ backgroundWorkNextAction,
9117
9236
  chartCard,
9118
9237
  choiceCard,
9119
9238
  configureProviderResilience,
@@ -9177,6 +9296,7 @@ export {
9177
9296
  planCard,
9178
9297
  prepareAITextInput,
9179
9298
  prepareAITextInputStep,
9299
+ prepareFormSubmission,
9180
9300
  providerStatusPage,
9181
9301
  remoteProvider,
9182
9302
  renderChartSvg,
@@ -9197,5 +9317,5 @@ export {
9197
9317
  xai
9198
9318
  };
9199
9319
 
9200
- //# debugId=94F48F9A5CBF872A64756E2164756E21
9320
+ //# debugId=75EDBEC482A1645464756E2164756E21
9201
9321
  //# sourceMappingURL=index.js.map