@librechat/agents 4.0.2 → 4.0.3
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/cjs/agents/AgentContext.cjs +3 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/decisions/structuredChat.cjs +2 -8
- package/dist/cjs/decisions/structuredChat.cjs.map +1 -1
- package/dist/cjs/decisions/structuredOutput.cjs +18 -0
- package/dist/cjs/decisions/structuredOutput.cjs.map +1 -0
- package/dist/cjs/graphs/Graph.cjs +25 -9
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +34 -12
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +7 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/providerTextBoundary.cjs +355 -0
- package/dist/cjs/llm/providerTextBoundary.cjs.map +1 -0
- package/dist/cjs/llm/providerTextChunk.cjs +14 -0
- package/dist/cjs/llm/providerTextChunk.cjs.map +1 -0
- package/dist/cjs/llm/providerTextControls.cjs +236 -0
- package/dist/cjs/llm/providerTextControls.cjs.map +1 -0
- package/dist/cjs/llm/stream/smoother.cjs +2 -1
- package/dist/cjs/llm/stream/smoother.cjs.map +1 -1
- package/dist/cjs/main.cjs +10 -1
- package/dist/cjs/protection/providerText.cjs +224 -0
- package/dist/cjs/protection/providerText.cjs.map +1 -0
- package/dist/cjs/protection/providerTextInput.cjs +25 -0
- package/dist/cjs/protection/providerTextInput.cjs.map +1 -0
- package/dist/cjs/run.cjs +7 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +42 -14
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +26 -8
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/SubagentTool.cjs +11 -3
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +6 -5
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +2 -2
- package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +66 -16
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs +4 -2
- package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
- package/dist/cjs/tools/subagent/childGraphConfig.cjs +3 -1
- package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -1
- package/dist/cjs/tools/subagent/diagnostics.cjs +48 -5
- package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -1
- package/dist/cjs/tools/subagent/hostArgs.cjs +214 -0
- package/dist/cjs/tools/subagent/hostArgs.cjs.map +1 -0
- package/dist/cjs/tools/subagent/index.cjs +1 -0
- package/dist/cjs/tools/toolErrorContent.cjs +1 -1
- package/dist/esm/agents/AgentContext.mjs +3 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/decisions/structuredChat.mjs +2 -8
- package/dist/esm/decisions/structuredChat.mjs.map +1 -1
- package/dist/esm/decisions/structuredOutput.mjs +18 -0
- package/dist/esm/decisions/structuredOutput.mjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +25 -9
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +34 -12
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +7 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/providerTextBoundary.mjs +355 -0
- package/dist/esm/llm/providerTextBoundary.mjs.map +1 -0
- package/dist/esm/llm/providerTextChunk.mjs +13 -0
- package/dist/esm/llm/providerTextChunk.mjs.map +1 -0
- package/dist/esm/llm/providerTextControls.mjs +230 -0
- package/dist/esm/llm/providerTextControls.mjs.map +1 -0
- package/dist/esm/llm/stream/smoother.mjs +2 -1
- package/dist/esm/llm/stream/smoother.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -3
- package/dist/esm/protection/providerText.mjs +218 -0
- package/dist/esm/protection/providerText.mjs.map +1 -0
- package/dist/esm/protection/providerTextInput.mjs +24 -0
- package/dist/esm/protection/providerTextInput.mjs.map +1 -0
- package/dist/esm/run.mjs +7 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +42 -14
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +26 -8
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/SubagentTool.mjs +11 -3
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +6 -5
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +2 -2
- package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +66 -16
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs +4 -2
- package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
- package/dist/esm/tools/subagent/childGraphConfig.mjs +3 -1
- package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -1
- package/dist/esm/tools/subagent/diagnostics.mjs +46 -6
- package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -1
- package/dist/esm/tools/subagent/hostArgs.mjs +207 -0
- package/dist/esm/tools/subagent/hostArgs.mjs.map +1 -0
- package/dist/esm/tools/subagent/index.mjs +1 -0
- package/dist/esm/tools/toolErrorContent.mjs +1 -1
- package/dist/types/decisions/structuredChat.d.ts +3 -2
- package/dist/types/decisions/structuredOutput.d.ts +4 -0
- package/dist/types/graphs/Graph.d.ts +6 -3
- package/dist/types/index.d.ts +2 -0
- package/dist/types/llm/invoke.d.ts +4 -1
- package/dist/types/llm/providerTextBoundary.d.ts +4 -0
- package/dist/types/llm/providerTextChunk.d.ts +7 -0
- package/dist/types/llm/providerTextControls.d.ts +18 -0
- package/dist/types/protection/providerText.d.ts +77 -0
- package/dist/types/protection/providerTextInput.d.ts +3 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/tools/BashExecutor.d.ts +4 -0
- package/dist/types/tools/CodeExecutor.d.ts +4 -0
- package/dist/types/tools/ReadFile.d.ts +4 -0
- package/dist/types/tools/SkillTool.d.ts +4 -0
- package/dist/types/tools/SubagentTool.d.ts +2 -0
- package/dist/types/tools/ToolSearch.d.ts +4 -0
- package/dist/types/tools/search/schema.d.ts +4 -0
- package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +2 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +10 -2
- package/dist/types/tools/subagent/SubagentReplay.d.ts +3 -1
- package/dist/types/tools/subagent/diagnostics.d.ts +27 -3
- package/dist/types/tools/subagent/hostArgs.d.ts +57 -0
- package/dist/types/tools/subagent/index.d.ts +3 -2
- package/dist/types/types/graph.d.ts +36 -0
- package/dist/types/types/run.d.ts +4 -2
- package/dist/types/types/tools.d.ts +2 -0
- package/package.json +4 -4
- package/src/agents/AgentContext.ts +3 -2
- package/src/decisions/structuredChat.ts +9 -15
- package/src/decisions/structuredOutput.ts +25 -0
- package/src/graphs/Graph.ts +53 -9
- package/src/index.ts +7 -0
- package/src/llm/invoke.ts +48 -7
- package/src/llm/openai/index.ts +8 -0
- package/src/llm/providerTextBoundary.ts +446 -0
- package/src/llm/providerTextChunk.ts +15 -0
- package/src/llm/providerTextControls.ts +173 -0
- package/src/llm/stream/smoother.ts +4 -2
- package/src/protection/providerText.ts +325 -0
- package/src/protection/providerTextInput.ts +21 -0
- package/src/run.ts +9 -0
- package/src/stream.ts +33 -7
- package/src/summarization/node.ts +32 -14
- package/src/tools/SubagentTool.ts +14 -3
- package/src/tools/ToolNode.ts +7 -3
- package/src/tools/subagent/SubagentExecutionRegistry.ts +6 -2
- package/src/tools/subagent/SubagentExecutor.ts +145 -18
- package/src/tools/subagent/SubagentReplay.ts +10 -3
- package/src/tools/subagent/childGraphConfig.ts +12 -1
- package/src/tools/subagent/diagnostics.ts +98 -6
- package/src/tools/subagent/hostArgs.ts +413 -0
- package/src/tools/subagent/index.ts +9 -0
- package/src/types/graph.ts +41 -0
- package/src/types/run.ts +4 -2
- package/src/types/tools.ts +6 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
2
|
+
import { PreparedSubagentError } from '@/tools/preparedSubagents';
|
|
3
|
+
import { StreamLimitExceededError } from '@/llm/streamLimits';
|
|
4
|
+
|
|
5
|
+
export const PROVIDER_TEXT_PROTECTION_VERSION = 1;
|
|
6
|
+
|
|
7
|
+
export type ProviderTextProtectionErrorCode =
|
|
8
|
+
| 'blocked'
|
|
9
|
+
| 'unavailable'
|
|
10
|
+
| 'timeout'
|
|
11
|
+
| 'overflow'
|
|
12
|
+
| 'cancelled'
|
|
13
|
+
| 'unsupported'
|
|
14
|
+
| 'incompatible';
|
|
15
|
+
|
|
16
|
+
export type ProviderTextProtectionResult =
|
|
17
|
+
| {
|
|
18
|
+
readonly version: 1;
|
|
19
|
+
readonly ok: true;
|
|
20
|
+
readonly value: {
|
|
21
|
+
readonly content: string;
|
|
22
|
+
readonly replacements: number;
|
|
23
|
+
readonly categories: readonly {
|
|
24
|
+
readonly category: string;
|
|
25
|
+
readonly count: number;
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
readonly version: 1;
|
|
31
|
+
readonly ok: false;
|
|
32
|
+
readonly error: { readonly code: ProviderTextProtectionErrorCode };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Trusted host adapter. Classification must reject code, JSON and ambiguous text. */
|
|
36
|
+
export interface ProviderTextProtection {
|
|
37
|
+
readonly version: 1;
|
|
38
|
+
readonly timeoutMs: number;
|
|
39
|
+
readonly maxAttemptBytes: number;
|
|
40
|
+
/** Shared across all attempts using this policy, including child graphs. */
|
|
41
|
+
readonly maxBufferedBytes: number;
|
|
42
|
+
readonly classify: (content: string) => 'prose' | 'unsupported';
|
|
43
|
+
readonly inspect: (input: {
|
|
44
|
+
readonly version: 1;
|
|
45
|
+
readonly content: string;
|
|
46
|
+
readonly target: {
|
|
47
|
+
readonly source: 'message';
|
|
48
|
+
readonly field: 'text';
|
|
49
|
+
readonly provenance: 'model';
|
|
50
|
+
};
|
|
51
|
+
readonly signal: AbortSignal;
|
|
52
|
+
}) => ProviderTextProtectionResult | Promise<ProviderTextProtectionResult>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class ProviderTextProtectionError extends Error {
|
|
56
|
+
constructor(readonly code: ProviderTextProtectionErrorCode) {
|
|
57
|
+
const safeCode = errorCodes.has(code) ? code : 'incompatible';
|
|
58
|
+
super(`Provider text protection: ${safeCode}`);
|
|
59
|
+
this.code = safeCode;
|
|
60
|
+
this.name = 'ProviderTextProtectionError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const restartCancellations = new WeakSet<ProviderTextProtectionError>();
|
|
65
|
+
|
|
66
|
+
/** Internal control flow, never a host policy decision or public capability. */
|
|
67
|
+
export function createProviderTextRestartCancellation(): ProviderTextProtectionError {
|
|
68
|
+
const error = new ProviderTextProtectionError('cancelled');
|
|
69
|
+
restartCancellations.add(error);
|
|
70
|
+
return error;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isProviderTextRestartCancellation(error: unknown): boolean {
|
|
74
|
+
return error instanceof ProviderTextProtectionError && restartCancellations.has(error);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function protectionAbortError(parent?: AbortSignal): ProviderTextProtectionError | StreamLimitExceededError | PreparedSubagentError {
|
|
78
|
+
const reason: unknown = parent?.reason;
|
|
79
|
+
if (reason instanceof StreamLimitExceededError || reason instanceof PreparedSubagentError) return reason;
|
|
80
|
+
if (reason instanceof ProviderTextProtectionError) {
|
|
81
|
+
return isProviderTextRestartCancellation(reason) ? reason : new ProviderTextProtectionError(reason.code);
|
|
82
|
+
}
|
|
83
|
+
return new ProviderTextProtectionError('cancelled');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const protectedSignals = new WeakSet<AbortSignal>();
|
|
87
|
+
|
|
88
|
+
/** Provider smoothing must stay lazy while the release boundary owns raw text. */
|
|
89
|
+
export function isProtectedProviderTextSignal(signal?: AbortSignal): boolean {
|
|
90
|
+
return signal != null && protectedSignals.has(signal);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const budgets = new WeakMap<ProviderTextProtection, { bytes: number }>();
|
|
94
|
+
const errorCodes = new Set<ProviderTextProtectionErrorCode>([
|
|
95
|
+
'blocked',
|
|
96
|
+
'unavailable',
|
|
97
|
+
'timeout',
|
|
98
|
+
'overflow',
|
|
99
|
+
'cancelled',
|
|
100
|
+
'unsupported',
|
|
101
|
+
'incompatible',
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
export function validateProviderTextProtection(
|
|
105
|
+
policy: ProviderTextProtection
|
|
106
|
+
): void {
|
|
107
|
+
const version: number = policy.version;
|
|
108
|
+
if (version !== 1)
|
|
109
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
110
|
+
if (
|
|
111
|
+
typeof policy.inspect !== 'function' ||
|
|
112
|
+
typeof policy.classify !== 'function'
|
|
113
|
+
) {
|
|
114
|
+
throw new ProviderTextProtectionError('unavailable');
|
|
115
|
+
}
|
|
116
|
+
for (const value of [
|
|
117
|
+
policy.timeoutMs,
|
|
118
|
+
policy.maxAttemptBytes,
|
|
119
|
+
policy.maxBufferedBytes,
|
|
120
|
+
]) {
|
|
121
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
122
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (
|
|
126
|
+
policy.timeoutMs > 2_147_483_647 ||
|
|
127
|
+
policy.maxAttemptBytes > policy.maxBufferedBytes
|
|
128
|
+
) {
|
|
129
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** A lease stays charged while a cancellation-ignoring producer/handler still owns raw text. */
|
|
134
|
+
export class ProviderTextAttempt {
|
|
135
|
+
private readonly controller = new AbortController();
|
|
136
|
+
private readonly budget: { bytes: number };
|
|
137
|
+
private readonly deadline: number;
|
|
138
|
+
private bytes = 0;
|
|
139
|
+
private textLength = 0;
|
|
140
|
+
private readonly fragments: string[] = [];
|
|
141
|
+
private readonly pending = new Set<Promise<unknown>>();
|
|
142
|
+
private readonly timer: ReturnType<typeof setTimeout>;
|
|
143
|
+
private readonly abort: () => void;
|
|
144
|
+
private finished = false;
|
|
145
|
+
|
|
146
|
+
constructor(
|
|
147
|
+
private readonly policy: ProviderTextProtection,
|
|
148
|
+
private readonly parent?: AbortSignal
|
|
149
|
+
) {
|
|
150
|
+
validateProviderTextProtection(policy);
|
|
151
|
+
this.deadline = performance.now() + policy.timeoutMs;
|
|
152
|
+
this.budget = budgets.get(policy) ?? { bytes: 0 };
|
|
153
|
+
budgets.set(policy, this.budget);
|
|
154
|
+
if (parent?.aborted === true) throw protectionAbortError(parent);
|
|
155
|
+
this.charge(512);
|
|
156
|
+
protectedSignals.add(this.signal);
|
|
157
|
+
this.abort = (): void =>
|
|
158
|
+
this.controller.abort(protectionAbortError(this.parent));
|
|
159
|
+
parent?.addEventListener('abort', this.abort, { once: true });
|
|
160
|
+
this.timer = setTimeout(() => {
|
|
161
|
+
this.controller.abort(new ProviderTextProtectionError('timeout'));
|
|
162
|
+
}, policy.timeoutMs);
|
|
163
|
+
this.timer.unref();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get signal(): AbortSignal {
|
|
167
|
+
return this.controller.signal;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
check(): void {
|
|
171
|
+
if (this.signal.aborted) throw this.signal.reason;
|
|
172
|
+
if (this.parent?.aborted === true)
|
|
173
|
+
throw protectionAbortError(this.parent);
|
|
174
|
+
if (performance.now() >= this.deadline) {
|
|
175
|
+
this.controller.abort(new ProviderTextProtectionError('timeout'));
|
|
176
|
+
throw this.signal.reason;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private charge(bytes: number): void {
|
|
181
|
+
if (
|
|
182
|
+
this.bytes + bytes > this.policy.maxAttemptBytes ||
|
|
183
|
+
this.budget.bytes + bytes > this.policy.maxBufferedBytes
|
|
184
|
+
) {
|
|
185
|
+
this.controller.abort(new ProviderTextProtectionError('overflow'));
|
|
186
|
+
throw this.signal.reason;
|
|
187
|
+
}
|
|
188
|
+
this.bytes += bytes;
|
|
189
|
+
this.budget.bytes += bytes;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
append(text: string, cumulativeReplay = false): void {
|
|
193
|
+
this.check();
|
|
194
|
+
if (cumulativeReplay && this.hasCandidatePrefix(text)) text = text.slice(this.textLength);
|
|
195
|
+
if (!text) return;
|
|
196
|
+
const bytes = text.length * 2;
|
|
197
|
+
this.charge(bytes * 2 + 64);
|
|
198
|
+
this.fragments.push(text);
|
|
199
|
+
this.textLength += text.length;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Compare buffered fragments without allocating an uncharged joined copy. */
|
|
203
|
+
private hasCandidatePrefix(text: string): boolean {
|
|
204
|
+
if (this.textLength === 0 || text.length < this.textLength) return false;
|
|
205
|
+
let offset = 0;
|
|
206
|
+
for (const fragment of this.fragments) {
|
|
207
|
+
if (!text.startsWith(fragment, offset)) return false;
|
|
208
|
+
offset += fragment.length;
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
matchesText(text: string): boolean {
|
|
214
|
+
return text.length === this.textLength && (text.length === 0 || this.hasCandidatePrefix(text));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
retain(bytes: number): void {
|
|
218
|
+
this.check();
|
|
219
|
+
this.charge(bytes);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
observeChunk(): void {
|
|
223
|
+
this.check();
|
|
224
|
+
this.charge(128);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async wait<T>(work: Promise<T>): Promise<T> {
|
|
228
|
+
this.pending.add(work);
|
|
229
|
+
return new Promise<T>((resolve, reject) => {
|
|
230
|
+
const abort = (): void => {
|
|
231
|
+
this.signal.removeEventListener('abort', abort);
|
|
232
|
+
reject(this.signal.reason);
|
|
233
|
+
};
|
|
234
|
+
this.signal.addEventListener('abort', abort, { once: true });
|
|
235
|
+
if (this.signal.aborted) abort();
|
|
236
|
+
const settle = (): void => {
|
|
237
|
+
this.signal.removeEventListener('abort', abort);
|
|
238
|
+
this.pending.delete(work);
|
|
239
|
+
};
|
|
240
|
+
void work.then(
|
|
241
|
+
(value) => { settle(); resolve(value); },
|
|
242
|
+
(error: Error) => { settle(); reject(error); }
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async release(): Promise<string> {
|
|
248
|
+
this.check();
|
|
249
|
+
const content = this.fragments.join('');
|
|
250
|
+
if (!content) return '';
|
|
251
|
+
let result: ProviderTextProtectionResult;
|
|
252
|
+
try {
|
|
253
|
+
const treatment = this.policy.classify(content);
|
|
254
|
+
this.check();
|
|
255
|
+
if (treatment !== 'prose') {
|
|
256
|
+
throw new ProviderTextProtectionError('unsupported');
|
|
257
|
+
}
|
|
258
|
+
result = await this.wait(
|
|
259
|
+
Promise.resolve(
|
|
260
|
+
this.policy.inspect({
|
|
261
|
+
version: 1,
|
|
262
|
+
content,
|
|
263
|
+
target: { source: 'message', field: 'text', provenance: 'model' },
|
|
264
|
+
signal: this.signal,
|
|
265
|
+
})
|
|
266
|
+
)
|
|
267
|
+
);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
this.check();
|
|
270
|
+
if (isProviderTextRestartCancellation(error)) throw error;
|
|
271
|
+
if (error instanceof ProviderTextProtectionError) throw new ProviderTextProtectionError(error.code);
|
|
272
|
+
throw new ProviderTextProtectionError('unavailable');
|
|
273
|
+
}
|
|
274
|
+
this.check();
|
|
275
|
+
const canonical = validateResult(result);
|
|
276
|
+
this.check();
|
|
277
|
+
this.charge(canonical.length * 2);
|
|
278
|
+
return canonical;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
finish(): void {
|
|
282
|
+
if (this.finished) return;
|
|
283
|
+
this.finished = true;
|
|
284
|
+
clearTimeout(this.timer);
|
|
285
|
+
this.parent?.removeEventListener('abort', this.abort);
|
|
286
|
+
const dispose = (): void => {
|
|
287
|
+
this.fragments.length = 0;
|
|
288
|
+
this.budget.bytes -= this.bytes;
|
|
289
|
+
this.bytes = 0;
|
|
290
|
+
};
|
|
291
|
+
if (this.pending.size === 0) dispose();
|
|
292
|
+
else void Promise.allSettled([...this.pending]).then(dispose);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
interface UncheckedResult {
|
|
297
|
+
version?: number;
|
|
298
|
+
ok?: boolean;
|
|
299
|
+
value?: { content?: string; replacements?: number; categories?: readonly { category?: string; count?: number }[] };
|
|
300
|
+
error?: { code?: ProviderTextProtectionErrorCode };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function validateResult(result: UncheckedResult | undefined): string {
|
|
304
|
+
try {
|
|
305
|
+
if (result?.version !== 1 || typeof result.ok !== 'boolean') {
|
|
306
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
307
|
+
}
|
|
308
|
+
if (!result.ok) {
|
|
309
|
+
const code = result.error?.code;
|
|
310
|
+
throw new ProviderTextProtectionError(code != null && errorCodes.has(code) ? code : 'incompatible');
|
|
311
|
+
}
|
|
312
|
+
const value = result.value;
|
|
313
|
+
if (typeof value?.content !== 'string' || typeof value.replacements !== 'number' ||
|
|
314
|
+
!Number.isSafeInteger(value.replacements) || value.replacements < 0 ||
|
|
315
|
+
!Array.isArray(value.categories) || value.categories.length > 32 ||
|
|
316
|
+
value.categories.some((entry) => typeof entry.category !== 'string' ||
|
|
317
|
+
typeof entry.count !== 'number' || !Number.isSafeInteger(entry.count) || entry.count <= 0)) {
|
|
318
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
319
|
+
}
|
|
320
|
+
return value.content;
|
|
321
|
+
} catch (error) {
|
|
322
|
+
if (error instanceof ProviderTextProtectionError) throw error;
|
|
323
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const inputs = new WeakMap<object, { prototype: object | null; descriptors: PropertyDescriptorMap }>();
|
|
2
|
+
|
|
3
|
+
/** Internal provenance for the SDK's pure instruction transform, not a host capability. */
|
|
4
|
+
export function markProviderTextInput<T extends object>(input: T): T {
|
|
5
|
+
inputs.set(input, { prototype: Object.getPrototypeOf(input), descriptors: Object.getOwnPropertyDescriptors(input) });
|
|
6
|
+
return input;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isProviderTextInput(input: object): boolean {
|
|
10
|
+
const original = inputs.get(input);
|
|
11
|
+
if (original == null || original.prototype !== Object.getPrototypeOf(input)) return false;
|
|
12
|
+
const current: Partial<PropertyDescriptorMap> = Object.getOwnPropertyDescriptors(input);
|
|
13
|
+
const keys = Object.keys(original.descriptors);
|
|
14
|
+
return keys.length === Object.keys(current).length && keys.every((key) => {
|
|
15
|
+
const before = original.descriptors[key];
|
|
16
|
+
const after = current[key];
|
|
17
|
+
return after != null && before.value === after.value && before.get === after.get &&
|
|
18
|
+
before.set === after.set && before.writable === after.writable &&
|
|
19
|
+
before.enumerable === after.enumerable && before.configurable === after.configurable;
|
|
20
|
+
});
|
|
21
|
+
}
|
package/src/run.ts
CHANGED
|
@@ -102,6 +102,7 @@ import {
|
|
|
102
102
|
createTitleRunnable,
|
|
103
103
|
} from '@/utils/title';
|
|
104
104
|
import { applyGraphRuntimeConfig } from '@/graphs/applyGraphRuntimeConfig';
|
|
105
|
+
import { ProviderTextProtectionError } from '@/protection/providerText';
|
|
105
106
|
import { LANGFUSE_OPERATION_METADATA_KEY } from '@/langfuseOperation';
|
|
106
107
|
import { createTokenCounter, encodingForModel } from '@/utils/tokens';
|
|
107
108
|
import { stampSyntheticProviderMessage } from '@/messages/provenance';
|
|
@@ -477,6 +478,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
477
478
|
private subagentUsageSink?: t.SubagentUsageSink;
|
|
478
479
|
private preemption?: t.StreamPreemption;
|
|
479
480
|
private maxStopContinuations: number;
|
|
481
|
+
private providerTextProtection?: t.StandardGraphInput['providerTextProtection'];
|
|
480
482
|
private streamLimits?: t.StreamLimits;
|
|
481
483
|
private subagentTasks?: t.SubagentTaskConfig;
|
|
482
484
|
private subagentContext?: t.SubagentContextAdapter;
|
|
@@ -578,10 +580,15 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
578
580
|
config.maxStopContinuations
|
|
579
581
|
);
|
|
580
582
|
this.streamLimits = config.streamLimits;
|
|
583
|
+
this.providerTextProtection = config.providerTextProtection;
|
|
581
584
|
|
|
582
585
|
if (!config.graphConfig) {
|
|
583
586
|
throw new Error('Graph config not provided');
|
|
584
587
|
}
|
|
588
|
+
if ('providerTextProtection' in config.graphConfig &&
|
|
589
|
+
config.graphConfig.providerTextProtection != null) {
|
|
590
|
+
throw new ProviderTextProtectionError('incompatible');
|
|
591
|
+
}
|
|
585
592
|
|
|
586
593
|
/** Handle different graph types */
|
|
587
594
|
if (config.graphConfig.type === 'multi-agent') {
|
|
@@ -678,6 +685,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
678
685
|
onSubagentResolutionFailure: this.onSubagentResolutionFailure,
|
|
679
686
|
preemption: this.preemption,
|
|
680
687
|
streamLimits: this.streamLimits,
|
|
688
|
+
providerTextProtection: this.providerTextProtection,
|
|
681
689
|
toolExecution: this.toolExecution,
|
|
682
690
|
clientDelegatedToolNames: this.clientDelegatedToolNames,
|
|
683
691
|
},
|
|
@@ -725,6 +733,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
725
733
|
onSubagentResolutionFailure: this.onSubagentResolutionFailure,
|
|
726
734
|
preemption: this.preemption,
|
|
727
735
|
streamLimits: this.streamLimits,
|
|
736
|
+
providerTextProtection: this.providerTextProtection,
|
|
728
737
|
toolExecution: this.toolExecution,
|
|
729
738
|
},
|
|
730
739
|
});
|
package/src/stream.ts
CHANGED
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
import { createToolCallsDispatchedEvent, safeDispatchCustomEvent } from '@/utils/events';
|
|
59
59
|
import { resolveToolOutcome, outcomeFieldsFromResult } from '@/tools/intentArg';
|
|
60
60
|
import { snapshotValidatedModelChunk } from '@/graphs/acceptedModelResponse';
|
|
61
|
+
import { ProviderTextProtectionError } from '@/protection/providerText';
|
|
61
62
|
import { TOOL_OUTPUT_REF_PATTERN } from '@/tools/toolOutputReferences';
|
|
62
63
|
import { formatToolErrorContent } from '@/tools/toolErrorContent';
|
|
63
64
|
import { PreparedSubagentError } from '@/tools/preparedSubagents';
|
|
@@ -1687,6 +1688,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
1687
1688
|
graph.breakerAbort instanceof AbortController
|
|
1688
1689
|
? graph.breakerAbort
|
|
1689
1690
|
: undefined;
|
|
1691
|
+
const entrySignals = [eventBreaker?.signal, graph.signal, graph.config.signal];
|
|
1690
1692
|
/** Immutable scope captured at handler entry. A reset while this
|
|
1691
1693
|
* handler is suspended in an await replaces the object, so ONE
|
|
1692
1694
|
* reference comparison proves the event still belongs to the live run
|
|
@@ -1695,13 +1697,16 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
1695
1697
|
const runScopeInvalidated = (): boolean =>
|
|
1696
1698
|
entryRunScope != null && graph.runScope !== entryRunScope;
|
|
1697
1699
|
const throwIfRunBreakerTripped = (): void => {
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1700
|
+
for (const signal of entrySignals) {
|
|
1701
|
+
if (signal?.aborted === true &&
|
|
1702
|
+
(signal.reason instanceof StreamLimitExceededError ||
|
|
1703
|
+
signal.reason instanceof PreparedSubagentError ||
|
|
1704
|
+
signal.reason instanceof ProviderTextProtectionError)) {
|
|
1705
|
+
throw signal.reason;
|
|
1706
|
+
}
|
|
1707
|
+
if (signal?.aborted === true && graph.providerTextProtection != null) {
|
|
1708
|
+
throw new ProviderTextProtectionError('cancelled');
|
|
1709
|
+
}
|
|
1705
1710
|
}
|
|
1706
1711
|
};
|
|
1707
1712
|
|
|
@@ -1797,6 +1802,8 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
1797
1802
|
metadata,
|
|
1798
1803
|
agentContext,
|
|
1799
1804
|
});
|
|
1805
|
+
if (runScopeInvalidated()) return;
|
|
1806
|
+
throwIfRunBreakerTripped();
|
|
1800
1807
|
if (skipHandling) {
|
|
1801
1808
|
return;
|
|
1802
1809
|
}
|
|
@@ -1831,6 +1838,8 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
1831
1838
|
content,
|
|
1832
1839
|
metadata,
|
|
1833
1840
|
});
|
|
1841
|
+
if (runScopeInvalidated()) return;
|
|
1842
|
+
throwIfRunBreakerTripped();
|
|
1834
1843
|
}
|
|
1835
1844
|
|
|
1836
1845
|
if (
|
|
@@ -2033,6 +2042,23 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
2033
2042
|
});
|
|
2034
2043
|
runStep = graph.getRunStep(stepId);
|
|
2035
2044
|
}
|
|
2045
|
+
if (
|
|
2046
|
+
graph.providerTextProtection != null &&
|
|
2047
|
+
runStep?.type === StepTypes.TOOL_CALLS &&
|
|
2048
|
+
agentContext.currentTokenType === ContentTypes.TEXT &&
|
|
2049
|
+
(typeof content === 'string' || content.every(isTextContentPart))
|
|
2050
|
+
) {
|
|
2051
|
+
stepId = await dispatchMessageCreationStep({
|
|
2052
|
+
graph,
|
|
2053
|
+
stepKey,
|
|
2054
|
+
content,
|
|
2055
|
+
contentType: ContentTypes.TEXT,
|
|
2056
|
+
metadata,
|
|
2057
|
+
});
|
|
2058
|
+
if (runScopeInvalidated()) return;
|
|
2059
|
+
throwIfRunBreakerTripped();
|
|
2060
|
+
runStep = graph.getRunStep(stepId);
|
|
2061
|
+
}
|
|
2036
2062
|
if (!runStep) {
|
|
2037
2063
|
console.warn(`\n
|
|
2038
2064
|
==============================================================
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
resolveClientOptionsModel,
|
|
57
57
|
} from '@/llm/request';
|
|
58
58
|
import { renderCompactionSemanticIndex } from '@/summarization/semanticIndex';
|
|
59
|
+
import { ProviderTextProtectionError } from '@/protection/providerText';
|
|
59
60
|
import { safeDispatchCustomEvent, emitAgentLog } from '@/utils/events';
|
|
60
61
|
import { prepareToolsForPromptCache } from '@/llm/promptCacheTools';
|
|
61
62
|
import { attemptInvoke, tryFallbackProviders } from '@/llm/invoke';
|
|
@@ -739,16 +740,6 @@ async function executeSummarizationWithFallback(params: {
|
|
|
739
740
|
summaryText = result.text;
|
|
740
741
|
summaryUsage = result.usage;
|
|
741
742
|
} catch (primaryError) {
|
|
742
|
-
const primaryDescribed = describeProviderError(
|
|
743
|
-
primaryError,
|
|
744
|
-
clientConfig.provider,
|
|
745
|
-
clientConfig.modelName
|
|
746
|
-
);
|
|
747
|
-
log('error', `Summarization LLM call failed ${primaryDescribed.suffix}`, {
|
|
748
|
-
...primaryDescribed.data,
|
|
749
|
-
messagesToRefineCount: messages.length,
|
|
750
|
-
});
|
|
751
|
-
|
|
752
743
|
/**
|
|
753
744
|
* A tripped stream circuit breaker is a safety abort, not a
|
|
754
745
|
* summarization failure to recover from: retrying on fallback providers
|
|
@@ -758,7 +749,7 @@ async function executeSummarizationWithFallback(params: {
|
|
|
758
749
|
* rejects the run. Rethrown so the summarize node fails the run with
|
|
759
750
|
* the actionable limit error, consistent with agent turns and subagents.
|
|
760
751
|
*/
|
|
761
|
-
if (primaryError instanceof StreamLimitExceededError) {
|
|
752
|
+
if (primaryError instanceof StreamLimitExceededError || primaryError instanceof ProviderTextProtectionError) {
|
|
762
753
|
throw primaryError;
|
|
763
754
|
}
|
|
764
755
|
/** A parallel branch's trip aborts the composed summarization signal,
|
|
@@ -777,6 +768,16 @@ async function executeSummarizationWithFallback(params: {
|
|
|
777
768
|
}
|
|
778
769
|
}
|
|
779
770
|
|
|
771
|
+
const primaryDescribed = describeProviderError(
|
|
772
|
+
primaryError,
|
|
773
|
+
clientConfig.provider,
|
|
774
|
+
clientConfig.modelName
|
|
775
|
+
);
|
|
776
|
+
log('error', `Summarization LLM call failed ${primaryDescribed.suffix}`, {
|
|
777
|
+
...primaryDescribed.data,
|
|
778
|
+
messagesToRefineCount: messages.length,
|
|
779
|
+
});
|
|
780
|
+
|
|
780
781
|
const rawFallbacks = (
|
|
781
782
|
clientConfig.clientOptions as unknown as t.LLMConfig | undefined
|
|
782
783
|
)?.fallbacks;
|
|
@@ -819,7 +820,9 @@ async function executeSummarizationWithFallback(params: {
|
|
|
819
820
|
);
|
|
820
821
|
}
|
|
821
822
|
} catch (fbErr) {
|
|
822
|
-
|
|
823
|
+
const trip = findStreamLimitAbortReason(graph?.getBreakerSignal?.(), summarizeConfig?.signal);
|
|
824
|
+
if (trip != null) throw trip;
|
|
825
|
+
if (fbErr instanceof StreamLimitExceededError || fbErr instanceof ProviderTextProtectionError) {
|
|
823
826
|
throw fbErr;
|
|
824
827
|
}
|
|
825
828
|
const fbDescribed = describeFallbackError(fbErr, fallbacks);
|
|
@@ -842,6 +845,8 @@ async function executeSummarizationWithFallback(params: {
|
|
|
842
845
|
}
|
|
843
846
|
}
|
|
844
847
|
|
|
848
|
+
const trip = findStreamLimitAbortReason(graph?.getBreakerSignal?.(), summarizeConfig?.signal);
|
|
849
|
+
if (trip != null) throw trip;
|
|
845
850
|
return { text: summaryText, usage: summaryUsage, usedMetadataStub };
|
|
846
851
|
}
|
|
847
852
|
|
|
@@ -875,6 +880,11 @@ async function dispatchCompletionEvents(params: {
|
|
|
875
880
|
messagesAfterCount,
|
|
876
881
|
} = params;
|
|
877
882
|
|
|
883
|
+
const assertNotTripped = (): void => {
|
|
884
|
+
const trip = findStreamLimitAbortReason(graph.getBreakerSignal?.(), runnableConfig?.signal);
|
|
885
|
+
if (trip != null) throw trip;
|
|
886
|
+
};
|
|
887
|
+
assertNotTripped();
|
|
878
888
|
runStep.summary = summaryBlock;
|
|
879
889
|
if (summaryUsage) {
|
|
880
890
|
runStep.usage = {
|
|
@@ -892,6 +902,7 @@ async function dispatchCompletionEvents(params: {
|
|
|
892
902
|
runnableConfig
|
|
893
903
|
);
|
|
894
904
|
|
|
905
|
+
assertNotTripped();
|
|
895
906
|
if (runnableConfig) {
|
|
896
907
|
await safeDispatchCustomEvent(
|
|
897
908
|
GraphEvents.ON_SUMMARIZE_COMPLETE,
|
|
@@ -904,6 +915,7 @@ async function dispatchCompletionEvents(params: {
|
|
|
904
915
|
);
|
|
905
916
|
}
|
|
906
917
|
|
|
918
|
+
assertNotTripped();
|
|
907
919
|
const sessionId = graph.runId ?? '';
|
|
908
920
|
if (graph.hookRegistry?.hasHookFor('PostCompact', sessionId) === true) {
|
|
909
921
|
const threadId = (
|
|
@@ -1017,11 +1029,11 @@ function withBreakerSignal<T extends { signal?: AbortSignal }>(
|
|
|
1017
1029
|
* child's private breaker never fires for it. */
|
|
1018
1030
|
function findStreamLimitAbortReason(
|
|
1019
1031
|
...signals: Array<AbortSignal | undefined>
|
|
1020
|
-
): StreamLimitExceededError | undefined {
|
|
1032
|
+
): StreamLimitExceededError | ProviderTextProtectionError | undefined {
|
|
1021
1033
|
for (const signal of signals) {
|
|
1022
1034
|
if (
|
|
1023
1035
|
signal?.aborted === true &&
|
|
1024
|
-
signal.reason instanceof StreamLimitExceededError
|
|
1036
|
+
(signal.reason instanceof StreamLimitExceededError || signal.reason instanceof ProviderTextProtectionError)
|
|
1025
1037
|
) {
|
|
1026
1038
|
return signal.reason;
|
|
1027
1039
|
}
|
|
@@ -1526,6 +1538,8 @@ export function createSummarizeNode({
|
|
|
1526
1538
|
return { summarizationRequest: undefined };
|
|
1527
1539
|
}
|
|
1528
1540
|
|
|
1541
|
+
const postCallTrip = findStreamLimitAbortReason(entryBreakerSignal, config?.signal);
|
|
1542
|
+
if (postCallTrip != null) throw postCallTrip;
|
|
1529
1543
|
const summaryText = enrichSummary(rawText, messagesToRefine);
|
|
1530
1544
|
|
|
1531
1545
|
const tokenCount = await computeSummaryTokenCount(
|
|
@@ -1533,6 +1547,8 @@ export function createSummarizeNode({
|
|
|
1533
1547
|
agentContext
|
|
1534
1548
|
);
|
|
1535
1549
|
|
|
1550
|
+
const commitTrip = findStreamLimitAbortReason(entryBreakerSignal, config?.signal);
|
|
1551
|
+
if (commitTrip != null) throw commitTrip;
|
|
1536
1552
|
if (usedIntraTurnFallback) {
|
|
1537
1553
|
agentContext.setSummary(summaryText, tokenCount, {
|
|
1538
1554
|
precedesMessages: true,
|
|
@@ -1712,6 +1728,8 @@ export function createSummarizationChunkHandler({
|
|
|
1712
1728
|
throw error;
|
|
1713
1729
|
}
|
|
1714
1730
|
}
|
|
1731
|
+
const protectionTrip = findStreamLimitAbortReason(graph?.getBreakerController?.().signal, config.signal);
|
|
1732
|
+
if (protectionTrip != null) throw protectionTrip;
|
|
1715
1733
|
const chunkAny = chunk as Parameters<typeof getChunkContent>[0]['chunk'];
|
|
1716
1734
|
const raw = getChunkContent({ chunk: chunkAny, provider, reasoningKey });
|
|
1717
1735
|
if (raw == null || (typeof raw === 'string' && !raw)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JsonSchemaType, LCTool } from '@/types/tools';
|
|
2
2
|
import type { SubagentConfig } from '@/types';
|
|
3
|
+
import { buildSubagentHostArgProperties } from '@/tools/subagent/hostArgs';
|
|
3
4
|
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
4
5
|
import { Constants } from '@/common';
|
|
5
6
|
|
|
@@ -33,6 +34,9 @@ const RUN_IN_BACKGROUND_PROP_DESCRIPTION =
|
|
|
33
34
|
const SUBAGENT_THREAD_PROP_DESCRIPTION =
|
|
34
35
|
'Continue a host-owned child thread using a fresh execution lease. The saved thread must belong to this scope and subagent type. Only available with run_in_background.';
|
|
35
36
|
|
|
37
|
+
const HOST_ARGS_DESCRIPTION =
|
|
38
|
+
'\n\nOPTIONAL ARGUMENTS:\n- Some types accept extra arguments, listed in brackets after the type. Each is optional: omit it to let the host choose. Where values are listed, pass one listed for the selected type; where it says any text, pass a short value of your own.';
|
|
39
|
+
|
|
36
40
|
export const SubagentToolSchema = {
|
|
37
41
|
type: 'object',
|
|
38
42
|
properties: {
|
|
@@ -69,8 +73,13 @@ export function buildSubagentToolParams(
|
|
|
69
73
|
description: string;
|
|
70
74
|
} {
|
|
71
75
|
const types = configs.map((c) => c.type);
|
|
76
|
+
const hostArgs = buildSubagentHostArgProperties(configs);
|
|
72
77
|
const typeDescriptions = configs
|
|
73
|
-
.map((c) =>
|
|
78
|
+
.map((c) => {
|
|
79
|
+
const summary = hostArgs.summaries.get(c.type);
|
|
80
|
+
const line = `- "${c.type}" (${c.name}): ${c.description}`;
|
|
81
|
+
return summary == null ? line : `${line} [optional ${summary}]`;
|
|
82
|
+
})
|
|
74
83
|
.join('\n');
|
|
75
84
|
|
|
76
85
|
return {
|
|
@@ -96,8 +105,7 @@ export function buildSubagentToolParams(
|
|
|
96
105
|
},
|
|
97
106
|
}
|
|
98
107
|
: {}),
|
|
99
|
-
...(options.background === true &&
|
|
100
|
-
options.threadContinuation === true
|
|
108
|
+
...(options.background === true && options.threadContinuation === true
|
|
101
109
|
? {
|
|
102
110
|
subagent_thread_id: {
|
|
103
111
|
type: 'string',
|
|
@@ -105,10 +113,13 @@ export function buildSubagentToolParams(
|
|
|
105
113
|
},
|
|
106
114
|
}
|
|
107
115
|
: {}),
|
|
116
|
+
...hostArgs.properties,
|
|
108
117
|
},
|
|
109
118
|
required: ['description', 'subagent_type'],
|
|
110
119
|
},
|
|
111
120
|
description: `${SubagentToolDescription}${
|
|
121
|
+
hostArgs.summaries.size > 0 ? HOST_ARGS_DESCRIPTION : ''
|
|
122
|
+
}${
|
|
112
123
|
options.background === true
|
|
113
124
|
? '\n\nBACKGROUND EXECUTION:\n- Set run_in_background to true when you do not need the result immediately. The call returns a background_task_id; use the host background-task tools to poll, steer, queue, interrupt, or cancel it.'
|
|
114
125
|
: ''
|