@dudousxd/nestjs-agent-react 0.11.0 → 0.13.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 +22 -2
- package/dist/index.cjs +125 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -2
- package/dist/index.d.ts +49 -2
- package/dist/index.js +119 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -186,12 +186,32 @@ const transcript = useChatTranscript({
|
|
|
186
186
|
});
|
|
187
187
|
|
|
188
188
|
// block.kind === 'elicitation' →
|
|
189
|
-
// { toolCallId, preamble, questionCount, isPending, outcome, error,
|
|
190
|
-
// questions: [{ id, prompt, position, multiple, selected,
|
|
189
|
+
// { toolCallId, preamble, questionCount, isPending, isValid, outcome, error,
|
|
190
|
+
// questions: [{ id, prompt, description, input, position, multiple, selected, value, error,
|
|
191
|
+
// isPristine, setValue(raw),
|
|
191
192
|
// options: [{ value, label, hotkey, isSelected, isDefault, select() }] }],
|
|
192
193
|
// answer: { available, isSubmitting, run() }, skip: { … } }
|
|
193
194
|
```
|
|
194
195
|
|
|
196
|
+
**Typed questions.** A question with `input` asks for a value instead of a pick — `input.type` is
|
|
197
|
+
`text` · `textarea` · `number` · `boolean` · `date` · `email` · `url` · `select` (a pick from
|
|
198
|
+
`options` again), with optional `placeholder`, `required`, `min`, `max` and `pattern`. `options` may
|
|
199
|
+
be empty for one. Render the control from `input.type` and hand whatever it produces to
|
|
200
|
+
`question.setValue(raw)`: it is coerced with `coerceAnswer` to the canonical strings the wire
|
|
201
|
+
carries (numbers as decimals, booleans as `"true"`/`"false"`, dates as `YYYY-MM-DD`). `question.error`
|
|
202
|
+
is the server's own verdict on the current selection (`validateAnswer`), and `block.isValid` is all
|
|
203
|
+
of them — the answer route answers `400` with the same message for anything it would refuse.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
{q.input?.type === 'number' ? (
|
|
207
|
+
<input type="number" value={q.value} placeholder={q.input.placeholder}
|
|
208
|
+
min={q.input.min} max={q.input.max} onChange={(e) => q.setValue(e.target.value)} />
|
|
209
|
+
) : q.input?.type === 'boolean' ? (
|
|
210
|
+
<input type="checkbox" checked={q.value === 'true'} onChange={(e) => q.setValue(e.target.checked)} />
|
|
211
|
+
) : null}
|
|
212
|
+
{q.error && !q.isPristine ? <small role="alert">{q.error}</small> : null}
|
|
213
|
+
```
|
|
214
|
+
|
|
195
215
|
Every option the agent pre-picked comes back as `isDefault` and starts `isSelected`, so confirming
|
|
196
216
|
is enough. `answer.run()` submits **only the questions the user touched** — the rest are omitted on
|
|
197
217
|
purpose, so the server applies the same defaults it showed and the settled row still records which
|
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(src_exports, {
|
|
|
44
44
|
approvalCountdown: () => approvalCountdown,
|
|
45
45
|
backgroundRunsFromThread: () => backgroundRunsFromThread,
|
|
46
46
|
buildTranscriptBlocks: () => buildTranscriptBlocks,
|
|
47
|
+
coerceAnswer: () => coerceAnswer,
|
|
47
48
|
correctedCallIds: () => correctedCallIds,
|
|
48
49
|
createSkillsSource: () => createSkillsSource,
|
|
49
50
|
describeTimestamp: () => describeTimestamp,
|
|
@@ -76,7 +77,9 @@ __export(src_exports, {
|
|
|
76
77
|
useSpeechRecognition: () => useSpeechRecognition,
|
|
77
78
|
useStickToBottom: () => useStickToBottom,
|
|
78
79
|
useToolCatalog: () => useToolCatalog,
|
|
79
|
-
useTranscriptItem: () => useTranscriptItem
|
|
80
|
+
useTranscriptItem: () => useTranscriptItem,
|
|
81
|
+
validateAnswer: () => import_nestjs_agent_core.validateElicitationAnswer,
|
|
82
|
+
validateAnswerValue: () => import_nestjs_agent_core.validateElicitationValue
|
|
80
83
|
});
|
|
81
84
|
module.exports = __toCommonJS(src_exports);
|
|
82
85
|
|
|
@@ -539,6 +542,9 @@ var AgentChatTransport = class {
|
|
|
539
542
|
props: event.props,
|
|
540
543
|
...event.version !== void 0 ? {
|
|
541
544
|
version: event.version
|
|
545
|
+
} : {},
|
|
546
|
+
...event.toolCallId !== void 0 ? {
|
|
547
|
+
toolCallId: event.toolCallId
|
|
542
548
|
} : {}
|
|
543
549
|
}
|
|
544
550
|
});
|
|
@@ -1057,8 +1063,66 @@ var import_react6 = __toESM(require("react"), 1);
|
|
|
1057
1063
|
var import_react5 = require("react");
|
|
1058
1064
|
|
|
1059
1065
|
// src/transcript/model.ts
|
|
1066
|
+
var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
|
|
1060
1067
|
var import_ai2 = require("ai");
|
|
1061
1068
|
|
|
1069
|
+
// src/elicitation/answers.ts
|
|
1070
|
+
var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
|
|
1071
|
+
function pad(value) {
|
|
1072
|
+
return String(value).padStart(2, "0");
|
|
1073
|
+
}
|
|
1074
|
+
__name(pad, "pad");
|
|
1075
|
+
function toDay(date) {
|
|
1076
|
+
if (Number.isNaN(date.getTime())) {
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
1080
|
+
}
|
|
1081
|
+
__name(toDay, "toDay");
|
|
1082
|
+
function coerceOne(type, raw) {
|
|
1083
|
+
if (raw === null || raw === void 0) {
|
|
1084
|
+
return null;
|
|
1085
|
+
}
|
|
1086
|
+
if (Array.isArray(raw)) {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
if (raw instanceof Date) {
|
|
1090
|
+
return type === "date" || type === void 0 ? toDay(raw) : raw.toISOString();
|
|
1091
|
+
}
|
|
1092
|
+
if (typeof raw === "boolean") {
|
|
1093
|
+
return raw ? "true" : "false";
|
|
1094
|
+
}
|
|
1095
|
+
if (typeof raw === "number") {
|
|
1096
|
+
return Number.isFinite(raw) ? String(raw) : null;
|
|
1097
|
+
}
|
|
1098
|
+
const text = raw;
|
|
1099
|
+
switch (type) {
|
|
1100
|
+
case "number":
|
|
1101
|
+
case "email":
|
|
1102
|
+
case "url":
|
|
1103
|
+
case "date":
|
|
1104
|
+
return text.trim() === "" ? null : text.trim();
|
|
1105
|
+
case "boolean": {
|
|
1106
|
+
const lowered = text.trim().toLowerCase();
|
|
1107
|
+
if (lowered === "true" || lowered === "on" || lowered === "yes") return "true";
|
|
1108
|
+
if (lowered === "false" || lowered === "off" || lowered === "no") return "false";
|
|
1109
|
+
return lowered === "" ? null : text;
|
|
1110
|
+
}
|
|
1111
|
+
default:
|
|
1112
|
+
return text === "" ? null : text;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
__name(coerceOne, "coerceOne");
|
|
1116
|
+
function coerceAnswer(question, raw) {
|
|
1117
|
+
const type = question.input?.type;
|
|
1118
|
+
const items = Array.isArray(raw) ? raw : [
|
|
1119
|
+
raw
|
|
1120
|
+
];
|
|
1121
|
+
const values = items.map((item) => coerceOne(type, item)).filter((value) => value !== null);
|
|
1122
|
+
return question.multiple === true ? values : values.slice(0, 1);
|
|
1123
|
+
}
|
|
1124
|
+
__name(coerceAnswer, "coerceAnswer");
|
|
1125
|
+
|
|
1062
1126
|
// src/presentation/tool-activity.ts
|
|
1063
1127
|
var import_ai = require("ai");
|
|
1064
1128
|
|
|
@@ -1595,11 +1659,16 @@ function readElicitationRequest(part) {
|
|
|
1595
1659
|
}
|
|
1596
1660
|
const questions = [];
|
|
1597
1661
|
for (const candidate of input.questions) {
|
|
1598
|
-
if (!isRecord2(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string"
|
|
1662
|
+
if (!isRecord2(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
const input2 = (0, import_nestjs_agent_core2.readElicitationInput)(candidate.input) ?? null;
|
|
1666
|
+
const rawOptions = Array.isArray(candidate.options) ? candidate.options : [];
|
|
1667
|
+
if (rawOptions.length === 0 && (input2 === null || input2.type === "select")) {
|
|
1599
1668
|
return null;
|
|
1600
1669
|
}
|
|
1601
1670
|
const options = [];
|
|
1602
|
-
for (const option of
|
|
1671
|
+
for (const option of rawOptions) {
|
|
1603
1672
|
if (!isRecord2(option) || typeof option.value !== "string" || typeof option.label !== "string") {
|
|
1604
1673
|
return null;
|
|
1605
1674
|
}
|
|
@@ -1612,7 +1681,10 @@ function readElicitationRequest(part) {
|
|
|
1612
1681
|
questions.push({
|
|
1613
1682
|
id: candidate.id,
|
|
1614
1683
|
prompt: candidate.prompt,
|
|
1684
|
+
description: typeof candidate.description === "string" ? candidate.description : null,
|
|
1685
|
+
input: input2,
|
|
1615
1686
|
multiple: candidate.multiple === true,
|
|
1687
|
+
allowFreeText: candidate.allowFreeText === true,
|
|
1616
1688
|
defaults: Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [],
|
|
1617
1689
|
options
|
|
1618
1690
|
});
|
|
@@ -1648,12 +1720,38 @@ function buildElicitationBlock(key, part, request, options) {
|
|
|
1648
1720
|
const picked = isPending ? options.picked(toolCallId, question.id) : void 0;
|
|
1649
1721
|
const selected = picked ?? outcome?.answers[question.id] ?? question.defaults;
|
|
1650
1722
|
const pick = /* @__PURE__ */ __name((values) => options.pick(toolCallId, question.id, values), "pick");
|
|
1723
|
+
const error = isPending ? (0, import_nestjs_agent_core2.validateElicitationAnswer)({
|
|
1724
|
+
id: question.id,
|
|
1725
|
+
prompt: question.prompt,
|
|
1726
|
+
options: question.options.map(({ value, label }) => ({
|
|
1727
|
+
value,
|
|
1728
|
+
label
|
|
1729
|
+
})),
|
|
1730
|
+
...question.input !== null ? {
|
|
1731
|
+
input: question.input
|
|
1732
|
+
} : {},
|
|
1733
|
+
...question.multiple ? {
|
|
1734
|
+
multiple: true
|
|
1735
|
+
} : {},
|
|
1736
|
+
...question.allowFreeText ? {
|
|
1737
|
+
allowFreeText: true
|
|
1738
|
+
} : {}
|
|
1739
|
+
}, selected) : null;
|
|
1651
1740
|
return {
|
|
1652
1741
|
id: question.id,
|
|
1653
1742
|
prompt: question.prompt,
|
|
1743
|
+
description: question.description,
|
|
1744
|
+
input: question.input,
|
|
1654
1745
|
multiple: question.multiple,
|
|
1655
1746
|
position: index + 1,
|
|
1656
1747
|
selected,
|
|
1748
|
+
value: selected[0] ?? "",
|
|
1749
|
+
error,
|
|
1750
|
+
setValue: /* @__PURE__ */ __name((raw) => {
|
|
1751
|
+
if (isPending) {
|
|
1752
|
+
pick(coerceAnswer(question, raw));
|
|
1753
|
+
}
|
|
1754
|
+
}, "setValue"),
|
|
1657
1755
|
isPristine: isPending ? picked === void 0 : outcome?.defaulted.includes(question.id) ?? true,
|
|
1658
1756
|
options: question.options.map((option) => ({
|
|
1659
1757
|
value: option.value,
|
|
@@ -1688,6 +1786,7 @@ function buildElicitationBlock(key, part, request, options) {
|
|
|
1688
1786
|
questions,
|
|
1689
1787
|
questionCount: questions.length,
|
|
1690
1788
|
isPending,
|
|
1789
|
+
isValid: questions.every((question) => question.error === null),
|
|
1691
1790
|
outcome,
|
|
1692
1791
|
error: options.errorOf(toolCallId),
|
|
1693
1792
|
answer: {
|
|
@@ -1802,7 +1901,8 @@ function readUiComponent(part) {
|
|
|
1802
1901
|
id,
|
|
1803
1902
|
component: data.component,
|
|
1804
1903
|
props: isRecord2(data.props) ? data.props : {},
|
|
1805
|
-
version: typeof data.version === "number" ? data.version : null
|
|
1904
|
+
version: typeof data.version === "number" ? data.version : null,
|
|
1905
|
+
toolCallId: typeof data.toolCallId === "string" ? data.toolCallId : null
|
|
1806
1906
|
};
|
|
1807
1907
|
}
|
|
1808
1908
|
__name(readUiComponent, "readUiComponent");
|
|
@@ -3196,7 +3296,7 @@ function useApprovalCountdown(expiresAt, options = {}) {
|
|
|
3196
3296
|
__name(useApprovalCountdown, "useApprovalCountdown");
|
|
3197
3297
|
|
|
3198
3298
|
// src/stored-message-to-ui-message.ts
|
|
3199
|
-
var
|
|
3299
|
+
var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
|
|
3200
3300
|
function isRefusal(result) {
|
|
3201
3301
|
if (result.denied === true) {
|
|
3202
3302
|
return true;
|
|
@@ -3209,7 +3309,7 @@ function refusalReason(result) {
|
|
|
3209
3309
|
const { output } = result;
|
|
3210
3310
|
if (output !== null && typeof output === "object" && "reason" in output) {
|
|
3211
3311
|
const reason = output.reason;
|
|
3212
|
-
return typeof reason === "string" && reason !==
|
|
3312
|
+
return typeof reason === "string" && reason !== import_nestjs_agent_core3.DEFAULT_REFUSAL_REASON ? reason : void 0;
|
|
3213
3313
|
}
|
|
3214
3314
|
return void 0;
|
|
3215
3315
|
}
|
|
@@ -3240,8 +3340,10 @@ function storedMessageToUiMessage(message) {
|
|
|
3240
3340
|
url: attachment.url
|
|
3241
3341
|
});
|
|
3242
3342
|
}
|
|
3343
|
+
const toolCallIds = new Set((message.toolCalls ?? []).map((call) => call.id));
|
|
3344
|
+
const pushedByCall = /* @__PURE__ */ new Map();
|
|
3243
3345
|
for (const component of message.ui ?? []) {
|
|
3244
|
-
|
|
3346
|
+
const part = {
|
|
3245
3347
|
type: "data-ui",
|
|
3246
3348
|
id: component.id,
|
|
3247
3349
|
data: {
|
|
@@ -3250,9 +3352,19 @@ function storedMessageToUiMessage(message) {
|
|
|
3250
3352
|
props: component.props,
|
|
3251
3353
|
...component.version !== void 0 ? {
|
|
3252
3354
|
version: component.version
|
|
3355
|
+
} : {},
|
|
3356
|
+
...component.toolCallId !== void 0 ? {
|
|
3357
|
+
toolCallId: component.toolCallId
|
|
3253
3358
|
} : {}
|
|
3254
3359
|
}
|
|
3255
|
-
}
|
|
3360
|
+
};
|
|
3361
|
+
if (component.toolCallId !== void 0 && toolCallIds.has(component.toolCallId)) {
|
|
3362
|
+
const list = pushedByCall.get(component.toolCallId) ?? [];
|
|
3363
|
+
list.push(part);
|
|
3364
|
+
pushedByCall.set(component.toolCallId, list);
|
|
3365
|
+
} else {
|
|
3366
|
+
parts.push(part);
|
|
3367
|
+
}
|
|
3256
3368
|
}
|
|
3257
3369
|
for (const approval of message.approvals ?? []) {
|
|
3258
3370
|
parts.push({
|
|
@@ -3323,6 +3435,7 @@ function storedMessageToUiMessage(message) {
|
|
|
3323
3435
|
input: call.input
|
|
3324
3436
|
}
|
|
3325
3437
|
});
|
|
3438
|
+
parts.push(...pushedByCall.get(call.id) ?? []);
|
|
3326
3439
|
}
|
|
3327
3440
|
return {
|
|
3328
3441
|
id: message.id,
|
|
@@ -3757,6 +3870,7 @@ __name(mergeHeaders, "mergeHeaders");
|
|
|
3757
3870
|
approvalCountdown,
|
|
3758
3871
|
backgroundRunsFromThread,
|
|
3759
3872
|
buildTranscriptBlocks,
|
|
3873
|
+
coerceAnswer,
|
|
3760
3874
|
correctedCallIds,
|
|
3761
3875
|
createSkillsSource,
|
|
3762
3876
|
describeTimestamp,
|
|
@@ -3789,6 +3903,8 @@ __name(mergeHeaders, "mergeHeaders");
|
|
|
3789
3903
|
useSpeechRecognition,
|
|
3790
3904
|
useStickToBottom,
|
|
3791
3905
|
useToolCatalog,
|
|
3792
|
-
useTranscriptItem
|
|
3906
|
+
useTranscriptItem,
|
|
3907
|
+
validateAnswer,
|
|
3908
|
+
validateAnswerValue
|
|
3793
3909
|
});
|
|
3794
3910
|
//# sourceMappingURL=index.cjs.map
|