@anvia/client 1.0.0-rc.6 → 1.0.0

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/README.md CHANGED
@@ -38,6 +38,10 @@ subpath; importing Client does not load the Agent runtime or its infrastructure
38
38
  - `parseUIMessage` and `parseUIMessages` validate externally loaded UI state.
39
39
 
40
40
  Tool-call start, delta, and end events are automatic when the provider exposes streamed arguments.
41
+ `UIToolMessagePart` states are exact: `input-streaming` carries raw partial text,
42
+ `input-available` carries parsed JSON input, and terminal `output-available` or `error` parts retain
43
+ that input with their result. `uiMessagesToMessages()` rejects partial calls instead of replaying
44
+ incomplete JSON or inventing empty arguments.
41
45
  Errors are masked by default. Use `mapError` only at the server adapter boundary when an application
42
46
  intentionally exposes a safe error shape. Non-JSON outputs require an explicit `mapOutput`; returning
43
47
  `undefined` intentionally omits the output, while returning `null` exposes JSON `null`.
package/dist/index.d.ts CHANGED
@@ -72,6 +72,32 @@ type UIDataPart<Data extends ClientDataMap> = {
72
72
  data: Data[Name];
73
73
  };
74
74
  }[keyof Data & string];
75
+ type UIToolMessagePartBase = {
76
+ id: string;
77
+ type: "tool";
78
+ toolName: string;
79
+ toolCallId: string;
80
+ callId?: string;
81
+ internalCallId?: string;
82
+ turn?: number;
83
+ signature?: string;
84
+ };
85
+ type UIToolMessagePart = (UIToolMessagePartBase & {
86
+ state: "input-streaming";
87
+ input: string;
88
+ }) | (UIToolMessagePartBase & {
89
+ state: "input-available";
90
+ input: JsonValue;
91
+ }) | (UIToolMessagePartBase & {
92
+ state: "output-available";
93
+ input: JsonValue;
94
+ output: JsonValue;
95
+ resultContent?: readonly ToolResultContentPart[];
96
+ }) | (UIToolMessagePartBase & {
97
+ state: "error";
98
+ input: JsonValue;
99
+ error: UIError;
100
+ });
75
101
  type UIMessagePart<Data extends ClientDataMap = ClientDataMap> = {
76
102
  id: string;
77
103
  type: "text";
@@ -83,21 +109,7 @@ type UIMessagePart<Data extends ClientDataMap = ClientDataMap> = {
83
109
  text: string;
84
110
  reasoningId?: string;
85
111
  content?: readonly ReasoningDetail[];
86
- } | {
87
- id: string;
88
- type: "tool";
89
- toolName: string;
90
- toolCallId: string;
91
- callId?: string;
92
- internalCallId?: string;
93
- turn?: number;
94
- state: "input-streaming" | "input-available" | "output-available" | "error";
95
- input?: JsonValue;
96
- output?: JsonValue;
97
- resultContent?: readonly ToolResultContentPart[];
98
- signature?: string;
99
- error?: UIError;
100
- } | {
112
+ } | UIToolMessagePart | {
101
113
  id: string;
102
114
  type: "source";
103
115
  source: CompletionSource;
@@ -110,9 +122,6 @@ type UIMessagePart<Data extends ClientDataMap = ClientDataMap> = {
110
122
  type: "error";
111
123
  error: UIError;
112
124
  };
113
- type UIToolMessagePart = Extract<UIMessagePart, {
114
- type: "tool";
115
- }>;
116
125
  type ClientStreamCursor = {
117
126
  streamId: string;
118
127
  after: number;
@@ -243,7 +252,7 @@ type ClientStandardStreamEvent<Metadata extends JsonObject, Data extends ClientD
243
252
  callId?: string;
244
253
  internalCallId?: string;
245
254
  toolName: string;
246
- input?: JsonValue;
255
+ input: JsonValue;
247
256
  result: {
248
257
  status: "success";
249
258
  output: JsonValue;
package/dist/index.js CHANGED
@@ -59,11 +59,12 @@ function uiMessagesToMessages(messages) {
59
59
  if (part.content !== void 0) reasoning = { ...reasoning, details: part.content };
60
60
  content2.push(reasoning);
61
61
  } else if (part.type === "tool") {
62
+ assertReplayableToolPart(part);
62
63
  let toolCall = {
63
64
  type: "tool-call",
64
65
  toolCallId: part.toolCallId,
65
66
  toolName: part.toolName,
66
- input: part.input ?? {}
67
+ input: part.input
67
68
  };
68
69
  if (part.callId !== void 0) toolCall = { ...toolCall, callId: part.callId };
69
70
  if (part.signature !== void 0) toolCall = { ...toolCall, signature: part.signature };
@@ -90,9 +91,12 @@ function uiMessagesToMessages(messages) {
90
91
  continue;
91
92
  }
92
93
  assertOnlyParts(message, ["tool", "data", "error"]);
93
- const content = message.parts.flatMap(
94
- (part) => part.type === "tool" && (part.state === "output-available" || part.state === "error") ? [toolResultFromPart(part)] : []
95
- );
94
+ const content = [];
95
+ for (const part of message.parts) {
96
+ if (part.type !== "tool") continue;
97
+ assertToolResultPart(part);
98
+ content.push(toolResultFromPart(part));
99
+ }
96
100
  result.push({ role: "tool", content, ...metadataField(message.metadata) });
97
101
  }
98
102
  return result;
@@ -147,7 +151,13 @@ function messagesToUIMessages(messages) {
147
151
  for (const content of message.content) {
148
152
  if (content.type !== "tool-result") continue;
149
153
  const location = (content.callId === void 0 ? void 0 : byCallId.get(content.callId)) ?? byToolCallId.get(content.toolCallId);
150
- const part = toolResultToUIMessagePart(content, toolNameAt(result, location));
154
+ const ownerPart = toolPartAt(result, location);
155
+ if (ownerPart === void 0 || ownerPart.state === "input-streaming") {
156
+ throw new TypeError(
157
+ `Tool result "${content.toolCallId}" requires a matching completed tool call.`
158
+ );
159
+ }
160
+ const part = toolResultToUIMessagePart(content, ownerPart.toolName, ownerPart.input);
151
161
  if (message.metadata === void 0 && location !== void 0) {
152
162
  const owner = result[location.messageIndex];
153
163
  const existing = owner?.parts[location.partIndex];
@@ -233,12 +243,12 @@ function toolResultFromPart(part) {
233
243
  }
234
244
  function toolOutputFromPart(part) {
235
245
  if (part.state === "error") {
236
- return { type: "error-text", value: part.error?.message ?? "Tool execution failed." };
246
+ return { type: "error-text", value: part.error.message };
237
247
  }
238
248
  if (part.resultContent !== void 0) {
239
249
  return { type: "content", value: part.resultContent };
240
250
  }
241
- const output = part.output ?? null;
251
+ const output = part.output;
242
252
  return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: output };
243
253
  }
244
254
  function attachmentToUserContent(attachment) {
@@ -293,13 +303,14 @@ function contentToAttachment(content) {
293
303
  else attachment.text = content.data.text;
294
304
  return attachment;
295
305
  }
296
- function toolResultToUIMessagePart(content, fallbackToolName) {
306
+ function toolResultToUIMessagePart(content, fallbackToolName, input) {
297
307
  const output = content.output;
298
308
  const common = {
299
309
  id: toolPartId(content.toolCallId),
300
310
  type: "tool",
301
- toolName: content.toolName || fallbackToolName || "tool",
302
- toolCallId: content.toolCallId
311
+ toolName: content.toolName || fallbackToolName,
312
+ toolCallId: content.toolCallId,
313
+ input
303
314
  };
304
315
  if (content.callId !== void 0) Object.assign(common, { callId: content.callId });
305
316
  if (output.type === "error-text" || output.type === "error-json") {
@@ -334,10 +345,28 @@ function toolResultOutput(output) {
334
345
  (part) => part.type === "text" ? { ...part } : { ...part, data: { ...part.data } }
335
346
  );
336
347
  }
337
- function toolNameAt(messages, location) {
348
+ function toolPartAt(messages, location) {
338
349
  if (location === void 0) return void 0;
339
350
  const part = messages[location.messageIndex]?.parts[location.partIndex];
340
- return part?.type === "tool" ? part.toolName : void 0;
351
+ return part?.type === "tool" ? part : void 0;
352
+ }
353
+ function assertReplayableToolPart(part) {
354
+ if (part.state === "input-streaming") {
355
+ throw new TypeError(
356
+ `Tool call "${part.toolCallId}" is still streaming and cannot be converted to a model message.`
357
+ );
358
+ }
359
+ if (!isJsonValue(part.input)) {
360
+ throw new TypeError(`Tool call "${part.toolCallId}" input must be a strict JSON value.`);
361
+ }
362
+ }
363
+ function assertToolResultPart(part) {
364
+ assertReplayableToolPart(part);
365
+ if (part.state === "input-available") {
366
+ throw new TypeError(
367
+ `Tool message part "${part.toolCallId}" requires an output or execution error.`
368
+ );
369
+ }
341
370
  }
342
371
  function textFromParts(parts) {
343
372
  return parts.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
@@ -612,6 +641,7 @@ function parseClientStreamEvent(value, options = {}) {
612
641
  ]);
613
642
  requireStrings(event, ["messageId", "partId", "toolCallId", "toolName"], value);
614
643
  requireOptionalStrings(event, ["callId", "internalCallId"], value);
644
+ if (!isJsonValue2(event.input)) invalid("tool_result.input", value);
615
645
  if (!isRecord(event.result)) invalid("tool_result.result", value);
616
646
  if (event.result.status === "success") {
617
647
  if (!hasOnlyKeys(event.result, ["status", "output", "content"]) || !isJsonValue2(event.result.output) || event.result.content !== void 0 && (!Array.isArray(event.result.content) || !event.result.content.every(isToolResultContent))) {
@@ -700,6 +730,10 @@ function parseClientStreamEvent(value, options = {}) {
700
730
  "originalMessageCount",
701
731
  "compactedMessageCount",
702
732
  "retainedMessageCount",
733
+ "originalTokenCount",
734
+ "compactedTokenCount",
735
+ "retainedTokenCount",
736
+ "resultTokenCount",
703
737
  "attempts",
704
738
  "usage"
705
739
  ]);
@@ -733,6 +767,10 @@ function parseClientStreamEvent(value, options = {}) {
733
767
  "originalMessageCount",
734
768
  "compactedMessageCount",
735
769
  "retainedMessageCount",
770
+ "originalTokenCount",
771
+ "compactedTokenCount",
772
+ "retainedTokenCount",
773
+ "resultTokenCount",
736
774
  "attempts",
737
775
  "usage"
738
776
  ],
@@ -864,6 +902,10 @@ function validateMemoryCompactionForGuard(value) {
864
902
  "originalMessageCount",
865
903
  "compactedMessageCount",
866
904
  "retainedMessageCount",
905
+ "originalTokenCount",
906
+ "compactedTokenCount",
907
+ "retainedTokenCount",
908
+ "resultTokenCount",
867
909
  "attempts",
868
910
  "usage"
869
911
  ],
@@ -930,7 +972,7 @@ function isUIMessagePart(value) {
930
972
  if (value.type === "error") {
931
973
  return hasOnlyKeys(value, ["id", "type", "error"]) && isClientStreamError(value.error);
932
974
  }
933
- return value.type === "tool" && hasOnlyKeys(value, [
975
+ if (value.type !== "tool" || !hasOnlyKeys(value, [
934
976
  "id",
935
977
  "type",
936
978
  "toolName",
@@ -944,9 +986,19 @@ function isUIMessagePart(value) {
944
986
  "resultContent",
945
987
  "signature",
946
988
  "error"
947
- ]) && typeof value.toolName === "string" && typeof value.toolCallId === "string" && (value.callId === void 0 || typeof value.callId === "string") && (value.internalCallId === void 0 || typeof value.internalCallId === "string") && (value.turn === void 0 || isPositiveEventId(value.turn)) && (value.signature === void 0 || typeof value.signature === "string") && (value.input === void 0 || isJsonValue2(value.input)) && (value.output === void 0 || isJsonValue2(value.output)) && (value.resultContent === void 0 || Array.isArray(value.resultContent) && value.resultContent.every(isToolResultContent)) && (value.error === void 0 || isClientStreamError(value.error)) && ["input-streaming", "input-available", "output-available", "error"].includes(
948
- value.state
949
- );
989
+ ]) || typeof value.toolName !== "string" || typeof value.toolCallId !== "string" || value.callId !== void 0 && typeof value.callId !== "string" || value.internalCallId !== void 0 && typeof value.internalCallId !== "string" || value.turn !== void 0 && !isPositiveEventId(value.turn) || value.signature !== void 0 && typeof value.signature !== "string") {
990
+ return false;
991
+ }
992
+ if (value.state === "input-streaming") {
993
+ return typeof value.input === "string" && value.output === void 0 && value.resultContent === void 0 && value.error === void 0;
994
+ }
995
+ if (value.state === "input-available") {
996
+ return isJsonValue2(value.input) && value.output === void 0 && value.resultContent === void 0 && value.error === void 0;
997
+ }
998
+ if (value.state === "output-available") {
999
+ return isJsonValue2(value.input) && isJsonValue2(value.output) && (value.resultContent === void 0 || Array.isArray(value.resultContent) && value.resultContent.every(isToolResultContent)) && value.error === void 0;
1000
+ }
1001
+ return value.state === "error" && isJsonValue2(value.input) && value.output === void 0 && value.resultContent === void 0 && isClientStreamError(value.error);
950
1002
  }
951
1003
  function isReasoningContent(value) {
952
1004
  if (!isRecord(value) || typeof value.type !== "string") return false;
@@ -1037,7 +1089,15 @@ function validateUsageAndContext(event, original) {
1037
1089
  }
1038
1090
  }
1039
1091
  function validateMemoryCompactionInfo(value, original) {
1040
- for (const field of ["originalMessageCount", "compactedMessageCount", "retainedMessageCount"]) {
1092
+ for (const field of [
1093
+ "originalMessageCount",
1094
+ "compactedMessageCount",
1095
+ "retainedMessageCount",
1096
+ "originalTokenCount",
1097
+ "compactedTokenCount",
1098
+ "retainedTokenCount",
1099
+ "resultTokenCount"
1100
+ ]) {
1041
1101
  if (!isEventId(value[field])) invalid(`memory_compaction.${field}`, original);
1042
1102
  }
1043
1103
  if (!isPositiveEventId(value.attempts)) invalid("memory_compaction.attempts", original);
@@ -1261,6 +1321,10 @@ async function* translateAgentEvent(runtime, event, scope) {
1261
1321
  originalMessageCount: event.originalMessageCount,
1262
1322
  compactedMessageCount: event.compactedMessageCount,
1263
1323
  retainedMessageCount: event.retainedMessageCount,
1324
+ originalTokenCount: event.originalTokenCount,
1325
+ compactedTokenCount: event.compactedTokenCount,
1326
+ retainedTokenCount: event.retainedTokenCount,
1327
+ resultTokenCount: event.resultTokenCount,
1264
1328
  attempts: event.attempts,
1265
1329
  usage: event.usage
1266
1330
  });
@@ -1372,18 +1436,20 @@ async function* translateAgentEvent(runtime, event, scope) {
1372
1436
  decision: event.decision
1373
1437
  });
1374
1438
  return;
1375
- case "final": {
1376
- const result = event.result;
1377
- if (result.status === "suspended" && scope === void 0) {
1439
+ case "response":
1440
+ case "interaction":
1441
+ case "blocked": {
1442
+ const result = event;
1443
+ if (result.type === "interaction" && scope === void 0) {
1378
1444
  yield scopedEvent(runtime.runId, turn, scope, {
1379
1445
  type: "interaction",
1380
1446
  interaction: result.interaction
1381
1447
  });
1382
1448
  }
1383
- const output = result.status === "completed" ? clientOutput(result.output, runtime.options.mapOutput) : void 0;
1449
+ const output = result.type === "response" ? clientOutput(result.output, runtime.options.mapOutput) : void 0;
1384
1450
  const runEnd = {
1385
1451
  type: "run_end",
1386
- status: result.status,
1452
+ status: agentOutcomeStatus(result.type),
1387
1453
  text: result.text,
1388
1454
  usage: result.usage
1389
1455
  };
@@ -1759,25 +1825,43 @@ function contentToUIMessageParts(content, state) {
1759
1825
  }
1760
1826
  if (contentPart.type === "tool-call") {
1761
1827
  const tool = getToolState(state, contentPart.toolCallId, contentPart.toolName);
1762
- const part = {
1828
+ const partBase = {
1763
1829
  id: tool.partId,
1764
1830
  type: "tool",
1765
1831
  toolName: contentPart.toolName,
1766
- toolCallId: contentPart.toolCallId,
1767
- state: tool.result?.status === "error" ? "error" : tool.result ? "output-available" : "input-available",
1768
- input: contentPart.input
1832
+ toolCallId: contentPart.toolCallId
1769
1833
  };
1770
- if (contentPart.callId !== void 0) part.callId = contentPart.callId;
1771
- if (tool.internalCallId !== void 0) part.internalCallId = tool.internalCallId;
1772
- if (state.turn !== void 0) part.turn = state.turn;
1773
- if (contentPart.signature !== void 0) part.signature = contentPart.signature;
1834
+ if (contentPart.callId !== void 0) Object.assign(partBase, { callId: contentPart.callId });
1835
+ if (tool.internalCallId !== void 0) {
1836
+ Object.assign(partBase, { internalCallId: tool.internalCallId });
1837
+ }
1838
+ if (state.turn !== void 0) Object.assign(partBase, { turn: state.turn });
1839
+ if (contentPart.signature !== void 0) {
1840
+ Object.assign(partBase, { signature: contentPart.signature });
1841
+ }
1774
1842
  if (tool.result?.status === "success") {
1775
- part.output = tool.result.output;
1843
+ const part = {
1844
+ ...partBase,
1845
+ state: "output-available",
1846
+ input: contentPart.input,
1847
+ output: tool.result.output
1848
+ };
1776
1849
  if (tool.result.content !== void 0) part.resultContent = tool.result.content;
1777
- } else if (tool.result?.status === "error") {
1778
- part.error = tool.result.error;
1850
+ return part;
1779
1851
  }
1780
- return part;
1852
+ if (tool.result?.status === "error") {
1853
+ return {
1854
+ ...partBase,
1855
+ state: "error",
1856
+ input: contentPart.input,
1857
+ error: tool.result.error
1858
+ };
1859
+ }
1860
+ return {
1861
+ ...partBase,
1862
+ state: "input-available",
1863
+ input: contentPart.input
1864
+ };
1781
1865
  }
1782
1866
  const attachment = contentAttachment(contentPart);
1783
1867
  return {
@@ -1939,7 +2023,12 @@ function scopeKey(scope) {
1939
2023
  ].join(":");
1940
2024
  }
1941
2025
  function isRootTerminalAgentEvent(event) {
1942
- return event.type === "final" || event.type === "error";
2026
+ return event.type === "response" || event.type === "interaction" || event.type === "blocked" || event.type === "error";
2027
+ }
2028
+ function agentOutcomeStatus(type) {
2029
+ if (type === "response") return "completed";
2030
+ if (type === "interaction") return "suspended";
2031
+ return "blocked";
1943
2032
  }
1944
2033
  function propagateCancellation(source, transform) {
1945
2034
  return {
@@ -2193,21 +2282,22 @@ function applyClientStreamEvent(messages, event) {
2193
2282
  }
2194
2283
  case "tool_call_delta":
2195
2284
  return updatePart(messages, event.messageId, event.partId, (current) => {
2196
- const part = current?.type === "tool" ? { ...current } : {
2285
+ const part = current?.type === "tool" ? toolPartBase(current) : {
2197
2286
  id: event.partId,
2198
2287
  type: "tool",
2199
2288
  toolName: event.toolName ?? "",
2200
- toolCallId: event.toolCallId,
2201
- state: "input-streaming"
2289
+ toolCallId: event.toolCallId
2202
2290
  };
2203
- const previous = typeof part.input === "string" ? part.input : "";
2204
- part.input = event.mode === "replace" ? event.delta : `${previous}${event.delta}`;
2205
- part.state = "input-streaming";
2291
+ const previous = current?.type === "tool" && typeof current.input === "string" ? current.input : "";
2206
2292
  if (event.toolName !== void 0) part.toolName = event.toolName;
2207
2293
  if (event.callId !== void 0) part.callId = event.callId;
2208
2294
  if (event.signature !== void 0) part.signature = event.signature;
2209
2295
  if (event.turn !== void 0) part.turn = event.turn;
2210
- return part;
2296
+ return {
2297
+ ...part,
2298
+ state: "input-streaming",
2299
+ input: event.mode === "replace" ? event.delta : `${previous}${event.delta}`
2300
+ };
2211
2301
  });
2212
2302
  case "tool_call_end": {
2213
2303
  const part = {
@@ -2225,26 +2315,31 @@ function applyClientStreamEvent(messages, event) {
2225
2315
  }
2226
2316
  case "tool_result":
2227
2317
  return updatePart(messages, event.messageId, event.partId, (current) => {
2228
- const part = current?.type === "tool" ? { ...current } : {
2318
+ const part = current?.type === "tool" ? toolPartBase(current) : {
2229
2319
  id: event.partId,
2230
2320
  type: "tool",
2231
2321
  toolName: event.toolName,
2232
- toolCallId: event.toolCallId,
2233
- state: "input-available"
2322
+ toolCallId: event.toolCallId
2234
2323
  };
2235
2324
  if (event.callId !== void 0) part.callId = event.callId;
2236
2325
  if (event.internalCallId !== void 0) part.internalCallId = event.internalCallId;
2237
- if (event.input !== void 0) part.input = event.input;
2238
2326
  if (event.turn !== void 0) part.turn = event.turn;
2239
2327
  if (event.result.status === "error") {
2240
- part.state = "error";
2241
- part.error = event.result.error;
2242
- } else {
2243
- part.state = "output-available";
2244
- part.output = event.result.output;
2245
- if (event.result.content !== void 0) part.resultContent = event.result.content;
2328
+ return {
2329
+ ...part,
2330
+ state: "error",
2331
+ input: event.input,
2332
+ error: event.result.error
2333
+ };
2246
2334
  }
2247
- return part;
2335
+ const completed = {
2336
+ ...part,
2337
+ state: "output-available",
2338
+ input: event.input,
2339
+ output: event.result.output
2340
+ };
2341
+ if (event.result.content !== void 0) completed.resultContent = event.result.content;
2342
+ return completed;
2248
2343
  });
2249
2344
  case "source":
2250
2345
  return upsertPart(messages, event.messageId, {
@@ -2314,6 +2409,21 @@ function applyClientStreamEvent(messages, event) {
2314
2409
  return messages;
2315
2410
  }
2316
2411
  }
2412
+ function toolPartBase(part) {
2413
+ const base = {
2414
+ id: part.id,
2415
+ type: part.type,
2416
+ toolName: part.toolName,
2417
+ toolCallId: part.toolCallId
2418
+ };
2419
+ if (part.callId !== void 0) Object.assign(base, { callId: part.callId });
2420
+ if (part.internalCallId !== void 0) {
2421
+ Object.assign(base, { internalCallId: part.internalCallId });
2422
+ }
2423
+ if (part.turn !== void 0) Object.assign(base, { turn: part.turn });
2424
+ if (part.signature !== void 0) Object.assign(base, { signature: part.signature });
2425
+ return base;
2426
+ }
2317
2427
  function reconcileFinalParts(current, final) {
2318
2428
  const clientOnly = current.filter(
2319
2429
  (part) => part.type === "source" || part.type === "data" || part.type === "error"