@dudousxd/nestjs-agent-core 0.20.0 → 0.21.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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as ModelMessage, a as ToolDefinition, b as ToolCallRequest, c as MessageUsage, d as AgentUiComponent, A as Actor, e as ThreadSummary, f as ThreadDetail, S as StoredMessage, g as ToolResult, h as MessageAttachment, i as ToolCallStatus, U as UsagePurpose, j as ToolSpec, Q as QuotaState, k as AgentRunInput, H as HumanReply, l as ToolKind, m as ToolCallApproval, T as ToolHandler, n as HistoryPolicy, O as OutputProcessor, P as ProcessorContext, I as InputProcessor, o as ProcessedPrompt, p as ModelAnswer, q as PageContext, r as AgentDefinition, s as AgentDelegation, D as DetachedDelivery, t as AiToolCtx, u as PromptBuilder, v as PromptContributor, w as ToolTransientRetrySetting, x as AgentIntake, y as Decision, E as ElicitationRequest, L as LlmStepEnvelope, z as ToolStepEnvelope } from './tool-B2Dq9ZwF.cjs';
2
- export { B as ASK_TOOL_DESCRIPTION, C as ASK_TOOL_NAME, F as AgentApprovalRequest, G as AgentApprovalSettlement, J as AgentCatalogEntry, K as AgentHistoryWindow, N as AgentStreamEvent, R as AskToolInput, V as DEFAULT_INCREMENTAL_LOOKBACK_CHARS, W as DEFAULT_INTAKE_PREAMBLE, X as DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS, Y as DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS, Z as ElicitationOption, _ as ElicitationOutcome, $ as ElicitationQuestion, a0 as ElicitationReply, a1 as ElicitationResult, a2 as HistoryPolicyContext, a3 as HistorySelection, a4 as HistorySummary, a5 as IncrementalGating, a6 as InvokeWithTransientRetryOptions, a7 as MAX_ASK_QUESTIONS, a8 as MessageRole, a9 as OutputRejectedError, aa as OutputVerdict, ab as ProcessorFailedError, ac as PromptContext, ad as QuotaView, ae as ToolCallApprovalStatus, af as ToolCatalogEntry, ag as ToolConfirmation, ah as ToolPresentation, ai as ToolPresentationTone, aj as ToolResultField, ak as ToolResultView, al as ToolStepCtx, am as ToolTransientRetryNumbers, an as ToolTransientRetryOptions, ao as askInputSchema, ap as askToolDefinition, aq as decodeStreamEvent, ar as encodeStreamEvent, as as invokeWithTransientRetry, at as isTransientToolError, au as normalizeElicitationReply, av as renderElicitationAnswers, aw as resolveElicitation, ax as resolveToolTransientRetryNumbers, ay as settleElicitation } from './tool-B2Dq9ZwF.cjs';
1
+ import { M as ModelMessage, a as ToolDefinition, b as ToolCallRequest, c as MessageUsage, d as AgentUiComponent, A as Actor, e as ThreadSummary, f as ThreadDetail, S as StoredMessage, g as ToolResult, h as MessageAttachment, i as ToolCallStatus, U as UsagePurpose, j as ToolSpec, Q as QuotaState, k as AgentRunInput, H as HumanReply, l as ToolKind, m as ToolCallApproval, T as ToolHandler, n as HistoryPolicy, O as OutputProcessor, P as ProcessorContext, I as InputProcessor, o as ProcessedPrompt, p as ModelAnswer, q as PageContext, r as AgentDefinition, s as AgentDelegation, D as DetachedDelivery, t as AiToolCtx, u as PromptBuilder, v as PromptContributor, w as ToolTransientRetrySetting, x as AgentIntake, y as Decision, E as ElicitationRequest, L as LlmStepEnvelope, z as ToolStepEnvelope } from './tool-CwXibnce.cjs';
2
+ export { B as ASK_TOOL_DESCRIPTION, C as ASK_TOOL_NAME, F as AgentApprovalRequest, G as AgentApprovalSettlement, J as AgentCatalogEntry, K as AgentHistoryWindow, N as AgentStreamEvent, R as AskToolInput, V as DEFAULT_INCREMENTAL_LOOKBACK_CHARS, W as DEFAULT_INTAKE_PREAMBLE, X as DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS, Y as DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS, Z as ELICITATION_INPUT_TYPES, _ as ElicitationInput, $ as ElicitationInputType, a0 as ElicitationOption, a1 as ElicitationOutcome, a2 as ElicitationQuestion, a3 as ElicitationReply, a4 as ElicitationResult, a5 as HistoryPolicyContext, a6 as HistorySelection, a7 as HistorySummary, a8 as IncrementalGating, a9 as InvokeWithTransientRetryOptions, aa as MAX_ASK_QUESTIONS, ab as MessageRole, ac as OutputRejectedError, ad as OutputVerdict, ae as ProcessorFailedError, af as PromptContext, ag as QuotaView, ah as ToolCallApprovalStatus, ai as ToolCatalogEntry, aj as ToolConfirmation, ak as ToolPresentation, al as ToolPresentationTone, am as ToolResultField, an as ToolResultView, ao as ToolStepCtx, ap as ToolTransientRetryNumbers, aq as ToolTransientRetryOptions, ar as askInputSchema, as as askToolDefinition, at as decodeStreamEvent, au as encodeStreamEvent, av as invokeWithTransientRetry, aw as isTransientToolError, ax as isTypedQuestion, ay as normalizeElicitationReply, az as questionOptions, aA as readElicitationInput, aB as readElicitationQuestions, aC as renderElicitationAnswers, aD as resolveElicitation, aE as resolveToolTransientRetryNumbers, aF as settleElicitation, aG as validateElicitationAnswer, aH as validateElicitationValue } from './tool-CwXibnce.cjs';
3
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import { ChannelRegistry } from '@dudousxd/nestjs-diagnostics';
5
5
 
