@dudousxd/nestjs-agent-core 0.20.0 → 0.22.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 +3 -1
- package/dist/guardrails/index.d.cts +1 -1
- package/dist/guardrails/index.d.ts +1 -1
- package/dist/index.cjs +495 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +484 -34
- package/dist/index.js.map +1 -1
- package/dist/{tool-B2Dq9ZwF.d.cts → tool-BQqFt06V.d.cts} +127 -2
- package/dist/{tool-B2Dq9ZwF.d.ts → tool-BQqFt06V.d.ts} +127 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -157,6 +157,95 @@ function withTurnFrames(result, frames) {
|
|
|
157
157
|
}
|
|
158
158
|
__name(withTurnFrames, "withTurnFrames");
|
|
159
159
|
|
|
160
|
+
// src/tool-ui.ts
|
|
161
|
+
var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
|
|
162
|
+
function wrapToolStepOutput(output, ui) {
|
|
163
|
+
if (ui.length === 0) {
|
|
164
|
+
return output;
|
|
165
|
+
}
|
|
166
|
+
const wrapped = {
|
|
167
|
+
[TOOL_STEP_UI]: 1,
|
|
168
|
+
output,
|
|
169
|
+
ui: [
|
|
170
|
+
...ui
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
return wrapped;
|
|
174
|
+
}
|
|
175
|
+
__name(wrapToolStepOutput, "wrapToolStepOutput");
|
|
176
|
+
function unwrapToolStepOutput(raw) {
|
|
177
|
+
if (typeof raw === "object" && raw !== null && raw[TOOL_STEP_UI] === 1) {
|
|
178
|
+
const wrapped = raw;
|
|
179
|
+
return {
|
|
180
|
+
output: wrapped.output,
|
|
181
|
+
ui: Array.isArray(wrapped.ui) ? wrapped.ui : []
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
output: raw,
|
|
186
|
+
ui: []
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
__name(unwrapToolStepOutput, "unwrapToolStepOutput");
|
|
190
|
+
function createUiCollector(toolCallId, write) {
|
|
191
|
+
const pushed = /* @__PURE__ */ new Map();
|
|
192
|
+
let next = 0;
|
|
193
|
+
const emit2 = /* @__PURE__ */ __name(async (component, props, options = {}) => {
|
|
194
|
+
if (typeof component !== "string" || component.length === 0) {
|
|
195
|
+
throw new Error("emitUi: component must be a non-empty string");
|
|
196
|
+
}
|
|
197
|
+
if (typeof props !== "object" || props === null || Array.isArray(props)) {
|
|
198
|
+
throw new Error("emitUi: props must be a JSON object");
|
|
199
|
+
}
|
|
200
|
+
let id = options.id;
|
|
201
|
+
if (id === void 0) {
|
|
202
|
+
id = `${toolCallId}:ui:${next}`;
|
|
203
|
+
next += 1;
|
|
204
|
+
}
|
|
205
|
+
const entry = {
|
|
206
|
+
id,
|
|
207
|
+
component,
|
|
208
|
+
// Snapshot: the frame and the persisted value are what the tool pushed at THIS moment, not
|
|
209
|
+
// whatever the object it handed over looks like when the step settles.
|
|
210
|
+
props: JSON.parse(JSON.stringify(props)),
|
|
211
|
+
...options.version !== void 0 ? {
|
|
212
|
+
version: options.version
|
|
213
|
+
} : {},
|
|
214
|
+
toolCallId
|
|
215
|
+
};
|
|
216
|
+
pushed.set(id, entry);
|
|
217
|
+
await write?.({
|
|
218
|
+
kind: "ui",
|
|
219
|
+
...entry
|
|
220
|
+
});
|
|
221
|
+
return {
|
|
222
|
+
id
|
|
223
|
+
};
|
|
224
|
+
}, "emit");
|
|
225
|
+
return {
|
|
226
|
+
emit: emit2,
|
|
227
|
+
components: /* @__PURE__ */ __name(() => [
|
|
228
|
+
...pushed.values()
|
|
229
|
+
], "components"),
|
|
230
|
+
restart: /* @__PURE__ */ __name(() => {
|
|
231
|
+
next = 0;
|
|
232
|
+
}, "restart")
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
__name(createUiCollector, "createUiCollector");
|
|
236
|
+
function mergeUi(...lists) {
|
|
237
|
+
const merged = /* @__PURE__ */ new Map();
|
|
238
|
+
for (const list of lists) {
|
|
239
|
+
for (const component of list ?? []) {
|
|
240
|
+
merged.set(component.id, component);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return [
|
|
244
|
+
...merged.values()
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
__name(mergeUi, "mergeUi");
|
|
248
|
+
|
|
160
249
|
// src/spi/pricing-store.ts
|
|
161
250
|
async function seedModelPrices(store, prices) {
|
|
162
251
|
for (const price of prices) {
|
|
@@ -831,11 +920,235 @@ ${detail}`;
|
|
|
831
920
|
}
|
|
832
921
|
__name(repairInstruction, "repairInstruction");
|
|
833
922
|
|
|
834
|
-
// src/elicitation.ts
|
|
835
|
-
|
|
836
|
-
|
|
923
|
+
// src/elicitation-input.ts
|
|
924
|
+
var ELICITATION_INPUT_TYPES = [
|
|
925
|
+
"text",
|
|
926
|
+
"textarea",
|
|
927
|
+
"number",
|
|
928
|
+
"boolean",
|
|
929
|
+
"date",
|
|
930
|
+
"email",
|
|
931
|
+
"url",
|
|
932
|
+
"select"
|
|
933
|
+
];
|
|
934
|
+
function questionOptions(question) {
|
|
935
|
+
return question.options ?? [];
|
|
936
|
+
}
|
|
937
|
+
__name(questionOptions, "questionOptions");
|
|
938
|
+
function isTypedQuestion(question) {
|
|
939
|
+
return question.input !== void 0 && question.input.type !== "select";
|
|
940
|
+
}
|
|
941
|
+
__name(isTypedQuestion, "isTypedQuestion");
|
|
942
|
+
var DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
943
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
944
|
+
var NUMBER_PATTERN = /^-?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
|
|
945
|
+
function isDate(value) {
|
|
946
|
+
const match = DATE_PATTERN.exec(value);
|
|
947
|
+
if (match === null) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
const [, year, month, day] = match;
|
|
951
|
+
const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day)));
|
|
952
|
+
return date.getUTCFullYear() === Number(year) && date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day);
|
|
953
|
+
}
|
|
954
|
+
__name(isDate, "isDate");
|
|
955
|
+
function isHttpUrl(value) {
|
|
956
|
+
try {
|
|
957
|
+
const url = new URL(value);
|
|
958
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
959
|
+
} catch {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
__name(isHttpUrl, "isHttpUrl");
|
|
964
|
+
function fullMatch(pattern, value) {
|
|
965
|
+
try {
|
|
966
|
+
return new RegExp(`^(?:${pattern})$`, "u").test(value);
|
|
967
|
+
} catch {
|
|
968
|
+
return void 0;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
__name(fullMatch, "fullMatch");
|
|
972
|
+
function asNumber(bound) {
|
|
973
|
+
if (typeof bound === "number" && Number.isFinite(bound)) {
|
|
974
|
+
return bound;
|
|
975
|
+
}
|
|
976
|
+
if (typeof bound === "string" && NUMBER_PATTERN.test(bound.trim())) {
|
|
977
|
+
return Number(bound);
|
|
978
|
+
}
|
|
979
|
+
return void 0;
|
|
980
|
+
}
|
|
981
|
+
__name(asNumber, "asNumber");
|
|
982
|
+
function validateElicitationValue(question, value) {
|
|
983
|
+
const input = question.input;
|
|
984
|
+
if (input === void 0 || input.type === "select") {
|
|
985
|
+
if (question.allowFreeText === true) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
return questionOptions(question).some((option) => option.value === value) ? null : "must be one of the offered options";
|
|
989
|
+
}
|
|
990
|
+
switch (input.type) {
|
|
991
|
+
case "number": {
|
|
992
|
+
if (!NUMBER_PATTERN.test(value.trim()) || !Number.isFinite(Number(value))) {
|
|
993
|
+
return "must be a number";
|
|
994
|
+
}
|
|
995
|
+
const number = Number(value);
|
|
996
|
+
const min = asNumber(input.min);
|
|
997
|
+
const max = asNumber(input.max);
|
|
998
|
+
if (min !== void 0 && number < min) {
|
|
999
|
+
return `must be at least ${min}`;
|
|
1000
|
+
}
|
|
1001
|
+
if (max !== void 0 && number > max) {
|
|
1002
|
+
return `must be at most ${max}`;
|
|
1003
|
+
}
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
case "boolean":
|
|
1007
|
+
return value === "true" || value === "false" ? null : 'must be "true" or "false"';
|
|
1008
|
+
case "date": {
|
|
1009
|
+
if (!isDate(value)) {
|
|
1010
|
+
return "must be a date as YYYY-MM-DD";
|
|
1011
|
+
}
|
|
1012
|
+
if (typeof input.min === "string" && isDate(input.min) && value < input.min) {
|
|
1013
|
+
return `must be on or after ${input.min}`;
|
|
1014
|
+
}
|
|
1015
|
+
if (typeof input.max === "string" && isDate(input.max) && value > input.max) {
|
|
1016
|
+
return `must be on or before ${input.max}`;
|
|
1017
|
+
}
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
case "email":
|
|
1021
|
+
if (!EMAIL_PATTERN.test(value)) {
|
|
1022
|
+
return "must be an email address";
|
|
1023
|
+
}
|
|
1024
|
+
break;
|
|
1025
|
+
case "url":
|
|
1026
|
+
if (!isHttpUrl(value)) {
|
|
1027
|
+
return "must be an http(s) URL";
|
|
1028
|
+
}
|
|
1029
|
+
break;
|
|
1030
|
+
case "text":
|
|
1031
|
+
case "textarea": {
|
|
1032
|
+
const min = asNumber(input.min);
|
|
1033
|
+
const max = asNumber(input.max);
|
|
1034
|
+
if (min !== void 0 && value.length < min) {
|
|
1035
|
+
return `must be at least ${min} characters`;
|
|
1036
|
+
}
|
|
1037
|
+
if (max !== void 0 && value.length > max) {
|
|
1038
|
+
return `must be at most ${max} characters`;
|
|
1039
|
+
}
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
if (input.pattern !== void 0 && fullMatch(input.pattern, value) === false) {
|
|
1044
|
+
return "does not match the expected format";
|
|
1045
|
+
}
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
__name(validateElicitationValue, "validateElicitationValue");
|
|
1049
|
+
function validateElicitationAnswer(question, values) {
|
|
1050
|
+
const present = values.filter((value) => value !== "");
|
|
1051
|
+
if (present.length === 0) {
|
|
1052
|
+
return question.input?.required === true ? "requires an answer" : null;
|
|
1053
|
+
}
|
|
1054
|
+
if (question.multiple !== true && present.length > 1) {
|
|
1055
|
+
return "takes a single value";
|
|
1056
|
+
}
|
|
1057
|
+
for (const value of present) {
|
|
1058
|
+
const problem = validateElicitationValue(question, value);
|
|
1059
|
+
if (problem !== null) {
|
|
1060
|
+
return problem;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
__name(validateElicitationAnswer, "validateElicitationAnswer");
|
|
1066
|
+
function readElicitationInput(raw) {
|
|
1067
|
+
if (typeof raw !== "object" || raw === null) {
|
|
1068
|
+
return void 0;
|
|
1069
|
+
}
|
|
1070
|
+
const candidate = raw;
|
|
1071
|
+
const type = candidate.type;
|
|
1072
|
+
if (typeof type !== "string" || !ELICITATION_INPUT_TYPES.includes(type)) {
|
|
1073
|
+
return void 0;
|
|
1074
|
+
}
|
|
1075
|
+
const bound = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isFinite(value) || typeof value === "string" ? value : void 0, "bound");
|
|
1076
|
+
const min = bound(candidate.min);
|
|
1077
|
+
const max = bound(candidate.max);
|
|
1078
|
+
return {
|
|
1079
|
+
type,
|
|
1080
|
+
...typeof candidate.placeholder === "string" ? {
|
|
1081
|
+
placeholder: candidate.placeholder
|
|
1082
|
+
} : {},
|
|
1083
|
+
...candidate.required === true ? {
|
|
1084
|
+
required: true
|
|
1085
|
+
} : {},
|
|
1086
|
+
...min !== void 0 ? {
|
|
1087
|
+
min
|
|
1088
|
+
} : {},
|
|
1089
|
+
...max !== void 0 ? {
|
|
1090
|
+
max
|
|
1091
|
+
} : {},
|
|
1092
|
+
...typeof candidate.pattern === "string" ? {
|
|
1093
|
+
pattern: candidate.pattern
|
|
1094
|
+
} : {}
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
__name(readElicitationInput, "readElicitationInput");
|
|
1098
|
+
function readElicitationQuestions(input) {
|
|
1099
|
+
if (typeof input !== "object" || input === null) {
|
|
1100
|
+
return [];
|
|
1101
|
+
}
|
|
1102
|
+
const raw = input.questions;
|
|
1103
|
+
if (!Array.isArray(raw)) {
|
|
1104
|
+
return [];
|
|
1105
|
+
}
|
|
1106
|
+
const questions = [];
|
|
1107
|
+
for (const item of raw) {
|
|
1108
|
+
if (typeof item !== "object" || item === null) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const candidate = item;
|
|
1112
|
+
if (typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
const options = Array.isArray(candidate.options) ? candidate.options.flatMap((option) => {
|
|
1116
|
+
if (typeof option !== "object" || option === null) {
|
|
1117
|
+
return [];
|
|
1118
|
+
}
|
|
1119
|
+
const { value, label } = option;
|
|
1120
|
+
return typeof value === "string" && typeof label === "string" ? [
|
|
1121
|
+
{
|
|
1122
|
+
value,
|
|
1123
|
+
label
|
|
1124
|
+
}
|
|
1125
|
+
] : [];
|
|
1126
|
+
}) : [];
|
|
1127
|
+
const typed = readElicitationInput(candidate.input);
|
|
1128
|
+
const defaults = Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [];
|
|
1129
|
+
questions.push({
|
|
1130
|
+
id: candidate.id,
|
|
1131
|
+
prompt: candidate.prompt,
|
|
1132
|
+
options,
|
|
1133
|
+
...typed !== void 0 ? {
|
|
1134
|
+
input: typed
|
|
1135
|
+
} : {},
|
|
1136
|
+
...defaults.length > 0 ? {
|
|
1137
|
+
defaults
|
|
1138
|
+
} : {},
|
|
1139
|
+
...candidate.multiple === true ? {
|
|
1140
|
+
multiple: true
|
|
1141
|
+
} : {},
|
|
1142
|
+
...candidate.allowFreeText === true ? {
|
|
1143
|
+
allowFreeText: true
|
|
1144
|
+
} : {}
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
return questions;
|
|
837
1148
|
}
|
|
838
|
-
__name(
|
|
1149
|
+
__name(readElicitationQuestions, "readElicitationQuestions");
|
|
1150
|
+
|
|
1151
|
+
// src/elicitation.ts
|
|
839
1152
|
function normalizeElicitationReply(reply) {
|
|
840
1153
|
if (typeof reply !== "object" || reply === null) {
|
|
841
1154
|
return {
|
|
@@ -871,8 +1184,7 @@ function resolveElicitation(request, raw) {
|
|
|
871
1184
|
defaulted.push(question.id);
|
|
872
1185
|
continue;
|
|
873
1186
|
}
|
|
874
|
-
const
|
|
875
|
-
const valid = question.allowFreeText === true ? submitted : submitted.filter((value) => allowed.has(value));
|
|
1187
|
+
const valid = submitted.filter((value) => value !== "" && validateElicitationValue(question, value) === null);
|
|
876
1188
|
answers[question.id] = question.multiple === true ? valid : valid.slice(0, 1);
|
|
877
1189
|
}
|
|
878
1190
|
return {
|
|
@@ -893,7 +1205,7 @@ __name(settleElicitation, "settleElicitation");
|
|
|
893
1205
|
function renderElicitationAnswers(request, outcome) {
|
|
894
1206
|
const lines = request.questions.map((question) => {
|
|
895
1207
|
const chosen = outcome.answers[question.id] ?? [];
|
|
896
|
-
const labels = chosen.map((value) => question.
|
|
1208
|
+
const labels = chosen.map((value) => questionOptions(question).find((option) => option.value === value)?.label ?? value);
|
|
897
1209
|
return `${question.prompt} \u2192 ${labels.length > 0 ? labels.join(", ") : "(no answer)"}`;
|
|
898
1210
|
});
|
|
899
1211
|
const preface = outcome.skipped ? "The user declined to answer and asked you to proceed on these assumptions:" : "The user answered:";
|
|
@@ -959,7 +1271,23 @@ function parseQuestion(raw, path, issues) {
|
|
|
959
1271
|
], "must be a non-empty string"));
|
|
960
1272
|
return void 0;
|
|
961
1273
|
}
|
|
962
|
-
|
|
1274
|
+
let input;
|
|
1275
|
+
if (candidate.input !== void 0) {
|
|
1276
|
+
input = readElicitationInput(candidate.input);
|
|
1277
|
+
if (input === void 0) {
|
|
1278
|
+
issues.push(issue([
|
|
1279
|
+
...path,
|
|
1280
|
+
"input",
|
|
1281
|
+
"type"
|
|
1282
|
+
], `must be one of ${ELICITATION_INPUT_TYPES.join(", ")}`));
|
|
1283
|
+
return void 0;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const described = typeof candidate.description === "string" && candidate.description.length > 0 ? {
|
|
1287
|
+
description: candidate.description
|
|
1288
|
+
} : {};
|
|
1289
|
+
const typed = input !== void 0 && input.type !== "select";
|
|
1290
|
+
if (!typed && (!Array.isArray(candidate.options) || candidate.options.length === 0)) {
|
|
963
1291
|
issues.push(issue([
|
|
964
1292
|
...path,
|
|
965
1293
|
"options"
|
|
@@ -967,7 +1295,7 @@ function parseQuestion(raw, path, issues) {
|
|
|
967
1295
|
return void 0;
|
|
968
1296
|
}
|
|
969
1297
|
const options = [];
|
|
970
|
-
for (const [index, rawOption] of candidate.options.entries()) {
|
|
1298
|
+
for (const [index, rawOption] of (Array.isArray(candidate.options) ? candidate.options : []).entries()) {
|
|
971
1299
|
const option = parseOption(rawOption, [
|
|
972
1300
|
...path,
|
|
973
1301
|
"options",
|
|
@@ -977,6 +1305,33 @@ function parseQuestion(raw, path, issues) {
|
|
|
977
1305
|
options.push(option);
|
|
978
1306
|
}
|
|
979
1307
|
}
|
|
1308
|
+
if (typed) {
|
|
1309
|
+
const question = {
|
|
1310
|
+
id: candidate.id,
|
|
1311
|
+
prompt: candidate.prompt,
|
|
1312
|
+
...described,
|
|
1313
|
+
...options.length > 0 ? {
|
|
1314
|
+
options
|
|
1315
|
+
} : {},
|
|
1316
|
+
input,
|
|
1317
|
+
...candidate.multiple === true ? {
|
|
1318
|
+
multiple: true
|
|
1319
|
+
} : {}
|
|
1320
|
+
};
|
|
1321
|
+
const offered2 = Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [];
|
|
1322
|
+
const defaults2 = offered2.filter((value) => validateElicitationValue(question, value) === null);
|
|
1323
|
+
if (offered2.length > 0 && defaults2.length === 0) {
|
|
1324
|
+
issues.push(issue([
|
|
1325
|
+
...path,
|
|
1326
|
+
"defaults"
|
|
1327
|
+
], `must be valid ${input.type} values`));
|
|
1328
|
+
return void 0;
|
|
1329
|
+
}
|
|
1330
|
+
return defaults2.length > 0 ? {
|
|
1331
|
+
...question,
|
|
1332
|
+
defaults: defaults2
|
|
1333
|
+
} : question;
|
|
1334
|
+
}
|
|
980
1335
|
if (!Array.isArray(candidate.defaults) || candidate.defaults.length === 0) {
|
|
981
1336
|
issues.push(issue([
|
|
982
1337
|
...path,
|
|
@@ -996,7 +1351,11 @@ function parseQuestion(raw, path, issues) {
|
|
|
996
1351
|
return {
|
|
997
1352
|
id: candidate.id,
|
|
998
1353
|
prompt: candidate.prompt,
|
|
1354
|
+
...described,
|
|
999
1355
|
options,
|
|
1356
|
+
...input !== void 0 ? {
|
|
1357
|
+
input
|
|
1358
|
+
} : {},
|
|
1000
1359
|
defaults,
|
|
1001
1360
|
...candidate.multiple === true ? {
|
|
1002
1361
|
multiple: true
|
|
@@ -1024,9 +1383,7 @@ var ASK_JSON_SCHEMA = {
|
|
|
1024
1383
|
additionalProperties: false,
|
|
1025
1384
|
required: [
|
|
1026
1385
|
"id",
|
|
1027
|
-
"prompt"
|
|
1028
|
-
"options",
|
|
1029
|
-
"defaults"
|
|
1386
|
+
"prompt"
|
|
1030
1387
|
],
|
|
1031
1388
|
properties: {
|
|
1032
1389
|
id: {
|
|
@@ -1036,6 +1393,50 @@ var ASK_JSON_SCHEMA = {
|
|
|
1036
1393
|
prompt: {
|
|
1037
1394
|
type: "string"
|
|
1038
1395
|
},
|
|
1396
|
+
description: {
|
|
1397
|
+
type: "string",
|
|
1398
|
+
description: "A line of help under the prompt."
|
|
1399
|
+
},
|
|
1400
|
+
input: {
|
|
1401
|
+
type: "object",
|
|
1402
|
+
additionalProperties: false,
|
|
1403
|
+
required: [
|
|
1404
|
+
"type"
|
|
1405
|
+
],
|
|
1406
|
+
description: 'Ask for a typed value instead of offering options. Omit for a pick from options. Answers come back as strings: numbers as decimals, booleans as "true"/"false", dates as YYYY-MM-DD.',
|
|
1407
|
+
properties: {
|
|
1408
|
+
type: {
|
|
1409
|
+
type: "string",
|
|
1410
|
+
enum: [
|
|
1411
|
+
...ELICITATION_INPUT_TYPES
|
|
1412
|
+
]
|
|
1413
|
+
},
|
|
1414
|
+
placeholder: {
|
|
1415
|
+
type: "string"
|
|
1416
|
+
},
|
|
1417
|
+
required: {
|
|
1418
|
+
type: "boolean"
|
|
1419
|
+
},
|
|
1420
|
+
min: {
|
|
1421
|
+
type: [
|
|
1422
|
+
"number",
|
|
1423
|
+
"string"
|
|
1424
|
+
],
|
|
1425
|
+
description: "number: minimum; text: minimum length; date: earliest YYYY-MM-DD."
|
|
1426
|
+
},
|
|
1427
|
+
max: {
|
|
1428
|
+
type: [
|
|
1429
|
+
"number",
|
|
1430
|
+
"string"
|
|
1431
|
+
],
|
|
1432
|
+
description: "number: maximum; text: maximum length; date: latest YYYY-MM-DD."
|
|
1433
|
+
},
|
|
1434
|
+
pattern: {
|
|
1435
|
+
type: "string",
|
|
1436
|
+
description: "A regex the whole value must match."
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
},
|
|
1039
1440
|
multiple: {
|
|
1040
1441
|
type: "boolean",
|
|
1041
1442
|
description: "Allow more than one option to be chosen."
|
|
@@ -1050,7 +1451,7 @@ var ASK_JSON_SCHEMA = {
|
|
|
1050
1451
|
items: {
|
|
1051
1452
|
type: "string"
|
|
1052
1453
|
},
|
|
1053
|
-
description: "REQUIRED
|
|
1454
|
+
description: "REQUIRED for a pick from options: the option values you would pick yourself, so the user can confirm rather than decide. For a typed input, the value you would enter, when there is a sensible one."
|
|
1054
1455
|
},
|
|
1055
1456
|
options: {
|
|
1056
1457
|
type: "array",
|
|
@@ -2859,6 +3260,11 @@ async function claimToolCall(turn, call) {
|
|
|
2859
3260
|
...spec?.detached === true ? {
|
|
2860
3261
|
detached: true
|
|
2861
3262
|
} : {},
|
|
3263
|
+
// Same for `terminal`: absent unless declared, so a deployment with no terminal tool writes
|
|
3264
|
+
// the bytes it always has, and a replay reads the branch back instead of re-deciding it.
|
|
3265
|
+
...spec?.terminal === true ? {
|
|
3266
|
+
terminal: true
|
|
3267
|
+
} : {},
|
|
2862
3268
|
...approval !== void 0 ? {
|
|
2863
3269
|
approval
|
|
2864
3270
|
} : {}
|
|
@@ -2880,6 +3286,9 @@ async function claimToolCall(turn, call) {
|
|
|
2880
3286
|
...persisted?.detached === true ? {
|
|
2881
3287
|
detached: true
|
|
2882
3288
|
} : {},
|
|
3289
|
+
...persisted?.terminal === true ? {
|
|
3290
|
+
terminal: true
|
|
3291
|
+
} : {},
|
|
2883
3292
|
...toolType === "action" && persisted?.approval !== void 0 ? {
|
|
2884
3293
|
approval: persisted.approval
|
|
2885
3294
|
} : {},
|
|
@@ -3046,7 +3455,7 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3046
3455
|
if (claimed.toolType === "memory") {
|
|
3047
3456
|
return await rememberIntoTurn(turn, claimed, startedAt);
|
|
3048
3457
|
}
|
|
3049
|
-
let
|
|
3458
|
+
let raw;
|
|
3050
3459
|
if (hooks.dispatchTool) {
|
|
3051
3460
|
const stepCtx = {
|
|
3052
3461
|
actor: input.actor,
|
|
@@ -3069,28 +3478,47 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3069
3478
|
} : {},
|
|
3070
3479
|
// Numeric-only: the handler applies withToolTimeout AND its own local `classify` — see
|
|
3071
3480
|
// ToolStepEnvelope.transientRetry.
|
|
3072
|
-
transientRetry: resolveToolTransientRetryNumbers(deps.toolTransientRetry)
|
|
3481
|
+
transientRetry: resolveToolTransientRetryNumbers(deps.toolTransientRetry),
|
|
3482
|
+
// The handler hands back what the tool pushed through `ctx.emitUi` with its output.
|
|
3483
|
+
collectUi: true
|
|
3073
3484
|
};
|
|
3074
|
-
|
|
3485
|
+
raw = await hooks.dispatchTool(resolvedCall, envelope);
|
|
3075
3486
|
} else {
|
|
3076
|
-
const invocation = hooks.step(`tool:${call.id}`, () =>
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3487
|
+
const invocation = hooks.step(`tool:${call.id}`, async () => {
|
|
3488
|
+
const ui2 = createUiCollector(call.id, (event) => turn.writer.write(encodeStreamEvent(event)));
|
|
3489
|
+
const output2 = await traceToolExecution(hooks.runId, {
|
|
3490
|
+
toolCallId: call.id,
|
|
3491
|
+
toolName: call.name,
|
|
3492
|
+
toolType
|
|
3493
|
+
}, () => invokeWithTransientRetry(() => {
|
|
3494
|
+
ui2.restart();
|
|
3495
|
+
return deps.registry.invoke(call.name, call.input, {
|
|
3496
|
+
...ctx,
|
|
3497
|
+
emitUi: ui2.emit
|
|
3498
|
+
}, deps.rolesPolicy);
|
|
3499
|
+
}, deps.toolTransientRetry ?? {}, {
|
|
3500
|
+
...hooks.isControlFlowError !== void 0 ? {
|
|
3501
|
+
isControlFlowError: hooks.isControlFlowError
|
|
3502
|
+
} : {},
|
|
3503
|
+
onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
|
|
3504
|
+
publishAgentToolRetry({
|
|
3505
|
+
toolName: call.name,
|
|
3506
|
+
toolCallId: call.id,
|
|
3507
|
+
attempt,
|
|
3508
|
+
message: retryError instanceof Error ? retryError.message : String(retryError)
|
|
3509
|
+
});
|
|
3510
|
+
}, "onRetry")
|
|
3511
|
+
}));
|
|
3512
|
+
return wrapToolStepOutput(output2, ui2.components());
|
|
3513
|
+
});
|
|
3514
|
+
raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
|
|
3515
|
+
}
|
|
3516
|
+
const { output, ui } = unwrapToolStepOutput(raw);
|
|
3517
|
+
if (ui.length > 0) {
|
|
3518
|
+
turn.toolUi.set(call.id, ui);
|
|
3519
|
+
}
|
|
3520
|
+
if (claimed.terminal === true) {
|
|
3521
|
+
turn.halt.terminal = true;
|
|
3094
3522
|
}
|
|
3095
3523
|
return {
|
|
3096
3524
|
status: "executed",
|
|
@@ -3988,6 +4416,10 @@ ${block}`;
|
|
|
3988
4416
|
hooks,
|
|
3989
4417
|
messageId: assistant.id,
|
|
3990
4418
|
writer,
|
|
4419
|
+
toolUi: /* @__PURE__ */ new Map(),
|
|
4420
|
+
halt: {
|
|
4421
|
+
terminal: false
|
|
4422
|
+
},
|
|
3991
4423
|
...skillOffer !== void 0 ? {
|
|
3992
4424
|
skills: skillOffer
|
|
3993
4425
|
} : {},
|
|
@@ -4020,6 +4452,10 @@ ${block}`;
|
|
|
4020
4452
|
assistantMessage.toolResults = settledResults;
|
|
4021
4453
|
await hooks.step(`stream:tool-outputs:${i}`, async () => {
|
|
4022
4454
|
await deps.store.setMessageToolResults(assistant.id, settledResults);
|
|
4455
|
+
const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
|
|
4456
|
+
if (toolUi.length > 0) {
|
|
4457
|
+
await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
|
|
4458
|
+
}
|
|
4023
4459
|
for (const result of results) {
|
|
4024
4460
|
await writer.write(encodeStreamEvent(outputFrame(result)));
|
|
4025
4461
|
}
|
|
@@ -4034,6 +4470,9 @@ ${block}`;
|
|
|
4034
4470
|
} : {}
|
|
4035
4471
|
}));
|
|
4036
4472
|
});
|
|
4473
|
+
if (turnCalls.halt.terminal) {
|
|
4474
|
+
break;
|
|
4475
|
+
}
|
|
4037
4476
|
}
|
|
4038
4477
|
if (history.title === "" || history.title === "New chat") {
|
|
4039
4478
|
await hooks.step("persist:title", () => deps.store.setTitle(input.threadId, deriveTitle(input.userText)));
|
|
@@ -4129,6 +4568,7 @@ export {
|
|
|
4129
4568
|
DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
|
|
4130
4569
|
DefaultApprovalPolicy,
|
|
4131
4570
|
DefaultRolesPolicy,
|
|
4571
|
+
ELICITATION_INPUT_TYPES,
|
|
4132
4572
|
GLOBAL_SCOPE,
|
|
4133
4573
|
MAX_ASK_QUESTIONS,
|
|
4134
4574
|
OutputRejectedError,
|
|
@@ -4162,6 +4602,7 @@ export {
|
|
|
4162
4602
|
compositeSkillProvider,
|
|
4163
4603
|
createFrameBuffer,
|
|
4164
4604
|
createIncrementalGate,
|
|
4605
|
+
createUiCollector,
|
|
4165
4606
|
dayBoundsUtc,
|
|
4166
4607
|
decodeStreamEvent,
|
|
4167
4608
|
defaultCanDecide,
|
|
@@ -4184,10 +4625,12 @@ export {
|
|
|
4184
4625
|
isReplayIntegrityError,
|
|
4185
4626
|
isToolEnabled,
|
|
4186
4627
|
isTransientToolError,
|
|
4628
|
+
isTypedQuestion,
|
|
4187
4629
|
loadSkill,
|
|
4188
4630
|
mayDecideApproval,
|
|
4189
4631
|
memoryForgetVerdict,
|
|
4190
4632
|
memoryWriteVerdict,
|
|
4633
|
+
mergeUi,
|
|
4191
4634
|
normalizeDelegation,
|
|
4192
4635
|
normalizeElicitationReply,
|
|
4193
4636
|
observeTurnFrames,
|
|
@@ -4205,6 +4648,9 @@ export {
|
|
|
4205
4648
|
publishAgentSkillsResolved,
|
|
4206
4649
|
publishAgentToolCall,
|
|
4207
4650
|
publishAgentToolRetry,
|
|
4651
|
+
questionOptions,
|
|
4652
|
+
readElicitationInput,
|
|
4653
|
+
readElicitationQuestions,
|
|
4208
4654
|
releaseGatedFrames,
|
|
4209
4655
|
rememberInputSchema,
|
|
4210
4656
|
rememberToolDefinition,
|
|
@@ -4235,6 +4681,9 @@ export {
|
|
|
4235
4681
|
traceLlmTurn,
|
|
4236
4682
|
traceToolExecution,
|
|
4237
4683
|
truncateDetailContent,
|
|
4684
|
+
unwrapToolStepOutput,
|
|
4685
|
+
validateElicitationAnswer,
|
|
4686
|
+
validateElicitationValue,
|
|
4238
4687
|
validateStructured,
|
|
4239
4688
|
windowHistory,
|
|
4240
4689
|
withAskTool,
|
|
@@ -4242,6 +4691,7 @@ export {
|
|
|
4242
4691
|
withSkillTool,
|
|
4243
4692
|
withToolTimeout,
|
|
4244
4693
|
withTurnFrames,
|
|
4694
|
+
wrapToolStepOutput,
|
|
4245
4695
|
writeMemory
|
|
4246
4696
|
};
|
|
4247
4697
|
//# sourceMappingURL=index.js.map
|