@cadraos/sdk 0.3.14-p46.4 → 0.3.14

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 (38) hide show
  1. package/README.md +104 -53
  2. package/dist/ai-functions/react/index.cjs +176 -0
  3. package/dist/ai-functions/react/index.cjs.map +1 -0
  4. package/dist/ai-functions/react/index.d.cts +129 -0
  5. package/dist/ai-functions/react/index.d.ts +129 -0
  6. package/dist/ai-functions/react/index.js +153 -0
  7. package/dist/ai-functions/react/index.js.map +1 -0
  8. package/dist/ai-functions/server/index.cjs +296 -0
  9. package/dist/ai-functions/server/index.cjs.map +1 -0
  10. package/dist/ai-functions/server/index.d.cts +334 -0
  11. package/dist/ai-functions/server/index.d.ts +334 -0
  12. package/dist/ai-functions/server/index.js +284 -0
  13. package/dist/ai-functions/server/index.js.map +1 -0
  14. package/dist/chat/index.cjs +299 -17
  15. package/dist/chat/index.cjs.map +1 -1
  16. package/dist/chat/index.d.cts +208 -0
  17. package/dist/chat/index.d.ts +208 -0
  18. package/dist/chat/index.js +299 -17
  19. package/dist/chat/index.js.map +1 -1
  20. package/dist/index.cjs +221 -3
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +269 -1
  23. package/dist/index.d.ts +269 -1
  24. package/dist/index.js +215 -4
  25. package/dist/index.js.map +1 -1
  26. package/dist/kanban/index.cjs +214 -3
  27. package/dist/kanban/index.cjs.map +1 -1
  28. package/dist/kanban/index.d.cts +208 -0
  29. package/dist/kanban/index.d.ts +208 -0
  30. package/dist/kanban/index.js +214 -3
  31. package/dist/kanban/index.js.map +1 -1
  32. package/dist/teams/index.cjs +214 -3
  33. package/dist/teams/index.cjs.map +1 -1
  34. package/dist/teams/index.d.cts +208 -0
  35. package/dist/teams/index.d.ts +208 -0
  36. package/dist/teams/index.js +214 -3
  37. package/dist/teams/index.js.map +1 -1
  38. package/package.json +21 -1
