@absolutejs/ai 0.6.0 → 0.6.2

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,19 @@ 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.2 — 2026-10-22
10
+
11
+ ### Added
12
+
13
+ - **Add stopAfterToolTurn to complete sibling tool actions before a UI handoff and retain error recovery without an unnecessary model turn** (`streamAIWithTools`, `StreamAIWithToolsOptions`)
14
+ - **Support current Sonnet 5.5, Haiku 5.5, GPT-6.1 Sol and GPT-6 Luna reasoning and input capacity** (`MODEL_REASONING`, `openaiResponses`, `anthropic`)
15
+
16
+ ## 0.6.1 — 2026-10-20
17
+
18
+ ### Added
19
+
20
+ - **Add typed forms and shared task next actions for top-up continuation** (`FormField`, `formCard`, `parseFormSpec`, `prepareFormSubmission`, `FormSubmission`, `BackgroundWorkCard`, `backgroundWorkNextAction`)
21
+
9
22
  ## 0.6.0 — 2026-10-20
10
23
 
11
24
  ### Added
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,48 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/ai",
4
4
  "releases": [
5
+ {
6
+ "version": "0.6.2",
7
+ "date": "2026-10-22",
8
+ "changes": [
9
+ {
10
+ "kind": "added",
11
+ "summary": "Add stopAfterToolTurn to complete sibling tool actions before a UI handoff and retain error recovery without an unnecessary model turn",
12
+ "symbols": [
13
+ "streamAIWithTools",
14
+ "StreamAIWithToolsOptions"
15
+ ]
16
+ },
17
+ {
18
+ "kind": "added",
19
+ "summary": "Support current Sonnet 5.5, Haiku 5.5, GPT-6.1 Sol and GPT-6 Luna reasoning and input capacity",
20
+ "symbols": [
21
+ "MODEL_REASONING",
22
+ "openaiResponses",
23
+ "anthropic"
24
+ ]
25
+ }
26
+ ]
27
+ },
28
+ {
29
+ "changes": [
30
+ {
31
+ "kind": "added",
32
+ "summary": "Add typed forms and shared task next actions for top-up continuation",
33
+ "symbols": [
34
+ "FormField",
35
+ "formCard",
36
+ "parseFormSpec",
37
+ "prepareFormSubmission",
38
+ "FormSubmission",
39
+ "BackgroundWorkCard",
40
+ "backgroundWorkNextAction"
41
+ ]
42
+ }
43
+ ],
44
+ "date": "2026-10-20",
45
+ "version": "0.6.1"
46
+ },
5
47
  {
6
48
  "changes": [
7
49
  {
package/dist/ai/index.js CHANGED
@@ -2009,6 +2009,7 @@ var streamAIWithTools = async function* (options) {
2009
2009
  maxTurns = DEFAULT_STREAM_TOOL_MAX_TURNS,
2010
2010
  provider,
2011
2011
  stopAfterTools,
2012
+ stopAfterToolTurn,
2012
2013
  validateInput,
2013
2014
  contextPolicy,
2014
2015
  toolChoice,
@@ -2139,6 +2140,8 @@ var streamAIWithTools = async function* (options) {
2139
2140
  messages.push({ content: blocks, role: "assistant" });
2140
2141
  const resultBlocks = [];
2141
2142
  let terminalToolSucceeded = false;
2143
+ let toolFailed = false;
2144
+ let handoffToolSucceeded = false;
2142
2145
  for (const call of pending) {
2143
2146
  yield {
2144
2147
  id: call.id,
@@ -2148,6 +2151,7 @@ var streamAIWithTools = async function* (options) {
2148
2151
  };
2149
2152
  const startedAt = Date.now();
2150
2153
  const { ok, output } = await executeToolHandler(tools, call);
2154
+ toolFailed ||= !ok;
2151
2155
  const result = toolOutputText(output);
2152
2156
  executedKeys.add(toolCallKey(call));
2153
2157
  yield {
@@ -2164,8 +2168,10 @@ var streamAIWithTools = async function* (options) {
2164
2168
  terminalToolSucceeded = true;
2165
2169
  break;
2166
2170
  }
2171
+ if (ok && stopAfterToolTurn?.includes(call.name))
2172
+ handoffToolSucceeded = true;
2167
2173
  }
2168
- if (terminalToolSucceeded)
2174
+ if (terminalToolSucceeded || handoffToolSucceeded && !toolFailed)
2169
2175
  break;
2170
2176
  messages.push({ content: resultBlocks, role: "user" });
2171
2177
  if (turn >= Math.max(1, maxTurns)) {
@@ -2751,6 +2757,11 @@ var parseFormField = (raw) => {
2751
2757
  if (!name || !label || !type)
2752
2758
  return null;
2753
2759
  const field = { label, name, type };
2760
+ if (raw.valueType !== undefined) {
2761
+ if (!["string", "number", "integer", "boolean"].includes(String(raw.valueType)))
2762
+ return null;
2763
+ field.valueType = raw.valueType;
2764
+ }
2754
2765
  const placeholder = cleanString(raw.placeholder, LABEL_MAX_CHARS);
2755
2766
  if (placeholder)
2756
2767
  field.placeholder = placeholder;
@@ -3122,6 +3133,11 @@ var formCard = {
3122
3133
  items: { type: "string" },
3123
3134
  type: "array"
3124
3135
  },
3136
+ valueType: {
3137
+ enum: ["string", "number", "integer", "boolean"],
3138
+ type: "string",
3139
+ description: "Submitted scalar type, especially integer for numeric select options"
3140
+ },
3125
3141
  placeholder: { type: "string" },
3126
3142
  required: { type: "boolean" },
3127
3143
  type: { enum: [...FORM_FIELD_TYPES], type: "string" },
@@ -3628,6 +3644,68 @@ var renderChartSvg = (spec, options = {}) => {
3628
3644
  const body = spec.type === "donut" ? donutSvg(spec, frame) : cartesianSvg(spec, frame);
3629
3645
  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>`;
3630
3646
  };
3647
+ // src/ai/ui/formSubmission.ts
3648
+ var prepareFormSubmission = (form, values) => {
3649
+ const input = { ...form.submit.input };
3650
+ const errors = {};
3651
+ for (const field of form.fields) {
3652
+ if (["__proto__", "constructor", "prototype"].includes(field.name)) {
3653
+ errors.form = "Invalid field name";
3654
+ continue;
3655
+ }
3656
+ const raw = values[field.name];
3657
+ if (raw === undefined || raw === null || typeof raw === "string" && !raw.trim()) {
3658
+ if (field.required)
3659
+ errors[field.name] = "This field is required";
3660
+ continue;
3661
+ }
3662
+ if (field.type === "select" && !field.options?.includes(String(raw))) {
3663
+ errors[field.name] = "Choose one of the available options";
3664
+ continue;
3665
+ }
3666
+ const type = field.valueType ?? (field.type === "number" ? "number" : field.type === "checkbox" ? "boolean" : "string");
3667
+ let value = raw;
3668
+ if (type === "number" || type === "integer") {
3669
+ value = typeof raw === "number" ? raw : typeof raw === "string" && /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/iu.test(raw.trim()) ? Number(raw) : NaN;
3670
+ if (typeof value !== "number" || !Number.isFinite(value) || type === "integer" && !Number.isSafeInteger(value)) {
3671
+ errors[field.name] = type === "integer" ? "Enter a whole number" : "Enter a number";
3672
+ continue;
3673
+ }
3674
+ } else if (type === "boolean") {
3675
+ if (![true, false, "true", "false"].includes(raw)) {
3676
+ errors[field.name] = "Choose yes or no";
3677
+ continue;
3678
+ }
3679
+ value = raw === true || raw === "true";
3680
+ if (field.type === "checkbox" && field.required && !value) {
3681
+ errors[field.name] = "This field is required";
3682
+ continue;
3683
+ }
3684
+ } else {
3685
+ if (typeof raw !== "string") {
3686
+ errors[field.name] = "Enter text";
3687
+ continue;
3688
+ }
3689
+ value = field.type === "password" ? raw : raw.trim();
3690
+ }
3691
+ input[field.name] = value;
3692
+ }
3693
+ return Object.keys(errors).length ? { ok: false, errors } : { ok: true, input };
3694
+ };
3695
+ // src/ai/ui/backgroundWork.ts
3696
+ var backgroundWorkNextAction = (work) => {
3697
+ if (!work.workId || !Number.isSafeInteger(work.totalSteps) || work.totalSteps < 1 || !Number.isSafeInteger(work.completedSteps) || work.completedSteps < 0 || work.completedSteps > work.totalSteps)
3698
+ throw new Error("Invalid background work progress");
3699
+ if (work.status === "completed")
3700
+ return { kind: "view_results", label: "View results" };
3701
+ if (work.status === "paused" && work.pauseReason === "funding")
3702
+ return work.fundingConfirmed ? { kind: "continue", label: "Continue research" } : { kind: "top_up", label: "Add credits" };
3703
+ if (work.status === "unknown")
3704
+ return { kind: "check_status", label: "Check status" };
3705
+ if (work.hasResults)
3706
+ return { kind: "view_results", label: "View saved results" };
3707
+ return { kind: "check_status", label: "Check status" };
3708
+ };
3631
3709
  // src/ai/conversationManager.ts
3632
3710
  var NOT_FOUND2 = -1;
3633
3711
  var TITLE_MAX_LENGTH2 = 80;
@@ -4603,6 +4681,16 @@ var LIMITS = {
4603
4681
  contextWindowTokens: 1050000,
4604
4682
  maxOutputTokens: 128000
4605
4683
  },
4684
+ "gpt-6.1-sol": {
4685
+ maxInputTokens: 922000,
4686
+ contextWindowTokens: 1050000,
4687
+ maxOutputTokens: 128000
4688
+ },
4689
+ "gpt-6-luna": {
4690
+ maxInputTokens: 922000,
4691
+ contextWindowTokens: 1050000,
4692
+ maxOutputTokens: 128000
4693
+ },
4606
4694
  "gpt-6-astra": {
4607
4695
  maxInputTokens: 922000,
4608
4696
  contextWindowTokens: 1050000,
@@ -4923,6 +5011,17 @@ var mistral = profile("effort", ["none", "high"], "Minimal thinking or full reas
4923
5011
  var qwen = profile("budget", ["none", "low", "medium", "high"], haiku.description, "https://help.aliyun.com/en/model-studio/deep-thinking");
4924
5012
  var MODEL_REASONING = {
4925
5013
  "anthropic:claude-opus-5-5": claude,
5014
+ "anthropic:claude-sonnet-5-5": claude,
5015
+ "anthropic:claude-haiku-5-5": claude,
5016
+ "openai:gpt-6.1-sol": {
5017
+ ...gpt,
5018
+ efforts: ["low", "medium", "high", "xhigh", "max"],
5019
+ source: "https://developers.openai.com/api/docs/models/gpt-6.1-sol"
5020
+ },
5021
+ "openai:gpt-6-luna": {
5022
+ ...gpt,
5023
+ source: "https://developers.openai.com/api/docs/models/gpt-6-luna"
5024
+ },
4926
5025
  "anthropic:claude-fable-5-1": claude,
4927
5026
  "anthropic:claude-opus-5": claude,
4928
5027
  "anthropic:claude-sonnet-5": claude,
@@ -4973,7 +5072,8 @@ var ANTHROPIC_EFFORT = [
4973
5072
  /fable-5/,
4974
5073
  /mythos-5/,
4975
5074
  /opus-5/,
4976
- /sonnet-5/
5075
+ /sonnet-5/,
5076
+ /haiku-5/
4977
5077
  ];
4978
5078
  var ANTHROPIC_ADAPTIVE_ONLY = [];
4979
5079
  var ANTHROPIC_LEGACY_THINKING = [
@@ -4991,7 +5091,8 @@ var ANTHROPIC_NO_SAMPLING = [
4991
5091
  /fable-5/,
4992
5092
  /mythos-5/,
4993
5093
  /opus-5/,
4994
- /sonnet-5/
5094
+ /sonnet-5/,
5095
+ /haiku-5/
4995
5096
  ];
4996
5097
  var ANTHROPIC_MAX_EFFORT = [
4997
5098
  /opus-4-[678]/,
@@ -9160,6 +9261,7 @@ export {
9160
9261
  aiChat,
9161
9262
  alibaba,
9162
9263
  anthropic,
9264
+ backgroundWorkNextAction,
9163
9265
  chartCard,
9164
9266
  choiceCard,
9165
9267
  configureProviderResilience,
@@ -9223,6 +9325,7 @@ export {
9223
9325
  planCard,
9224
9326
  prepareAITextInput,
9225
9327
  prepareAITextInputStep,
9328
+ prepareFormSubmission,
9226
9329
  providerStatusPage,
9227
9330
  remoteProvider,
9228
9331
  renderChartSvg,
@@ -9243,5 +9346,5 @@ export {
9243
9346
  xai
9244
9347
  };
9245
9348
 
9246
- //# debugId=9A889A121344253764756E2164756E21
9349
+ //# debugId=577984AE55403A4664756E2164756E21
9247
9350
  //# sourceMappingURL=index.js.map