@@ -489,6 +489,13 @@ interface AgentStore {
489
489
  * expired. Absent → every call is treated as the requester's, with no expiry (the old behaviour).
490
490
  */
491
491
  toolCallApproval?(toolCallId: string): Promise<ToolCallApprovalState | null>;
492
+ /**
493
+ * OPTIONAL: the input a call was recorded with, or `null` when the call is unknown. Read by the
494
+ * answer route to check a reply against the questions it answers (a typed question's rules, a
495
+ * `required` one left empty) before it is signalled. Absent → answers are checked for shape only,
496
+ * and the loop drops what it cannot settle.
497
+ */
498
+ toolCallInput?(toolCallId: string): Promise<unknown>;
492
499
  /**
493
500
  * OPTIONAL: of `mediaIds`, the ones a message that still exists — in a thread owned by
494
501
  * `actorRef` — still carries as an attachment. The inverse of
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as ModelMessage, a as ToolDefinition, b as ToolCallRequest, c as MessageUsage, d as AgentUiComponent, A as Actor, e as ThreadSummary, f as ThreadDetail, S as StoredMessage, g as ToolResult, h as MessageAttachment, i as ToolCallStatus, U as UsagePurpose, j as ToolSpec, Q as QuotaState, k as AgentRunInput, H as HumanReply, l as ToolKind, m as ToolCallApproval, T as ToolHandler, n as HistoryPolicy, O as OutputProcessor, P as ProcessorContext, I as InputProcessor, o as ProcessedPrompt, p as ModelAnswer, q as PageContext, r as AgentDefinition, s as AgentDelegation, D as DetachedDelivery, t as AiToolCtx, u as PromptBuilder, v as PromptContributor, w as ToolTransientRetrySetting, x as AgentIntake, y as Decision, E as ElicitationRequest, L as LlmStepEnvelope, z as ToolStepEnvelope } from './tool-B2Dq9ZwF.js';
2
- export { B as ASK_TOOL_DESCRIPTION, C as ASK_TOOL_NAME, F as AgentApprovalRequest, G as AgentApprovalSettlement, J as AgentCatalogEntry, K as AgentHistoryWindow, N as AgentStreamEvent, R as AskToolInput, V as DEFAULT_INCREMENTAL_LOOKBACK_CHARS, W as DEFAULT_INTAKE_PREAMBLE, X as DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS, Y as DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS, Z as ElicitationOption, _ as ElicitationOutcome, $ as ElicitationQuestion, a0 as ElicitationReply, a1 as ElicitationResult, a2 as HistoryPolicyContext, a3 as HistorySelection, a4 as HistorySummary, a5 as IncrementalGating, a6 as InvokeWithTransientRetryOptions, a7 as MAX_ASK_QUESTIONS, a8 as MessageRole, a9 as OutputRejectedError, aa as OutputVerdict, ab as ProcessorFailedError, ac as PromptContext, ad as QuotaView, ae as ToolCallApprovalStatus, af as ToolCatalogEntry, ag as ToolConfirmation, ah as ToolPresentation, ai as ToolPresentationTone, aj as ToolResultField, ak as ToolResultView, al as ToolStepCtx, am as ToolTransientRetryNumbers, an as ToolTransientRetryOptions, ao as askInputSchema, ap as askToolDefinition, aq as decodeStreamEvent, ar as encodeStreamEvent, as as invokeWithTransientRetry, at as isTransientToolError, au as normalizeElicitationReply, av as renderElicitationAnswers, aw as resolveElicitation, ax as resolveToolTransientRetryNumbers, ay as settleElicitation } from './tool-B2Dq9ZwF.js';
1
+ import { M as ModelMessage, a as ToolDefinition, b as ToolCallRequest, c as MessageUsage, d as AgentUiComponent, A as Actor, e as ThreadSummary, f as ThreadDetail, S as StoredMessage, g as ToolResult, h as MessageAttachment, i as ToolCallStatus, U as UsagePurpose, j as ToolSpec, Q as QuotaState, k as AgentRunInput, H as HumanReply, l as ToolKind, m as ToolCallApproval, T as ToolHandler, n as HistoryPolicy, O as OutputProcessor, P as ProcessorContext, I as InputProcessor, o as ProcessedPrompt, p as ModelAnswer, q as PageContext, r as AgentDefinition, s as AgentDelegation, D as DetachedDelivery, t as AiToolCtx, u as PromptBuilder, v as PromptContributor, w as ToolTransientRetrySetting, x as AgentIntake, y as Decision, E as ElicitationRequest, L as LlmStepEnvelope, z as ToolStepEnvelope } from './tool-CwXibnce.js';
2
+ export { B as ASK_TOOL_DESCRIPTION, C as ASK_TOOL_NAME, F as AgentApprovalRequest, G as AgentApprovalSettlement, J as AgentCatalogEntry, K as AgentHistoryWindow, N as AgentStreamEvent, R as AskToolInput, V as DEFAULT_INCREMENTAL_LOOKBACK_CHARS, W as DEFAULT_INTAKE_PREAMBLE, X as DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS, Y as DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS, Z as ELICITATION_INPUT_TYPES, _ as ElicitationInput, $ as ElicitationInputType, a0 as ElicitationOption, a1 as ElicitationOutcome, a2 as ElicitationQuestion, a3 as ElicitationReply, a4 as ElicitationResult, a5 as HistoryPolicyContext, a6 as HistorySelection, a7 as HistorySummary, a8 as IncrementalGating, a9 as InvokeWithTransientRetryOptions, aa as MAX_ASK_QUESTIONS, ab as MessageRole, ac as OutputRejectedError, ad as OutputVerdict, ae as ProcessorFailedError, af as PromptContext, ag as QuotaView, ah as ToolCallApprovalStatus, ai as ToolCatalogEntry, aj as ToolConfirmation, ak as ToolPresentation, al as ToolPresentationTone, am as ToolResultField, an as ToolResultView, ao as ToolStepCtx, ap as ToolTransientRetryNumbers, aq as ToolTransientRetryOptions, ar as askInputSchema, as as askToolDefinition, at as decodeStreamEvent, au as encodeStreamEvent, av as invokeWithTransientRetry, aw as isTransientToolError, ax as isTypedQuestion, ay as normalizeElicitationReply, az as questionOptions, aA as readElicitationInput, aB as readElicitationQuestions, aC as renderElicitationAnswers, aD as resolveElicitation, aE as resolveToolTransientRetryNumbers, aF as settleElicitation, aG as validateElicitationAnswer, aH as validateElicitationValue } from './tool-CwXibnce.js';
3
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import { ChannelRegistry } from '@dudousxd/nestjs-diagnostics';
5
5
 
@@ -489,6 +489,13 @@ interface AgentStore {
489
489
  * expired. Absent → every call is treated as the requester's, with no expiry (the old behaviour).
490
490
  */
491
491
  toolCallApproval?(toolCallId: string): Promise<ToolCallApprovalState | null>;
492
+ /**
493
+ * OPTIONAL: the input a call was recorded with, or `null` when the call is unknown. Read by the
494
+ * answer route to check a reply against the questions it answers (a typed question's rules, a
495
+ * `required` one left empty) before it is signalled. Absent → answers are checked for shape only,
496
+ * and the loop drops what it cannot settle.
497
+ */
498
+ toolCallInput?(toolCallId: string): Promise<unknown>;
492
499
  /**
493
500
  * OPTIONAL: of `mediaIds`, the ones a message that still exists — in a thread owned by
494
501
  * `actorRef` — still carries as an attachment. The inverse of
package/dist/index.js CHANGED
@@ -831,11 +831,235 @@ ${detail}`;
831
831
  }
832
832
  __name(repairInstruction, "repairInstruction");
833
833
 
834
- // src/elicitation.ts
835
- function optionValues(question) {
836
- return new Set(question.options.map((option) => option.value));
834
+ // src/elicitation-input.ts
835
+ var ELICITATION_INPUT_TYPES = [
836
+ "text",
837
+ "textarea",
838
+ "number",
839
+ "boolean",
840
+ "date",
841
+ "email",
842
+ "url",
843
+ "select"
844
+ ];
845
+ function questionOptions(question) {
846
+ return question.options ?? [];
847
+ }
848
+ __name(questionOptions, "questionOptions");
849
+ function isTypedQuestion(question) {
850
+ return question.input !== void 0 && question.input.type !== "select";
851
+ }
852
+ __name(isTypedQuestion, "isTypedQuestion");
853
+ var DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
854
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
855
+ var NUMBER_PATTERN = /^-?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
856
+ function isDate(value) {
857
+ const match = DATE_PATTERN.exec(value);
858
+ if (match === null) {
859
+ return false;
860
+ }
861
+ const [, year, month, day] = match;
862
+ const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day)));
863
+ return date.getUTCFullYear() === Number(year) && date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day);
864
+ }
865
+ __name(isDate, "isDate");
866
+ function isHttpUrl(value) {
867
+ try {
868
+ const url = new URL(value);
869
+ return url.protocol === "http:" || url.protocol === "https:";
870
+ } catch {
871
+ return false;
872
+ }
873
+ }
874
+ __name(isHttpUrl, "isHttpUrl");
875
+ function fullMatch(pattern, value) {
876
+ try {
877
+ return new RegExp(`^(?:${pattern})$`, "u").test(value);
878
+ } catch {
879
+ return void 0;
880
+ }
881
+ }
882
+ __name(fullMatch, "fullMatch");
883
+ function asNumber(bound) {
884
+ if (typeof bound === "number" && Number.isFinite(bound)) {
885
+ return bound;
886
+ }
887
+ if (typeof bound === "string" && NUMBER_PATTERN.test(bound.trim())) {
888
+ return Number(bound);
889
+ }
890
+ return void 0;
891
+ }
892
+ __name(asNumber, "asNumber");
893
+ function validateElicitationValue(question, value) {
894
+ const input = question.input;
895
+ if (input === void 0 || input.type === "select") {
896
+ if (question.allowFreeText === true) {
897
+ return null;
898
+ }
899
+ return questionOptions(question).some((option) => option.value === value) ? null : "must be one of the offered options";
900
+ }
901
+ switch (input.type) {
902
+ case "number": {
903
+ if (!NUMBER_PATTERN.test(value.trim()) || !Number.isFinite(Number(value))) {
904
+ return "must be a number";
905
+ }
906
+ const number = Number(value);
907
+ const min = asNumber(input.min);
908
+ const max = asNumber(input.max);
909
+ if (min !== void 0 && number < min) {
910
+ return `must be at least ${min}`;
911
+ }
912
+ if (max !== void 0 && number > max) {
913
+ return `must be at most ${max}`;
914
+ }
915
+ return null;
916
+ }
917
+ case "boolean":
918
+ return value === "true" || value === "false" ? null : 'must be "true" or "false"';
919
+ case "date": {
920
+ if (!isDate(value)) {
921
+ return "must be a date as YYYY-MM-DD";
922
+ }
923
+ if (typeof input.min === "string" && isDate(input.min) && value < input.min) {
924
+ return `must be on or after ${input.min}`;
925
+ }
926
+ if (typeof input.max === "string" && isDate(input.max) && value > input.max) {
927
+ return `must be on or before ${input.max}`;
928
+ }
929
+ return null;
930
+ }
931
+ case "email":
932
+ if (!EMAIL_PATTERN.test(value)) {
933
+ return "must be an email address";
934
+ }
935
+ break;
936
+ case "url":
937
+ if (!isHttpUrl(value)) {
938
+ return "must be an http(s) URL";
939
+ }
940
+ break;
941
+ case "text":
942
+ case "textarea": {
943
+ const min = asNumber(input.min);
944
+ const max = asNumber(input.max);
945
+ if (min !== void 0 && value.length < min) {
946
+ return `must be at least ${min} characters`;
947
+ }
948
+ if (max !== void 0 && value.length > max) {
949
+ return `must be at most ${max} characters`;
950
+ }
951
+ break;
952
+ }
953
+ }
954
+ if (input.pattern !== void 0 && fullMatch(input.pattern, value) === false) {
955
+ return "does not match the expected format";
956
+ }
957
+ return null;
958
+ }
959
+ __name(validateElicitationValue, "validateElicitationValue");
960
+ function validateElicitationAnswer(question, values) {
961
+ const present = values.filter((value) => value !== "");
962
+ if (present.length === 0) {
963
+ return question.input?.required === true ? "requires an answer" : null;
964
+ }
965
+ if (question.multiple !== true && present.length > 1) {
966
+ return "takes a single value";
967
+ }
968
+ for (const value of present) {
969
+ const problem = validateElicitationValue(question, value);
970
+ if (problem !== null) {
971
+ return problem;
972
+ }
973
+ }
974
+ return null;
837
975
  }
838
- __name(optionValues, "optionValues");
976
+ __name(validateElicitationAnswer, "validateElicitationAnswer");
977
+ function readElicitationInput(raw) {
978
+ if (typeof raw !== "object" || raw === null) {
979
+ return void 0;
980
+ }
981
+ const candidate = raw;
982
+ const type = candidate.type;
983
+ if (typeof type !== "string" || !ELICITATION_INPUT_TYPES.includes(type)) {
984
+ return void 0;
985
+ }
986
+ const bound = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isFinite(value) || typeof value === "string" ? value : void 0, "bound");
987
+ const min = bound(candidate.min);
988
+ const max = bound(candidate.max);
989
+ return {
990
+ type,
991
+ ...typeof candidate.placeholder === "string" ? {
992
+ placeholder: candidate.placeholder
993
+ } : {},
994
+ ...candidate.required === true ? {
995
+ required: true
996
+ } : {},
997
+ ...min !== void 0 ? {
998
+ min
999
+ } : {},
1000
+ ...max !== void 0 ? {
1001
+ max
1002
+ } : {},
1003
+ ...typeof candidate.pattern === "string" ? {
1004
+ pattern: candidate.pattern
1005
+ } : {}
1006
+ };
1007
+ }
1008
+ __name(readElicitationInput, "readElicitationInput");
1009
+ function readElicitationQuestions(input) {
1010
+ if (typeof input !== "object" || input === null) {
1011
+ return [];
1012
+ }
1013
+ const raw = input.questions;
1014
+ if (!Array.isArray(raw)) {
1015
+ return [];
1016
+ }
1017
+ const questions = [];
1018
+ for (const item of raw) {
1019
+ if (typeof item !== "object" || item === null) {
1020
+ continue;
1021
+ }
1022
+ const candidate = item;
1023
+ if (typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
1024
+ continue;
1025
+ }
1026
+ const options = Array.isArray(candidate.options) ? candidate.options.flatMap((option) => {
1027
+ if (typeof option !== "object" || option === null) {
1028
+ return [];
1029
+ }
1030
+ const { value, label } = option;
1031
+ return typeof value === "string" && typeof label === "string" ? [
1032
+ {
1033
+ value,
1034
+ label
1035
+ }
1036
+ ] : [];
1037
+ }) : [];
1038
+ const typed = readElicitationInput(candidate.input);
1039
+ const defaults = Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [];
1040
+ questions.push({
1041
+ id: candidate.id,
1042
+ prompt: candidate.prompt,
1043
+ options,
1044
+ ...typed !== void 0 ? {
1045
+ input: typed
1046
+ } : {},
1047
+ ...defaults.length > 0 ? {
1048
+ defaults
1049
+ } : {},
1050
+ ...candidate.multiple === true ? {
1051
+ multiple: true
1052
+ } : {},
1053
+ ...candidate.allowFreeText === true ? {
1054
+ allowFreeText: true
1055
+ } : {}
1056
+ });
1057
+ }
1058
+ return questions;
1059
+ }
1060
+ __name(readElicitationQuestions, "readElicitationQuestions");
1061
+
1062
+ // src/elicitation.ts
839
1063
  function normalizeElicitationReply(reply) {
840
1064
  if (typeof reply !== "object" || reply === null) {
841
1065
  return {
@@ -871,8 +1095,7 @@ function resolveElicitation(request, raw) {
871
1095
  defaulted.push(question.id);
872
1096
  continue;
873
1097
  }
874
- const allowed = optionValues(question);
875
- const valid = question.allowFreeText === true ? submitted : submitted.filter((value) => allowed.has(value));
1098
+ const valid = submitted.filter((value) => value !== "" && validateElicitationValue(question, value) === null);
876
1099
  answers[question.id] = question.multiple === true ? valid : valid.slice(0, 1);
877
1100
  }
878
1101
  return {
@@ -893,7 +1116,7 @@ __name(settleElicitation, "settleElicitation");
893
1116
  function renderElicitationAnswers(request, outcome) {
894
1117
  const lines = request.questions.map((question) => {
895
1118
  const chosen = outcome.answers[question.id] ?? [];
896
- const labels = chosen.map((value) => question.options.find((option) => option.value === value)?.label ?? value);
1119
+ const labels = chosen.map((value) => questionOptions(question).find((option) => option.value === value)?.label ?? value);
897
1120
  return `${question.prompt} \u2192 ${labels.length > 0 ? labels.join(", ") : "(no answer)"}`;
898
1121
  });
899
1122
  const preface = outcome.skipped ? "The user declined to answer and asked you to proceed on these assumptions:" : "The user answered:";
@@ -959,7 +1182,23 @@ function parseQuestion(raw, path, issues) {
959
1182
  ], "must be a non-empty string"));
960
1183
  return void 0;
961
1184
  }
962
- if (!Array.isArray(candidate.options) || candidate.options.length === 0) {
1185
+ let input;
1186
+ if (candidate.input !== void 0) {
1187
+ input = readElicitationInput(candidate.input);
1188
+ if (input === void 0) {
1189
+ issues.push(issue([
1190
+ ...path,
1191
+ "input",
1192
+ "type"
1193
+ ], `must be one of ${ELICITATION_INPUT_TYPES.join(", ")}`));
1194
+ return void 0;
1195
+ }
1196
+ }
1197
+ const described = typeof candidate.description === "string" && candidate.description.length > 0 ? {
1198
+ description: candidate.description
1199
+ } : {};
1200
+ const typed = input !== void 0 && input.type !== "select";
1201
+ if (!typed && (!Array.isArray(candidate.options) || candidate.options.length === 0)) {
963
1202
  issues.push(issue([
964
1203
  ...path,
965
1204
  "options"
@@ -967,7 +1206,7 @@ function parseQuestion(raw, path, issues) {
967
1206
  return void 0;
968
1207
  }
969
1208
  const options = [];
970
- for (const [index, rawOption] of candidate.options.entries()) {
1209
+ for (const [index, rawOption] of (Array.isArray(candidate.options) ? candidate.options : []).entries()) {
971
1210
  const option = parseOption(rawOption, [
972
1211
  ...path,
973
1212
  "options",
@@ -977,6 +1216,33 @@ function parseQuestion(raw, path, issues) {
977
1216
  options.push(option);
978
1217
  }
979
1218
  }
1219
+ if (typed) {
1220
+ const question = {
1221
+ id: candidate.id,
1222
+ prompt: candidate.prompt,
1223
+ ...described,
1224
+ ...options.length > 0 ? {
1225
+ options
1226
+ } : {},
1227
+ input,
1228
+ ...candidate.multiple === true ? {
1229
+ multiple: true
1230
+ } : {}
1231
+ };
1232
+ const offered2 = Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [];
1233
+ const defaults2 = offered2.filter((value) => validateElicitationValue(question, value) === null);
1234
+ if (offered2.length > 0 && defaults2.length === 0) {
1235
+ issues.push(issue([
1236
+ ...path,
1237
+ "defaults"
1238
+ ], `must be valid ${input.type} values`));
1239
+ return void 0;
1240
+ }
1241
+ return defaults2.length > 0 ? {
1242
+ ...question,
1243
+ defaults: defaults2
1244
+ } : question;
1245
+ }
980
1246
  if (!Array.isArray(candidate.defaults) || candidate.defaults.length === 0) {
981
1247
  issues.push(issue([
982
1248
  ...path,
@@ -996,7 +1262,11 @@ function parseQuestion(raw, path, issues) {
996
1262
  return {
997
1263
  id: candidate.id,
998
1264
  prompt: candidate.prompt,
1265
+ ...described,
999
1266
  options,
1267
+ ...input !== void 0 ? {
1268
+ input
1269
+ } : {},
1000
1270
  defaults,
1001
1271
  ...candidate.multiple === true ? {
1002
1272
  multiple: true
@@ -1024,9 +1294,7 @@ var ASK_JSON_SCHEMA = {
1024
1294
  additionalProperties: false,
1025
1295
  required: [
1026
1296
  "id",
1027
- "prompt",
1028
- "options",
1029
- "defaults"
1297
+ "prompt"
1030
1298
  ],
1031
1299
  properties: {
1032
1300
  id: {
@@ -1036,6 +1304,50 @@ var ASK_JSON_SCHEMA = {
1036
1304
  prompt: {
1037
1305
  type: "string"
1038
1306
  },
1307
+ description: {
1308
+ type: "string",
1309
+ description: "A line of help under the prompt."
1310
+ },
1311
+ input: {
1312
+ type: "object",
1313
+ additionalProperties: false,
1314
+ required: [
1315
+ "type"
1316
+ ],
1317
+ 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.',
1318
+ properties: {
1319
+ type: {
1320
+ type: "string",
1321
+ enum: [
1322
+ ...ELICITATION_INPUT_TYPES
1323
+ ]
1324
+ },
1325
+ placeholder: {
1326
+ type: "string"
1327
+ },
1328
+ required: {
1329
+ type: "boolean"
1330
+ },
1331
+ min: {
1332
+ type: [
1333
+ "number",
1334
+ "string"
1335
+ ],
1336
+ description: "number: minimum; text: minimum length; date: earliest YYYY-MM-DD."
1337
+ },
1338
+ max: {
1339
+ type: [
1340
+ "number",
1341
+ "string"
1342
+ ],
1343
+ description: "number: maximum; text: maximum length; date: latest YYYY-MM-DD."
1344
+ },
1345
+ pattern: {
1346
+ type: "string",
1347
+ description: "A regex the whole value must match."
1348
+ }
1349
+ }
1350
+ },
1039
1351
  multiple: {
1040
1352
  type: "boolean",
1041
1353
  description: "Allow more than one option to be chosen."
@@ -1050,7 +1362,7 @@ var ASK_JSON_SCHEMA = {
1050
1362
  items: {
1051
1363
  type: "string"
1052
1364
  },
1053
- description: "REQUIRED. The option values you would pick yourself, so the user can confirm rather than decide."
1365
+ 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
1366
  },
1055
1367
  options: {
1056
1368
  type: "array",
@@ -4129,6 +4441,7 @@ export {
4129
4441
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
4130
4442
  DefaultApprovalPolicy,
4131
4443
  DefaultRolesPolicy,
4444
+ ELICITATION_INPUT_TYPES,
4132
4445
  GLOBAL_SCOPE,
4133
4446
  MAX_ASK_QUESTIONS,
4134
4447
  OutputRejectedError,
@@ -4184,6 +4497,7 @@ export {
4184
4497
  isReplayIntegrityError,
4185
4498
  isToolEnabled,
4186
4499
  isTransientToolError,
4500
+ isTypedQuestion,
4187
4501
  loadSkill,
4188
4502
  mayDecideApproval,
4189
4503
  memoryForgetVerdict,
@@ -4205,6 +4519,9 @@ export {
4205
4519
  publishAgentSkillsResolved,
4206
4520
  publishAgentToolCall,
4207
4521
  publishAgentToolRetry,
4522
+ questionOptions,
4523
+ readElicitationInput,
4524
+ readElicitationQuestions,
4208
4525
  releaseGatedFrames,
4209
4526
  rememberInputSchema,
4210
4527
  rememberToolDefinition,
@@ -4235,6 +4552,8 @@ export {
4235
4552
  traceLlmTurn,
4236
4553
  traceToolExecution,
4237
4554
  truncateDetailContent,
4555
+ validateElicitationAnswer,
4556
+ validateElicitationValue,
4238
4557
  validateStructured,
4239
4558
  windowHistory,
4240
4559
  withAskTool,