@dudousxd/nestjs-agent-react 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/dist/index.cjs +222 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -9
- package/dist/index.d.ts +72 -9
- package/dist/index.js +200 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -444,6 +444,10 @@ var AgentChatTransport = class {
|
|
|
444
444
|
});
|
|
445
445
|
}
|
|
446
446
|
break;
|
|
447
|
+
case "approval-settled":
|
|
448
|
+
ensureStep();
|
|
449
|
+
forwardAsData(event, false);
|
|
450
|
+
break;
|
|
447
451
|
case "ui":
|
|
448
452
|
ensureStep();
|
|
449
453
|
closeRuns();
|
|
@@ -743,6 +747,10 @@ var AgentClient = class {
|
|
|
743
747
|
cancelStream(runId) {
|
|
744
748
|
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
745
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
752
|
+
* the decision came through (the server records `'web'` when omitted).
|
|
753
|
+
*/
|
|
746
754
|
approveToolCall(input) {
|
|
747
755
|
return this.request("POST", "/agent/tool-call/approve", input);
|
|
748
756
|
}
|
|
@@ -1260,7 +1268,7 @@ function buildTranscriptBlocks(message, options) {
|
|
|
1260
1268
|
if (toolBuffer.length === 0) {
|
|
1261
1269
|
return;
|
|
1262
1270
|
}
|
|
1263
|
-
const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvals.get(part.toolCallId)
|
|
1271
|
+
const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvalOf(approvals.get(part.toolCallId), part), options.toolCatalog));
|
|
1264
1272
|
const roots = nestToolCalls(calls);
|
|
1265
1273
|
blocks.push({
|
|
1266
1274
|
kind: "tools",
|
|
@@ -1335,7 +1343,7 @@ function buildTranscriptBlocks(message, options) {
|
|
|
1335
1343
|
fileBuffer.push(part);
|
|
1336
1344
|
continue;
|
|
1337
1345
|
}
|
|
1338
|
-
if (part.type === APPROVAL_PART) {
|
|
1346
|
+
if (part.type === APPROVAL_PART || part.type === SETTLED_PART) {
|
|
1339
1347
|
continue;
|
|
1340
1348
|
}
|
|
1341
1349
|
flushAll();
|
|
@@ -1486,7 +1494,11 @@ function buildToolCall(part, options, approval, catalog) {
|
|
|
1486
1494
|
approve: {
|
|
1487
1495
|
available: awaiting && options?.canApprove === true,
|
|
1488
1496
|
isSubmitting: sending === "approve",
|
|
1489
|
-
|
|
1497
|
+
// Read by field, not passed through: `onClick={call.approve.run}` hands this a click event,
|
|
1498
|
+
// and a host's handler must not receive that as its options.
|
|
1499
|
+
run: /* @__PURE__ */ __name((approveOptions) => approveOptions?.remember === true ? options?.approve(toolCallId, {
|
|
1500
|
+
remember: true
|
|
1501
|
+
}) : options?.approve(toolCallId), "run")
|
|
1490
1502
|
},
|
|
1491
1503
|
reject: {
|
|
1492
1504
|
available: awaiting && options?.canReject === true,
|
|
@@ -1614,25 +1626,90 @@ function buildElicitationBlock(key, part, request, options) {
|
|
|
1614
1626
|
__name(buildElicitationBlock, "buildElicitationBlock");
|
|
1615
1627
|
var UI_PART = "data-ui";
|
|
1616
1628
|
var APPROVAL_PART = "data-approval-requested";
|
|
1629
|
+
var SETTLED_PART = "data-approval-settled";
|
|
1630
|
+
var SETTLED_STATUSES = [
|
|
1631
|
+
"approved",
|
|
1632
|
+
"rejected",
|
|
1633
|
+
"expired"
|
|
1634
|
+
];
|
|
1617
1635
|
function readApprovals(parts) {
|
|
1618
1636
|
const out = /* @__PURE__ */ new Map();
|
|
1637
|
+
const entry = /* @__PURE__ */ __name((id) => {
|
|
1638
|
+
let found = out.get(id);
|
|
1639
|
+
if (found === void 0) {
|
|
1640
|
+
found = {
|
|
1641
|
+
approver: null,
|
|
1642
|
+
expiresAt: null,
|
|
1643
|
+
reason: null,
|
|
1644
|
+
status: null,
|
|
1645
|
+
remember: false,
|
|
1646
|
+
decidedBy: null,
|
|
1647
|
+
decidedVia: null,
|
|
1648
|
+
decisionReason: null
|
|
1649
|
+
};
|
|
1650
|
+
out.set(id, found);
|
|
1651
|
+
}
|
|
1652
|
+
return found;
|
|
1653
|
+
}, "entry");
|
|
1619
1654
|
for (const part of parts) {
|
|
1620
|
-
if (part.type !== APPROVAL_PART) {
|
|
1655
|
+
if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART) {
|
|
1621
1656
|
continue;
|
|
1622
1657
|
}
|
|
1623
1658
|
const data = part.data;
|
|
1624
|
-
if (!isRecord2(data) || typeof data.id !== "string"
|
|
1659
|
+
if (!isRecord2(data) || typeof data.id !== "string") {
|
|
1625
1660
|
continue;
|
|
1626
1661
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1662
|
+
const approval = entry(data.id);
|
|
1663
|
+
if (part.type === APPROVAL_PART) {
|
|
1664
|
+
if (typeof data.approver !== "string") {
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
approval.approver = data.approver;
|
|
1668
|
+
approval.expiresAt = typeof data.expiresAt === "string" ? data.expiresAt : null;
|
|
1669
|
+
approval.reason = typeof data.reason === "string" ? data.reason : null;
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
if (typeof data.status !== "string" || !SETTLED_STATUSES.includes(data.status)) {
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
approval.status = data.status;
|
|
1676
|
+
if (approval.approver === null && typeof data.approver === "string") {
|
|
1677
|
+
approval.approver = data.approver;
|
|
1678
|
+
}
|
|
1679
|
+
approval.remember = data.remember === true;
|
|
1680
|
+
approval.decidedBy = typeof data.decidedBy === "string" ? data.decidedBy : null;
|
|
1681
|
+
approval.decidedVia = typeof data.decidedVia === "string" ? data.decidedVia : null;
|
|
1682
|
+
approval.decisionReason = typeof data.reason === "string" ? data.reason : null;
|
|
1632
1683
|
}
|
|
1633
1684
|
return out;
|
|
1634
1685
|
}
|
|
1635
1686
|
__name(readApprovals, "readApprovals");
|
|
1687
|
+
function approvalOf(parts, call) {
|
|
1688
|
+
if (parts === void 0 || parts.approver === null) {
|
|
1689
|
+
return null;
|
|
1690
|
+
}
|
|
1691
|
+
return {
|
|
1692
|
+
approver: parts.approver,
|
|
1693
|
+
expiresAt: parts.expiresAt,
|
|
1694
|
+
reason: parts.reason,
|
|
1695
|
+
status: parts.status ?? statusFromPart(call),
|
|
1696
|
+
remember: parts.remember,
|
|
1697
|
+
decidedBy: parts.decidedBy,
|
|
1698
|
+
decidedVia: parts.decidedVia,
|
|
1699
|
+
decisionReason: parts.decisionReason
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
__name(approvalOf, "approvalOf");
|
|
1703
|
+
function statusFromPart(call) {
|
|
1704
|
+
if (call.state === "output-denied") {
|
|
1705
|
+
return "rejected";
|
|
1706
|
+
}
|
|
1707
|
+
if (call.state === "output-available" || call.state === "output-error") {
|
|
1708
|
+
return "approved";
|
|
1709
|
+
}
|
|
1710
|
+
return "pending";
|
|
1711
|
+
}
|
|
1712
|
+
__name(statusFromPart, "statusFromPart");
|
|
1636
1713
|
function readUiComponent(part) {
|
|
1637
1714
|
const data = part.data;
|
|
1638
1715
|
if (!isRecord2(data) || typeof data.component !== "string") {
|
|
@@ -2018,10 +2095,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2018
2095
|
}, [
|
|
2019
2096
|
settle
|
|
2020
2097
|
]);
|
|
2021
|
-
const approve = useCallback3((toolCallId) => {
|
|
2098
|
+
const approve = useCallback3((toolCallId, options2) => {
|
|
2022
2099
|
const onApprove = latest.current.options.onApprove;
|
|
2023
2100
|
if (onApprove) {
|
|
2024
|
-
settle(toolCallId, "approve", () => onApprove(toolCallId));
|
|
2101
|
+
settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
|
|
2025
2102
|
}
|
|
2026
2103
|
}, [
|
|
2027
2104
|
settle
|
|
@@ -2996,6 +3073,49 @@ function useToolCatalog(options) {
|
|
|
2996
3073
|
}
|
|
2997
3074
|
__name(useToolCatalog, "useToolCatalog");
|
|
2998
3075
|
|
|
3076
|
+
// src/approvals/countdown.ts
|
|
3077
|
+
import { useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
|
|
3078
|
+
function approvalCountdown(expiresAt, now = Date.now()) {
|
|
3079
|
+
const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
|
|
3080
|
+
if (!Number.isFinite(deadline)) {
|
|
3081
|
+
return {
|
|
3082
|
+
remainingMs: null,
|
|
3083
|
+
isExpired: false
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
const remainingMs = Math.max(0, deadline - now);
|
|
3087
|
+
return {
|
|
3088
|
+
remainingMs,
|
|
3089
|
+
isExpired: remainingMs === 0
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
__name(approvalCountdown, "approvalCountdown");
|
|
3093
|
+
function useApprovalCountdown(expiresAt, options = {}) {
|
|
3094
|
+
const { intervalMs = 1e3 } = options;
|
|
3095
|
+
const nowRef = useRef6(options.now ?? Date.now);
|
|
3096
|
+
nowRef.current = options.now ?? Date.now;
|
|
3097
|
+
const [state, setState] = useState8(() => approvalCountdown(expiresAt, nowRef.current()));
|
|
3098
|
+
useEffect8(() => {
|
|
3099
|
+
const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
|
|
3100
|
+
const first = read();
|
|
3101
|
+
setState(first);
|
|
3102
|
+
if (first.remainingMs === null || first.isExpired) {
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
const timer = setInterval(() => {
|
|
3106
|
+
const next = read();
|
|
3107
|
+
setState(next);
|
|
3108
|
+
if (next.isExpired) clearInterval(timer);
|
|
3109
|
+
}, intervalMs);
|
|
3110
|
+
return () => clearInterval(timer);
|
|
3111
|
+
}, [
|
|
3112
|
+
expiresAt,
|
|
3113
|
+
intervalMs
|
|
3114
|
+
]);
|
|
3115
|
+
return state;
|
|
3116
|
+
}
|
|
3117
|
+
__name(useApprovalCountdown, "useApprovalCountdown");
|
|
3118
|
+
|
|
2999
3119
|
// src/stored-message-to-ui-message.ts
|
|
3000
3120
|
import { DEFAULT_REFUSAL_REASON } from "@dudousxd/nestjs-agent-core";
|
|
3001
3121
|
function isRefusal(result) {
|
|
@@ -3055,6 +3175,41 @@ function storedMessageToUiMessage(message) {
|
|
|
3055
3175
|
}
|
|
3056
3176
|
});
|
|
3057
3177
|
}
|
|
3178
|
+
for (const approval of message.approvals ?? []) {
|
|
3179
|
+
parts.push({
|
|
3180
|
+
type: "data-approval-requested",
|
|
3181
|
+
id: approval.toolCallId,
|
|
3182
|
+
data: {
|
|
3183
|
+
id: approval.toolCallId,
|
|
3184
|
+
approver: approval.approver,
|
|
3185
|
+
...approval.expiresAt !== void 0 ? {
|
|
3186
|
+
expiresAt: approval.expiresAt
|
|
3187
|
+
} : {}
|
|
3188
|
+
}
|
|
3189
|
+
});
|
|
3190
|
+
if (approval.status !== "pending") {
|
|
3191
|
+
parts.push({
|
|
3192
|
+
type: "data-approval-settled",
|
|
3193
|
+
id: approval.toolCallId,
|
|
3194
|
+
data: {
|
|
3195
|
+
id: approval.toolCallId,
|
|
3196
|
+
status: approval.status,
|
|
3197
|
+
...approval.decidedBy !== void 0 ? {
|
|
3198
|
+
decidedBy: approval.decidedBy
|
|
3199
|
+
} : {},
|
|
3200
|
+
...approval.decidedVia !== void 0 ? {
|
|
3201
|
+
decidedVia: approval.decidedVia
|
|
3202
|
+
} : {},
|
|
3203
|
+
...approval.remember === true ? {
|
|
3204
|
+
remember: true
|
|
3205
|
+
} : {},
|
|
3206
|
+
...approval.reason !== void 0 ? {
|
|
3207
|
+
reason: approval.reason
|
|
3208
|
+
} : {}
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3058
3213
|
for (const call of message.toolCalls ?? []) {
|
|
3059
3214
|
const result = message.toolResults?.find((candidate) => candidate.id === call.id);
|
|
3060
3215
|
const toolKind2 = call.kind;
|
|
@@ -3157,19 +3312,19 @@ __name(sumUsage, "sumUsage");
|
|
|
3157
3312
|
|
|
3158
3313
|
// src/use-agent-chat.ts
|
|
3159
3314
|
import { useChat } from "@ai-sdk/react";
|
|
3160
|
-
import { useCallback as useCallback6, useEffect as
|
|
3315
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useMemo as useMemo4, useRef as useRef7, useState as useState9 } from "react";
|
|
3161
3316
|
function useAgentChat(options) {
|
|
3162
|
-
const latest =
|
|
3317
|
+
const latest = useRef7(options);
|
|
3163
3318
|
latest.current = options;
|
|
3164
|
-
const [runId, setRunId] =
|
|
3165
|
-
const runIdRef =
|
|
3319
|
+
const [runId, setRunId] = useState9(options.resumeRunId);
|
|
3320
|
+
const runIdRef = useRef7(runId);
|
|
3166
3321
|
runIdRef.current = runId;
|
|
3167
|
-
const settlingRunIdRef =
|
|
3168
|
-
const autoResumeRunIdRef =
|
|
3169
|
-
const [autoResume, setAutoResume] =
|
|
3170
|
-
const [activeRunId, setActiveRunId] =
|
|
3171
|
-
const regenerateNext =
|
|
3172
|
-
const createdThreadId =
|
|
3322
|
+
const settlingRunIdRef = useRef7(void 0);
|
|
3323
|
+
const autoResumeRunIdRef = useRef7(void 0);
|
|
3324
|
+
const [autoResume, setAutoResume] = useState9(false);
|
|
3325
|
+
const [activeRunId, setActiveRunId] = useState9(null);
|
|
3326
|
+
const regenerateNext = useRef7(false);
|
|
3327
|
+
const createdThreadId = useRef7(void 0);
|
|
3173
3328
|
const client = useMemo4(() => {
|
|
3174
3329
|
if (options.client) return options.client;
|
|
3175
3330
|
return new AgentClient({
|
|
@@ -3185,7 +3340,7 @@ function useAgentChat(options) {
|
|
|
3185
3340
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
3186
3341
|
});
|
|
3187
3342
|
}, []);
|
|
3188
|
-
|
|
3343
|
+
useEffect9(() => {
|
|
3189
3344
|
if (!options.resume || options.threadId === void 0) return;
|
|
3190
3345
|
let cancelled = false;
|
|
3191
3346
|
const threadId = options.threadId;
|
|
@@ -3254,7 +3409,7 @@ function useAgentChat(options) {
|
|
|
3254
3409
|
}, "onAttemptStart")
|
|
3255
3410
|
});
|
|
3256
3411
|
}, []);
|
|
3257
|
-
const backgroundRef =
|
|
3412
|
+
const backgroundRef = useRef7(() => Promise.resolve());
|
|
3258
3413
|
const chat = useChat({
|
|
3259
3414
|
transport,
|
|
3260
3415
|
resume: options.resumeRunId !== void 0 || autoResume,
|
|
@@ -3285,9 +3440,9 @@ function useAgentChat(options) {
|
|
|
3285
3440
|
}
|
|
3286
3441
|
}, "onFinish")
|
|
3287
3442
|
});
|
|
3288
|
-
const chatRef =
|
|
3443
|
+
const chatRef = useRef7(chat);
|
|
3289
3444
|
chatRef.current = chat;
|
|
3290
|
-
const turnInFlight =
|
|
3445
|
+
const turnInFlight = useRef7(false);
|
|
3291
3446
|
const isTurnInFlight = useCallback6(() => turnInFlight.current || transport.isAttemptLive, [
|
|
3292
3447
|
transport
|
|
3293
3448
|
]);
|
|
@@ -3307,9 +3462,9 @@ function useAgentChat(options) {
|
|
|
3307
3462
|
toolCallId,
|
|
3308
3463
|
output
|
|
3309
3464
|
}), []);
|
|
3310
|
-
const [threads, setThreads] =
|
|
3311
|
-
const [quota, setQuota] =
|
|
3312
|
-
const [backgroundRuns, setBackgroundRuns] =
|
|
3465
|
+
const [threads, setThreads] = useState9([]);
|
|
3466
|
+
const [quota, setQuota] = useState9(null);
|
|
3467
|
+
const [backgroundRuns, setBackgroundRuns] = useState9([]);
|
|
3313
3468
|
const refreshBackground = useCallback6(async () => {
|
|
3314
3469
|
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
3315
3470
|
if (threadId === void 0) {
|
|
@@ -3335,7 +3490,7 @@ function useAgentChat(options) {
|
|
|
3335
3490
|
]);
|
|
3336
3491
|
backgroundRef.current = refreshBackground;
|
|
3337
3492
|
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
3338
|
-
|
|
3493
|
+
useEffect9(() => {
|
|
3339
3494
|
if (options.background !== true || options.threadId === void 0) {
|
|
3340
3495
|
return;
|
|
3341
3496
|
}
|
|
@@ -3345,7 +3500,7 @@ function useAgentChat(options) {
|
|
|
3345
3500
|
options.threadId,
|
|
3346
3501
|
refreshBackground
|
|
3347
3502
|
]);
|
|
3348
|
-
|
|
3503
|
+
useEffect9(() => {
|
|
3349
3504
|
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
3350
3505
|
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
3351
3506
|
return;
|
|
@@ -3413,18 +3568,27 @@ function useAgentChat(options) {
|
|
|
3413
3568
|
}, [
|
|
3414
3569
|
client
|
|
3415
3570
|
]);
|
|
3416
|
-
const approve = useCallback6(async ({ toolCallId }) => {
|
|
3571
|
+
const approve = useCallback6(async ({ toolCallId, remember, via }) => {
|
|
3417
3572
|
await client.approveToolCall({
|
|
3418
|
-
toolCallId
|
|
3573
|
+
toolCallId,
|
|
3574
|
+
...remember === true ? {
|
|
3575
|
+
remember: true
|
|
3576
|
+
} : {},
|
|
3577
|
+
...via !== void 0 ? {
|
|
3578
|
+
via
|
|
3579
|
+
} : {}
|
|
3419
3580
|
});
|
|
3420
3581
|
}, [
|
|
3421
3582
|
client
|
|
3422
3583
|
]);
|
|
3423
|
-
const reject = useCallback6(async ({ toolCallId, reason }) => {
|
|
3584
|
+
const reject = useCallback6(async ({ toolCallId, reason, via }) => {
|
|
3424
3585
|
await client.rejectToolCall({
|
|
3425
3586
|
toolCallId,
|
|
3426
3587
|
...reason !== void 0 ? {
|
|
3427
3588
|
reason
|
|
3589
|
+
} : {},
|
|
3590
|
+
...via !== void 0 ? {
|
|
3591
|
+
via
|
|
3428
3592
|
} : {}
|
|
3429
3593
|
});
|
|
3430
3594
|
}, [
|
|
@@ -3510,6 +3674,7 @@ export {
|
|
|
3510
3674
|
MessageList,
|
|
3511
3675
|
SPEECH_LANGUAGES,
|
|
3512
3676
|
applyCompletion,
|
|
3677
|
+
approvalCountdown,
|
|
3513
3678
|
backgroundRunsFromThread,
|
|
3514
3679
|
buildTranscriptBlocks,
|
|
3515
3680
|
correctedCallIds,
|
|
@@ -3537,6 +3702,7 @@ export {
|
|
|
3537
3702
|
toolCallState,
|
|
3538
3703
|
toolCatalogFrom,
|
|
3539
3704
|
useAgentChat,
|
|
3705
|
+
useApprovalCountdown,
|
|
3540
3706
|
useChatTranscript,
|
|
3541
3707
|
useComposerAutocomplete,
|
|
3542
3708
|
useElapsed,
|