@librechat/agents 4.0.2 → 4.0.4

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.
Files changed (162) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/decisions/structuredChat.cjs +2 -8
  4. package/dist/cjs/decisions/structuredChat.cjs.map +1 -1
  5. package/dist/cjs/decisions/structuredOutput.cjs +18 -0
  6. package/dist/cjs/decisions/structuredOutput.cjs.map +1 -0
  7. package/dist/cjs/graphs/Graph.cjs +25 -9
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/llm/google/index.cjs +1 -1
  10. package/dist/cjs/llm/invoke.cjs +34 -12
  11. package/dist/cjs/llm/invoke.cjs.map +1 -1
  12. package/dist/cjs/llm/openai/index.cjs +7 -0
  13. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  14. package/dist/cjs/llm/providerTextBoundary.cjs +355 -0
  15. package/dist/cjs/llm/providerTextBoundary.cjs.map +1 -0
  16. package/dist/cjs/llm/providerTextChunk.cjs +14 -0
  17. package/dist/cjs/llm/providerTextChunk.cjs.map +1 -0
  18. package/dist/cjs/llm/providerTextControls.cjs +236 -0
  19. package/dist/cjs/llm/providerTextControls.cjs.map +1 -0
  20. package/dist/cjs/llm/stream/smoother.cjs +2 -1
  21. package/dist/cjs/llm/stream/smoother.cjs.map +1 -1
  22. package/dist/cjs/main.cjs +18 -1
  23. package/dist/cjs/protection/providerText.cjs +224 -0
  24. package/dist/cjs/protection/providerText.cjs.map +1 -0
  25. package/dist/cjs/protection/providerTextInput.cjs +25 -0
  26. package/dist/cjs/protection/providerTextInput.cjs.map +1 -0
  27. package/dist/cjs/run.cjs +7 -1
  28. package/dist/cjs/run.cjs.map +1 -1
  29. package/dist/cjs/stream.cjs +42 -14
  30. package/dist/cjs/stream.cjs.map +1 -1
  31. package/dist/cjs/summarization/node.cjs +26 -8
  32. package/dist/cjs/summarization/node.cjs.map +1 -1
  33. package/dist/cjs/tools/GitHubCompare.cjs +181 -0
  34. package/dist/cjs/tools/GitHubCompare.cjs.map +1 -0
  35. package/dist/cjs/tools/SubagentTool.cjs +11 -3
  36. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  37. package/dist/cjs/tools/ToolNode.cjs +6 -5
  38. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  39. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +2 -2
  40. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  41. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +66 -16
  42. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  43. package/dist/cjs/tools/subagent/SubagentReplay.cjs +4 -2
  44. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  45. package/dist/cjs/tools/subagent/childGraphConfig.cjs +3 -1
  46. package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -1
  47. package/dist/cjs/tools/subagent/diagnostics.cjs +48 -5
  48. package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -1
  49. package/dist/cjs/tools/subagent/hostArgs.cjs +214 -0
  50. package/dist/cjs/tools/subagent/hostArgs.cjs.map +1 -0
  51. package/dist/cjs/tools/subagent/index.cjs +1 -0
  52. package/dist/cjs/tools/toolErrorContent.cjs +1 -1
  53. package/dist/esm/agents/AgentContext.mjs +3 -2
  54. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  55. package/dist/esm/decisions/structuredChat.mjs +2 -8
  56. package/dist/esm/decisions/structuredChat.mjs.map +1 -1
  57. package/dist/esm/decisions/structuredOutput.mjs +18 -0
  58. package/dist/esm/decisions/structuredOutput.mjs.map +1 -0
  59. package/dist/esm/graphs/Graph.mjs +25 -9
  60. package/dist/esm/graphs/Graph.mjs.map +1 -1
  61. package/dist/esm/llm/google/index.mjs +1 -1
  62. package/dist/esm/llm/invoke.mjs +34 -12
  63. package/dist/esm/llm/invoke.mjs.map +1 -1
  64. package/dist/esm/llm/openai/index.mjs +7 -0
  65. package/dist/esm/llm/openai/index.mjs.map +1 -1
  66. package/dist/esm/llm/providerTextBoundary.mjs +355 -0
  67. package/dist/esm/llm/providerTextBoundary.mjs.map +1 -0
  68. package/dist/esm/llm/providerTextChunk.mjs +13 -0
  69. package/dist/esm/llm/providerTextChunk.mjs.map +1 -0
  70. package/dist/esm/llm/providerTextControls.mjs +230 -0
  71. package/dist/esm/llm/providerTextControls.mjs.map +1 -0
  72. package/dist/esm/llm/stream/smoother.mjs +2 -1
  73. package/dist/esm/llm/stream/smoother.mjs.map +1 -1
  74. package/dist/esm/main.mjs +6 -3
  75. package/dist/esm/protection/providerText.mjs +218 -0
  76. package/dist/esm/protection/providerText.mjs.map +1 -0
  77. package/dist/esm/protection/providerTextInput.mjs +24 -0
  78. package/dist/esm/protection/providerTextInput.mjs.map +1 -0
  79. package/dist/esm/run.mjs +7 -1
  80. package/dist/esm/run.mjs.map +1 -1
  81. package/dist/esm/stream.mjs +42 -14
  82. package/dist/esm/stream.mjs.map +1 -1
  83. package/dist/esm/summarization/node.mjs +26 -8
  84. package/dist/esm/summarization/node.mjs.map +1 -1
  85. package/dist/esm/tools/GitHubCompare.mjs +175 -0
  86. package/dist/esm/tools/GitHubCompare.mjs.map +1 -0
  87. package/dist/esm/tools/SubagentTool.mjs +11 -3
  88. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  89. package/dist/esm/tools/ToolNode.mjs +6 -5
  90. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  91. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +2 -2
  92. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  93. package/dist/esm/tools/subagent/SubagentExecutor.mjs +66 -16
  94. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  95. package/dist/esm/tools/subagent/SubagentReplay.mjs +4 -2
  96. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  97. package/dist/esm/tools/subagent/childGraphConfig.mjs +3 -1
  98. package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -1
  99. package/dist/esm/tools/subagent/diagnostics.mjs +46 -6
  100. package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -1
  101. package/dist/esm/tools/subagent/hostArgs.mjs +207 -0
  102. package/dist/esm/tools/subagent/hostArgs.mjs.map +1 -0
  103. package/dist/esm/tools/subagent/index.mjs +1 -0
  104. package/dist/esm/tools/toolErrorContent.mjs +1 -1
  105. package/dist/types/decisions/structuredChat.d.ts +3 -2
  106. package/dist/types/decisions/structuredOutput.d.ts +4 -0
  107. package/dist/types/graphs/Graph.d.ts +6 -3
  108. package/dist/types/index.d.ts +3 -0
  109. package/dist/types/llm/invoke.d.ts +4 -1
  110. package/dist/types/llm/providerTextBoundary.d.ts +4 -0
  111. package/dist/types/llm/providerTextChunk.d.ts +7 -0
  112. package/dist/types/llm/providerTextControls.d.ts +18 -0
  113. package/dist/types/protection/providerText.d.ts +77 -0
  114. package/dist/types/protection/providerTextInput.d.ts +3 -0
  115. package/dist/types/run.d.ts +1 -0
  116. package/dist/types/tools/BashExecutor.d.ts +4 -0
  117. package/dist/types/tools/CodeExecutor.d.ts +4 -0
  118. package/dist/types/tools/GitHubCompare.d.ts +104 -0
  119. package/dist/types/tools/ReadFile.d.ts +4 -0
  120. package/dist/types/tools/SkillTool.d.ts +4 -0
  121. package/dist/types/tools/SubagentTool.d.ts +2 -0
  122. package/dist/types/tools/ToolSearch.d.ts +4 -0
  123. package/dist/types/tools/search/schema.d.ts +4 -0
  124. package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +2 -0
  125. package/dist/types/tools/subagent/SubagentExecutor.d.ts +10 -2
  126. package/dist/types/tools/subagent/SubagentReplay.d.ts +3 -1
  127. package/dist/types/tools/subagent/diagnostics.d.ts +27 -3
  128. package/dist/types/tools/subagent/hostArgs.d.ts +57 -0
  129. package/dist/types/tools/subagent/index.d.ts +3 -2
  130. package/dist/types/types/graph.d.ts +36 -0
  131. package/dist/types/types/run.d.ts +4 -2
  132. package/dist/types/types/tools.d.ts +2 -0
  133. package/package.json +4 -4
  134. package/src/agents/AgentContext.ts +3 -2
  135. package/src/decisions/structuredChat.ts +9 -15
  136. package/src/decisions/structuredOutput.ts +25 -0
  137. package/src/graphs/Graph.ts +53 -9
  138. package/src/index.ts +8 -0
  139. package/src/llm/invoke.ts +48 -7
  140. package/src/llm/openai/index.ts +8 -0
  141. package/src/llm/providerTextBoundary.ts +446 -0
  142. package/src/llm/providerTextChunk.ts +15 -0
  143. package/src/llm/providerTextControls.ts +173 -0
  144. package/src/llm/stream/smoother.ts +4 -2
  145. package/src/protection/providerText.ts +325 -0
  146. package/src/protection/providerTextInput.ts +21 -0
  147. package/src/run.ts +9 -0
  148. package/src/stream.ts +33 -7
  149. package/src/summarization/node.ts +32 -14
  150. package/src/tools/GitHubCompare.ts +319 -0
  151. package/src/tools/SubagentTool.ts +14 -3
  152. package/src/tools/ToolNode.ts +7 -3
  153. package/src/tools/subagent/SubagentExecutionRegistry.ts +6 -2
  154. package/src/tools/subagent/SubagentExecutor.ts +145 -18
  155. package/src/tools/subagent/SubagentReplay.ts +10 -3
  156. package/src/tools/subagent/childGraphConfig.ts +12 -1
  157. package/src/tools/subagent/diagnostics.ts +98 -6
  158. package/src/tools/subagent/hostArgs.ts +413 -0
  159. package/src/tools/subagent/index.ts +9 -0
  160. package/src/types/graph.ts +41 -0
  161. package/src/types/run.ts +4 -2
  162. package/src/types/tools.ts +6 -1
