@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 +12 -0
- package/README.md +15 -0
- package/changelog.json +36 -0
- package/dist/ai/index.js +130 -10
- package/dist/ai/index.js.map +11 -9
- package/dist/ai/providers/anthropic.js +50 -10
- package/dist/ai/providers/anthropic.js.map +3 -3
- package/dist/ai/providers/openrouter.js +49 -10
- package/dist/ai/providers/openrouter.js.map +3 -3
- package/dist/ai/ui/index.js +75 -1
- package/dist/ai/ui/index.js.map +7 -5
- package/dist/angular/ai/index.js +9 -7
- package/dist/angular/ai/index.js.map +2 -2
- package/dist/src/ai/providers/anthropic.d.ts +1 -0
- package/dist/src/ai/ui/backgroundWork.d.ts +28 -0
- package/dist/src/ai/ui/catalog.d.ts +2 -0
- package/dist/src/ai/ui/formSubmission.d.ts +11 -0
- package/dist/src/ai/ui/index.d.ts +2 -0
- package/dist/types/ai.d.ts +28 -0
- package/package.json +1 -1
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
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
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 = [
|
|
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=
|
|
9320
|
+
//# debugId=75EDBEC482A1645464756E2164756E21
|
|
9201
9321
|
//# sourceMappingURL=index.js.map
|