@agents24/react 0.2.23 → 0.2.25
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/compatibility.json +5 -5
- package/dist/hitl-resolution.d.ts +10 -0
- package/dist/hitl-submission.d.ts +36 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +389 -127
- package/dist/index.js.map +1 -1
- package/dist/operation-ownership.d.ts +1 -0
- package/dist/provider.d.ts +9 -1
- package/dist/use-agent-chat-hitl-submissions.d.ts +20 -0
- package/dist/use-agent-chat.d.ts +3 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -571,11 +571,12 @@ function Agents24Provider({
|
|
|
571
571
|
createAbortController,
|
|
572
572
|
createId = createPortableId,
|
|
573
573
|
storage,
|
|
574
|
-
visibility
|
|
574
|
+
visibility,
|
|
575
|
+
onHitlTiming
|
|
575
576
|
}) {
|
|
576
577
|
const value = React2.useMemo(
|
|
577
|
-
() => ({ client, createAbortController, createId, storage, visibility: visibility ?? client.visibility }),
|
|
578
|
-
[client, createAbortController, createId, storage, visibility]
|
|
578
|
+
() => ({ client, createAbortController, createId, storage, visibility: visibility ?? client.visibility, onHitlTiming }),
|
|
579
|
+
[client, createAbortController, createId, storage, visibility, onHitlTiming]
|
|
579
580
|
);
|
|
580
581
|
return /* @__PURE__ */ jsx(Agents24Context.Provider, { value, children });
|
|
581
582
|
}
|
|
@@ -636,6 +637,41 @@ function createRuntimeEventFrameBuffer(apply, scheduleFrame) {
|
|
|
636
637
|
};
|
|
637
638
|
}
|
|
638
639
|
|
|
640
|
+
// src/hitl-resolution.ts
|
|
641
|
+
function applyHitlResolution(messages, receipt) {
|
|
642
|
+
return messages.map((message) => message.runId !== receipt.run_id ? message : {
|
|
643
|
+
...message,
|
|
644
|
+
parts: message.parts.map((part) => part.kind !== "hitl" || part.interruptId !== receipt.interrupt_id ? part : {
|
|
645
|
+
...part,
|
|
646
|
+
status: receipt.resolution.status,
|
|
647
|
+
resolution: {
|
|
648
|
+
...receipt.resolution,
|
|
649
|
+
answers: receipt.resolution.answers?.map((answer) => ({
|
|
650
|
+
questionId: answer.question_id,
|
|
651
|
+
selectedOptionIds: [...answer.selected_option_ids],
|
|
652
|
+
...answer.custom_text ? { customText: answer.custom_text } : {}
|
|
653
|
+
}))
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
function findHitlResolution(messages, runId, interruptId) {
|
|
659
|
+
return messages.filter((message) => message.runId === runId).flatMap((message) => message.parts).find((part) => part.kind === "hitl" && part.interruptId === interruptId && part.status !== "pending");
|
|
660
|
+
}
|
|
661
|
+
function preserveResolvedHitlParts(current, incoming) {
|
|
662
|
+
const resolved = current.filter((part) => part.kind === "hitl" && part.status !== "pending");
|
|
663
|
+
if (!resolved.length) return incoming;
|
|
664
|
+
const next = incoming.map((part) => {
|
|
665
|
+
if (part.kind !== "hitl" || part.status !== "pending") return part;
|
|
666
|
+
const known = resolved.find((item) => item.interruptId === part.interruptId);
|
|
667
|
+
return known ? { ...part, status: known.status, resolution: known.resolution } : part;
|
|
668
|
+
});
|
|
669
|
+
for (const part of resolved) if (!next.some((item) => item.kind === "hitl" && item.interruptId === part.interruptId)) {
|
|
670
|
+
next.splice(Math.min(current.indexOf(part), next.length), 0, part);
|
|
671
|
+
}
|
|
672
|
+
return next;
|
|
673
|
+
}
|
|
674
|
+
|
|
639
675
|
// src/state.ts
|
|
640
676
|
import {
|
|
641
677
|
compressionFromContextWindow as compressionFromContextWindow2,
|
|
@@ -751,6 +787,7 @@ function reconcileThreadTimeline(current, canonical, activeRunId, streamingMessa
|
|
|
751
787
|
const retained = current.filter((message) => !canonicalIds.has(message.id) && (message.isFinal || activeRunId && (!message.runId || message.runId === activeRunId)));
|
|
752
788
|
const reconciled = mounted.map((message) => {
|
|
753
789
|
const live = current.find((item) => item.id === message.id);
|
|
790
|
+
if (live && live.runId === message.runId) message = { ...message, parts: preserveResolvedHitlParts(live.parts, message.parts) };
|
|
754
791
|
return activeRunId && live?.role === "assistant" && live.runId === activeRunId && (live.id === streamingMessageId || !live.isFinal || live.responseCompletion) ? message.responseCompletion && !live.responseCompletion ? { ...message, id: live.id, durableId: message.durableId || live.durableId } : { ...message, ...live, feedback: message.feedback, durableId: message.durableId || live.durableId } : message;
|
|
755
792
|
});
|
|
756
793
|
const retainedIds = new Set(retained.map((message) => message.id));
|
|
@@ -894,7 +931,7 @@ function applyRuntimeEvent(state, event) {
|
|
|
894
931
|
delta,
|
|
895
932
|
typeof payload.block_id === "string" ? payload.block_id : `${runId || "run"}-assistant-text-${event.seq}`
|
|
896
933
|
) : projection.parts.length ? upsertResponseBlockParts(existing?.parts || [], projection.parts) : existing?.parts || [];
|
|
897
|
-
const lifecycleParts = reconcileModelTurnParts(projectedParts, event);
|
|
934
|
+
const lifecycleParts = reconcileModelTurnParts(preserveResolvedHitlParts(existing?.parts || [], projectedParts), event);
|
|
898
935
|
const compressionPart = contextApplies && explicitCompression?.active ? contextCompressionPartFromEvent(event, lifecycleParts) : null;
|
|
899
936
|
const parts = compressionPart ? upsertResponseBlockParts(lifecycleParts, [compressionPart]) : lifecycleParts;
|
|
900
937
|
const activeThreadId = eventThreadId || state.activeThreadId;
|
|
@@ -1033,7 +1070,7 @@ function captureChatOperation(read, options = {}) {
|
|
|
1033
1070
|
const owner = read();
|
|
1034
1071
|
return () => {
|
|
1035
1072
|
const current = read();
|
|
1036
|
-
return current.generation === owner.generation && (options.adoptThread || current.threadId === owner.threadId) && (!options.run || current.runId === owner.runId) && (!options.stream || current.stream === owner.stream);
|
|
1073
|
+
return current.client === owner.client && current.generation === owner.generation && (options.adoptThread || current.threadId === owner.threadId) && (!options.run || current.runId === owner.runId) && (!options.stream || current.stream === owner.stream);
|
|
1037
1074
|
};
|
|
1038
1075
|
}
|
|
1039
1076
|
|
|
@@ -1382,8 +1419,202 @@ function discardOptimisticSubmission(state, message) {
|
|
|
1382
1419
|
};
|
|
1383
1420
|
}
|
|
1384
1421
|
|
|
1422
|
+
// src/use-agent-chat-hitl-submissions.ts
|
|
1423
|
+
import * as React4 from "react";
|
|
1424
|
+
|
|
1425
|
+
// src/hitl-submission.ts
|
|
1426
|
+
var timers = globalThis;
|
|
1427
|
+
async function withHitlDeadline(factory, work, ms = 15e3) {
|
|
1428
|
+
const abort = factory();
|
|
1429
|
+
let timer;
|
|
1430
|
+
try {
|
|
1431
|
+
return await Promise.race([work(abort.signal), new Promise((_, reject) => {
|
|
1432
|
+
timer = timers.setTimeout(() => {
|
|
1433
|
+
abort.abort();
|
|
1434
|
+
reject(new Error("Submission acknowledgement timed out."));
|
|
1435
|
+
}, ms);
|
|
1436
|
+
})]);
|
|
1437
|
+
} finally {
|
|
1438
|
+
timers.clearTimeout(timer);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
function isDefinitiveHitlRejection(error) {
|
|
1442
|
+
const status = error?.status ?? error?.statusCode;
|
|
1443
|
+
return typeof status === "number" && status >= 400 && status < 500 && ![408, 409, 425, 429].includes(status);
|
|
1444
|
+
}
|
|
1445
|
+
function createHitlSubmission(options) {
|
|
1446
|
+
const response = { ...options.response, answers: options.response.answers?.map((answer) => ({
|
|
1447
|
+
...answer,
|
|
1448
|
+
selected_option_ids: [...answer.selected_option_ids]
|
|
1449
|
+
})) };
|
|
1450
|
+
let pending;
|
|
1451
|
+
let accepted = false;
|
|
1452
|
+
let failed = false;
|
|
1453
|
+
let uncertain = false;
|
|
1454
|
+
const now = options.now ?? Date.now;
|
|
1455
|
+
const publish = (state) => {
|
|
1456
|
+
if (options.isCurrent()) options.changed(state);
|
|
1457
|
+
};
|
|
1458
|
+
const accept = (receipt) => {
|
|
1459
|
+
if (!options.isCurrent() || accepted) return;
|
|
1460
|
+
accepted = true;
|
|
1461
|
+
uncertain = false;
|
|
1462
|
+
options.accept(receipt);
|
|
1463
|
+
publish({ action: receipt.resolution.action || response.action, status: "accepted" });
|
|
1464
|
+
};
|
|
1465
|
+
const retry = () => {
|
|
1466
|
+
if (pending) return pending;
|
|
1467
|
+
if (accepted || !options.isCurrent()) return Promise.resolve();
|
|
1468
|
+
failed = false;
|
|
1469
|
+
uncertain = false;
|
|
1470
|
+
publish({ action: response.action, status: "submitting" });
|
|
1471
|
+
pending = Promise.resolve().then(async () => {
|
|
1472
|
+
try {
|
|
1473
|
+
await withHitlDeadline(options.createAbortController, async (signal) => {
|
|
1474
|
+
const started = now();
|
|
1475
|
+
await options.prepare?.({ signal });
|
|
1476
|
+
options.timing?.("preparation", now() - started);
|
|
1477
|
+
if (!options.isCurrent() || signal.aborted) return;
|
|
1478
|
+
const submitted = now();
|
|
1479
|
+
const receipt = await options.send(response, options.idempotencyKey, signal);
|
|
1480
|
+
options.timing?.("acknowledgement", now() - submitted);
|
|
1481
|
+
accept(receipt);
|
|
1482
|
+
});
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
if (!options.isCurrent() || accepted) return;
|
|
1485
|
+
if (isDefinitiveHitlRejection(error)) {
|
|
1486
|
+
failed = true;
|
|
1487
|
+
publish({ action: response.action, status: "error", error: error instanceof Error ? error.message : "Submission was rejected." });
|
|
1488
|
+
throw error;
|
|
1489
|
+
}
|
|
1490
|
+
publish({ action: response.action, status: "checking" });
|
|
1491
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1492
|
+
try {
|
|
1493
|
+
const resolved = await withHitlDeadline(options.createAbortController, options.reconcile, 3e3);
|
|
1494
|
+
if (resolved) accept(resolved);
|
|
1495
|
+
} catch {
|
|
1496
|
+
}
|
|
1497
|
+
if (accepted || !options.isCurrent()) return;
|
|
1498
|
+
}
|
|
1499
|
+
uncertain = true;
|
|
1500
|
+
publish({ action: response.action, status: "uncertain", retry });
|
|
1501
|
+
}
|
|
1502
|
+
}).finally(() => {
|
|
1503
|
+
pending = void 0;
|
|
1504
|
+
});
|
|
1505
|
+
return pending;
|
|
1506
|
+
};
|
|
1507
|
+
return { retry, get failed() {
|
|
1508
|
+
return failed;
|
|
1509
|
+
}, get uncertain() {
|
|
1510
|
+
return uncertain;
|
|
1511
|
+
} };
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// src/use-agent-chat-hitl-submissions.ts
|
|
1515
|
+
function hitlSubmissionKey(threadId, runId, interruptId, generation) {
|
|
1516
|
+
return `${generation}:${threadId}:${runId}:${interruptId}`;
|
|
1517
|
+
}
|
|
1518
|
+
function useAgentChatHitlSubmissions(options) {
|
|
1519
|
+
const latest = React4.useRef(options);
|
|
1520
|
+
latest.current = options;
|
|
1521
|
+
const mounted = React4.useRef(true);
|
|
1522
|
+
const operations = React4.useRef(/* @__PURE__ */ new Map());
|
|
1523
|
+
const [states, setStates] = React4.useState({});
|
|
1524
|
+
const clientRef = React4.useRef(options.client);
|
|
1525
|
+
if (clientRef.current !== options.client) {
|
|
1526
|
+
clientRef.current = options.client;
|
|
1527
|
+
operations.current.clear();
|
|
1528
|
+
}
|
|
1529
|
+
React4.useEffect(() => {
|
|
1530
|
+
mounted.current = true;
|
|
1531
|
+
return () => {
|
|
1532
|
+
mounted.current = false;
|
|
1533
|
+
operations.current.clear();
|
|
1534
|
+
};
|
|
1535
|
+
}, []);
|
|
1536
|
+
const resumeHitl = React4.useCallback((part, response, submission) => {
|
|
1537
|
+
const captured = latest.current;
|
|
1538
|
+
const { client, runtime, stateRef, ownOperation, pageSize } = captured;
|
|
1539
|
+
const threadId = stateRef.current.activeThreadId;
|
|
1540
|
+
const runId = part.runId || stateRef.current.messages.find((message) => message.parts.some((candidate) => candidate.id === part.id))?.runId || stateRef.current.activeRunId;
|
|
1541
|
+
if (!threadId || !runId) return Promise.reject(new Error("No paused run is available to resume."));
|
|
1542
|
+
const isOwned = ownOperation();
|
|
1543
|
+
const isCurrent = () => mounted.current && latest.current.client === client && isOwned() && (!stateRef.current.activeRunId || stateRef.current.activeRunId === runId);
|
|
1544
|
+
const key = hitlSubmissionKey(threadId, runId, part.interruptId, captured.selectionGeneration.current);
|
|
1545
|
+
const live = stateRef.current.messages.filter((message) => message.runId === runId).flatMap((message) => message.parts).find((candidate) => candidate.kind === "hitl" && candidate.interruptId === part.interruptId);
|
|
1546
|
+
if (part.status !== "pending" || live && live.status !== "pending") return Promise.resolve();
|
|
1547
|
+
const previous = operations.current.get(key);
|
|
1548
|
+
if (previous && !previous.failed) return previous.retry();
|
|
1549
|
+
if (["cancelling", "cancelled", "failed", "completed"].includes(stateRef.current.runState)) return Promise.resolve();
|
|
1550
|
+
const operation = createHitlSubmission({
|
|
1551
|
+
response,
|
|
1552
|
+
idempotencyKey: runtime.createId(),
|
|
1553
|
+
prepare: submission?.prepare,
|
|
1554
|
+
createAbortController: runtime.createAbortController,
|
|
1555
|
+
isCurrent,
|
|
1556
|
+
send: (decision, idempotencyKey, signal) => client.hitl.resume({ runId, interruptId: part.interruptId, ...decision, idempotencyKey, signal }),
|
|
1557
|
+
accept: (receipt) => {
|
|
1558
|
+
captured.accept(receipt);
|
|
1559
|
+
if (!["completed", "failed", "cancelled"].includes(receipt.run_status || "")) captured.attach(receipt.run_id);
|
|
1560
|
+
},
|
|
1561
|
+
reconcile: async (signal) => {
|
|
1562
|
+
const [page, detail] = await Promise.all([
|
|
1563
|
+
client.threads.messages.list({ threadId, limit: pageSize, signal }),
|
|
1564
|
+
client.threads.get({ threadId, limit: pageSize, signal })
|
|
1565
|
+
]);
|
|
1566
|
+
if (!isCurrent()) return null;
|
|
1567
|
+
const part2 = findHitlResolution(threadTimelineToMessages(page.items), runId, capturedPart.interruptId);
|
|
1568
|
+
if (!part2?.resolution) return null;
|
|
1569
|
+
return {
|
|
1570
|
+
run_id: runId,
|
|
1571
|
+
thread_id: threadId,
|
|
1572
|
+
interrupt_id: part2.interruptId,
|
|
1573
|
+
run_status: detail.active_run?.run_id === runId ? detail.active_run.status : detail.last_run_id === runId ? detail.last_run_status || void 0 : void 0,
|
|
1574
|
+
resolution: {
|
|
1575
|
+
...part2.resolution,
|
|
1576
|
+
status: part2.status,
|
|
1577
|
+
action: part2.resolution.action || null,
|
|
1578
|
+
outcome: part2.resolution.outcome || part2.status,
|
|
1579
|
+
resolved_at: part2.resolution.resolved_at,
|
|
1580
|
+
answers: part2.resolution.answers?.map((answer) => ({
|
|
1581
|
+
question_id: answer.questionId,
|
|
1582
|
+
selected_option_ids: answer.selectedOptionIds,
|
|
1583
|
+
custom_text: answer.customText
|
|
1584
|
+
}))
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
},
|
|
1588
|
+
changed: (state) => {
|
|
1589
|
+
if (isCurrent()) setStates((current) => ({ ...current, [key]: state }));
|
|
1590
|
+
},
|
|
1591
|
+
timing: (stage, durationMs) => runtime.onHitlTiming?.({ stage, durationMs })
|
|
1592
|
+
});
|
|
1593
|
+
const capturedPart = part;
|
|
1594
|
+
operations.current.set(key, operation);
|
|
1595
|
+
return operation.retry();
|
|
1596
|
+
}, []);
|
|
1597
|
+
const selection = options.stateRef.current.activeThreadId;
|
|
1598
|
+
const generation = options.selectionGeneration.current;
|
|
1599
|
+
React4.useEffect(() => {
|
|
1600
|
+
const prefix = `${generation}:${selection}:`;
|
|
1601
|
+
for (const key of operations.current.keys()) if (!key.startsWith(prefix)) operations.current.delete(key);
|
|
1602
|
+
setStates((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key.startsWith(prefix))));
|
|
1603
|
+
}, [options.client, selection, generation]);
|
|
1604
|
+
return { resumeHitl, getHitlSubmissionState: (part) => {
|
|
1605
|
+
const runId = part.runId || options.stateRef.current.messages.find((message) => message.parts.some((candidate) => candidate.id === part.id))?.runId || options.stateRef.current.activeRunId;
|
|
1606
|
+
const key = runId ? hitlSubmissionKey(selection, runId, part.interruptId, generation) : null;
|
|
1607
|
+
const state = key && operations.current.has(key) ? states[key] : void 0;
|
|
1608
|
+
if (!state) return void 0;
|
|
1609
|
+
if (part.status !== "pending") {
|
|
1610
|
+
return !options.stateRef.current.activeRunId || ["completed", "failed", "cancelled"].includes(options.stateRef.current.runState) ? void 0 : { action: String(part.resolution?.action || state.action), status: "accepted" };
|
|
1611
|
+
}
|
|
1612
|
+
return state;
|
|
1613
|
+
} };
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1385
1616
|
// src/use-agent-chat.ts
|
|
1386
|
-
import * as
|
|
1617
|
+
import * as React8 from "react";
|
|
1387
1618
|
|
|
1388
1619
|
// src/refresh-chat-summaries.ts
|
|
1389
1620
|
async function refreshChatSummaries(input) {
|
|
@@ -1433,17 +1664,17 @@ async function refreshChatSummaries(input) {
|
|
|
1433
1664
|
}
|
|
1434
1665
|
|
|
1435
1666
|
// src/use-agent-chat-thread-list.ts
|
|
1436
|
-
import * as
|
|
1667
|
+
import * as React5 from "react";
|
|
1437
1668
|
function summaryToStored(thread) {
|
|
1438
1669
|
return { ...thread, id: String(thread.id), messages: [], isHydrated: false };
|
|
1439
1670
|
}
|
|
1440
1671
|
function useAgentChatThreadList({ client, threadPageSize, stateRef, update, clearOperationError, failOperation }) {
|
|
1441
|
-
const [isLoadingThreads, setIsLoadingThreads] =
|
|
1442
|
-
const [isLoadingMoreThreads, setIsLoadingMoreThreads] =
|
|
1443
|
-
const [threadTotal, setThreadTotal] =
|
|
1444
|
-
const refreshPromiseRef =
|
|
1445
|
-
const eventsDuringRefreshRef =
|
|
1446
|
-
const refreshThreads =
|
|
1672
|
+
const [isLoadingThreads, setIsLoadingThreads] = React5.useState(false);
|
|
1673
|
+
const [isLoadingMoreThreads, setIsLoadingMoreThreads] = React5.useState(false);
|
|
1674
|
+
const [threadTotal, setThreadTotal] = React5.useState();
|
|
1675
|
+
const refreshPromiseRef = React5.useRef(null);
|
|
1676
|
+
const eventsDuringRefreshRef = React5.useRef(null);
|
|
1677
|
+
const refreshThreads = React5.useCallback(() => {
|
|
1447
1678
|
if (refreshPromiseRef.current) return refreshPromiseRef.current;
|
|
1448
1679
|
clearOperationError();
|
|
1449
1680
|
setIsLoadingThreads(true);
|
|
@@ -1478,7 +1709,7 @@ function useAgentChatThreadList({ client, threadPageSize, stateRef, update, clea
|
|
|
1478
1709
|
}).catch(() => void 0);
|
|
1479
1710
|
return request;
|
|
1480
1711
|
}, [clearOperationError, client, failOperation, threadPageSize, update]);
|
|
1481
|
-
const loadMoreThreads =
|
|
1712
|
+
const loadMoreThreads = React5.useCallback(async () => {
|
|
1482
1713
|
if (isLoadingMoreThreads) return;
|
|
1483
1714
|
const skip = stateRef.current.threads.length;
|
|
1484
1715
|
if (threadTotal !== void 0 && skip >= threadTotal) return;
|
|
@@ -1513,10 +1744,10 @@ function useAgentChatThreadList({ client, threadPageSize, stateRef, update, clea
|
|
|
1513
1744
|
}
|
|
1514
1745
|
|
|
1515
1746
|
// src/use-agent-chat-feedback.ts
|
|
1516
|
-
import * as
|
|
1747
|
+
import * as React6 from "react";
|
|
1517
1748
|
function useAgentChatFeedback({ client, update, clearOperationError, failOperation, ownOperation, refreshMessages }) {
|
|
1518
|
-
const mutations =
|
|
1519
|
-
const captureRead =
|
|
1749
|
+
const mutations = React6.useRef(/* @__PURE__ */ new Map());
|
|
1750
|
+
const captureRead = React6.useCallback(() => {
|
|
1520
1751
|
const revisions = new Map([...mutations.current].map(([run, mutation]) => [run, mutation.readRevision]));
|
|
1521
1752
|
return (incoming, current) => incoming.map((message) => {
|
|
1522
1753
|
const mutation = message.runId ? mutations.current.get(message.runId) : void 0;
|
|
@@ -1525,7 +1756,7 @@ function useAgentChatFeedback({ client, update, clearOperationError, failOperati
|
|
|
1525
1756
|
return live ? { ...message, feedback: live.feedback } : message;
|
|
1526
1757
|
});
|
|
1527
1758
|
}, []);
|
|
1528
|
-
const setFeedback =
|
|
1759
|
+
const setFeedback = React6.useCallback(async (message, input) => {
|
|
1529
1760
|
const runId = message.runId;
|
|
1530
1761
|
if (!runId || message.role !== "assistant" || message.isFinal === false) {
|
|
1531
1762
|
const error = new Error("Only persisted final responses can receive feedback.");
|
|
@@ -1575,7 +1806,7 @@ function useAgentChatFeedback({ client, update, clearOperationError, failOperati
|
|
|
1575
1806
|
}
|
|
1576
1807
|
|
|
1577
1808
|
// src/use-agent-chat-queue-actions.ts
|
|
1578
|
-
import * as
|
|
1809
|
+
import * as React7 from "react";
|
|
1579
1810
|
function useAgentChatQueueActions({
|
|
1580
1811
|
queue,
|
|
1581
1812
|
stateRef,
|
|
@@ -1586,7 +1817,7 @@ function useAgentChatQueueActions({
|
|
|
1586
1817
|
attachRun,
|
|
1587
1818
|
ownOperation
|
|
1588
1819
|
}) {
|
|
1589
|
-
const steer =
|
|
1820
|
+
const steer = React7.useCallback(async (text2) => {
|
|
1590
1821
|
const threadId = stateRef.current.activeThreadId;
|
|
1591
1822
|
if (!threadId || !text2.trim()) throw new Error("An active thread and text are required to steer.");
|
|
1592
1823
|
const isCurrent = ownOperation();
|
|
@@ -1599,7 +1830,7 @@ function useAgentChatQueueActions({
|
|
|
1599
1830
|
throw error;
|
|
1600
1831
|
}
|
|
1601
1832
|
}, [clearOperationError, createId, failOperation, queue.submit, refreshMessages, stateRef, ownOperation]);
|
|
1602
|
-
const editPendingMessage =
|
|
1833
|
+
const editPendingMessage = React7.useCallback(async (message, input) => {
|
|
1603
1834
|
await queue.edit(message, {
|
|
1604
1835
|
text: input.text,
|
|
1605
1836
|
attachmentIds: input.attachmentIds ?? message.attachment_ids,
|
|
@@ -1607,10 +1838,10 @@ function useAgentChatQueueActions({
|
|
|
1607
1838
|
requestedModelId: input.requestedModelId
|
|
1608
1839
|
});
|
|
1609
1840
|
}, [queue.edit]);
|
|
1610
|
-
const steerPendingMessage =
|
|
1841
|
+
const steerPendingMessage = React7.useCallback(async (message) => {
|
|
1611
1842
|
await queue.edit(message, { text: message.text, attachmentIds: message.attachment_ids, mode: "steer" });
|
|
1612
1843
|
}, [queue.edit]);
|
|
1613
|
-
const resumeQueue =
|
|
1844
|
+
const resumeQueue = React7.useCallback(async () => {
|
|
1614
1845
|
if (!stateRef.current.activeThreadId) return;
|
|
1615
1846
|
const isCurrent = ownOperation();
|
|
1616
1847
|
const result = await queue.resume();
|
|
@@ -1638,7 +1869,7 @@ function useAgentChat(options = {}) {
|
|
|
1638
1869
|
const client = options.client || runtime.client;
|
|
1639
1870
|
const pageSize = options.pageSize ?? 30;
|
|
1640
1871
|
const threadPageSize = options.threadPageSize ?? 50;
|
|
1641
|
-
const [state, setState] =
|
|
1872
|
+
const [state, setState] = React8.useState(() => ({
|
|
1642
1873
|
...initialAgentChatState,
|
|
1643
1874
|
activeThreadId: resolveInitialActiveThreadId(
|
|
1644
1875
|
options,
|
|
@@ -1646,37 +1877,51 @@ function useAgentChat(options = {}) {
|
|
|
1646
1877
|
),
|
|
1647
1878
|
threads: runtime.storage?.loadThreads() ?? []
|
|
1648
1879
|
}));
|
|
1649
|
-
const [isLoadingHistory, setIsLoadingHistory] =
|
|
1650
|
-
const [isLoadingOlder, setIsLoadingOlder] =
|
|
1651
|
-
const [isSubmitting, setIsSubmitting] =
|
|
1652
|
-
const streamControllerRef =
|
|
1653
|
-
const threadOpenControllerRef =
|
|
1654
|
-
const threadOpenGenerationRef =
|
|
1655
|
-
const attachRunRef =
|
|
1656
|
-
const messageRefreshVersionRef =
|
|
1657
|
-
const promotedRunHintRef =
|
|
1658
|
-
const promotedAttachRef =
|
|
1659
|
-
const olderRequestRef =
|
|
1660
|
-
const submissionGenerationRef =
|
|
1661
|
-
const
|
|
1880
|
+
const [isLoadingHistory, setIsLoadingHistory] = React8.useState(false);
|
|
1881
|
+
const [isLoadingOlder, setIsLoadingOlder] = React8.useState(false);
|
|
1882
|
+
const [isSubmitting, setIsSubmitting] = React8.useState(false);
|
|
1883
|
+
const streamControllerRef = React8.useRef(null);
|
|
1884
|
+
const threadOpenControllerRef = React8.useRef(null);
|
|
1885
|
+
const threadOpenGenerationRef = React8.useRef(0);
|
|
1886
|
+
const attachRunRef = React8.useRef(async () => void 0);
|
|
1887
|
+
const messageRefreshVersionRef = React8.useRef(0);
|
|
1888
|
+
const promotedRunHintRef = React8.useRef(null);
|
|
1889
|
+
const promotedAttachRef = React8.useRef(null);
|
|
1890
|
+
const olderRequestRef = React8.useRef(null);
|
|
1891
|
+
const submissionGenerationRef = React8.useRef(0);
|
|
1892
|
+
const continuationTiming = React8.useRef(/* @__PURE__ */ new Map());
|
|
1893
|
+
const resolutionFences = React8.useRef(/* @__PURE__ */ new Map());
|
|
1894
|
+
const clientIdentityRef = React8.useRef(client);
|
|
1895
|
+
if (clientIdentityRef.current !== client) {
|
|
1896
|
+
resolutionFences.current.clear();
|
|
1897
|
+
continuationTiming.current.clear();
|
|
1898
|
+
clientIdentityRef.current = client;
|
|
1899
|
+
}
|
|
1900
|
+
const stateRef = React8.useRef(state);
|
|
1662
1901
|
stateRef.current = state;
|
|
1663
|
-
const ownOperation =
|
|
1902
|
+
const ownOperation = React8.useCallback((options2) => captureChatOperation(() => ({
|
|
1903
|
+
client: clientIdentityRef.current,
|
|
1664
1904
|
generation: threadOpenGenerationRef.current,
|
|
1665
1905
|
threadId: stateRef.current.activeThreadId,
|
|
1666
1906
|
runId: stateRef.current.activeRunId,
|
|
1667
1907
|
stream: streamControllerRef.current
|
|
1668
1908
|
}), options2), []);
|
|
1669
|
-
const persist =
|
|
1909
|
+
const persist = React8.useCallback((next) => {
|
|
1670
1910
|
runtime.storage?.saveThreads(next.threads);
|
|
1671
1911
|
runtime.storage?.saveActiveThreadId?.(next.activeThreadId);
|
|
1672
1912
|
}, [runtime.storage]);
|
|
1673
|
-
const update =
|
|
1674
|
-
|
|
1913
|
+
const update = React8.useCallback((updater) => {
|
|
1914
|
+
let next = updater(stateRef.current);
|
|
1915
|
+
for (const receipt of resolutionFences.current.values()) next = {
|
|
1916
|
+
...next,
|
|
1917
|
+
messages: applyHitlResolution(next.messages, receipt),
|
|
1918
|
+
threads: next.threads.map((thread) => thread.id !== receipt.thread_id ? thread : { ...thread, messages: applyHitlResolution(thread.messages, receipt) })
|
|
1919
|
+
};
|
|
1675
1920
|
stateRef.current = next;
|
|
1676
1921
|
persist(next);
|
|
1677
1922
|
setState(next);
|
|
1678
1923
|
}, [persist]);
|
|
1679
|
-
const queueTransport =
|
|
1924
|
+
const queueTransport = React8.useMemo(() => ({
|
|
1680
1925
|
list: (threadId, signal) => client.threads.messages.list({ threadId, limit: pageSize, signal }),
|
|
1681
1926
|
submit: (input) => client.threads.messages.submit(input),
|
|
1682
1927
|
edit: (message, input) => client.threads.messages.edit({
|
|
@@ -1694,7 +1939,7 @@ function useAgentChat(options = {}) {
|
|
|
1694
1939
|
},
|
|
1695
1940
|
resume: (threadId) => client.threads.resumeQueue({ threadId })
|
|
1696
1941
|
}), [client, pageSize]);
|
|
1697
|
-
const attachPromotion =
|
|
1942
|
+
const attachPromotion = React8.useCallback((runId, threadId) => {
|
|
1698
1943
|
if (stateRef.current.activeThreadId !== threadId) return;
|
|
1699
1944
|
promotedRunHintRef.current = runId;
|
|
1700
1945
|
if (stateRef.current.activeRunId === runId || promotedAttachRef.current === runId) return;
|
|
@@ -1704,7 +1949,7 @@ function useAgentChat(options = {}) {
|
|
|
1704
1949
|
if (isCurrent() && promotedAttachRef.current === runId) promotedAttachRef.current = null;
|
|
1705
1950
|
});
|
|
1706
1951
|
}, [ownOperation]);
|
|
1707
|
-
const onQueuePromotion =
|
|
1952
|
+
const onQueuePromotion = React8.useCallback((message, runId) => {
|
|
1708
1953
|
if (stateRef.current.activeThreadId !== message.thread_id) return;
|
|
1709
1954
|
const userMessage = threadTimelineToMessages([{ ...message, run_id: runId, status: "delivered" }])[0];
|
|
1710
1955
|
update((current) => current.activeThreadId === message.thread_id ? {
|
|
@@ -1729,20 +1974,20 @@ function useAgentChat(options = {}) {
|
|
|
1729
1974
|
onDiscard: (message) => update((current) => discardOptimisticSubmission(current, message))
|
|
1730
1975
|
});
|
|
1731
1976
|
const { pendingMessages, messageQueue } = queue;
|
|
1732
|
-
const eventBuffer =
|
|
1977
|
+
const eventBuffer = React8.useMemo(
|
|
1733
1978
|
() => createRuntimeEventFrameBuffer(
|
|
1734
1979
|
(events) => update((current) => applyRuntimeEvents(current, events)),
|
|
1735
1980
|
runtimeEventFrameScheduler()
|
|
1736
1981
|
),
|
|
1737
1982
|
[update]
|
|
1738
1983
|
);
|
|
1739
|
-
const clearOperationError =
|
|
1984
|
+
const clearOperationError = React8.useCallback((operation) => {
|
|
1740
1985
|
update((current) => clearAgentChatOperationError(current, operation));
|
|
1741
1986
|
}, [update]);
|
|
1742
|
-
const clearBackgroundError =
|
|
1987
|
+
const clearBackgroundError = React8.useCallback(() => {
|
|
1743
1988
|
update(clearAgentChatBackgroundError);
|
|
1744
1989
|
}, [update]);
|
|
1745
|
-
const failOperation =
|
|
1990
|
+
const failOperation = React8.useCallback((operation, cause) => {
|
|
1746
1991
|
update((current) => withAgentChatOperationError(current, operation, cause));
|
|
1747
1992
|
}, [update]);
|
|
1748
1993
|
const {
|
|
@@ -1753,7 +1998,7 @@ function useAgentChat(options = {}) {
|
|
|
1753
1998
|
refreshThreads,
|
|
1754
1999
|
loadMoreThreads
|
|
1755
2000
|
} = useAgentChatThreadList({ client, threadPageSize, stateRef, update, clearOperationError, failOperation });
|
|
1756
|
-
const invalidateThreadOpen =
|
|
2001
|
+
const invalidateThreadOpen = React8.useCallback(() => {
|
|
1757
2002
|
threadOpenGenerationRef.current += 1;
|
|
1758
2003
|
threadOpenControllerRef.current?.abort("Thread selection changed.");
|
|
1759
2004
|
threadOpenControllerRef.current = null;
|
|
@@ -1762,26 +2007,28 @@ function useAgentChat(options = {}) {
|
|
|
1762
2007
|
promotedRunHintRef.current = null;
|
|
1763
2008
|
olderRequestRef.current = null;
|
|
1764
2009
|
queue.reset();
|
|
2010
|
+
resolutionFences.current.clear();
|
|
2011
|
+
continuationTiming.current.clear();
|
|
1765
2012
|
setIsSubmitting(false);
|
|
1766
2013
|
setIsLoadingOlder(false);
|
|
1767
2014
|
setIsLoadingHistory(false);
|
|
1768
2015
|
}, [queue.reset]);
|
|
1769
|
-
const detach =
|
|
2016
|
+
const detach = React8.useCallback(() => {
|
|
1770
2017
|
eventBuffer.flush();
|
|
1771
2018
|
streamControllerRef.current?.abort();
|
|
1772
2019
|
streamControllerRef.current = null;
|
|
1773
2020
|
update((current) => ({ ...current, streamingMessageId: null, runState: current.activeRunId ? "reconnecting" : "idle" }));
|
|
1774
2021
|
}, [eventBuffer, update]);
|
|
1775
|
-
const refreshMessagesRef =
|
|
2022
|
+
const refreshMessagesRef = React8.useRef(async () => void 0);
|
|
1776
2023
|
const { setFeedback, captureRead: captureFeedbackRead } = useAgentChatFeedback({
|
|
1777
2024
|
client,
|
|
1778
2025
|
update,
|
|
1779
2026
|
clearOperationError,
|
|
1780
2027
|
failOperation,
|
|
1781
2028
|
ownOperation,
|
|
1782
|
-
refreshMessages:
|
|
2029
|
+
refreshMessages: React8.useCallback(() => refreshMessagesRef.current(), [])
|
|
1783
2030
|
});
|
|
1784
|
-
const refreshMessages =
|
|
2031
|
+
const refreshMessages = React8.useCallback(async (threadId, promotedRunId, signal) => {
|
|
1785
2032
|
const target = threadId || stateRef.current.activeThreadId;
|
|
1786
2033
|
if (!target) return;
|
|
1787
2034
|
const isCurrent = ownOperation();
|
|
@@ -1804,13 +2051,13 @@ function useAgentChat(options = {}) {
|
|
|
1804
2051
|
});
|
|
1805
2052
|
}, [client, ownOperation, pageSize, queue.hydrate, update, captureFeedbackRead]);
|
|
1806
2053
|
refreshMessagesRef.current = refreshMessages;
|
|
1807
|
-
|
|
2054
|
+
React8.useEffect(() => () => {
|
|
1808
2055
|
threadOpenGenerationRef.current += 1;
|
|
1809
2056
|
eventBuffer.dispose();
|
|
1810
2057
|
streamControllerRef.current?.abort();
|
|
1811
2058
|
threadOpenControllerRef.current?.abort();
|
|
1812
2059
|
}, [eventBuffer]);
|
|
1813
|
-
const openThread =
|
|
2060
|
+
const openThread = React8.useCallback(async (threadId) => {
|
|
1814
2061
|
const previousSelection = captureAgentChatThreadSelection(stateRef.current);
|
|
1815
2062
|
invalidateThreadOpen();
|
|
1816
2063
|
const requestController = runtime.createAbortController();
|
|
@@ -1883,7 +2130,17 @@ function useAgentChat(options = {}) {
|
|
|
1883
2130
|
}
|
|
1884
2131
|
}
|
|
1885
2132
|
}, [clearOperationError, client, detach, failOperation, pageSize, queue.hydrate, runtime, update, invalidateThreadOpen, ownOperation, captureFeedbackRead]);
|
|
1886
|
-
const consumeEvent =
|
|
2133
|
+
const consumeEvent = React8.useCallback((event) => {
|
|
2134
|
+
const timing = continuationTiming.current.get(event.run_id);
|
|
2135
|
+
if (timing && !timing.attached) {
|
|
2136
|
+
timing.attached = true;
|
|
2137
|
+
runtime.onHitlTiming?.({ stage: "attachment", durationMs: Date.now() - timing.acknowledgedAt });
|
|
2138
|
+
}
|
|
2139
|
+
if (timing && !timing.active && ["assistant.delta", "model.turn.started", "tool.started"].includes(event.event)) {
|
|
2140
|
+
timing.active = true;
|
|
2141
|
+
runtime.onHitlTiming?.({ stage: "first_activity", durationMs: Date.now() - timing.acknowledgedAt });
|
|
2142
|
+
}
|
|
2143
|
+
if (["run.completed", "run.cancelled", "run.failed"].includes(event.event)) continuationTiming.current.delete(event.run_id);
|
|
1887
2144
|
const eventThreadId = typeof event.payload?.thread_id === "string" ? event.payload.thread_id.trim() : "";
|
|
1888
2145
|
if (eventThreadId && !stateRef.current.activeThreadId) {
|
|
1889
2146
|
queue.confirmThreadAdmission(eventThreadId);
|
|
@@ -1891,8 +2148,8 @@ function useAgentChat(options = {}) {
|
|
|
1891
2148
|
setIsSubmitting(false);
|
|
1892
2149
|
}
|
|
1893
2150
|
eventBuffer.push(event);
|
|
1894
|
-
}, [eventBuffer, update, queue.confirmThreadAdmission]);
|
|
1895
|
-
const attachRun =
|
|
2151
|
+
}, [eventBuffer, update, queue.confirmThreadAdmission, runtime.onHitlTiming]);
|
|
2152
|
+
const attachRun = React8.useCallback(async (runId, cursor) => {
|
|
1896
2153
|
eventBuffer.flush();
|
|
1897
2154
|
streamControllerRef.current?.abort();
|
|
1898
2155
|
const controller = runtime.createAbortController();
|
|
@@ -1910,7 +2167,7 @@ function useAgentChat(options = {}) {
|
|
|
1910
2167
|
if (!result.detached && result.threadId) await refreshMessages(result.threadId);
|
|
1911
2168
|
} catch (error) {
|
|
1912
2169
|
if (isCurrent() && !isAbortLike(error)) {
|
|
1913
|
-
update((current) => ({ ...current, runState: "
|
|
2170
|
+
update((current) => ({ ...current, runState: "reconnecting", streamingMessageId: null }));
|
|
1914
2171
|
failOperation("attach_run", error);
|
|
1915
2172
|
throw error;
|
|
1916
2173
|
}
|
|
@@ -1919,7 +2176,7 @@ function useAgentChat(options = {}) {
|
|
|
1919
2176
|
}
|
|
1920
2177
|
}, [clearOperationError, client, consumeEvent, eventBuffer, failOperation, refreshMessages, runtime, update, ownOperation]);
|
|
1921
2178
|
attachRunRef.current = attachRun;
|
|
1922
|
-
const submit =
|
|
2179
|
+
const submit = React8.useCallback(async (input) => {
|
|
1923
2180
|
if (!input.text.trim() && !input.attachmentIds?.length && !input.uploads?.length) return;
|
|
1924
2181
|
const existingThreadId = stateRef.current.activeThreadId;
|
|
1925
2182
|
if (existingThreadId || queue.hasPendingAdmission()) {
|
|
@@ -2090,12 +2347,12 @@ function useAgentChat(options = {}) {
|
|
|
2090
2347
|
attachRun,
|
|
2091
2348
|
ownOperation
|
|
2092
2349
|
});
|
|
2093
|
-
const startNewThread =
|
|
2350
|
+
const startNewThread = React8.useCallback(() => {
|
|
2094
2351
|
invalidateThreadOpen();
|
|
2095
2352
|
detach();
|
|
2096
2353
|
update((current) => ({ ...initialAgentChatState, threads: current.threads }));
|
|
2097
2354
|
}, [detach, invalidateThreadOpen, update]);
|
|
2098
|
-
const deleteThread =
|
|
2355
|
+
const deleteThread = React8.useCallback(async (threadId) => {
|
|
2099
2356
|
const isCurrent = ownOperation();
|
|
2100
2357
|
clearOperationError();
|
|
2101
2358
|
try {
|
|
@@ -2109,7 +2366,7 @@ function useAgentChat(options = {}) {
|
|
|
2109
2366
|
}
|
|
2110
2367
|
}
|
|
2111
2368
|
}, [clearOperationError, client, failOperation, ownOperation, runtime, startNewThread, update]);
|
|
2112
|
-
const cancelRun =
|
|
2369
|
+
const cancelRun = React8.useCallback(async () => {
|
|
2113
2370
|
const runId = stateRef.current.activeRunId;
|
|
2114
2371
|
if (!runId || stateRef.current.runState === "cancelling") return;
|
|
2115
2372
|
const isCurrent = ownOperation({ run: true, stream: true });
|
|
@@ -2150,29 +2407,27 @@ function useAgentChat(options = {}) {
|
|
|
2150
2407
|
}
|
|
2151
2408
|
}
|
|
2152
2409
|
}, [clearOperationError, client, detach, failOperation, runtime, update, ownOperation]);
|
|
2153
|
-
const resumeHitl =
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
...response,
|
|
2167
|
-
idempotencyKey: runtime.createId()
|
|
2410
|
+
const { resumeHitl, getHitlSubmissionState } = useAgentChatHitlSubmissions({
|
|
2411
|
+
client,
|
|
2412
|
+
runtime,
|
|
2413
|
+
pageSize,
|
|
2414
|
+
stateRef,
|
|
2415
|
+
ownOperation,
|
|
2416
|
+
selectionGeneration: threadOpenGenerationRef,
|
|
2417
|
+
accept: (receipt) => {
|
|
2418
|
+
resolutionFences.current.set(`${receipt.run_id}:${receipt.interrupt_id}`, receipt);
|
|
2419
|
+
continuationTiming.current.set(receipt.run_id, { acknowledgedAt: Date.now(), attached: false, active: false });
|
|
2420
|
+
update((current) => {
|
|
2421
|
+
const status = receipt.run_status;
|
|
2422
|
+
return current.activeRunId === receipt.run_id && (status === "completed" || status === "failed" || status === "cancelled") ? { ...current, activeRunId: null, runState: status, streamingMessageId: null } : current;
|
|
2168
2423
|
});
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
if (
|
|
2172
|
-
|
|
2424
|
+
},
|
|
2425
|
+
attach: (runId) => {
|
|
2426
|
+
if (["completed", "failed", "cancelled", "cancelling"].includes(stateRef.current.runState)) return;
|
|
2427
|
+
void attachRunRef.current(runId, stateRef.current.cursor ?? void 0).catch(() => void 0);
|
|
2173
2428
|
}
|
|
2174
|
-
}
|
|
2175
|
-
const loadOlder =
|
|
2429
|
+
});
|
|
2430
|
+
const loadOlder = React8.useCallback(() => {
|
|
2176
2431
|
const thread = stateRef.current.threads.find((item) => item.id === stateRef.current.activeThreadId);
|
|
2177
2432
|
if (thread?.nextBeforeTurnIndex == null) return Promise.resolve();
|
|
2178
2433
|
const cursor = thread.nextBeforeTurnIndex;
|
|
@@ -2211,7 +2466,7 @@ function useAgentChat(options = {}) {
|
|
|
2211
2466
|
olderRequestRef.current = { key, promise };
|
|
2212
2467
|
return promise;
|
|
2213
2468
|
}, [clearOperationError, client, failOperation, ownOperation, pageSize, update, captureFeedbackRead]);
|
|
2214
|
-
const regenerate =
|
|
2469
|
+
const regenerate = React8.useCallback(async (message) => {
|
|
2215
2470
|
const index = stateRef.current.messages.findIndex((item) => item.id === message.id);
|
|
2216
2471
|
const user = stateRef.current.messages.slice(0, index).reverse().find((item) => item.role === "user");
|
|
2217
2472
|
if (user) await submit({
|
|
@@ -2297,7 +2552,7 @@ function useAgentChat(options = {}) {
|
|
|
2297
2552
|
}
|
|
2298
2553
|
}
|
|
2299
2554
|
});
|
|
2300
|
-
|
|
2555
|
+
React8.useEffect(() => {
|
|
2301
2556
|
const threadId = options.activeThreadId;
|
|
2302
2557
|
const activeThread2 = stateRef.current.threads.find((thread) => thread.id === stateRef.current.activeThreadId);
|
|
2303
2558
|
if (shouldHydrateSelectedThread(threadId, stateRef.current.activeThreadId, Boolean(activeThread2?.isHydrated))) {
|
|
@@ -2337,6 +2592,7 @@ function useAgentChat(options = {}) {
|
|
|
2337
2592
|
resumeQueue,
|
|
2338
2593
|
steer,
|
|
2339
2594
|
resumeHitl,
|
|
2595
|
+
getHitlSubmissionState,
|
|
2340
2596
|
setFeedback,
|
|
2341
2597
|
startNewThread,
|
|
2342
2598
|
submit
|
|
@@ -2344,15 +2600,15 @@ function useAgentChat(options = {}) {
|
|
|
2344
2600
|
}
|
|
2345
2601
|
|
|
2346
2602
|
// src/use-agent-resources.ts
|
|
2347
|
-
import * as
|
|
2603
|
+
import * as React9 from "react";
|
|
2348
2604
|
function useAgentThreads(options = {}) {
|
|
2349
2605
|
const { client, createAbortController } = useAgents24Runtime();
|
|
2350
|
-
const [threads, setThreads] =
|
|
2351
|
-
const [total, setTotal] =
|
|
2352
|
-
const [isLoading, setIsLoading] =
|
|
2353
|
-
const [error, setError] =
|
|
2354
|
-
const controllerRef =
|
|
2355
|
-
const load =
|
|
2606
|
+
const [threads, setThreads] = React9.useState([]);
|
|
2607
|
+
const [total, setTotal] = React9.useState();
|
|
2608
|
+
const [isLoading, setIsLoading] = React9.useState(true);
|
|
2609
|
+
const [error, setError] = React9.useState(null);
|
|
2610
|
+
const controllerRef = React9.useRef(null);
|
|
2611
|
+
const load = React9.useCallback(async () => {
|
|
2356
2612
|
controllerRef.current?.abort();
|
|
2357
2613
|
const controller = createAbortController();
|
|
2358
2614
|
controllerRef.current = controller;
|
|
@@ -2371,7 +2627,7 @@ function useAgentThreads(options = {}) {
|
|
|
2371
2627
|
}
|
|
2372
2628
|
}
|
|
2373
2629
|
}, [client, createAbortController, options.limit, options.skip]);
|
|
2374
|
-
|
|
2630
|
+
React9.useEffect(() => {
|
|
2375
2631
|
void load();
|
|
2376
2632
|
return () => controllerRef.current?.abort();
|
|
2377
2633
|
}, [load]);
|
|
@@ -2379,11 +2635,11 @@ function useAgentThreads(options = {}) {
|
|
|
2379
2635
|
}
|
|
2380
2636
|
function useAgentThread(threadId, options = {}) {
|
|
2381
2637
|
const { client, createAbortController } = useAgents24Runtime();
|
|
2382
|
-
const [thread, setThread] =
|
|
2383
|
-
const [isLoading, setIsLoading] =
|
|
2384
|
-
const [error, setError] =
|
|
2385
|
-
const controllerRef =
|
|
2386
|
-
const load =
|
|
2638
|
+
const [thread, setThread] = React9.useState(null);
|
|
2639
|
+
const [isLoading, setIsLoading] = React9.useState(Boolean(threadId));
|
|
2640
|
+
const [error, setError] = React9.useState(null);
|
|
2641
|
+
const controllerRef = React9.useRef(null);
|
|
2642
|
+
const load = React9.useCallback(async () => {
|
|
2387
2643
|
if (!threadId) {
|
|
2388
2644
|
controllerRef.current?.abort();
|
|
2389
2645
|
controllerRef.current = null;
|
|
@@ -2410,7 +2666,7 @@ function useAgentThread(threadId, options = {}) {
|
|
|
2410
2666
|
}
|
|
2411
2667
|
}
|
|
2412
2668
|
}, [client, createAbortController, options.beforeTurnIndex, options.limit, threadId]);
|
|
2413
|
-
|
|
2669
|
+
React9.useEffect(() => {
|
|
2414
2670
|
void load();
|
|
2415
2671
|
return () => controllerRef.current?.abort();
|
|
2416
2672
|
}, [load]);
|
|
@@ -2418,11 +2674,11 @@ function useAgentThread(threadId, options = {}) {
|
|
|
2418
2674
|
}
|
|
2419
2675
|
function useThreadComputer(threadId) {
|
|
2420
2676
|
const { client, createAbortController } = useAgents24Runtime();
|
|
2421
|
-
const [computer, setComputer] =
|
|
2422
|
-
const [isLoading, setIsLoading] =
|
|
2423
|
-
const [error, setError] =
|
|
2424
|
-
const controllerRef =
|
|
2425
|
-
const refresh =
|
|
2677
|
+
const [computer, setComputer] = React9.useState(null);
|
|
2678
|
+
const [isLoading, setIsLoading] = React9.useState(Boolean(threadId));
|
|
2679
|
+
const [error, setError] = React9.useState(null);
|
|
2680
|
+
const controllerRef = React9.useRef(null);
|
|
2681
|
+
const refresh = React9.useCallback(async () => {
|
|
2426
2682
|
controllerRef.current?.abort();
|
|
2427
2683
|
if (!threadId) {
|
|
2428
2684
|
setComputer(null);
|
|
@@ -2447,19 +2703,19 @@ function useThreadComputer(threadId) {
|
|
|
2447
2703
|
}
|
|
2448
2704
|
}
|
|
2449
2705
|
}, [client, createAbortController, threadId]);
|
|
2450
|
-
|
|
2706
|
+
React9.useEffect(() => {
|
|
2451
2707
|
void refresh();
|
|
2452
2708
|
return () => controllerRef.current?.abort();
|
|
2453
2709
|
}, [refresh]);
|
|
2454
|
-
const list =
|
|
2710
|
+
const list = React9.useCallback((input) => {
|
|
2455
2711
|
if (!threadId) throw new Error("A thread is required to list computer files");
|
|
2456
2712
|
return client.threads.computer.list({ threadId, ...input });
|
|
2457
2713
|
}, [client, threadId]);
|
|
2458
|
-
const read =
|
|
2714
|
+
const read = React9.useCallback((path) => {
|
|
2459
2715
|
if (!threadId) throw new Error("A thread is required to read a computer file");
|
|
2460
2716
|
return client.threads.computer.read({ threadId, path });
|
|
2461
2717
|
}, [client, threadId]);
|
|
2462
|
-
const createContentAccess =
|
|
2718
|
+
const createContentAccess = React9.useCallback((path, download = false) => {
|
|
2463
2719
|
if (!threadId) throw new Error("A thread is required to access a computer file");
|
|
2464
2720
|
return client.threads.computer.createContentAccess({ threadId, path, download });
|
|
2465
2721
|
}, [client, threadId]);
|
|
@@ -2467,16 +2723,16 @@ function useThreadComputer(threadId) {
|
|
|
2467
2723
|
}
|
|
2468
2724
|
function useAgentRun() {
|
|
2469
2725
|
const { client, createAbortController, createId } = useAgents24Runtime();
|
|
2470
|
-
const controllerRef =
|
|
2471
|
-
const [isAttached, setIsAttached] =
|
|
2472
|
-
const [error, setError] =
|
|
2473
|
-
|
|
2474
|
-
const detach =
|
|
2726
|
+
const controllerRef = React9.useRef(null);
|
|
2727
|
+
const [isAttached, setIsAttached] = React9.useState(false);
|
|
2728
|
+
const [error, setError] = React9.useState(null);
|
|
2729
|
+
React9.useEffect(() => () => controllerRef.current?.abort(), []);
|
|
2730
|
+
const detach = React9.useCallback(() => {
|
|
2475
2731
|
controllerRef.current?.abort();
|
|
2476
2732
|
controllerRef.current = null;
|
|
2477
2733
|
setIsAttached(false);
|
|
2478
2734
|
}, []);
|
|
2479
|
-
const attach =
|
|
2735
|
+
const attach = React9.useCallback(async (runId, onEvent, cursor) => {
|
|
2480
2736
|
detach();
|
|
2481
2737
|
const controller = createAbortController();
|
|
2482
2738
|
controllerRef.current = controller;
|
|
@@ -2492,7 +2748,7 @@ function useAgentRun() {
|
|
|
2492
2748
|
setIsAttached(false);
|
|
2493
2749
|
}
|
|
2494
2750
|
}, [client, createAbortController, detach]);
|
|
2495
|
-
const cancel =
|
|
2751
|
+
const cancel = React9.useCallback(async (runId) => {
|
|
2496
2752
|
const result = await client.runs.cancel({ runId, idempotencyKey: createId() });
|
|
2497
2753
|
detach();
|
|
2498
2754
|
return result;
|
|
@@ -2501,9 +2757,9 @@ function useAgentRun() {
|
|
|
2501
2757
|
}
|
|
2502
2758
|
function useAgentAttachments() {
|
|
2503
2759
|
const { client, createId } = useAgents24Runtime();
|
|
2504
|
-
const [uploads, setUploads] =
|
|
2505
|
-
const [isUploading, setIsUploading] =
|
|
2506
|
-
const uploadBatch =
|
|
2760
|
+
const [uploads, setUploads] = React9.useState({});
|
|
2761
|
+
const [isUploading, setIsUploading] = React9.useState(false);
|
|
2762
|
+
const uploadBatch = React9.useCallback(async (input) => {
|
|
2507
2763
|
setIsUploading(true);
|
|
2508
2764
|
try {
|
|
2509
2765
|
const results = await client.attachments.uploadBatch({ uploads: input.uploads, threadId: input.threadId, requestedAgentId: input.requestedAgentId, idempotencyKey: createId(), signal: input.signal });
|
|
@@ -2516,7 +2772,7 @@ function useAgentAttachments() {
|
|
|
2516
2772
|
setIsUploading(false);
|
|
2517
2773
|
}
|
|
2518
2774
|
}, [client, createId]);
|
|
2519
|
-
const createContentAccess =
|
|
2775
|
+
const createContentAccess = React9.useCallback(
|
|
2520
2776
|
(input) => client.attachments.createContentAccess(input),
|
|
2521
2777
|
[client]
|
|
2522
2778
|
);
|
|
@@ -2524,9 +2780,9 @@ function useAgentAttachments() {
|
|
|
2524
2780
|
}
|
|
2525
2781
|
function useAgentHitl() {
|
|
2526
2782
|
const { client, createId } = useAgents24Runtime();
|
|
2527
|
-
const [isResolving, setIsResolving] =
|
|
2528
|
-
const [lastResult, setLastResult] =
|
|
2529
|
-
const resume =
|
|
2783
|
+
const [isResolving, setIsResolving] = React9.useState(false);
|
|
2784
|
+
const [lastResult, setLastResult] = React9.useState(null);
|
|
2785
|
+
const resume = React9.useCallback(async (input) => {
|
|
2530
2786
|
setIsResolving(true);
|
|
2531
2787
|
try {
|
|
2532
2788
|
const result = await client.hitl.resume({ ...input, idempotencyKey: createId() });
|
|
@@ -2540,8 +2796,8 @@ function useAgentHitl() {
|
|
|
2540
2796
|
}
|
|
2541
2797
|
function useAgentMcp() {
|
|
2542
2798
|
const { client, createId } = useAgents24Runtime();
|
|
2543
|
-
const [isAuthorizing, setIsAuthorizing] =
|
|
2544
|
-
const startAuthorization =
|
|
2799
|
+
const [isAuthorizing, setIsAuthorizing] = React9.useState(false);
|
|
2800
|
+
const startAuthorization = React9.useCallback(async (input) => {
|
|
2545
2801
|
setIsAuthorizing(true);
|
|
2546
2802
|
try {
|
|
2547
2803
|
return await client.mcp.startAuthorization({ ...input, idempotencyKey: createId() });
|
|
@@ -2549,13 +2805,14 @@ function useAgentMcp() {
|
|
|
2549
2805
|
setIsAuthorizing(false);
|
|
2550
2806
|
}
|
|
2551
2807
|
}, [client, createId]);
|
|
2552
|
-
const redeemCallback =
|
|
2808
|
+
const redeemCallback = React9.useCallback(async (input) => client.mcp.redeemCallback({ ...input, idempotencyKey: createId() }), [client, createId]);
|
|
2553
2809
|
return { isAuthorizing, redeemCallback, startAuthorization };
|
|
2554
2810
|
}
|
|
2555
2811
|
export {
|
|
2556
2812
|
Agents24Provider,
|
|
2557
2813
|
adoptStreamingThread,
|
|
2558
2814
|
appendStableStreamingTurn,
|
|
2815
|
+
applyHitlResolution,
|
|
2559
2816
|
applyRuntimeEvent,
|
|
2560
2817
|
applyRuntimeEvents,
|
|
2561
2818
|
applyThreadSummaryEvent,
|
|
@@ -2569,12 +2826,15 @@ export {
|
|
|
2569
2826
|
computerResourceTargets,
|
|
2570
2827
|
contextCompressionPartFromEvent,
|
|
2571
2828
|
createChatObservation,
|
|
2829
|
+
createHitlSubmission,
|
|
2572
2830
|
createPortableId,
|
|
2573
2831
|
createRuntimeEventFrameBuffer,
|
|
2574
2832
|
discardOptimisticSubmission,
|
|
2833
|
+
findHitlResolution,
|
|
2575
2834
|
getThreadActivity,
|
|
2576
2835
|
initialAgentChatState,
|
|
2577
2836
|
isAbortLike,
|
|
2837
|
+
isDefinitiveHitlRejection,
|
|
2578
2838
|
latestThreadContext,
|
|
2579
2839
|
modelTurnPartFromEvent,
|
|
2580
2840
|
optimisticSubmissionState,
|
|
@@ -2583,6 +2843,7 @@ export {
|
|
|
2583
2843
|
preferNewestThreadSummary,
|
|
2584
2844
|
prependThreadTimeline,
|
|
2585
2845
|
preserveMountedMessageIds,
|
|
2846
|
+
preserveResolvedHitlParts,
|
|
2586
2847
|
promoteThreadMessage,
|
|
2587
2848
|
reconcileRefreshedThreads,
|
|
2588
2849
|
reconcileThreadTimeline,
|
|
@@ -2606,6 +2867,7 @@ export {
|
|
|
2606
2867
|
useThreadComputer,
|
|
2607
2868
|
useThreadMessageQueue,
|
|
2608
2869
|
withAgentChatBackgroundError,
|
|
2609
|
-
withAgentChatOperationError
|
|
2870
|
+
withAgentChatOperationError,
|
|
2871
|
+
withHitlDeadline
|
|
2610
2872
|
};
|
|
2611
2873
|
//# sourceMappingURL=index.js.map
|