@@ -0,0 +1,413 @@
1
+ import { createHash } from 'crypto';
2
+ import type {
3
+ SubagentHostArgs,
4
+ SubagentHostArgSpec,
5
+ SubagentHostArgSpecs,
6
+ } from '@/types/graph';
7
+ import type { JsonSchemaType } from '@/types/tools';
8
+
9
+ /** Bounds on host-declared subagent call arguments and their values. */
10
+ export const SUBAGENT_HOST_ARG_LIMITS = Object.freeze({
11
+ argsPerSubagent: 8,
12
+ distinctArgs: 16,
13
+ enumValues: 64,
14
+ mergedEnumValues: 256,
15
+ valueLength: 256,
16
+ descriptionLength: 1024,
17
+ });
18
+
19
+ const HOST_ARG_NAME_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
20
+ /** Advertises the runtime's no-control-character rule; fixed and linear-time. */
21
+ const FREE_FORM_SCHEMA_PATTERN = '^[^\\u0000-\\u001f\\u007f]*$';
22
+ const RESERVED_HOST_ARG_NAMES: ReadonlySet<string> = new Set([
23
+ 'intent',
24
+ 'description',
25
+ 'subagent_type',
26
+ 'run_in_background',
27
+ 'subagent_thread_id',
28
+ ]);
29
+
30
+ /** Any subagent entry that may declare host arguments. */
31
+ export type SubagentHostArgDeclaration = {
32
+ type: string;
33
+ hostArgs?: SubagentHostArgSpecs;
34
+ };
35
+
36
+ export type SubagentHostArgsResult =
37
+ | { ok: true; hostArgs?: SubagentHostArgs }
38
+ | { ok: false; message: string };
39
+
40
+ type HostArgEntry = readonly [string, SubagentHostArgSpec];
41
+
42
+ type MergedHostArg = {
43
+ description: string;
44
+ values: string[];
45
+ seenValues: Set<string>;
46
+ freeForm: boolean;
47
+ maxLength: number;
48
+ };
49
+
50
+ /** A name a host may declare: lowercase snake case, not a built-in subagent argument. */
51
+ export function isSubagentHostArgName(name: string): boolean {
52
+ return HOST_ARG_NAME_PATTERN.test(name) && !RESERVED_HOST_ARG_NAMES.has(name);
53
+ }
54
+
55
+ /** Length in Unicode code points, the unit JSON Schema `maxLength` counts. */
56
+ function countCodePoints(value: string): number {
57
+ let count = 0;
58
+ for (const _ of value) {
59
+ count += 1;
60
+ }
61
+ return count;
62
+ }
63
+
64
+ function hasControlCharacter(value: string): boolean {
65
+ for (let i = 0; i < value.length; i++) {
66
+ const code = value.charCodeAt(i);
67
+ if (code < 0x20 || code === 0x7f) {
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+
74
+ function isBoundedText(value: unknown, maxLength: number): value is string {
75
+ return (
76
+ typeof value === 'string' &&
77
+ value.trim().length > 0 &&
78
+ countCodePoints(value) <= maxLength &&
79
+ !hasControlCharacter(value)
80
+ );
81
+ }
82
+
83
+ function getFreeFormMaxLength(spec: SubagentHostArgSpec): number {
84
+ return spec.maxLength ?? SUBAGENT_HOST_ARG_LIMITS.valueLength;
85
+ }
86
+
87
+ function validateEnum(label: string, values: readonly string[]): void {
88
+ if (
89
+ !Array.isArray(values) ||
90
+ values.length === 0 ||
91
+ values.length > SUBAGENT_HOST_ARG_LIMITS.enumValues
92
+ ) {
93
+ throw new Error(
94
+ `${label} enum must list 1-${SUBAGENT_HOST_ARG_LIMITS.enumValues} values.`
95
+ );
96
+ }
97
+ const seen = new Set<string>();
98
+ for (const value of values) {
99
+ if (!isBoundedText(value, SUBAGENT_HOST_ARG_LIMITS.valueLength)) {
100
+ throw new Error(
101
+ `${label} enum values must be non-blank strings of at most ${SUBAGENT_HOST_ARG_LIMITS.valueLength} characters without control characters.`
102
+ );
103
+ }
104
+ if (seen.has(value)) {
105
+ throw new Error(`${label} enum values must be unique.`);
106
+ }
107
+ seen.add(value);
108
+ }
109
+ }
110
+
111
+ function validateFreeForm(label: string, spec: SubagentHostArgSpec): void {
112
+ const maxLength = getFreeFormMaxLength(spec);
113
+ if (
114
+ !Number.isSafeInteger(maxLength) ||
115
+ maxLength < 1 ||
116
+ maxLength > SUBAGENT_HOST_ARG_LIMITS.valueLength
117
+ ) {
118
+ throw new Error(
119
+ `${label} maxLength must be an integer from 1 to ${SUBAGENT_HOST_ARG_LIMITS.valueLength}.`
120
+ );
121
+ }
122
+ }
123
+
124
+ function validateSpec(
125
+ type: string,
126
+ name: string,
127
+ spec: SubagentHostArgSpec | undefined
128
+ ): asserts spec is SubagentHostArgSpec {
129
+ const label = `Subagent "${type}" host argument "${name}"`;
130
+ if (!isSubagentHostArgName(name)) {
131
+ throw new Error(
132
+ `${label} must match ${HOST_ARG_NAME_PATTERN.source} and must not reuse a built-in subagent argument name.`
133
+ );
134
+ }
135
+ if (spec == null) {
136
+ throw new Error(`${label} must be an object.`);
137
+ }
138
+ if ((spec as { pattern?: unknown }).pattern !== undefined) {
139
+ throw new Error(
140
+ `${label} cannot declare a pattern; validate the format in the resolver.`
141
+ );
142
+ }
143
+ if (
144
+ typeof spec.description !== 'string' ||
145
+ spec.description.trim().length === 0 ||
146
+ spec.description.length > SUBAGENT_HOST_ARG_LIMITS.descriptionLength
147
+ ) {
148
+ throw new Error(
149
+ `${label} needs a description of at most ${SUBAGENT_HOST_ARG_LIMITS.descriptionLength} characters.`
150
+ );
151
+ }
152
+ if (spec.enum == null) {
153
+ validateFreeForm(label, spec);
154
+ return;
155
+ }
156
+ if (spec.maxLength != null) {
157
+ throw new Error(`${label} cannot combine enum with maxLength.`);
158
+ }
159
+ validateEnum(label, spec.enum);
160
+ }
161
+
162
+ /**
163
+ * Returns a subagent's validated host argument declarations in declaration
164
+ * order. Throws on a malformed or oversized declaration.
165
+ */
166
+ export function getSubagentHostArgSpecs(
167
+ config: SubagentHostArgDeclaration
168
+ ): readonly HostArgEntry[] {
169
+ const { hostArgs } = config;
170
+ if (hostArgs == null) {
171
+ return [];
172
+ }
173
+ if (typeof hostArgs !== 'object' || Array.isArray(hostArgs)) {
174
+ throw new Error(`Subagent "${config.type}" hostArgs must be an object.`);
175
+ }
176
+ const entries = Object.entries(hostArgs);
177
+ if (entries.length > SUBAGENT_HOST_ARG_LIMITS.argsPerSubagent) {
178
+ throw new Error(
179
+ `Subagent "${config.type}" declares more than ${SUBAGENT_HOST_ARG_LIMITS.argsPerSubagent} host arguments.`
180
+ );
181
+ }
182
+ for (const [name, spec] of entries) {
183
+ validateSpec(config.type, name, spec);
184
+ }
185
+ return entries;
186
+ }
187
+
188
+ /** Every host argument name declared by at least one subagent. */
189
+ export function collectSubagentHostArgNames(
190
+ configs: readonly SubagentHostArgDeclaration[]
191
+ ): ReadonlySet<string> {
192
+ const names = new Set<string>();
193
+ for (const config of configs) {
194
+ for (const [name] of getSubagentHostArgSpecs(config)) {
195
+ names.add(name);
196
+ }
197
+ }
198
+ if (names.size > SUBAGENT_HOST_ARG_LIMITS.distinctArgs) {
199
+ throw new Error(
200
+ `Subagents declare more than ${SUBAGENT_HOST_ARG_LIMITS.distinctArgs} distinct host arguments.`
201
+ );
202
+ }
203
+ return names;
204
+ }
205
+
206
+ function mergeSpec(
207
+ merged: Map<string, MergedHostArg>,
208
+ name: string,
209
+ spec: SubagentHostArgSpec
210
+ ): void {
211
+ let entry = merged.get(name);
212
+ if (entry == null) {
213
+ entry = {
214
+ description: spec.description,
215
+ values: [],
216
+ seenValues: new Set(),
217
+ freeForm: false,
218
+ maxLength: 0,
219
+ };
220
+ merged.set(name, entry);
221
+ }
222
+ if (spec.enum == null) {
223
+ entry.freeForm = true;
224
+ entry.maxLength = Math.max(entry.maxLength, getFreeFormMaxLength(spec));
225
+ return;
226
+ }
227
+ for (const value of spec.enum) {
228
+ entry.maxLength = Math.max(entry.maxLength, countCodePoints(value));
229
+ if (!entry.seenValues.has(value)) {
230
+ entry.seenValues.add(value);
231
+ entry.values.push(value);
232
+ }
233
+ }
234
+ if (entry.values.length > SUBAGENT_HOST_ARG_LIMITS.mergedEnumValues) {
235
+ throw new Error(
236
+ `Subagent host argument "${name}" lists more than ${SUBAGENT_HOST_ARG_LIMITS.mergedEnumValues} distinct values across subagents.`
237
+ );
238
+ }
239
+ }
240
+
241
+ function toPropertySchema(entry: MergedHostArg): JsonSchemaType {
242
+ if (!entry.freeForm) {
243
+ return {
244
+ type: 'string',
245
+ description: entry.description,
246
+ enum: entry.values,
247
+ };
248
+ }
249
+ return {
250
+ type: 'string',
251
+ description: entry.description,
252
+ maxLength: entry.maxLength,
253
+ pattern: FREE_FORM_SCHEMA_PATTERN,
254
+ };
255
+ }
256
+
257
+ function summarizeSpecs(entries: readonly HostArgEntry[]): string {
258
+ return entries
259
+ .map(
260
+ ([name, spec]) =>
261
+ `${name}: ${
262
+ spec.enum == null
263
+ ? `any text up to ${getFreeFormMaxLength(spec)} characters`
264
+ : spec.enum.join(' | ')
265
+ }`
266
+ )
267
+ .join('; ');
268
+ }
269
+
270
+ /**
271
+ * Builds one optional tool property per declared host argument plus a
272
+ * per-type summary of the values each subagent accepts. The first
273
+ * declaration's description wins; enums are unioned across subagents.
274
+ */
275
+ export function buildSubagentHostArgProperties(
276
+ configs: readonly SubagentHostArgDeclaration[]
277
+ ): {
278
+ properties: Record<string, JsonSchemaType>;
279
+ summaries: ReadonlyMap<string, string>;
280
+ } {
281
+ const merged = new Map<string, MergedHostArg>();
282
+ const summaries = new Map<string, string>();
283
+ for (const config of configs) {
284
+ const entries = getSubagentHostArgSpecs(config);
285
+ if (entries.length === 0) {
286
+ continue;
287
+ }
288
+ for (const [name, spec] of entries) {
289
+ mergeSpec(merged, name, spec);
290
+ }
291
+ summaries.set(config.type, summarizeSpecs(entries));
292
+ }
293
+ if (merged.size > SUBAGENT_HOST_ARG_LIMITS.distinctArgs) {
294
+ throw new Error(
295
+ `Subagents declare more than ${SUBAGENT_HOST_ARG_LIMITS.distinctArgs} distinct host arguments.`
296
+ );
297
+ }
298
+ const properties: Record<string, JsonSchemaType> = {};
299
+ for (const [name, entry] of merged) {
300
+ properties[name] = toPropertySchema(entry);
301
+ }
302
+ return { properties, summaries };
303
+ }
304
+
305
+ /**
306
+ * Reads the declared host argument properties from raw tool input. Missing,
307
+ * null, and blank values count as omitted; other non-string values are
308
+ * rejected rather than coerced.
309
+ */
310
+ export function pickSubagentHostArgInput(
311
+ input: object,
312
+ names: ReadonlySet<string>
313
+ ): SubagentHostArgsResult {
314
+ if (names.size === 0) {
315
+ return { ok: true };
316
+ }
317
+ const values = input as Readonly<Record<string, unknown>>;
318
+ const picked: Record<string, string> = {};
319
+ let count = 0;
320
+ for (const name of names) {
321
+ if (!Object.prototype.hasOwnProperty.call(values, name)) {
322
+ continue;
323
+ }
324
+ const value = values[name];
325
+ if (value == null) {
326
+ continue;
327
+ }
328
+ if (typeof value !== 'string') {
329
+ return { ok: false, message: `Error: "${name}" must be a string.` };
330
+ }
331
+ if (value.trim() === '') {
332
+ continue;
333
+ }
334
+ picked[name] = value;
335
+ count += 1;
336
+ }
337
+ return count === 0 ? { ok: true } : { ok: true, hostArgs: picked };
338
+ }
339
+
340
+ function checkValue(
341
+ type: string,
342
+ name: string,
343
+ spec: SubagentHostArgSpec,
344
+ value: string
345
+ ): string | undefined {
346
+ const omit = `Omit "${name}" to let the host choose.`;
347
+ if (spec.enum != null) {
348
+ return spec.enum.includes(value)
349
+ ? undefined
350
+ : `Error: "${name}" for subagent "${type}" must be one of: ${spec.enum.join(', ')}. ${omit}`;
351
+ }
352
+ const maxLength = getFreeFormMaxLength(spec);
353
+ return isBoundedText(value, maxLength)
354
+ ? undefined
355
+ : `Error: "${name}" for subagent "${type}" must be at most ${maxLength} characters without control characters. ${omit}`;
356
+ }
357
+
358
+ /**
359
+ * Checks call values against the selected subagent's declarations and
360
+ * returns them frozen in canonical key order. Rejects arguments the selected
361
+ * subagent does not declare, including ones declared by a sibling.
362
+ */
363
+ export function resolveSubagentHostArgs(
364
+ config: SubagentHostArgDeclaration,
365
+ hostArgs: SubagentHostArgs | undefined
366
+ ): SubagentHostArgsResult {
367
+ if (hostArgs == null) {
368
+ return { ok: true };
369
+ }
370
+ const names = Object.keys(hostArgs).sort();
371
+ if (names.length === 0) {
372
+ return { ok: true };
373
+ }
374
+ const specs = new Map(getSubagentHostArgSpecs(config));
375
+ const resolved: Record<string, string> = {};
376
+ for (const name of names) {
377
+ const spec = specs.get(name);
378
+ if (spec == null) {
379
+ return {
380
+ ok: false,
381
+ message: isSubagentHostArgName(name)
382
+ ? `Error: Subagent "${config.type}" does not accept "${name}". Omit it for this subagent type.`
383
+ : `Error: Subagent "${config.type}" received an unsupported argument.`,
384
+ };
385
+ }
386
+ const value = hostArgs[name];
387
+ if (typeof value !== 'string') {
388
+ return { ok: false, message: `Error: "${name}" must be a string.` };
389
+ }
390
+ const error = checkValue(config.type, name, spec, value);
391
+ if (error != null) {
392
+ return { ok: false, message: error };
393
+ }
394
+ resolved[name] = value;
395
+ }
396
+ return { ok: true, hostArgs: Object.freeze(resolved) };
397
+ }
398
+
399
+ /** Opaque, value-free identity of a call's host arguments for replay records. */
400
+ export function getSubagentHostArgsDigest(
401
+ hostArgs: SubagentHostArgs | undefined
402
+ ): string | undefined {
403
+ if (hostArgs == null) {
404
+ return undefined;
405
+ }
406
+ const entries = Object.entries(hostArgs).sort(([left], [right]) =>
407
+ left < right ? -1 : 1
408
+ );
409
+ if (entries.length === 0) {
410
+ return undefined;
411
+ }
412
+ return createHash('sha256').update(JSON.stringify(entries)).digest('hex');
413
+ }
@@ -21,9 +21,18 @@ export { InMemorySubagentTaskStore } from './InMemorySubagentTaskStore';
21
21
  export type { InMemorySubagentTaskStoreOptions } from './InMemorySubagentTaskStore';
22
22
  export {
23
23
  SubagentResolutionError,
24
+ SubagentHostArgumentError,
24
25
  getSubagentResolutionFailureMessage,
26
+ getSubagentHostArgumentFailureMessage,
25
27
  } from './diagnostics';
28
+ export {
29
+ SUBAGENT_HOST_ARG_LIMITS,
30
+ buildSubagentHostArgProperties,
31
+ resolveSubagentHostArgs,
32
+ } from './hostArgs';
26
33
  export type {
34
+ SubagentHostArgumentFailure,
35
+ SubagentHostArgumentRejection,
27
36
  SubagentResolutionPhase,
28
37
  SubagentResolutionCause,
29
38
  SubagentResolutionContext,
@@ -1,3 +1,4 @@
1
+ import type { ProviderTextProtection } from '@/protection/providerText';
1
2
  // src/types/graph.ts
2
3
  import type {
3
4
  BaseMessage,
@@ -408,6 +409,7 @@ export type ModelEndData =
408
409
  | undefined;
409
410
  export type GraphTools = GenericTool[] | BindToolsInput[] | GoogleAIToolType[];
410
411
  export type StandardGraphInput = {
412
+ providerTextProtection?: ProviderTextProtection;
411
413
  runId?: string;
412
414
  signal?: AbortSignal;
413
415
  agents: AgentInputs[];
@@ -574,6 +576,31 @@ export interface SubagentResolveConfigurable {
574
576
  user_id?: string;
575
577
  }
576
578
 
579
+ /**
580
+ * Optional string argument a host lets the parent model pass on a subagent
581
+ * call, such as where the child should run. The SDK checks the declared shape
582
+ * before resolution; the host still authorizes the value in its resolver.
583
+ */
584
+ export interface SubagentHostArgSpec {
585
+ /** Model-facing explanation of the argument and when to pass it. */
586
+ description: string;
587
+ /**
588
+ * Values this subagent accepts. Omit to accept a bounded free-form string,
589
+ * whose format the resolver must check.
590
+ */
591
+ enum?: readonly string[];
592
+ /** Maximum length of a free-form value (at most 256). */
593
+ maxLength?: number;
594
+ }
595
+
596
+ /** Host argument declarations keyed by tool-call property name. */
597
+ export type SubagentHostArgSpecs = Readonly<
598
+ Record<string, SubagentHostArgSpec>
599
+ >;
600
+
601
+ /** Validated host argument values from one subagent call. */
602
+ export type SubagentHostArgs = Readonly<Record<string, string>>;
603
+
577
604
  /** Runtime context supplied when a host lazily resolves a selected subagent. */
578
605
  export interface SubagentResolveContext {
579
606
  /** Stable subagent identity selected by the model. */
@@ -592,6 +619,13 @@ export interface SubagentResolveContext {
592
619
  signal: AbortSignal;
593
620
  /** Stable, sanitized host context from the parent tool invocation. */
594
621
  configurable?: Readonly<SubagentResolveConfigurable>;
622
+ /**
623
+ * Values the parent passed for this subagent's declared `hostArgs`, already
624
+ * checked against the declaration. Omitted when the call passed none. Bound
625
+ * to the execution: a reconstruction with different values is rejected
626
+ * before the resolver runs.
627
+ */
628
+ hostArgs?: SubagentHostArgs;
595
629
  }
596
630
 
597
631
  /** Host contract for resolving a selected subagent's full graph inputs. */
@@ -623,6 +657,12 @@ export interface SubagentConfig extends SubagentConfigBase {
623
657
  * `(context.executionId, context.descriptor.configId)`.
624
658
  */
625
659
  resolveAgentInputs?: SubagentAgentInputsResolver;
660
+ /**
661
+ * Optional per-call arguments the parent model may pass for this subagent,
662
+ * delivered to `resolveAgentInputs` as `context.hostArgs`. Only lazy
663
+ * configs accept them. Declaring none leaves the tool schema unchanged.
664
+ */
665
+ hostArgs?: SubagentHostArgSpecs;
626
666
  /** When true, reuse the parent's AgentInputs (context isolation without separate config). */
627
667
  self?: boolean;
628
668
  }
@@ -639,6 +679,7 @@ export interface GraphSubagentConfig extends SubagentConfigBase {
639
679
  kind: 'graph';
640
680
  configId?: never;
641
681
  resolveAgentInputs?: never;
682
+ hostArgs?: never;
642
683
  allowNested?: false;
643
684
  agents: AgentInputs[];
644
685
  /**
package/src/types/run.ts CHANGED
@@ -33,8 +33,9 @@ export type LegacyGraphConfig = BaseGraphConfig & {
33
33
  g.StandardGraphInput,
34
34
  /** `streamLimits` is excluded because legacy graphs receive limits only
35
35
  * via the top-level `RunConfig.streamLimits`; accepting the field here
36
- * would type-check but be silently ignored by `createLegacyGraph`. */
37
- 'provider' | 'clientOptions' | 'agents' | 'streamLimits'
36
+ * would type-check but be silently ignored by `createLegacyGraph`.
37
+ * Required provider-text protection also belongs only to RunConfig. */
38
+ 'provider' | 'clientOptions' | 'agents' | 'streamLimits' | 'providerTextProtection'
38
39
  > &
39
40
  Omit<g.AgentInputs, 'provider' | 'clientOptions' | 'agentId'>;
40
41
 
@@ -336,6 +337,7 @@ export type RunConfig = {
336
337
  * tool-call argument byte cap is ON by default, the per-turn event cap is
337
338
  * opt-in. See {@link StreamLimits}.
338
339
  */
340
+ providerTextProtection?: g.StandardGraphInput['providerTextProtection'];
339
341
  streamLimits?: StreamLimits;
340
342
  returnContent?: boolean;
341
343
  tokenCounter?: TokenCounter;
@@ -314,7 +314,10 @@ export type ToolNodeOptions = {
314
314
  /** SDK-owned checkpoint snapshot for open run-step lifecycle state. */
315
315
  createRunStepResumeState?: () => RunStepResumeState;
316
316
  /** Internal ownership bridge, awaited before ToolNode can execute a batch. */
317
- onToolCallsClaimed?: (messageId: string, config: RunnableConfig) => Promise<void>;
317
+ onToolCallsClaimed?: (
318
+ messageId: string,
319
+ config: RunnableConfig
320
+ ) => Promise<void>;
318
321
  };
319
322
 
320
323
  export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
@@ -550,6 +553,8 @@ export type JsonSchemaType = {
550
553
  properties?: Record<string, JsonSchemaType>;
551
554
  required?: string[];
552
555
  description?: string;
556
+ pattern?: string;
557
+ maxLength?: number;
553
558
  additionalProperties?: boolean | JsonSchemaType;
554
559
  };
555
560