@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.cjs
CHANGED
|
@@ -63,6 +63,7 @@ __export(src_exports, {
|
|
|
63
63
|
DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS: () => DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
|
|
64
64
|
DefaultApprovalPolicy: () => DefaultApprovalPolicy,
|
|
65
65
|
DefaultRolesPolicy: () => DefaultRolesPolicy,
|
|
66
|
+
ELICITATION_INPUT_TYPES: () => ELICITATION_INPUT_TYPES,
|
|
66
67
|
GLOBAL_SCOPE: () => GLOBAL_SCOPE,
|
|
67
68
|
MAX_ASK_QUESTIONS: () => MAX_ASK_QUESTIONS,
|
|
68
69
|
OutputRejectedError: () => OutputRejectedError,
|
|
@@ -96,6 +97,7 @@ __export(src_exports, {
|
|
|
96
97
|
compositeSkillProvider: () => compositeSkillProvider,
|
|
97
98
|
createFrameBuffer: () => createFrameBuffer,
|
|
98
99
|
createIncrementalGate: () => createIncrementalGate,
|
|
100
|
+
createUiCollector: () => createUiCollector,
|
|
99
101
|
dayBoundsUtc: () => dayBoundsUtc,
|
|
100
102
|
decodeStreamEvent: () => decodeStreamEvent,
|
|
101
103
|
defaultCanDecide: () => defaultCanDecide,
|
|
@@ -118,10 +120,12 @@ __export(src_exports, {
|
|
|
118
120
|
isReplayIntegrityError: () => isReplayIntegrityError,
|
|
119
121
|
isToolEnabled: () => isToolEnabled,
|
|
120
122
|
isTransientToolError: () => isTransientToolError,
|
|
123
|
+
isTypedQuestion: () => isTypedQuestion,
|
|
121
124
|
loadSkill: () => loadSkill,
|
|
122
125
|
mayDecideApproval: () => mayDecideApproval,
|
|
123
126
|
memoryForgetVerdict: () => memoryForgetVerdict,
|
|
124
127
|
memoryWriteVerdict: () => memoryWriteVerdict,
|
|
128
|
+
mergeUi: () => mergeUi,
|
|
125
129
|
normalizeDelegation: () => normalizeDelegation,
|
|
126
130
|
normalizeElicitationReply: () => normalizeElicitationReply,
|
|
127
131
|
observeTurnFrames: () => observeTurnFrames,
|
|
@@ -139,6 +143,9 @@ __export(src_exports, {
|
|
|
139
143
|
publishAgentSkillsResolved: () => publishAgentSkillsResolved,
|
|
140
144
|
publishAgentToolCall: () => publishAgentToolCall,
|
|
141
145
|
publishAgentToolRetry: () => publishAgentToolRetry,
|
|
146
|
+
questionOptions: () => questionOptions,
|
|
147
|
+
readElicitationInput: () => readElicitationInput,
|
|
148
|
+
readElicitationQuestions: () => readElicitationQuestions,
|
|
142
149
|
releaseGatedFrames: () => releaseGatedFrames,
|
|
143
150
|
rememberInputSchema: () => rememberInputSchema,
|
|
144
151
|
rememberToolDefinition: () => rememberToolDefinition,
|
|
@@ -169,6 +176,9 @@ __export(src_exports, {
|
|
|
169
176
|
traceLlmTurn: () => traceLlmTurn,
|
|
170
177
|
traceToolExecution: () => traceToolExecution,
|
|
171
178
|
truncateDetailContent: () => truncateDetailContent,
|
|
179
|
+
unwrapToolStepOutput: () => unwrapToolStepOutput,
|
|
180
|
+
validateElicitationAnswer: () => validateElicitationAnswer,
|
|
181
|
+
validateElicitationValue: () => validateElicitationValue,
|
|
172
182
|
validateStructured: () => validateStructured,
|
|
173
183
|
windowHistory: () => windowHistory,
|
|
174
184
|
withAskTool: () => withAskTool,
|
|
@@ -176,6 +186,7 @@ __export(src_exports, {
|
|
|
176
186
|
withSkillTool: () => withSkillTool,
|
|
177
187
|
withToolTimeout: () => withToolTimeout,
|
|
178
188
|
withTurnFrames: () => withTurnFrames,
|
|
189
|
+
wrapToolStepOutput: () => wrapToolStepOutput,
|
|
179
190
|
writeMemory: () => writeMemory
|
|
180
191
|
});
|
|
181
192
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -336,6 +347,95 @@ function withTurnFrames(result, frames) {
|
|
|
336
347
|
}
|
|
337
348
|
__name(withTurnFrames, "withTurnFrames");
|
|
338
349
|
|
|
350
|
+
// src/tool-ui.ts
|
|
351
|
+
var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
|
|
352
|
+
function wrapToolStepOutput(output, ui) {
|
|
353
|
+
if (ui.length === 0) {
|
|
354
|
+
return output;
|
|
355
|
+
}
|
|
356
|
+
const wrapped = {
|
|
357
|
+
[TOOL_STEP_UI]: 1,
|
|
358
|
+
output,
|
|
359
|
+
ui: [
|
|
360
|
+
...ui
|
|
361
|
+
]
|
|
362
|
+
};
|
|
363
|
+
return wrapped;
|
|
364
|
+
}
|
|
365
|
+
__name(wrapToolStepOutput, "wrapToolStepOutput");
|
|
366
|
+
function unwrapToolStepOutput(raw) {
|
|
367
|
+
if (typeof raw === "object" && raw !== null && raw[TOOL_STEP_UI] === 1) {
|
|
368
|
+
const wrapped = raw;
|
|
369
|
+
return {
|
|
370
|
+
output: wrapped.output,
|
|
371
|
+
ui: Array.isArray(wrapped.ui) ? wrapped.ui : []
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
output: raw,
|
|
376
|
+
ui: []
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
__name(unwrapToolStepOutput, "unwrapToolStepOutput");
|
|
380
|
+
function createUiCollector(toolCallId, write) {
|
|
381
|
+
const pushed = /* @__PURE__ */ new Map();
|
|
382
|
+
let next = 0;
|
|
383
|
+
const emit2 = /* @__PURE__ */ __name(async (component, props, options = {}) => {
|
|
384
|
+
if (typeof component !== "string" || component.length === 0) {
|
|
385
|
+
throw new Error("emitUi: component must be a non-empty string");
|
|
386
|
+
}
|
|
387
|
+
if (typeof props !== "object" || props === null || Array.isArray(props)) {
|
|
388
|
+
throw new Error("emitUi: props must be a JSON object");
|
|
389
|
+
}
|
|
390
|
+
let id = options.id;
|
|
391
|
+
if (id === void 0) {
|
|
392
|
+
id = `${toolCallId}:ui:${next}`;
|
|
393
|
+
next += 1;
|
|
394
|
+
}
|
|
395
|
+
const entry = {
|
|
396
|
+
id,
|
|
397
|
+
component,
|
|
398
|
+
// Snapshot: the frame and the persisted value are what the tool pushed at THIS moment, not
|
|
399
|
+
// whatever the object it handed over looks like when the step settles.
|
|
400
|
+
props: JSON.parse(JSON.stringify(props)),
|
|
401
|
+
...options.version !== void 0 ? {
|
|
402
|
+
version: options.version
|
|
403
|
+
} : {},
|
|
404
|
+
toolCallId
|
|
405
|
+
};
|
|
406
|
+
pushed.set(id, entry);
|
|
407
|
+
await write?.({
|
|
408
|
+
kind: "ui",
|
|
409
|
+
...entry
|
|
410
|
+
});
|
|
411
|
+
return {
|
|
412
|
+
id
|
|
413
|
+
};
|
|
414
|
+
}, "emit");
|
|
415
|
+
return {
|
|
416
|
+
emit: emit2,
|
|
417
|
+
components: /* @__PURE__ */ __name(() => [
|
|
418
|
+
...pushed.values()
|
|
419
|
+
], "components"),
|
|
420
|
+
restart: /* @__PURE__ */ __name(() => {
|
|
421
|
+
next = 0;
|
|
422
|
+
}, "restart")
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
__name(createUiCollector, "createUiCollector");
|
|
426
|
+
function mergeUi(...lists) {
|
|
427
|
+
const merged = /* @__PURE__ */ new Map();
|
|
428
|
+
for (const list of lists) {
|
|
429
|
+
for (const component of list ?? []) {
|
|
430
|
+
merged.set(component.id, component);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return [
|
|
434
|
+
...merged.values()
|
|
435
|
+
];
|
|
436
|
+
}
|
|
437
|
+
__name(mergeUi, "mergeUi");
|
|
438
|
+
|
|
339
439
|
// src/spi/pricing-store.ts
|
|
340
440
|
async function seedModelPrices(store, prices) {
|
|
341
441
|
for (const price of prices) {
|
|
@@ -1010,11 +1110,235 @@ ${detail}`;
|
|
|
1010
1110
|
}
|
|
1011
1111
|
__name(repairInstruction, "repairInstruction");
|
|
1012
1112
|
|
|
1013
|
-
// src/elicitation.ts
|
|
1014
|
-
|
|
1015
|
-
|
|
1113
|
+
// src/elicitation-input.ts
|
|
1114
|
+
var ELICITATION_INPUT_TYPES = [
|
|
1115
|
+
"text",
|
|
1116
|
+
"textarea",
|
|
1117
|
+
"number",
|
|
1118
|
+
"boolean",
|
|
1119
|
+
"date",
|
|
1120
|
+
"email",
|
|
1121
|
+
"url",
|
|
1122
|
+
"select"
|
|
1123
|
+
];
|
|
1124
|
+
function questionOptions(question) {
|
|
1125
|
+
return question.options ?? [];
|
|
1126
|
+
}
|
|
1127
|
+
__name(questionOptions, "questionOptions");
|
|
1128
|
+
function isTypedQuestion(question) {
|
|
1129
|
+
return question.input !== void 0 && question.input.type !== "select";
|
|
1130
|
+
}
|
|
1131
|
+
__name(isTypedQuestion, "isTypedQuestion");
|
|
1132
|
+
var DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
1133
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1134
|
+
var NUMBER_PATTERN = /^-?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
|
|
1135
|
+
function isDate(value) {
|
|
1136
|
+
const match = DATE_PATTERN.exec(value);
|
|
1137
|
+
if (match === null) {
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
const [, year, month, day] = match;
|
|
1141
|
+
const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day)));
|
|
1142
|
+
return date.getUTCFullYear() === Number(year) && date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day);
|
|
1143
|
+
}
|
|
1144
|
+
__name(isDate, "isDate");
|
|
1145
|
+
function isHttpUrl(value) {
|
|
1146
|
+
try {
|
|
1147
|
+
const url = new URL(value);
|
|
1148
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
1149
|
+
} catch {
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
__name(isHttpUrl, "isHttpUrl");
|
|
1154
|
+
function fullMatch(pattern, value) {
|
|
1155
|
+
try {
|
|
1156
|
+
return new RegExp(`^(?:${pattern})$`, "u").test(value);
|
|
1157
|
+
} catch {
|
|
1158
|
+
return void 0;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
__name(fullMatch, "fullMatch");
|
|
1162
|
+
function asNumber(bound) {
|
|
1163
|
+
if (typeof bound === "number" && Number.isFinite(bound)) {
|
|
1164
|
+
return bound;
|
|
1165
|
+
}
|
|
1166
|
+
if (typeof bound === "string" && NUMBER_PATTERN.test(bound.trim())) {
|
|
1167
|
+
return Number(bound);
|
|
1168
|
+
}
|
|
1169
|
+
return void 0;
|
|
1170
|
+
}
|
|
1171
|
+
__name(asNumber, "asNumber");
|
|
1172
|
+
function validateElicitationValue(question, value) {
|
|
1173
|
+
const input = question.input;
|
|
1174
|
+
if (input === void 0 || input.type === "select") {
|
|
1175
|
+
if (question.allowFreeText === true) {
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
return questionOptions(question).some((option) => option.value === value) ? null : "must be one of the offered options";
|
|
1179
|
+
}
|
|
1180
|
+
switch (input.type) {
|
|
1181
|
+
case "number": {
|
|
1182
|
+
if (!NUMBER_PATTERN.test(value.trim()) || !Number.isFinite(Number(value))) {
|
|
1183
|
+
return "must be a number";
|
|
1184
|
+
}
|
|
1185
|
+
const number = Number(value);
|
|
1186
|
+
const min = asNumber(input.min);
|
|
1187
|
+
const max = asNumber(input.max);
|
|
1188
|
+
if (min !== void 0 && number < min) {
|
|
1189
|
+
return `must be at least ${min}`;
|
|
1190
|
+
}
|
|
1191
|
+
if (max !== void 0 && number > max) {
|
|
1192
|
+
return `must be at most ${max}`;
|
|
1193
|
+
}
|
|
1194
|
+
return null;
|
|
1195
|
+
}
|
|
1196
|
+
case "boolean":
|
|
1197
|
+
return value === "true" || value === "false" ? null : 'must be "true" or "false"';
|
|
1198
|
+
case "date": {
|
|
1199
|
+
if (!isDate(value)) {
|
|
1200
|
+
return "must be a date as YYYY-MM-DD";
|
|
1201
|
+
}
|
|
1202
|
+
if (typeof input.min === "string" && isDate(input.min) && value < input.min) {
|
|
1203
|
+
return `must be on or after ${input.min}`;
|
|
1204
|
+
}
|
|
1205
|
+
if (typeof input.max === "string" && isDate(input.max) && value > input.max) {
|
|
1206
|
+
return `must be on or before ${input.max}`;
|
|
1207
|
+
}
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
case "email":
|
|
1211
|
+
if (!EMAIL_PATTERN.test(value)) {
|
|
1212
|
+
return "must be an email address";
|
|
1213
|
+
}
|
|
1214
|
+
break;
|
|
1215
|
+
case "url":
|
|
1216
|
+
if (!isHttpUrl(value)) {
|
|
1217
|
+
return "must be an http(s) URL";
|
|
1218
|
+
}
|
|
1219
|
+
break;
|
|
1220
|
+
case "text":
|
|
1221
|
+
case "textarea": {
|
|
1222
|
+
const min = asNumber(input.min);
|
|
1223
|
+
const max = asNumber(input.max);
|
|
1224
|
+
if (min !== void 0 && value.length < min) {
|
|
1225
|
+
return `must be at least ${min} characters`;
|
|
1226
|
+
}
|
|
1227
|
+
if (max !== void 0 && value.length > max) {
|
|
1228
|
+
return `must be at most ${max} characters`;
|
|
1229
|
+
}
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (input.pattern !== void 0 && fullMatch(input.pattern, value) === false) {
|
|
1234
|
+
return "does not match the expected format";
|
|
1235
|
+
}
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
__name(validateElicitationValue, "validateElicitationValue");
|
|
1239
|
+
function validateElicitationAnswer(question, values) {
|
|
1240
|
+
const present = values.filter((value) => value !== "");
|
|
1241
|
+
if (present.length === 0) {
|
|
1242
|
+
return question.input?.required === true ? "requires an answer" : null;
|
|
1243
|
+
}
|
|
1244
|
+
if (question.multiple !== true && present.length > 1) {
|
|
1245
|
+
return "takes a single value";
|
|
1246
|
+
}
|
|
1247
|
+
for (const value of present) {
|
|
1248
|
+
const problem = validateElicitationValue(question, value);
|
|
1249
|
+
if (problem !== null) {
|
|
1250
|
+
return problem;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
__name(validateElicitationAnswer, "validateElicitationAnswer");
|
|
1256
|
+
function readElicitationInput(raw) {
|
|
1257
|
+
if (typeof raw !== "object" || raw === null) {
|
|
1258
|
+
return void 0;
|
|
1259
|
+
}
|
|
1260
|
+
const candidate = raw;
|
|
1261
|
+
const type = candidate.type;
|
|
1262
|
+
if (typeof type !== "string" || !ELICITATION_INPUT_TYPES.includes(type)) {
|
|
1263
|
+
return void 0;
|
|
1264
|
+
}
|
|
1265
|
+
const bound = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isFinite(value) || typeof value === "string" ? value : void 0, "bound");
|
|
1266
|
+
const min = bound(candidate.min);
|
|
1267
|
+
const max = bound(candidate.max);
|
|
1268
|
+
return {
|
|
1269
|
+
type,
|
|
1270
|
+
...typeof candidate.placeholder === "string" ? {
|
|
1271
|
+
placeholder: candidate.placeholder
|
|
1272
|
+
} : {},
|
|
1273
|
+
...candidate.required === true ? {
|
|
1274
|
+
required: true
|
|
1275
|
+
} : {},
|
|
1276
|
+
...min !== void 0 ? {
|
|
1277
|
+
min
|
|
1278
|
+
} : {},
|
|
1279
|
+
...max !== void 0 ? {
|
|
1280
|
+
max
|
|
1281
|
+
} : {},
|
|
1282
|
+
...typeof candidate.pattern === "string" ? {
|
|
1283
|
+
pattern: candidate.pattern
|
|
1284
|
+
} : {}
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
__name(readElicitationInput, "readElicitationInput");
|
|
1288
|
+
function readElicitationQuestions(input) {
|
|
1289
|
+
if (typeof input !== "object" || input === null) {
|
|
1290
|
+
return [];
|
|
1291
|
+
}
|
|
1292
|
+
const raw = input.questions;
|
|
1293
|
+
if (!Array.isArray(raw)) {
|
|
1294
|
+
return [];
|
|
1295
|
+
}
|
|
1296
|
+
const questions = [];
|
|
1297
|
+
for (const item of raw) {
|
|
1298
|
+
if (typeof item !== "object" || item === null) {
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
const candidate = item;
|
|
1302
|
+
if (typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
const options = Array.isArray(candidate.options) ? candidate.options.flatMap((option) => {
|
|
1306
|
+
if (typeof option !== "object" || option === null) {
|
|
1307
|
+
return [];
|
|
1308
|
+
}
|
|
1309
|
+
const { value, label } = option;
|
|
1310
|
+
return typeof value === "string" && typeof label === "string" ? [
|
|
1311
|
+
{
|
|
1312
|
+
value,
|
|
1313
|
+
label
|
|
1314
|
+
}
|
|
1315
|
+
] : [];
|
|
1316
|
+
}) : [];
|
|
1317
|
+
const typed = readElicitationInput(candidate.input);
|
|
1318
|
+
const defaults = Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [];
|
|
1319
|
+
questions.push({
|
|
1320
|
+
id: candidate.id,
|
|
1321
|
+
prompt: candidate.prompt,
|
|
1322
|
+
options,
|
|
1323
|
+
...typed !== void 0 ? {
|
|
1324
|
+
input: typed
|
|
1325
|
+
} : {},
|
|
1326
|
+
...defaults.length > 0 ? {
|
|
1327
|
+
defaults
|
|
1328
|
+
} : {},
|
|
1329
|
+
...candidate.multiple === true ? {
|
|
1330
|
+
multiple: true
|
|
1331
|
+
} : {},
|
|
1332
|
+
...candidate.allowFreeText === true ? {
|
|
1333
|
+
allowFreeText: true
|
|
1334
|
+
} : {}
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
return questions;
|
|
1016
1338
|
}
|
|
1017
|
-
__name(
|
|
1339
|
+
__name(readElicitationQuestions, "readElicitationQuestions");
|
|
1340
|
+
|
|
1341
|
+
// src/elicitation.ts
|
|
1018
1342
|
function normalizeElicitationReply(reply) {
|
|
1019
1343
|
if (typeof reply !== "object" || reply === null) {
|
|
1020
1344
|
return {
|
|
@@ -1050,8 +1374,7 @@ function resolveElicitation(request, raw) {
|
|
|
1050
1374
|
defaulted.push(question.id);
|
|
1051
1375
|
continue;
|
|
1052
1376
|
}
|
|
1053
|
-
const
|
|
1054
|
-
const valid = question.allowFreeText === true ? submitted : submitted.filter((value) => allowed.has(value));
|
|
1377
|
+
const valid = submitted.filter((value) => value !== "" && validateElicitationValue(question, value) === null);
|
|
1055
1378
|
answers[question.id] = question.multiple === true ? valid : valid.slice(0, 1);
|
|
1056
1379
|
}
|
|
1057
1380
|
return {
|
|
@@ -1072,7 +1395,7 @@ __name(settleElicitation, "settleElicitation");
|
|
|
1072
1395
|
function renderElicitationAnswers(request, outcome) {
|
|
1073
1396
|
const lines = request.questions.map((question) => {
|
|
1074
1397
|
const chosen = outcome.answers[question.id] ?? [];
|
|
1075
|
-
const labels = chosen.map((value) => question.
|
|
1398
|
+
const labels = chosen.map((value) => questionOptions(question).find((option) => option.value === value)?.label ?? value);
|
|
1076
1399
|
return `${question.prompt} \u2192 ${labels.length > 0 ? labels.join(", ") : "(no answer)"}`;
|
|
1077
1400
|
});
|
|
1078
1401
|
const preface = outcome.skipped ? "The user declined to answer and asked you to proceed on these assumptions:" : "The user answered:";
|
|
@@ -1138,7 +1461,23 @@ function parseQuestion(raw, path, issues) {
|
|
|
1138
1461
|
], "must be a non-empty string"));
|
|
1139
1462
|
return void 0;
|
|
1140
1463
|
}
|
|
1141
|
-
|
|
1464
|
+
let input;
|
|
1465
|
+
if (candidate.input !== void 0) {
|
|
1466
|
+
input = readElicitationInput(candidate.input);
|
|
1467
|
+
if (input === void 0) {
|
|
1468
|
+
issues.push(issue([
|
|
1469
|
+
...path,
|
|
1470
|
+
"input",
|
|
1471
|
+
"type"
|
|
1472
|
+
], `must be one of ${ELICITATION_INPUT_TYPES.join(", ")}`));
|
|
1473
|
+
return void 0;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
const described = typeof candidate.description === "string" && candidate.description.length > 0 ? {
|
|
1477
|
+
description: candidate.description
|
|
1478
|
+
} : {};
|
|
1479
|
+
const typed = input !== void 0 && input.type !== "select";
|
|
1480
|
+
if (!typed && (!Array.isArray(candidate.options) || candidate.options.length === 0)) {
|
|
1142
1481
|
issues.push(issue([
|
|
1143
1482
|
...path,
|
|
1144
1483
|
"options"
|
|
@@ -1146,7 +1485,7 @@ function parseQuestion(raw, path, issues) {
|
|
|
1146
1485
|
return void 0;
|
|
1147
1486
|
}
|
|
1148
1487
|
const options = [];
|
|
1149
|
-
for (const [index, rawOption] of candidate.options.entries()) {
|
|
1488
|
+
for (const [index, rawOption] of (Array.isArray(candidate.options) ? candidate.options : []).entries()) {
|
|
1150
1489
|
const option = parseOption(rawOption, [
|
|
1151
1490
|
...path,
|
|
1152
1491
|
"options",
|
|
@@ -1156,6 +1495,33 @@ function parseQuestion(raw, path, issues) {
|
|
|
1156
1495
|
options.push(option);
|
|
1157
1496
|
}
|
|
1158
1497
|
}
|
|
1498
|
+
if (typed) {
|
|
1499
|
+
const question = {
|
|
1500
|
+
id: candidate.id,
|
|
1501
|
+
prompt: candidate.prompt,
|
|
1502
|
+
...described,
|
|
1503
|
+
...options.length > 0 ? {
|
|
1504
|
+
options
|
|
1505
|
+
} : {},
|
|
1506
|
+
input,
|
|
1507
|
+
...candidate.multiple === true ? {
|
|
1508
|
+
multiple: true
|
|
1509
|
+
} : {}
|
|
1510
|
+
};
|
|
1511
|
+
const offered2 = Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [];
|
|
1512
|
+
const defaults2 = offered2.filter((value) => validateElicitationValue(question, value) === null);
|
|
1513
|
+
if (offered2.length > 0 && defaults2.length === 0) {
|
|
1514
|
+
issues.push(issue([
|
|
1515
|
+
...path,
|
|
1516
|
+
"defaults"
|
|
1517
|
+
], `must be valid ${input.type} values`));
|
|
1518
|
+
return void 0;
|
|
1519
|
+
}
|
|
1520
|
+
return defaults2.length > 0 ? {
|
|
1521
|
+
...question,
|
|
1522
|
+
defaults: defaults2
|
|
1523
|
+
} : question;
|
|
1524
|
+
}
|
|
1159
1525
|
if (!Array.isArray(candidate.defaults) || candidate.defaults.length === 0) {
|
|
1160
1526
|
issues.push(issue([
|
|
1161
1527
|
...path,
|
|
@@ -1175,7 +1541,11 @@ function parseQuestion(raw, path, issues) {
|
|
|
1175
1541
|
return {
|
|
1176
1542
|
id: candidate.id,
|
|
1177
1543
|
prompt: candidate.prompt,
|
|
1544
|
+
...described,
|
|
1178
1545
|
options,
|
|
1546
|
+
...input !== void 0 ? {
|
|
1547
|
+
input
|
|
1548
|
+
} : {},
|
|
1179
1549
|
defaults,
|
|
1180
1550
|
...candidate.multiple === true ? {
|
|
1181
1551
|
multiple: true
|
|
@@ -1203,9 +1573,7 @@ var ASK_JSON_SCHEMA = {
|
|
|
1203
1573
|
additionalProperties: false,
|
|
1204
1574
|
required: [
|
|
1205
1575
|
"id",
|
|
1206
|
-
"prompt"
|
|
1207
|
-
"options",
|
|
1208
|
-
"defaults"
|
|
1576
|
+
"prompt"
|
|
1209
1577
|
],
|
|
1210
1578
|
properties: {
|
|
1211
1579
|
id: {
|
|
@@ -1215,6 +1583,50 @@ var ASK_JSON_SCHEMA = {
|
|
|
1215
1583
|
prompt: {
|
|
1216
1584
|
type: "string"
|
|
1217
1585
|
},
|
|
1586
|
+
description: {
|
|
1587
|
+
type: "string",
|
|
1588
|
+
description: "A line of help under the prompt."
|
|
1589
|
+
},
|
|
1590
|
+
input: {
|
|
1591
|
+
type: "object",
|
|
1592
|
+
additionalProperties: false,
|
|
1593
|
+
required: [
|
|
1594
|
+
"type"
|
|
1595
|
+
],
|
|
1596
|
+
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.',
|
|
1597
|
+
properties: {
|
|
1598
|
+
type: {
|
|
1599
|
+
type: "string",
|
|
1600
|
+
enum: [
|
|
1601
|
+
...ELICITATION_INPUT_TYPES
|
|
1602
|
+
]
|
|
1603
|
+
},
|
|
1604
|
+
placeholder: {
|
|
1605
|
+
type: "string"
|
|
1606
|
+
},
|
|
1607
|
+
required: {
|
|
1608
|
+
type: "boolean"
|
|
1609
|
+
},
|
|
1610
|
+
min: {
|
|
1611
|
+
type: [
|
|
1612
|
+
"number",
|
|
1613
|
+
"string"
|
|
1614
|
+
],
|
|
1615
|
+
description: "number: minimum; text: minimum length; date: earliest YYYY-MM-DD."
|
|
1616
|
+
},
|
|
1617
|
+
max: {
|
|
1618
|
+
type: [
|
|
1619
|
+
"number",
|
|
1620
|
+
"string"
|
|
1621
|
+
],
|
|
1622
|
+
description: "number: maximum; text: maximum length; date: latest YYYY-MM-DD."
|
|
1623
|
+
},
|
|
1624
|
+
pattern: {
|
|
1625
|
+
type: "string",
|
|
1626
|
+
description: "A regex the whole value must match."
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
},
|
|
1218
1630
|
multiple: {
|
|
1219
1631
|
type: "boolean",
|
|
1220
1632
|
description: "Allow more than one option to be chosen."
|
|
@@ -1229,7 +1641,7 @@ var ASK_JSON_SCHEMA = {
|
|
|
1229
1641
|
items: {
|
|
1230
1642
|
type: "string"
|
|
1231
1643
|
},
|
|
1232
|
-
description: "REQUIRED
|
|
1644
|
+
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."
|
|
1233
1645
|
},
|
|
1234
1646
|
options: {
|
|
1235
1647
|
type: "array",
|
|
@@ -3038,6 +3450,11 @@ async function claimToolCall(turn, call) {
|
|
|
3038
3450
|
...spec?.detached === true ? {
|
|
3039
3451
|
detached: true
|
|
3040
3452
|
} : {},
|
|
3453
|
+
// Same for `terminal`: absent unless declared, so a deployment with no terminal tool writes
|
|
3454
|
+
// the bytes it always has, and a replay reads the branch back instead of re-deciding it.
|
|
3455
|
+
...spec?.terminal === true ? {
|
|
3456
|
+
terminal: true
|
|
3457
|
+
} : {},
|
|
3041
3458
|
...approval !== void 0 ? {
|
|
3042
3459
|
approval
|
|
3043
3460
|
} : {}
|
|
@@ -3059,6 +3476,9 @@ async function claimToolCall(turn, call) {
|
|
|
3059
3476
|
...persisted?.detached === true ? {
|
|
3060
3477
|
detached: true
|
|
3061
3478
|
} : {},
|
|
3479
|
+
...persisted?.terminal === true ? {
|
|
3480
|
+
terminal: true
|
|
3481
|
+
} : {},
|
|
3062
3482
|
...toolType === "action" && persisted?.approval !== void 0 ? {
|
|
3063
3483
|
approval: persisted.approval
|
|
3064
3484
|
} : {},
|
|
@@ -3225,7 +3645,7 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3225
3645
|
if (claimed.toolType === "memory") {
|
|
3226
3646
|
return await rememberIntoTurn(turn, claimed, startedAt);
|
|
3227
3647
|
}
|
|
3228
|
-
let
|
|
3648
|
+
let raw;
|
|
3229
3649
|
if (hooks.dispatchTool) {
|
|
3230
3650
|
const stepCtx = {
|
|
3231
3651
|
actor: input.actor,
|
|
@@ -3248,28 +3668,47 @@ async function invokeClaimedTool(turn, claimed) {
|
|
|
3248
3668
|
} : {},
|
|
3249
3669
|
// Numeric-only: the handler applies withToolTimeout AND its own local `classify` — see
|
|
3250
3670
|
// ToolStepEnvelope.transientRetry.
|
|
3251
|
-
transientRetry: resolveToolTransientRetryNumbers(deps.toolTransientRetry)
|
|
3671
|
+
transientRetry: resolveToolTransientRetryNumbers(deps.toolTransientRetry),
|
|
3672
|
+
// The handler hands back what the tool pushed through `ctx.emitUi` with its output.
|
|
3673
|
+
collectUi: true
|
|
3252
3674
|
};
|
|
3253
|
-
|
|
3675
|
+
raw = await hooks.dispatchTool(resolvedCall, envelope);
|
|
3254
3676
|
} else {
|
|
3255
|
-
const invocation = hooks.step(`tool:${call.id}`, () =>
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3677
|
+
const invocation = hooks.step(`tool:${call.id}`, async () => {
|
|
3678
|
+
const ui2 = createUiCollector(call.id, (event) => turn.writer.write(encodeStreamEvent(event)));
|
|
3679
|
+
const output2 = await traceToolExecution(hooks.runId, {
|
|
3680
|
+
toolCallId: call.id,
|
|
3681
|
+
toolName: call.name,
|
|
3682
|
+
toolType
|
|
3683
|
+
}, () => invokeWithTransientRetry(() => {
|
|
3684
|
+
ui2.restart();
|
|
3685
|
+
return deps.registry.invoke(call.name, call.input, {
|
|
3686
|
+
...ctx,
|
|
3687
|
+
emitUi: ui2.emit
|
|
3688
|
+
}, deps.rolesPolicy);
|
|
3689
|
+
}, deps.toolTransientRetry ?? {}, {
|
|
3690
|
+
...hooks.isControlFlowError !== void 0 ? {
|
|
3691
|
+
isControlFlowError: hooks.isControlFlowError
|
|
3692
|
+
} : {},
|
|
3693
|
+
onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
|
|
3694
|
+
publishAgentToolRetry({
|
|
3695
|
+
toolName: call.name,
|
|
3696
|
+
toolCallId: call.id,
|
|
3697
|
+
attempt,
|
|
3698
|
+
message: retryError instanceof Error ? retryError.message : String(retryError)
|
|
3699
|
+
});
|
|
3700
|
+
}, "onRetry")
|
|
3701
|
+
}));
|
|
3702
|
+
return wrapToolStepOutput(output2, ui2.components());
|
|
3703
|
+
});
|
|
3704
|
+
raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
|
|
3705
|
+
}
|
|
3706
|
+
const { output, ui } = unwrapToolStepOutput(raw);
|
|
3707
|
+
if (ui.length > 0) {
|
|
3708
|
+
turn.toolUi.set(call.id, ui);
|
|
3709
|
+
}
|
|
3710
|
+
if (claimed.terminal === true) {
|
|
3711
|
+
turn.halt.terminal = true;
|
|
3273
3712
|
}
|
|
3274
3713
|
return {
|
|
3275
3714
|
status: "executed",
|
|
@@ -4167,6 +4606,10 @@ ${block}`;
|
|
|
4167
4606
|
hooks,
|
|
4168
4607
|
messageId: assistant.id,
|
|
4169
4608
|
writer,
|
|
4609
|
+
toolUi: /* @__PURE__ */ new Map(),
|
|
4610
|
+
halt: {
|
|
4611
|
+
terminal: false
|
|
4612
|
+
},
|
|
4170
4613
|
...skillOffer !== void 0 ? {
|
|
4171
4614
|
skills: skillOffer
|
|
4172
4615
|
} : {},
|
|
@@ -4199,6 +4642,10 @@ ${block}`;
|
|
|
4199
4642
|
assistantMessage.toolResults = settledResults;
|
|
4200
4643
|
await hooks.step(`stream:tool-outputs:${i}`, async () => {
|
|
4201
4644
|
await deps.store.setMessageToolResults(assistant.id, settledResults);
|
|
4645
|
+
const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
|
|
4646
|
+
if (toolUi.length > 0) {
|
|
4647
|
+
await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
|
|
4648
|
+
}
|
|
4202
4649
|
for (const result of results) {
|
|
4203
4650
|
await writer.write(encodeStreamEvent(outputFrame(result)));
|
|
4204
4651
|
}
|
|
@@ -4213,6 +4660,9 @@ ${block}`;
|
|
|
4213
4660
|
} : {}
|
|
4214
4661
|
}));
|
|
4215
4662
|
});
|
|
4663
|
+
if (turnCalls.halt.terminal) {
|
|
4664
|
+
break;
|
|
4665
|
+
}
|
|
4216
4666
|
}
|
|
4217
4667
|
if (history.title === "" || history.title === "New chat") {
|
|
4218
4668
|
await hooks.step("persist:title", () => deps.store.setTitle(input.threadId, deriveTitle(input.userText)));
|
|
@@ -4309,6 +4759,7 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4309
4759
|
DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
|
|
4310
4760
|
DefaultApprovalPolicy,
|
|
4311
4761
|
DefaultRolesPolicy,
|
|
4762
|
+
ELICITATION_INPUT_TYPES,
|
|
4312
4763
|
GLOBAL_SCOPE,
|
|
4313
4764
|
MAX_ASK_QUESTIONS,
|
|
4314
4765
|
OutputRejectedError,
|
|
@@ -4342,6 +4793,7 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4342
4793
|
compositeSkillProvider,
|
|
4343
4794
|
createFrameBuffer,
|
|
4344
4795
|
createIncrementalGate,
|
|
4796
|
+
createUiCollector,
|
|
4345
4797
|
dayBoundsUtc,
|
|
4346
4798
|
decodeStreamEvent,
|
|
4347
4799
|
defaultCanDecide,
|
|
@@ -4364,10 +4816,12 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4364
4816
|
isReplayIntegrityError,
|
|
4365
4817
|
isToolEnabled,
|
|
4366
4818
|
isTransientToolError,
|
|
4819
|
+
isTypedQuestion,
|
|
4367
4820
|
loadSkill,
|
|
4368
4821
|
mayDecideApproval,
|
|
4369
4822
|
memoryForgetVerdict,
|
|
4370
4823
|
memoryWriteVerdict,
|
|
4824
|
+
mergeUi,
|
|
4371
4825
|
normalizeDelegation,
|
|
4372
4826
|
normalizeElicitationReply,
|
|
4373
4827
|
observeTurnFrames,
|
|
@@ -4385,6 +4839,9 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4385
4839
|
publishAgentSkillsResolved,
|
|
4386
4840
|
publishAgentToolCall,
|
|
4387
4841
|
publishAgentToolRetry,
|
|
4842
|
+
questionOptions,
|
|
4843
|
+
readElicitationInput,
|
|
4844
|
+
readElicitationQuestions,
|
|
4388
4845
|
releaseGatedFrames,
|
|
4389
4846
|
rememberInputSchema,
|
|
4390
4847
|
rememberToolDefinition,
|
|
@@ -4415,6 +4872,9 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4415
4872
|
traceLlmTurn,
|
|
4416
4873
|
traceToolExecution,
|
|
4417
4874
|
truncateDetailContent,
|
|
4875
|
+
unwrapToolStepOutput,
|
|
4876
|
+
validateElicitationAnswer,
|
|
4877
|
+
validateElicitationValue,
|
|
4418
4878
|
validateStructured,
|
|
4419
4879
|
windowHistory,
|
|
4420
4880
|
withAskTool,
|
|
@@ -4422,6 +4882,7 @@ __name(runAgentLoop, "runAgentLoop");
|
|
|
4422
4882
|
withSkillTool,
|
|
4423
4883
|
withToolTimeout,
|
|
4424
4884
|
withTurnFrames,
|
|
4885
|
+
wrapToolStepOutput,
|
|
4425
4886
|
writeMemory
|
|
4426
4887
|
});
|
|
4427
4888
|
//# sourceMappingURL=index.cjs.map
|