@cadraos/sdk 0.3.14-p46.3 → 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.
- package/README.md +104 -53
- package/dist/ai-functions/react/index.cjs +176 -0
- package/dist/ai-functions/react/index.cjs.map +1 -0
- package/dist/ai-functions/react/index.d.cts +129 -0
- package/dist/ai-functions/react/index.d.ts +129 -0
- package/dist/ai-functions/react/index.js +153 -0
- package/dist/ai-functions/react/index.js.map +1 -0
- package/dist/ai-functions/server/index.cjs +296 -0
- package/dist/ai-functions/server/index.cjs.map +1 -0
- package/dist/ai-functions/server/index.d.cts +334 -0
- package/dist/ai-functions/server/index.d.ts +334 -0
- package/dist/ai-functions/server/index.js +284 -0
- package/dist/ai-functions/server/index.js.map +1 -0
- package/dist/board-host/index.cjs +217 -129
- package/dist/board-host/index.cjs.map +1 -1
- package/dist/board-host/index.d.cts +96 -1
- package/dist/board-host/index.d.ts +96 -1
- package/dist/board-host/index.js +218 -131
- package/dist/board-host/index.js.map +1 -1
- package/dist/board-host/server/index.d.cts +44 -0
- package/dist/board-host/server/index.d.ts +44 -0
- package/dist/chat/index.cjs +299 -17
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +225 -0
- package/dist/chat/index.d.ts +225 -0
- package/dist/chat/index.js +299 -17
- package/dist/chat/index.js.map +1 -1
- package/dist/index.cjs +221 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +286 -1
- package/dist/index.d.ts +286 -1
- package/dist/index.js +215 -4
- package/dist/index.js.map +1 -1
- package/dist/kanban/engine/index.cjs +774 -683
- package/dist/kanban/engine/index.cjs.map +1 -1
- package/dist/kanban/engine/index.d.cts +90 -1
- package/dist/kanban/engine/index.d.ts +90 -1
- package/dist/kanban/engine/index.js +774 -684
- package/dist/kanban/engine/index.js.map +1 -1
- package/dist/kanban/index.cjs +214 -3
- package/dist/kanban/index.cjs.map +1 -1
- package/dist/kanban/index.d.cts +252 -0
- package/dist/kanban/index.d.ts +252 -0
- package/dist/kanban/index.js +214 -3
- package/dist/kanban/index.js.map +1 -1
- package/dist/teams/index.cjs +214 -3
- package/dist/teams/index.cjs.map +1 -1
- package/dist/teams/index.d.cts +225 -0
- package/dist/teams/index.d.ts +225 -0
- package/dist/teams/index.js +214 -3
- package/dist/teams/index.js.map +1 -1
- package/package.json +21 -1
package/dist/chat/index.cjs
CHANGED
|
@@ -75,6 +75,14 @@ var CadraError = class extends Error {
|
|
|
75
75
|
details;
|
|
76
76
|
/** Request ID from server (for debugging) */
|
|
77
77
|
requestId;
|
|
78
|
+
/**
|
|
79
|
+
* The server's own `error.code` exactly as sent on the wire (e.g.
|
|
80
|
+
* `FUNCTION_NOT_PUBLISHED`, `DUPLICATE_REQUEST`, `VARIABLE_TYPE`).
|
|
81
|
+
* Populated by `parseApiError` for EVERY status. `code` keeps its historic
|
|
82
|
+
* per-class values (`conflict`, `server_error`, `validation_error`, …) for
|
|
83
|
+
* compatibility; branch on `serverCode` when you need the precise reason.
|
|
84
|
+
*/
|
|
85
|
+
serverCode;
|
|
78
86
|
constructor(message, code, status, details, requestId) {
|
|
79
87
|
super(message);
|
|
80
88
|
this.name = "CadraError";
|
|
@@ -152,6 +160,12 @@ function parseApiError(status, body, requestId) {
|
|
|
152
160
|
const message = errorBody?.error?.message ?? "An error occurred";
|
|
153
161
|
const code = errorBody?.error?.code ?? "unknown_error";
|
|
154
162
|
const details = errorBody?.error?.details;
|
|
163
|
+
const serverCode = typeof errorBody?.error?.code === "string" ? errorBody.error.code : void 0;
|
|
164
|
+
const error = buildApiError(status, message, code, details, requestId);
|
|
165
|
+
if (serverCode !== void 0) error.serverCode = serverCode;
|
|
166
|
+
return error;
|
|
167
|
+
}
|
|
168
|
+
function buildApiError(status, message, code, details, requestId) {
|
|
155
169
|
switch (status) {
|
|
156
170
|
case 400:
|
|
157
171
|
case 422: {
|
|
@@ -187,6 +201,42 @@ function parseApiError(status, body, requestId) {
|
|
|
187
201
|
}
|
|
188
202
|
}
|
|
189
203
|
|
|
204
|
+
// src/errors/ai-function-errors.ts
|
|
205
|
+
var AiFunctionDuplicateError = class extends ConflictError {
|
|
206
|
+
/** The existing run's execution id. */
|
|
207
|
+
executionId;
|
|
208
|
+
/** The existing run's status (`running`, a terminal status, or `abandoned`). */
|
|
209
|
+
runStatus;
|
|
210
|
+
constructor(message, details, requestId) {
|
|
211
|
+
super(message, details, requestId);
|
|
212
|
+
this.name = "AiFunctionDuplicateError";
|
|
213
|
+
this.serverCode = "DUPLICATE_REQUEST";
|
|
214
|
+
this.executionId = typeof details?.executionId === "string" ? details.executionId : void 0;
|
|
215
|
+
this.runStatus = typeof details?.status === "string" ? details.status : void 0;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var AiFunctionChargedError = class extends CadraError {
|
|
219
|
+
constructor(source) {
|
|
220
|
+
super(source.message, source.code, source.status, source.details, source.requestId);
|
|
221
|
+
this.name = "AiFunctionChargedError";
|
|
222
|
+
this.serverCode = source.serverCode;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
var AiFunctionParameterError = class extends ValidationError {
|
|
226
|
+
functionKey;
|
|
227
|
+
issues;
|
|
228
|
+
constructor(functionKey, issues) {
|
|
229
|
+
super(
|
|
230
|
+
`Invalid parameters for AI function "${functionKey}": ` + issues.map((i) => `${i.name} (${i.code})`).join(", "),
|
|
231
|
+
issues.map((i) => ({ field: i.name, message: i.code })),
|
|
232
|
+
{ functionKey, issues }
|
|
233
|
+
);
|
|
234
|
+
this.name = "AiFunctionParameterError";
|
|
235
|
+
this.functionKey = functionKey;
|
|
236
|
+
this.issues = issues;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
190
240
|
// src/utils/retry.ts
|
|
191
241
|
var DEFAULT_RETRY_CONFIG = {
|
|
192
242
|
maxRetries: 3,
|
|
@@ -305,7 +355,8 @@ var HttpClient = class {
|
|
|
305
355
|
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
306
356
|
}
|
|
307
357
|
const controller = new AbortController();
|
|
308
|
-
const
|
|
358
|
+
const timeoutMs = options.timeoutMs ?? this.config.timeout;
|
|
359
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
309
360
|
try {
|
|
310
361
|
const doFetch = this.config.fetch ?? fetch;
|
|
311
362
|
const response = await doFetch(url, {
|
|
@@ -341,7 +392,7 @@ var HttpClient = class {
|
|
|
341
392
|
} catch (error) {
|
|
342
393
|
clearTimeout(timeoutId);
|
|
343
394
|
if (error instanceof Error && error.name === "AbortError") {
|
|
344
|
-
throw new TimeoutError(`Request timed out after ${this.config.timeout}ms`);
|
|
395
|
+
throw new TimeoutError(`Request timed out after ${options.timeoutMs ?? this.config.timeout}ms`);
|
|
345
396
|
}
|
|
346
397
|
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
347
398
|
throw new NetworkError(`Network error: ${error.message}`);
|
|
@@ -373,7 +424,8 @@ var HttpClient = class {
|
|
|
373
424
|
path,
|
|
374
425
|
body,
|
|
375
426
|
idempotencyKey: options?.idempotencyKey,
|
|
376
|
-
skipRetry: options?.skipRetry
|
|
427
|
+
skipRetry: options?.skipRetry,
|
|
428
|
+
timeoutMs: options?.timeoutMs
|
|
377
429
|
});
|
|
378
430
|
}
|
|
379
431
|
/**
|
|
@@ -1132,6 +1184,10 @@ var ExecutionStreamControllerImpl = class {
|
|
|
1132
1184
|
toolName,
|
|
1133
1185
|
result: data.result,
|
|
1134
1186
|
durationMs: data.durationMs ?? 0,
|
|
1187
|
+
// CAD-243: pass the producer's failure verdict through.
|
|
1188
|
+
...data.isError === true ? { isError: true } : {},
|
|
1189
|
+
...typeof data.status === "string" ? { status: data.status } : {},
|
|
1190
|
+
...typeof data.error === "string" ? { error: data.error } : {},
|
|
1135
1191
|
forwardedFrom: event.data._forwardedFrom,
|
|
1136
1192
|
// CAD-87 S1-001 EventIdentity → (execId, seq) reducer dedup (S1-003).
|
|
1137
1193
|
sourceExecutionId: data.sourceExecutionId ?? data.executionId,
|
|
@@ -3370,6 +3426,156 @@ var RemoteRunnersAPI = class extends BaseResourceAPI {
|
|
|
3370
3426
|
}
|
|
3371
3427
|
};
|
|
3372
3428
|
|
|
3429
|
+
// src/resources/ai-functions.ts
|
|
3430
|
+
var AI_FUNCTION_RUN_TIMEOUT_MS = 6e4;
|
|
3431
|
+
var AI_FUNCTION_DESCRIPTOR_TTL_MS = 6e4;
|
|
3432
|
+
function checkAiFunctionParameters(parameters, variables) {
|
|
3433
|
+
const issues = [];
|
|
3434
|
+
const declared = /* @__PURE__ */ new Set();
|
|
3435
|
+
for (const p of parameters) {
|
|
3436
|
+
declared.add(p.name);
|
|
3437
|
+
const has = Object.prototype.hasOwnProperty.call(variables, p.name);
|
|
3438
|
+
const value = has ? variables[p.name] : void 0;
|
|
3439
|
+
if (value === void 0 || value === null) {
|
|
3440
|
+
if (p.required) issues.push({ name: p.name, code: "missing" });
|
|
3441
|
+
continue;
|
|
3442
|
+
}
|
|
3443
|
+
switch (p.type) {
|
|
3444
|
+
case "number":
|
|
3445
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
3446
|
+
issues.push({ name: p.name, code: "type" });
|
|
3447
|
+
}
|
|
3448
|
+
break;
|
|
3449
|
+
case "boolean":
|
|
3450
|
+
if (typeof value !== "boolean") issues.push({ name: p.name, code: "type" });
|
|
3451
|
+
break;
|
|
3452
|
+
case "enum":
|
|
3453
|
+
if (typeof value !== "string") issues.push({ name: p.name, code: "type" });
|
|
3454
|
+
else if (p.options && !p.options.includes(value)) {
|
|
3455
|
+
issues.push({ name: p.name, code: "option" });
|
|
3456
|
+
}
|
|
3457
|
+
break;
|
|
3458
|
+
case "string":
|
|
3459
|
+
case "text":
|
|
3460
|
+
if (typeof value !== "string") issues.push({ name: p.name, code: "type" });
|
|
3461
|
+
else if (typeof p.maxLength === "number" && value.length > p.maxLength) {
|
|
3462
|
+
issues.push({ name: p.name, code: "too_long" });
|
|
3463
|
+
}
|
|
3464
|
+
break;
|
|
3465
|
+
case "image":
|
|
3466
|
+
if (typeof value !== "string") issues.push({ name: p.name, code: "type" });
|
|
3467
|
+
break;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
for (const name of Object.keys(variables)) {
|
|
3471
|
+
if (!declared.has(name)) issues.push({ name, code: "unknown" });
|
|
3472
|
+
}
|
|
3473
|
+
return issues;
|
|
3474
|
+
}
|
|
3475
|
+
var AiFunctionsAPI = class extends BaseResourceAPI {
|
|
3476
|
+
descriptorCache = /* @__PURE__ */ new Map();
|
|
3477
|
+
constructor(http) {
|
|
3478
|
+
super(http, "/api/v1/ai-functions");
|
|
3479
|
+
}
|
|
3480
|
+
/**
|
|
3481
|
+
* List the functions this org can call — the index (D27a). Each entry carries
|
|
3482
|
+
* the live version's typed parameters; never prompt text.
|
|
3483
|
+
*/
|
|
3484
|
+
async list() {
|
|
3485
|
+
const data = await this.httpGet("");
|
|
3486
|
+
const functions = Array.isArray(data) ? data : data?.functions ?? [];
|
|
3487
|
+
for (const d of functions) this.cacheDescriptor(d);
|
|
3488
|
+
return functions;
|
|
3489
|
+
}
|
|
3490
|
+
/**
|
|
3491
|
+
* Get one function's descriptor by key (D27a).
|
|
3492
|
+
* `NotFoundError` (`FUNCTION_NOT_FOUND`) / `ConflictError` (`FUNCTION_NOT_PUBLISHED`).
|
|
3493
|
+
*/
|
|
3494
|
+
async get(functionKey) {
|
|
3495
|
+
const data = await this.httpGet(
|
|
3496
|
+
`/${encodeURIComponent(functionKey)}`
|
|
3497
|
+
);
|
|
3498
|
+
const descriptor = data && typeof data === "object" && "function" in data && !("functionKey" in data) ? data.function : data;
|
|
3499
|
+
this.cacheDescriptor(descriptor, functionKey);
|
|
3500
|
+
return descriptor;
|
|
3501
|
+
}
|
|
3502
|
+
/**
|
|
3503
|
+
* Read one of this org's own runs (safe projection, §6.5).
|
|
3504
|
+
*/
|
|
3505
|
+
async getRun(executionId) {
|
|
3506
|
+
return this.httpGet(`/runs/${encodeURIComponent(executionId)}`);
|
|
3507
|
+
}
|
|
3508
|
+
/** Forget cached descriptors (one key, or all). */
|
|
3509
|
+
clearDescriptorCache(functionKey) {
|
|
3510
|
+
if (functionKey) this.descriptorCache.delete(functionKey);
|
|
3511
|
+
else this.descriptorCache.clear();
|
|
3512
|
+
}
|
|
3513
|
+
async run(refOrKey, parametersOrOptions, shorthandOpts) {
|
|
3514
|
+
let ref;
|
|
3515
|
+
let options;
|
|
3516
|
+
if (typeof refOrKey === "string") {
|
|
3517
|
+
ref = { functionKey: refOrKey };
|
|
3518
|
+
options = { ...shorthandOpts ?? {}, variables: parametersOrOptions };
|
|
3519
|
+
} else {
|
|
3520
|
+
ref = refOrKey;
|
|
3521
|
+
options = parametersOrOptions;
|
|
3522
|
+
}
|
|
3523
|
+
const variables = options.variables ?? {};
|
|
3524
|
+
const functionKey = "functionKey" in ref ? ref.functionKey : void 0;
|
|
3525
|
+
const shouldCheck = functionKey !== void 0 && options.version === void 0 && options.validate !== false;
|
|
3526
|
+
if (shouldCheck) {
|
|
3527
|
+
const descriptor = await this.getCachedDescriptor(functionKey);
|
|
3528
|
+
const issues = checkAiFunctionParameters(descriptor.parameters, variables);
|
|
3529
|
+
if (issues.length > 0) {
|
|
3530
|
+
throw new AiFunctionParameterError(functionKey, issues);
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
const body = {
|
|
3534
|
+
ref,
|
|
3535
|
+
variables,
|
|
3536
|
+
version: options.version,
|
|
3537
|
+
idempotencyKey: options.idempotencyKey ?? generateExecutionId(),
|
|
3538
|
+
params: options.params
|
|
3539
|
+
};
|
|
3540
|
+
try {
|
|
3541
|
+
return await this.httpPost("/run", body, {
|
|
3542
|
+
skipRetry: true,
|
|
3543
|
+
timeoutMs: AI_FUNCTION_RUN_TIMEOUT_MS
|
|
3544
|
+
});
|
|
3545
|
+
} catch (error) {
|
|
3546
|
+
throw this.mapRunError(error, functionKey);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
// ── internals ──────────────────────────────────────────────────────────────
|
|
3550
|
+
cacheDescriptor(descriptor, key) {
|
|
3551
|
+
if (!descriptor || typeof descriptor !== "object") return;
|
|
3552
|
+
const cacheKey = key ?? descriptor.functionKey;
|
|
3553
|
+
if (!cacheKey) return;
|
|
3554
|
+
this.descriptorCache.set(cacheKey, {
|
|
3555
|
+
descriptor,
|
|
3556
|
+
expiresAt: Date.now() + AI_FUNCTION_DESCRIPTOR_TTL_MS
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
async getCachedDescriptor(functionKey) {
|
|
3560
|
+
const hit = this.descriptorCache.get(functionKey);
|
|
3561
|
+
if (hit && hit.expiresAt > Date.now()) return hit.descriptor;
|
|
3562
|
+
return this.get(functionKey);
|
|
3563
|
+
}
|
|
3564
|
+
mapRunError(error, functionKey) {
|
|
3565
|
+
if (!(error instanceof CadraError)) return error;
|
|
3566
|
+
if (error.serverCode === "DUPLICATE_REQUEST") {
|
|
3567
|
+
return new AiFunctionDuplicateError(error.message, error.details, error.requestId);
|
|
3568
|
+
}
|
|
3569
|
+
if (error.details?.charged === true) {
|
|
3570
|
+
return new AiFunctionChargedError(error);
|
|
3571
|
+
}
|
|
3572
|
+
if (functionKey && error.serverCode?.startsWith("VARIABLE_")) {
|
|
3573
|
+
this.descriptorCache.delete(functionKey);
|
|
3574
|
+
}
|
|
3575
|
+
return error;
|
|
3576
|
+
}
|
|
3577
|
+
};
|
|
3578
|
+
|
|
3373
3579
|
// src/client.ts
|
|
3374
3580
|
var DEFAULT_BASE_URL = "https://api.cadraos.com";
|
|
3375
3581
|
var DEFAULT_TIMEOUT = 3e4;
|
|
@@ -3416,6 +3622,10 @@ var CadraClient = class {
|
|
|
3416
3622
|
* Remote runner operations (list)
|
|
3417
3623
|
*/
|
|
3418
3624
|
remoteRunners;
|
|
3625
|
+
/**
|
|
3626
|
+
* AI function operations (list, get, run, getRun) — p93
|
|
3627
|
+
*/
|
|
3628
|
+
aiFunctions;
|
|
3419
3629
|
/**
|
|
3420
3630
|
* Create a new AI SaaS client
|
|
3421
3631
|
*
|
|
@@ -3505,6 +3715,7 @@ var CadraClient = class {
|
|
|
3505
3715
|
this.kanban = new KanbanAPI(this.httpClient, getStreamConfig);
|
|
3506
3716
|
this.remoteAgents = new RemoteAgentsAPI(this.httpClient, getStreamConfig);
|
|
3507
3717
|
this.remoteRunners = new RemoteRunnersAPI(this.httpClient);
|
|
3718
|
+
this.aiFunctions = new AiFunctionsAPI(this.httpClient);
|
|
3508
3719
|
}
|
|
3509
3720
|
/**
|
|
3510
3721
|
* Validate client options
|
|
@@ -4194,6 +4405,32 @@ function useExecutionStream(options) {
|
|
|
4194
4405
|
};
|
|
4195
4406
|
}
|
|
4196
4407
|
|
|
4408
|
+
// src/chat/utils/toolOutcome.ts
|
|
4409
|
+
var MAX_ERROR_CHARS = 500;
|
|
4410
|
+
function asRecord(value) {
|
|
4411
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4412
|
+
}
|
|
4413
|
+
function errorFrom(value) {
|
|
4414
|
+
if (typeof value === "string" && value.trim()) return value;
|
|
4415
|
+
const rec = asRecord(value);
|
|
4416
|
+
if (rec && typeof rec.message === "string" && rec.message.trim()) return rec.message;
|
|
4417
|
+
return void 0;
|
|
4418
|
+
}
|
|
4419
|
+
function isFailurePayload(result) {
|
|
4420
|
+
const rec = asRecord(result);
|
|
4421
|
+
if (!rec) return false;
|
|
4422
|
+
return rec.success === false || rec.isError === true;
|
|
4423
|
+
}
|
|
4424
|
+
function cap(text) {
|
|
4425
|
+
return text.length > MAX_ERROR_CHARS ? `${text.slice(0, MAX_ERROR_CHARS)}\u2026` : text;
|
|
4426
|
+
}
|
|
4427
|
+
function resolveToolOutcome(input) {
|
|
4428
|
+
const failed = input.isError === true || input.status === "failed" || input.success === false || input.payloadFailure === true || isFailurePayload(input.result);
|
|
4429
|
+
if (!failed) return { status: "completed" };
|
|
4430
|
+
const error = errorFrom(input.error) ?? errorFrom(asRecord(input.result)?.error) ?? "Tool call failed";
|
|
4431
|
+
return { status: "failed", error: cap(error) };
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4197
4434
|
// src/chat/hooks/history-cache.ts
|
|
4198
4435
|
var DEFAULT_HISTORY_STALE_MS = 1e4;
|
|
4199
4436
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -4369,9 +4606,10 @@ function applyEventInner(state, event) {
|
|
|
4369
4606
|
if (tc) {
|
|
4370
4607
|
toolCalls.set(event.id, {
|
|
4371
4608
|
...tc,
|
|
4372
|
-
status: "completed",
|
|
4609
|
+
status: event.status ?? "completed",
|
|
4373
4610
|
result: event.result,
|
|
4374
|
-
durationMs: event.durationMs
|
|
4611
|
+
durationMs: event.durationMs,
|
|
4612
|
+
...event.error !== void 0 ? { error: event.error } : {}
|
|
4375
4613
|
});
|
|
4376
4614
|
return { ...state, toolCalls };
|
|
4377
4615
|
}
|
|
@@ -4380,8 +4618,9 @@ function applyEventInner(state, event) {
|
|
|
4380
4618
|
name: event.toolName ?? "unknown",
|
|
4381
4619
|
args: {},
|
|
4382
4620
|
result: event.result,
|
|
4383
|
-
status: "completed",
|
|
4384
|
-
durationMs: event.durationMs
|
|
4621
|
+
status: event.status ?? "completed",
|
|
4622
|
+
durationMs: event.durationMs,
|
|
4623
|
+
...event.error !== void 0 ? { error: event.error } : {}
|
|
4385
4624
|
});
|
|
4386
4625
|
if (state.seenToolBlockIds.has(event.id)) {
|
|
4387
4626
|
return { ...state, toolCalls };
|
|
@@ -4631,8 +4870,16 @@ function useAgentChat(options) {
|
|
|
4631
4870
|
const streamMsgId = streamingMessageIdRef.current;
|
|
4632
4871
|
setMessages((prev) => {
|
|
4633
4872
|
const resolvedContent = streamingContentRef.current || finalOutput || "";
|
|
4873
|
+
const streamIdx = prev.findIndex((msg) => msg.id === streamMsgId);
|
|
4874
|
+
let turnStart = 0;
|
|
4875
|
+
for (let i = streamIdx - 1; i >= 0; i--) {
|
|
4876
|
+
if (prev[i].role === "user") {
|
|
4877
|
+
turnStart = i + 1;
|
|
4878
|
+
break;
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4634
4881
|
const duplicateExists = resolvedContent && prev.some(
|
|
4635
|
-
(msg) => msg.id !== streamMsgId && msg.role === "assistant" && !msg.isStreaming && msg.content === resolvedContent
|
|
4882
|
+
(msg, idx) => idx >= turnStart && msg.id !== streamMsgId && msg.role === "assistant" && !msg.isStreaming && msg.content === resolvedContent
|
|
4636
4883
|
);
|
|
4637
4884
|
if (duplicateExists) {
|
|
4638
4885
|
return prev.filter((msg) => msg.id !== streamMsgId);
|
|
@@ -4753,6 +5000,17 @@ function useAgentChat(options) {
|
|
|
4753
5000
|
const resultToolCallId = log.metadata?.toolCallId;
|
|
4754
5001
|
const result = log.metadata?.output;
|
|
4755
5002
|
const durationMs = log.durationMs;
|
|
5003
|
+
const outcome = resolveToolOutcome({
|
|
5004
|
+
result,
|
|
5005
|
+
success: log.metadata?.success,
|
|
5006
|
+
payloadFailure: log.metadata?.payloadFailure,
|
|
5007
|
+
isError: log.metadata?.isError,
|
|
5008
|
+
error: log.metadata?.error
|
|
5009
|
+
});
|
|
5010
|
+
const outcomeFields = {
|
|
5011
|
+
status: outcome.status,
|
|
5012
|
+
...outcome.error ? { error: outcome.error } : {}
|
|
5013
|
+
};
|
|
4756
5014
|
let matchedToolCall = resultToolCallId ? pendingToolCallsRef.current.get(resultToolCallId) : void 0;
|
|
4757
5015
|
if (!matchedToolCall) {
|
|
4758
5016
|
for (const [, tc] of pendingToolCallsRef.current) {
|
|
@@ -4766,7 +5024,7 @@ function useAgentChat(options) {
|
|
|
4766
5024
|
const completedToolCall = {
|
|
4767
5025
|
...matchedToolCall,
|
|
4768
5026
|
result,
|
|
4769
|
-
|
|
5027
|
+
...outcomeFields,
|
|
4770
5028
|
durationMs
|
|
4771
5029
|
};
|
|
4772
5030
|
updateToolCall(completedToolCall);
|
|
@@ -4777,7 +5035,7 @@ function useAgentChat(options) {
|
|
|
4777
5035
|
name: resultToolName || "unknown",
|
|
4778
5036
|
args: {},
|
|
4779
5037
|
result,
|
|
4780
|
-
|
|
5038
|
+
...outcomeFields,
|
|
4781
5039
|
durationMs
|
|
4782
5040
|
};
|
|
4783
5041
|
updateToolCall(lateToolCall);
|
|
@@ -4788,7 +5046,8 @@ function useAgentChat(options) {
|
|
|
4788
5046
|
id: resultToolCallId,
|
|
4789
5047
|
toolName: resultToolName,
|
|
4790
5048
|
result,
|
|
4791
|
-
durationMs
|
|
5049
|
+
durationMs,
|
|
5050
|
+
...outcomeFields
|
|
4792
5051
|
});
|
|
4793
5052
|
firedToolResultsRef.current.add(resultToolCallId);
|
|
4794
5053
|
}
|
|
@@ -4898,6 +5157,17 @@ function useAgentChat(options) {
|
|
|
4898
5157
|
const resultToolCallId = log.metadata?.toolCallId;
|
|
4899
5158
|
const result = log.metadata?.output;
|
|
4900
5159
|
const durationMs = log.durationMs;
|
|
5160
|
+
const outcome = resolveToolOutcome({
|
|
5161
|
+
result,
|
|
5162
|
+
success: log.metadata?.success,
|
|
5163
|
+
payloadFailure: log.metadata?.payloadFailure,
|
|
5164
|
+
isError: log.metadata?.isError,
|
|
5165
|
+
error: log.metadata?.error
|
|
5166
|
+
});
|
|
5167
|
+
const outcomeFields = {
|
|
5168
|
+
status: outcome.status,
|
|
5169
|
+
...outcome.error ? { error: outcome.error } : {}
|
|
5170
|
+
};
|
|
4901
5171
|
let matchedToolCall = resultToolCallId ? pendingToolCallsRef.current.get(resultToolCallId) : void 0;
|
|
4902
5172
|
if (!matchedToolCall) {
|
|
4903
5173
|
for (const [, tc] of pendingToolCallsRef.current) {
|
|
@@ -4911,7 +5181,7 @@ function useAgentChat(options) {
|
|
|
4911
5181
|
const completedToolCall = {
|
|
4912
5182
|
...matchedToolCall,
|
|
4913
5183
|
result,
|
|
4914
|
-
|
|
5184
|
+
...outcomeFields,
|
|
4915
5185
|
durationMs
|
|
4916
5186
|
};
|
|
4917
5187
|
updateToolCall(completedToolCall);
|
|
@@ -4922,7 +5192,7 @@ function useAgentChat(options) {
|
|
|
4922
5192
|
name: resultToolName || "unknown",
|
|
4923
5193
|
args: {},
|
|
4924
5194
|
result,
|
|
4925
|
-
|
|
5195
|
+
...outcomeFields,
|
|
4926
5196
|
durationMs
|
|
4927
5197
|
};
|
|
4928
5198
|
updateToolCall(lateToolCall);
|
|
@@ -4933,7 +5203,8 @@ function useAgentChat(options) {
|
|
|
4933
5203
|
id: resultToolCallId,
|
|
4934
5204
|
toolName: resultToolName,
|
|
4935
5205
|
result,
|
|
4936
|
-
durationMs
|
|
5206
|
+
durationMs,
|
|
5207
|
+
...outcomeFields
|
|
4937
5208
|
});
|
|
4938
5209
|
}
|
|
4939
5210
|
if (resultToolCallId) {
|
|
@@ -5182,12 +5453,22 @@ function useAgentChat(options) {
|
|
|
5182
5453
|
if (SILENT_TOOL_NAMES.has(toolName) || isUuidLikeToolName(toolName)) return;
|
|
5183
5454
|
if (firedToolResultsRef.current.has(toolCallId)) return;
|
|
5184
5455
|
const existing = pendingToolCallsRef.current.get(toolCallId);
|
|
5185
|
-
if (existing && existing.status === "completed") return;
|
|
5456
|
+
if (existing && (existing.status === "completed" || existing.status === "failed")) return;
|
|
5457
|
+
const outcome = resolveToolOutcome({
|
|
5458
|
+
result: data.result,
|
|
5459
|
+
isError: data.isError,
|
|
5460
|
+
status: data.status,
|
|
5461
|
+
error: data.error
|
|
5462
|
+
});
|
|
5463
|
+
const outcomeFields = {
|
|
5464
|
+
status: outcome.status,
|
|
5465
|
+
...outcome.error ? { error: outcome.error } : {}
|
|
5466
|
+
};
|
|
5186
5467
|
if (existing) {
|
|
5187
5468
|
const completedToolCall = {
|
|
5188
5469
|
...existing,
|
|
5189
5470
|
result: data.result,
|
|
5190
|
-
|
|
5471
|
+
...outcomeFields,
|
|
5191
5472
|
durationMs: data.durationMs
|
|
5192
5473
|
};
|
|
5193
5474
|
updateToolCall(completedToolCall);
|
|
@@ -5198,7 +5479,7 @@ function useAgentChat(options) {
|
|
|
5198
5479
|
name: toolName,
|
|
5199
5480
|
args: {},
|
|
5200
5481
|
result: data.result,
|
|
5201
|
-
|
|
5482
|
+
...outcomeFields,
|
|
5202
5483
|
durationMs: data.durationMs
|
|
5203
5484
|
};
|
|
5204
5485
|
updateToolCall(lateToolCall);
|
|
@@ -5209,6 +5490,7 @@ function useAgentChat(options) {
|
|
|
5209
5490
|
toolName,
|
|
5210
5491
|
result: data.result,
|
|
5211
5492
|
durationMs: data.durationMs,
|
|
5493
|
+
...outcomeFields,
|
|
5212
5494
|
// CAD-87 S1-003: (execId, seq) reducer dedup for replayed events.
|
|
5213
5495
|
execId: data.sourceExecutionId,
|
|
5214
5496
|
seq: data.seq
|