@@ -68,6 +68,14 @@ var CadraError = class extends Error {
68
68
  details;
69
69
  /** Request ID from server (for debugging) */
70
70
  requestId;
71
+ /**
72
+ * The server's own `error.code` exactly as sent on the wire (e.g.
73
+ * `FUNCTION_NOT_PUBLISHED`, `DUPLICATE_REQUEST`, `VARIABLE_TYPE`).
74
+ * Populated by `parseApiError` for EVERY status. `code` keeps its historic
75
+ * per-class values (`conflict`, `server_error`, `validation_error`, …) for
76
+ * compatibility; branch on `serverCode` when you need the precise reason.
77
+ */
78
+ serverCode;
71
79
  constructor(message, code, status, details, requestId) {
72
80
  super(message);
73
81
  this.name = "CadraError";
@@ -145,6 +153,12 @@ function parseApiError(status, body, requestId) {
145
153
  const message = errorBody?.error?.message ?? "An error occurred";
146
154
  const code = errorBody?.error?.code ?? "unknown_error";
147
155
  const details = errorBody?.error?.details;
156
+ const serverCode = typeof errorBody?.error?.code === "string" ? errorBody.error.code : void 0;
157
+ const error = buildApiError(status, message, code, details, requestId);
158
+ if (serverCode !== void 0) error.serverCode = serverCode;
159
+ return error;
160
+ }
161
+ function buildApiError(status, message, code, details, requestId) {
148
162
  switch (status) {
149
163
  case 400:
150
164
  case 422: {
@@ -180,6 +194,42 @@ function parseApiError(status, body, requestId) {
180
194
  }
181
195
  }
182
196
 
197
+ // src/errors/ai-function-errors.ts
198
+ var AiFunctionDuplicateError = class extends ConflictError {
199
+ /** The existing run's execution id. */
200
+ executionId;
201
+ /** The existing run's status (`running`, a terminal status, or `abandoned`). */
202
+ runStatus;
203
+ constructor(message, details, requestId) {
204
+ super(message, details, requestId);
205
+ this.name = "AiFunctionDuplicateError";
206
+ this.serverCode = "DUPLICATE_REQUEST";
207
+ this.executionId = typeof details?.executionId === "string" ? details.executionId : void 0;
208
+ this.runStatus = typeof details?.status === "string" ? details.status : void 0;
209
+ }
210
+ };
211
+ var AiFunctionChargedError = class extends CadraError {
212
+ constructor(source) {
213
+ super(source.message, source.code, source.status, source.details, source.requestId);
214
+ this.name = "AiFunctionChargedError";
215
+ this.serverCode = source.serverCode;
216
+ }
217
+ };
218
+ var AiFunctionParameterError = class extends ValidationError {
219
+ functionKey;
220
+ issues;
221
+ constructor(functionKey, issues) {
222
+ super(
223
+ `Invalid parameters for AI function "${functionKey}": ` + issues.map((i) => `${i.name} (${i.code})`).join(", "),
224
+ issues.map((i) => ({ field: i.name, message: i.code })),
225
+ { functionKey, issues }
226
+ );
227
+ this.name = "AiFunctionParameterError";
228
+ this.functionKey = functionKey;
229
+ this.issues = issues;
230
+ }
231
+ };
232
+
183
233
  // src/utils/retry.ts
184
234
  var DEFAULT_RETRY_CONFIG = {
185
235
  maxRetries: 3,
@@ -298,7 +348,8 @@ var HttpClient = class {
298
348
  headers["Idempotency-Key"] = options.idempotencyKey;
299
349
  }
300
350
  const controller = new AbortController();
301
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
351
+ const timeoutMs = options.timeoutMs ?? this.config.timeout;
352
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
302
353
  try {
303
354
  const doFetch = this.config.fetch ?? fetch;
304
355
  const response = await doFetch(url, {
@@ -334,7 +385,7 @@ var HttpClient = class {
334
385
  } catch (error) {
335
386
  clearTimeout(timeoutId);
336
387
  if (error instanceof Error && error.name === "AbortError") {
337
- throw new TimeoutError(`Request timed out after ${this.config.timeout}ms`);
388
+ throw new TimeoutError(`Request timed out after ${options.timeoutMs ?? this.config.timeout}ms`);
338
389
  }
339
390
  if (error instanceof TypeError && error.message.includes("fetch")) {
340
391
  throw new NetworkError(`Network error: ${error.message}`);
@@ -366,7 +417,8 @@ var HttpClient = class {
366
417
  path,
367
418
  body,
368
419
  idempotencyKey: options?.idempotencyKey,
369
- skipRetry: options?.skipRetry
420
+ skipRetry: options?.skipRetry,
421
+ timeoutMs: options?.timeoutMs
370
422
  });
371
423
  }
372
424
  /**
@@ -1125,6 +1177,10 @@ var ExecutionStreamControllerImpl = class {
1125
1177
  toolName,
1126
1178
  result: data.result,
1127
1179
  durationMs: data.durationMs ?? 0,
1180
+ // CAD-243: pass the producer's failure verdict through.
1181
+ ...data.isError === true ? { isError: true } : {},
1182
+ ...typeof data.status === "string" ? { status: data.status } : {},
1183
+ ...typeof data.error === "string" ? { error: data.error } : {},
1128
1184
  forwardedFrom: event.data._forwardedFrom,
1129
1185
  // CAD-87 S1-001 EventIdentity → (execId, seq) reducer dedup (S1-003).
1130
1186
  sourceExecutionId: data.sourceExecutionId ?? data.executionId,
@@ -3363,6 +3419,156 @@ var RemoteRunnersAPI = class extends BaseResourceAPI {
3363
3419
  }
3364
3420
  };
3365
3421
 
3422
+ // src/resources/ai-functions.ts
3423
+ var AI_FUNCTION_RUN_TIMEOUT_MS = 6e4;
3424
+ var AI_FUNCTION_DESCRIPTOR_TTL_MS = 6e4;
3425
+ function checkAiFunctionParameters(parameters, variables) {
3426
+ const issues = [];
3427
+ const declared = /* @__PURE__ */ new Set();
3428
+ for (const p of parameters) {
3429
+ declared.add(p.name);
3430
+ const has = Object.prototype.hasOwnProperty.call(variables, p.name);
3431
+ const value = has ? variables[p.name] : void 0;
3432
+ if (value === void 0 || value === null) {
3433
+ if (p.required) issues.push({ name: p.name, code: "missing" });
3434
+ continue;
3435
+ }
3436
+ switch (p.type) {
3437
+ case "number":
3438
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3439
+ issues.push({ name: p.name, code: "type" });
3440
+ }
3441
+ break;
3442
+ case "boolean":
3443
+ if (typeof value !== "boolean") issues.push({ name: p.name, code: "type" });
3444
+ break;
3445
+ case "enum":
3446
+ if (typeof value !== "string") issues.push({ name: p.name, code: "type" });
3447
+ else if (p.options && !p.options.includes(value)) {
3448
+ issues.push({ name: p.name, code: "option" });
3449
+ }
3450
+ break;
3451
+ case "string":
3452
+ case "text":
3453
+ if (typeof value !== "string") issues.push({ name: p.name, code: "type" });
3454
+ else if (typeof p.maxLength === "number" && value.length > p.maxLength) {
3455
+ issues.push({ name: p.name, code: "too_long" });
3456
+ }
3457
+ break;
3458
+ case "image":
3459
+ if (typeof value !== "string") issues.push({ name: p.name, code: "type" });
3460
+ break;
3461
+ }
3462
+ }
3463
+ for (const name of Object.keys(variables)) {
3464
+ if (!declared.has(name)) issues.push({ name, code: "unknown" });
3465
+ }
3466
+ return issues;
3467
+ }
3468
+ var AiFunctionsAPI = class extends BaseResourceAPI {
3469
+ descriptorCache = /* @__PURE__ */ new Map();
3470
+ constructor(http) {
3471
+ super(http, "/api/v1/ai-functions");
3472
+ }
3473
+ /**
3474
+ * List the functions this org can call — the index (D27a). Each entry carries
3475
+ * the live version's typed parameters; never prompt text.
3476
+ */
3477
+ async list() {
3478
+ const data = await this.httpGet("");
3479
+ const functions = Array.isArray(data) ? data : data?.functions ?? [];
3480
+ for (const d of functions) this.cacheDescriptor(d);
3481
+ return functions;
3482
+ }
3483
+ /**
3484
+ * Get one function's descriptor by key (D27a).
3485
+ * `NotFoundError` (`FUNCTION_NOT_FOUND`) / `ConflictError` (`FUNCTION_NOT_PUBLISHED`).
3486
+ */
3487
+ async get(functionKey) {
3488
+ const data = await this.httpGet(
3489
+ `/${encodeURIComponent(functionKey)}`
3490
+ );
3491
+ const descriptor = data && typeof data === "object" && "function" in data && !("functionKey" in data) ? data.function : data;
3492
+ this.cacheDescriptor(descriptor, functionKey);
3493
+ return descriptor;
3494
+ }
3495
+ /**
3496
+ * Read one of this org's own runs (safe projection, §6.5).
3497
+ */
3498
+ async getRun(executionId) {
3499
+ return this.httpGet(`/runs/${encodeURIComponent(executionId)}`);
3500
+ }
3501
+ /** Forget cached descriptors (one key, or all). */
3502
+ clearDescriptorCache(functionKey) {
3503
+ if (functionKey) this.descriptorCache.delete(functionKey);
3504
+ else this.descriptorCache.clear();
3505
+ }
3506
+ async run(refOrKey, parametersOrOptions, shorthandOpts) {
3507
+ let ref;
3508
+ let options;
3509
+ if (typeof refOrKey === "string") {
3510
+ ref = { functionKey: refOrKey };
3511
+ options = { ...shorthandOpts ?? {}, variables: parametersOrOptions };
3512
+ } else {
3513
+ ref = refOrKey;
3514
+ options = parametersOrOptions;
3515
+ }
3516
+ const variables = options.variables ?? {};
3517
+ const functionKey = "functionKey" in ref ? ref.functionKey : void 0;
3518
+ const shouldCheck = functionKey !== void 0 && options.version === void 0 && options.validate !== false;
3519
+ if (shouldCheck) {
3520
+ const descriptor = await this.getCachedDescriptor(functionKey);
3521
+ const issues = checkAiFunctionParameters(descriptor.parameters, variables);
3522
+ if (issues.length > 0) {
3523
+ throw new AiFunctionParameterError(functionKey, issues);
3524
+ }
3525
+ }
3526
+ const body = {
3527
+ ref,
3528
+ variables,
3529
+ version: options.version,
3530
+ idempotencyKey: options.idempotencyKey ?? generateExecutionId(),
3531
+ params: options.params
3532
+ };
3533
+ try {
3534
+ return await this.httpPost("/run", body, {
3535
+ skipRetry: true,
3536
+ timeoutMs: AI_FUNCTION_RUN_TIMEOUT_MS
3537
+ });
3538
+ } catch (error) {
3539
+ throw this.mapRunError(error, functionKey);
3540
+ }
3541
+ }
3542
+ // ── internals ──────────────────────────────────────────────────────────────
3543
+ cacheDescriptor(descriptor, key) {
3544
+ if (!descriptor || typeof descriptor !== "object") return;
3545
+ const cacheKey = key ?? descriptor.functionKey;
3546
+ if (!cacheKey) return;
3547
+ this.descriptorCache.set(cacheKey, {
3548
+ descriptor,
3549
+ expiresAt: Date.now() + AI_FUNCTION_DESCRIPTOR_TTL_MS
3550
+ });
3551
+ }
3552
+ async getCachedDescriptor(functionKey) {
3553
+ const hit = this.descriptorCache.get(functionKey);
3554
+ if (hit && hit.expiresAt > Date.now()) return hit.descriptor;
3555
+ return this.get(functionKey);
3556
+ }
3557
+ mapRunError(error, functionKey) {
3558
+ if (!(error instanceof CadraError)) return error;
3559
+ if (error.serverCode === "DUPLICATE_REQUEST") {
3560
+ return new AiFunctionDuplicateError(error.message, error.details, error.requestId);
3561
+ }
3562
+ if (error.details?.charged === true) {
3563
+ return new AiFunctionChargedError(error);
3564
+ }
3565
+ if (functionKey && error.serverCode?.startsWith("VARIABLE_")) {
3566
+ this.descriptorCache.delete(functionKey);
3567
+ }
3568
+ return error;
3569
+ }
3570
+ };
3571
+
3366
3572
  // src/client.ts
3367
3573
  var DEFAULT_BASE_URL = "https://api.cadraos.com";
3368
3574
  var DEFAULT_TIMEOUT = 3e4;
@@ -3409,6 +3615,10 @@ var CadraClient = class {
3409
3615
  * Remote runner operations (list)
3410
3616
  */
3411
3617
  remoteRunners;
3618
+ /**
3619
+ * AI function operations (list, get, run, getRun) — p93
3620
+ */
3621
+ aiFunctions;
3412
3622
  /**
3413
3623
  * Create a new AI SaaS client
3414
3624
  *
@@ -3498,6 +3708,7 @@ var CadraClient = class {
3498
3708
  this.kanban = new KanbanAPI(this.httpClient, getStreamConfig);
3499
3709
  this.remoteAgents = new RemoteAgentsAPI(this.httpClient, getStreamConfig);
3500
3710
  this.remoteRunners = new RemoteRunnersAPI(this.httpClient);
3711
+ this.aiFunctions = new AiFunctionsAPI(this.httpClient);
3501
3712
  }
3502
3713
  /**
3503
3714
  * Validate client options
@@ -4187,6 +4398,32 @@ function useExecutionStream(options) {
4187
4398
  };
4188
4399
  }
4189
4400
 
4401
+ // src/chat/utils/toolOutcome.ts
4402
+ var MAX_ERROR_CHARS = 500;
4403
+ function asRecord(value) {
4404
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
4405
+ }
4406
+ function errorFrom(value) {
4407
+ if (typeof value === "string" && value.trim()) return value;
4408
+ const rec = asRecord(value);
4409
+ if (rec && typeof rec.message === "string" && rec.message.trim()) return rec.message;
4410
+ return void 0;
4411
+ }
4412
+ function isFailurePayload(result) {
4413
+ const rec = asRecord(result);
4414
+ if (!rec) return false;
4415
+ return rec.success === false || rec.isError === true;
4416
+ }
4417
+ function cap(text) {
4418
+ return text.length > MAX_ERROR_CHARS ? `${text.slice(0, MAX_ERROR_CHARS)}\u2026` : text;
4419
+ }
4420
+ function resolveToolOutcome(input) {
4421
+ const failed = input.isError === true || input.status === "failed" || input.success === false || input.payloadFailure === true || isFailurePayload(input.result);
4422
+ if (!failed) return { status: "completed" };
4423
+ const error = errorFrom(input.error) ?? errorFrom(asRecord(input.result)?.error) ?? "Tool call failed";
4424
+ return { status: "failed", error: cap(error) };
4425
+ }
4426
+
4190
4427
  // src/chat/hooks/history-cache.ts
4191
4428
  var DEFAULT_HISTORY_STALE_MS = 1e4;
4192
4429
  var cache = /* @__PURE__ */ new Map();
@@ -4362,9 +4599,10 @@ function applyEventInner(state, event) {
4362
4599
  if (tc) {
4363
4600
  toolCalls.set(event.id, {
4364
4601
  ...tc,
4365
- status: "completed",
4602
+ status: event.status ?? "completed",
4366
4603
  result: event.result,
4367
- durationMs: event.durationMs
4604
+ durationMs: event.durationMs,
4605
+ ...event.error !== void 0 ? { error: event.error } : {}
4368
4606
  });
4369
4607
  return { ...state, toolCalls };
4370
4608
  }
@@ -4373,8 +4611,9 @@ function applyEventInner(state, event) {
4373
4611
  name: event.toolName ?? "unknown",
4374
4612
  args: {},
4375
4613
  result: event.result,
4376
- status: "completed",
4377
- durationMs: event.durationMs
4614
+ status: event.status ?? "completed",
4615
+ durationMs: event.durationMs,
4616
+ ...event.error !== void 0 ? { error: event.error } : {}
4378
4617
  });
4379
4618
  if (state.seenToolBlockIds.has(event.id)) {
4380
4619
  return { ...state, toolCalls };
@@ -4624,8 +4863,16 @@ function useAgentChat(options) {
4624
4863
  const streamMsgId = streamingMessageIdRef.current;
4625
4864
  setMessages((prev) => {
4626
4865
  const resolvedContent = streamingContentRef.current || finalOutput || "";
4866
+ const streamIdx = prev.findIndex((msg) => msg.id === streamMsgId);
4867
+ let turnStart = 0;
4868
+ for (let i = streamIdx - 1; i >= 0; i--) {
4869
+ if (prev[i].role === "user") {
4870
+ turnStart = i + 1;
4871
+ break;
4872
+ }
4873
+ }
4627
4874
  const duplicateExists = resolvedContent && prev.some(
4628
- (msg) => msg.id !== streamMsgId && msg.role === "assistant" && !msg.isStreaming && msg.content === resolvedContent
4875
+ (msg, idx) => idx >= turnStart && msg.id !== streamMsgId && msg.role === "assistant" && !msg.isStreaming && msg.content === resolvedContent
4629
4876
  );
4630
4877
  if (duplicateExists) {
4631
4878
  return prev.filter((msg) => msg.id !== streamMsgId);
@@ -4746,6 +4993,17 @@ function useAgentChat(options) {
4746
4993
  const resultToolCallId = log.metadata?.toolCallId;
4747
4994
  const result = log.metadata?.output;
4748
4995
  const durationMs = log.durationMs;
4996
+ const outcome = resolveToolOutcome({
4997
+ result,
4998
+ success: log.metadata?.success,
4999
+ payloadFailure: log.metadata?.payloadFailure,
5000
+ isError: log.metadata?.isError,
5001
+ error: log.metadata?.error
5002
+ });
5003
+ const outcomeFields = {
5004
+ status: outcome.status,
5005
+ ...outcome.error ? { error: outcome.error } : {}
5006
+ };
4749
5007
  let matchedToolCall = resultToolCallId ? pendingToolCallsRef.current.get(resultToolCallId) : void 0;
4750
5008
  if (!matchedToolCall) {
4751
5009
  for (const [, tc] of pendingToolCallsRef.current) {
@@ -4759,7 +5017,7 @@ function useAgentChat(options) {
4759
5017
  const completedToolCall = {
4760
5018
  ...matchedToolCall,
4761
5019
  result,
4762
- status: "completed",
5020
+ ...outcomeFields,
4763
5021
  durationMs
4764
5022
  };
4765
5023
  updateToolCall(completedToolCall);
@@ -4770,7 +5028,7 @@ function useAgentChat(options) {
4770
5028
  name: resultToolName || "unknown",
4771
5029
  args: {},
4772
5030
  result,
4773
- status: "completed",
5031
+ ...outcomeFields,
4774
5032
  durationMs
4775
5033
  };
4776
5034
  updateToolCall(lateToolCall);
@@ -4781,7 +5039,8 @@ function useAgentChat(options) {
4781
5039
  id: resultToolCallId,
4782
5040
  toolName: resultToolName,
4783
5041
  result,
4784
- durationMs
5042
+ durationMs,
5043
+ ...outcomeFields
4785
5044
  });
4786
5045
  firedToolResultsRef.current.add(resultToolCallId);
4787
5046
  }
@@ -4891,6 +5150,17 @@ function useAgentChat(options) {
4891
5150
  const resultToolCallId = log.metadata?.toolCallId;
4892
5151
  const result = log.metadata?.output;
4893
5152
  const durationMs = log.durationMs;
5153
+ const outcome = resolveToolOutcome({
5154
+ result,
5155
+ success: log.metadata?.success,
5156
+ payloadFailure: log.metadata?.payloadFailure,
5157
+ isError: log.metadata?.isError,
5158
+ error: log.metadata?.error
5159
+ });
5160
+ const outcomeFields = {
5161
+ status: outcome.status,
5162
+ ...outcome.error ? { error: outcome.error } : {}
5163
+ };
4894
5164
  let matchedToolCall = resultToolCallId ? pendingToolCallsRef.current.get(resultToolCallId) : void 0;
4895
5165
  if (!matchedToolCall) {
4896
5166
  for (const [, tc] of pendingToolCallsRef.current) {
@@ -4904,7 +5174,7 @@ function useAgentChat(options) {
4904
5174
  const completedToolCall = {
4905
5175
  ...matchedToolCall,
4906
5176
  result,
4907
- status: "completed",
5177
+ ...outcomeFields,
4908
5178
  durationMs
4909
5179
  };
4910
5180
  updateToolCall(completedToolCall);
@@ -4915,7 +5185,7 @@ function useAgentChat(options) {
4915
5185
  name: resultToolName || "unknown",
4916
5186
  args: {},
4917
5187
  result,
4918
- status: "completed",
5188
+ ...outcomeFields,
4919
5189
  durationMs
4920
5190
  };
4921
5191
  updateToolCall(lateToolCall);
@@ -4926,7 +5196,8 @@ function useAgentChat(options) {
4926
5196
  id: resultToolCallId,
4927
5197
  toolName: resultToolName,
4928
5198
  result,
4929
- durationMs
5199
+ durationMs,
5200
+ ...outcomeFields
4930
5201
  });
4931
5202
  }
4932
5203
  if (resultToolCallId) {
@@ -5175,12 +5446,22 @@ function useAgentChat(options) {
5175
5446
  if (SILENT_TOOL_NAMES.has(toolName) || isUuidLikeToolName(toolName)) return;
5176
5447
  if (firedToolResultsRef.current.has(toolCallId)) return;
5177
5448
  const existing = pendingToolCallsRef.current.get(toolCallId);
5178
- if (existing && existing.status === "completed") return;
5449
+ if (existing && (existing.status === "completed" || existing.status === "failed")) return;
5450
+ const outcome = resolveToolOutcome({
5451
+ result: data.result,
5452
+ isError: data.isError,
5453
+ status: data.status,
5454
+ error: data.error
5455
+ });
5456
+ const outcomeFields = {
5457
+ status: outcome.status,
5458
+ ...outcome.error ? { error: outcome.error } : {}
5459
+ };
5179
5460
  if (existing) {
5180
5461
  const completedToolCall = {
5181
5462
  ...existing,
5182
5463
  result: data.result,
5183
- status: "completed",
5464
+ ...outcomeFields,
5184
5465
  durationMs: data.durationMs
5185
5466
  };
5186
5467
  updateToolCall(completedToolCall);
@@ -5191,7 +5472,7 @@ function useAgentChat(options) {
5191
5472
  name: toolName,
5192
5473
  args: {},
5193
5474
  result: data.result,
5194
- status: "completed",
5475
+ ...outcomeFields,
5195
5476
  durationMs: data.durationMs
5196
5477
  };
5197
5478
  updateToolCall(lateToolCall);
@@ -5202,6 +5483,7 @@ function useAgentChat(options) {
5202
5483
  toolName,
5203
5484
  result: data.result,
5204
5485
  durationMs: data.durationMs,
5486
+ ...outcomeFields,
5205
5487
  // CAD-87 S1-003: (execId, seq) reducer dedup for replayed events.
5206
5488
  execId: data.sourceExecutionId,
5207
5489
  seq: data.seq