@librechat/agents 3.7.20 → 3.7.22
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/graphs/Graph.cjs +21 -4
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +21 -6
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/main.cjs +2 -0
- package/dist/cjs/stream.cjs +38 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +27 -10
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +6 -5
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +95 -18
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +130 -24
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/diagnostics.cjs +26 -0
- package/dist/cjs/tools/diagnostics.cjs.map +1 -0
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +1 -1
- package/dist/cjs/tools/preparedSubagents.cjs +129 -0
- package/dist/cjs/tools/preparedSubagents.cjs.map +1 -0
- package/dist/cjs/tools/search/rerankers.cjs +6 -4
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +14 -2
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +1 -1
- package/dist/esm/graphs/Graph.mjs +21 -4
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +21 -6
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/stream.mjs +39 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -2
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +2 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +27 -10
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +6 -5
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +95 -18
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +129 -25
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/diagnostics.mjs +25 -0
- package/dist/esm/tools/diagnostics.mjs.map +1 -0
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +1 -1
- package/dist/esm/tools/preparedSubagents.mjs +128 -0
- package/dist/esm/tools/preparedSubagents.mjs.map +1 -0
- package/dist/esm/tools/search/rerankers.mjs +6 -5
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +14 -2
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +1 -1
- package/dist/types/graphs/Graph.d.ts +6 -1
- package/dist/types/tools/CodeExecutor.d.ts +13 -2
- package/dist/types/tools/ToolNode.d.ts +13 -1
- package/dist/types/tools/ToolSearch.d.ts +46 -2
- package/dist/types/tools/diagnostics.d.ts +52 -0
- package/dist/types/tools/preparedSubagents.d.ts +26 -0
- package/dist/types/tools/search/rerankers.d.ts +10 -0
- package/dist/types/types/tools.d.ts +17 -0
- package/package.json +3 -2
- package/src/graphs/Graph.ts +63 -5
- package/src/llm/invoke.ts +35 -8
- package/src/stream.ts +70 -1
- package/src/tools/BashExecutor.ts +9 -4
- package/src/tools/BashProgrammaticToolCalling.ts +6 -3
- package/src/tools/CodeExecutor.ts +88 -15
- package/src/tools/ProgrammaticToolCalling.ts +24 -12
- package/src/tools/ToolNode.ts +189 -43
- package/src/tools/ToolSearch.ts +278 -28
- package/src/tools/diagnostics.ts +107 -0
- package/src/tools/preparedSubagents.ts +228 -0
- package/src/tools/search/rerankers.ts +18 -3
- package/src/tools/search/search.ts +30 -5
- package/src/types/tools.ts +17 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
2
|
+
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
3
|
+
import { stableStringify, normalizeError } from './eagerEventExecution';
|
|
4
|
+
|
|
5
|
+
/** A model attempt cannot safely retry after delegated work has started. */
|
|
6
|
+
export class PreparedSubagentError extends Error {
|
|
7
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
8
|
+
super(message, options);
|
|
9
|
+
this.name = 'PreparedSubagentError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Outcome =
|
|
14
|
+
| { output: unknown; error?: never }
|
|
15
|
+
| { error: Error; output?: never };
|
|
16
|
+
type Attempt = {
|
|
17
|
+
keys: Set<string>;
|
|
18
|
+
cancelled: boolean;
|
|
19
|
+
config: RunnableConfig;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const PREPARED_INVOCATION = Symbol('prepared-subagent-invocation');
|
|
23
|
+
type PreparedCall = ToolCall & { [PREPARED_INVOCATION]?: symbol };
|
|
24
|
+
|
|
25
|
+
type Reservation = {
|
|
26
|
+
token: symbol;
|
|
27
|
+
callId: string;
|
|
28
|
+
attempt: string;
|
|
29
|
+
fingerprint: string;
|
|
30
|
+
controller: AbortController;
|
|
31
|
+
outcome: Promise<Outcome>;
|
|
32
|
+
committed: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Owns speculative invocation work, never tool results or graph state. Only
|
|
37
|
+
* explicitly open model attempts admit work; closing an attempt fences late
|
|
38
|
+
* callbacks without retaining tombstones. Normal ToolNode execution adopts the
|
|
39
|
+
* raw output and performs its existing lifecycle/output processing once.
|
|
40
|
+
*/
|
|
41
|
+
export class PreparedSubagents {
|
|
42
|
+
private readonly attempts = new Map<string, Attempt>();
|
|
43
|
+
private readonly reservations = new Map<string, Reservation>();
|
|
44
|
+
private readonly running = new Set<AbortController>();
|
|
45
|
+
private epoch = 0;
|
|
46
|
+
|
|
47
|
+
begin(attempt: string, config: RunnableConfig = {}): void {
|
|
48
|
+
this.attempts.set(attempt, {
|
|
49
|
+
keys: new Set(),
|
|
50
|
+
cancelled: false,
|
|
51
|
+
config: {
|
|
52
|
+
...config,
|
|
53
|
+
configurable: { ...config.configurable },
|
|
54
|
+
metadata: { ...config.metadata },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getConfig(attempt: string): RunnableConfig | undefined {
|
|
60
|
+
const entry = this.attempts.get(attempt);
|
|
61
|
+
return entry?.cancelled === false ? entry.config : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isOpen(attempt: string): boolean {
|
|
65
|
+
return this.attempts.get(attempt)?.cancelled === false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
start(
|
|
69
|
+
attempt: string,
|
|
70
|
+
owner: string,
|
|
71
|
+
call: ToolCall,
|
|
72
|
+
capacity: number,
|
|
73
|
+
invoke: (signal: AbortSignal) => Promise<unknown>
|
|
74
|
+
): boolean {
|
|
75
|
+
const entries = this.attempts.get(attempt);
|
|
76
|
+
if (
|
|
77
|
+
entries == null ||
|
|
78
|
+
entries.cancelled ||
|
|
79
|
+
call.id == null ||
|
|
80
|
+
call.id === ''
|
|
81
|
+
) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const key = JSON.stringify([owner, call.id]);
|
|
85
|
+
const canonical = fingerprint(call);
|
|
86
|
+
const previous = this.reservations.get(key);
|
|
87
|
+
if (previous != null) {
|
|
88
|
+
if (previous.attempt !== attempt || previous.fingerprint !== canonical) {
|
|
89
|
+
throw new PreparedSubagentError(
|
|
90
|
+
'Conflicting eager subagent call identity.'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (
|
|
96
|
+
!Number.isSafeInteger(capacity) ||
|
|
97
|
+
capacity <= 0 ||
|
|
98
|
+
this.reservations.size >= capacity ||
|
|
99
|
+
this.running.size >= capacity
|
|
100
|
+
) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const controller = new AbortController();
|
|
104
|
+
this.running.add(controller);
|
|
105
|
+
const reservation: Reservation = {
|
|
106
|
+
token: Symbol(),
|
|
107
|
+
attempt,
|
|
108
|
+
callId: call.id,
|
|
109
|
+
fingerprint: canonical,
|
|
110
|
+
controller,
|
|
111
|
+
committed: false,
|
|
112
|
+
outcome: Promise.resolve()
|
|
113
|
+
.then(() => {
|
|
114
|
+
controller.signal.throwIfAborted();
|
|
115
|
+
return invoke(controller.signal);
|
|
116
|
+
})
|
|
117
|
+
.then(
|
|
118
|
+
(output): Outcome => ({ output }),
|
|
119
|
+
(error): Outcome => ({ error: normalizeError(error) })
|
|
120
|
+
)
|
|
121
|
+
.finally(() => {
|
|
122
|
+
this.running.delete(controller);
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
entries.keys.add(key);
|
|
126
|
+
this.reservations.set(key, reservation);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
finish(attempt: string, calls?: ToolCall[], cause?: unknown): void {
|
|
131
|
+
const entries = this.attempts.get(attempt);
|
|
132
|
+
this.attempts.delete(attempt);
|
|
133
|
+
if (entries == null || entries.keys.size === 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const finalCalls = new Map(calls?.map((call) => [call.id, call]));
|
|
137
|
+
for (const key of entries.keys) {
|
|
138
|
+
const record = this.reservations.get(key);
|
|
139
|
+
const finalCall =
|
|
140
|
+
record == null ? undefined : finalCalls.get(record.callId);
|
|
141
|
+
if (
|
|
142
|
+
entries.cancelled ||
|
|
143
|
+
record == null ||
|
|
144
|
+
record.attempt !== attempt ||
|
|
145
|
+
finalCall == null ||
|
|
146
|
+
fingerprint(finalCall) !== record.fingerprint
|
|
147
|
+
) {
|
|
148
|
+
const error = new PreparedSubagentError(
|
|
149
|
+
'The model attempt ended or changed after a subagent started; refusing automatic retry.',
|
|
150
|
+
{ cause }
|
|
151
|
+
);
|
|
152
|
+
for (const pendingKey of entries.keys) {
|
|
153
|
+
const pending = this.reservations.get(pendingKey);
|
|
154
|
+
if (pending?.attempt === attempt) {
|
|
155
|
+
pending.controller.abort(error);
|
|
156
|
+
this.reservations.delete(pendingKey);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
record.committed = true;
|
|
162
|
+
(finalCall as PreparedCall)[PREPARED_INVOCATION] = record.token;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
owns(owner: string, call: ToolCall): boolean {
|
|
167
|
+
const record = this.reservations.get(JSON.stringify([owner, call.id]));
|
|
168
|
+
return (
|
|
169
|
+
record != null &&
|
|
170
|
+
(call as PreparedCall)[PREPARED_INVOCATION] === record.token
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
take(owner: string, call: ToolCall): Promise<unknown> | undefined {
|
|
175
|
+
const key = JSON.stringify([owner, call.id]);
|
|
176
|
+
const record = this.reservations.get(key);
|
|
177
|
+
const token = (call as PreparedCall)[PREPARED_INVOCATION];
|
|
178
|
+
if (record == null && token == null) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
if (record == null || token !== record.token) {
|
|
182
|
+
return Promise.reject(
|
|
183
|
+
new PreparedSubagentError(
|
|
184
|
+
'Eager subagent invocation is no longer owned by this call.'
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
this.reservations.delete(key);
|
|
189
|
+
if (!record.committed || record.fingerprint !== fingerprint(call)) {
|
|
190
|
+
const error = new PreparedSubagentError(
|
|
191
|
+
'Subagent arguments changed after eager invocation.'
|
|
192
|
+
);
|
|
193
|
+
record.controller.abort(error);
|
|
194
|
+
return Promise.reject(error);
|
|
195
|
+
}
|
|
196
|
+
const epoch = this.epoch;
|
|
197
|
+
return record.outcome.then((result) => {
|
|
198
|
+
if (this.epoch !== epoch) {
|
|
199
|
+
throw new PreparedSubagentError(
|
|
200
|
+
'Eager subagent result belongs to a retired run.'
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
record.controller.signal.throwIfAborted();
|
|
204
|
+
if ('error' in result) {
|
|
205
|
+
throw result.error;
|
|
206
|
+
}
|
|
207
|
+
return result.output;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
clear(): void {
|
|
212
|
+
this.epoch++;
|
|
213
|
+
const reason = new PreparedSubagentError(
|
|
214
|
+
'Eager subagent execution was cancelled.'
|
|
215
|
+
);
|
|
216
|
+
for (const controller of this.running) {
|
|
217
|
+
controller.abort(reason);
|
|
218
|
+
}
|
|
219
|
+
this.reservations.clear();
|
|
220
|
+
for (const attempt of this.attempts.values()) {
|
|
221
|
+
attempt.cancelled = true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function fingerprint(call: ToolCall): string {
|
|
227
|
+
return stableStringify({ name: call.name, args: call.args });
|
|
228
|
+
}
|
|
@@ -21,6 +21,23 @@ const getDefaultCohereApiUrl = (): string =>
|
|
|
21
21
|
? process.env.COHERE_API_URL
|
|
22
22
|
: DEFAULT_COHERE_API_URL;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Ranking used whenever no reranker scores the chunks: the candidates' own
|
|
26
|
+
* order, capped at `topK`, with a neutral score.
|
|
27
|
+
*
|
|
28
|
+
* Exported because it is not only a *fallback*. With `rerankerType: 'none'`
|
|
29
|
+
* there is no reranker to fall back from, and the search pipeline needs the
|
|
30
|
+
* same ranking to hand the scraped text downstream — see `getHighlights` in
|
|
31
|
+
* `./search`.
|
|
32
|
+
*/
|
|
33
|
+
export const getDefaultRanking = (
|
|
34
|
+
documents: string[],
|
|
35
|
+
topK: number
|
|
36
|
+
): t.Highlight[] =>
|
|
37
|
+
documents
|
|
38
|
+
.slice(0, Math.min(topK, documents.length))
|
|
39
|
+
.map((doc) => ({ text: doc, score: 0 }));
|
|
40
|
+
|
|
24
41
|
export abstract class BaseReranker {
|
|
25
42
|
protected apiKey: string | undefined;
|
|
26
43
|
protected logger: t.Logger;
|
|
@@ -51,9 +68,7 @@ export abstract class BaseReranker {
|
|
|
51
68
|
documents: string[],
|
|
52
69
|
topK: number
|
|
53
70
|
): t.Highlight[] {
|
|
54
|
-
return documents
|
|
55
|
-
.slice(0, Math.min(topK, documents.length))
|
|
56
|
-
.map((doc) => ({ text: doc, score: 0 }));
|
|
71
|
+
return getDefaultRanking(documents, topK);
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
/** A direct caller has no enclosing search to fold into, so one
|
|
@@ -10,7 +10,7 @@ import { createKeenableAPI } from './keenable-search';
|
|
|
10
10
|
import { createTavilyAPI } from './tavily-search';
|
|
11
11
|
import { createSearchMetrics } from './metrics';
|
|
12
12
|
import { createCrwAPI } from './crw-search';
|
|
13
|
-
import { BaseReranker } from './rerankers';
|
|
13
|
+
import { BaseReranker, getDefaultRanking } from './rerankers';
|
|
14
14
|
|
|
15
15
|
/** Engines queried when `searxngSearchOptions.engines` is not configured. */
|
|
16
16
|
const DEFAULT_SEARXNG_ENGINES = 'google,bing,duckduckgo';
|
|
@@ -144,9 +144,20 @@ function createSourceUpdateCallback(sourceMap: Map<string, t.ValidSource>) {
|
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/** Provider label for the metrics summary when reranking is switched off.
|
|
148
|
+
* `undefined` would be indistinguishable from a reranker that never ran. */
|
|
149
|
+
const NO_RERANKER_PROVIDER = 'none';
|
|
150
|
+
|
|
147
151
|
/** Returns undefined without logging when there is nothing to rank: an empty
|
|
148
|
-
* scrape is already counted by the scrape summary
|
|
149
|
-
*
|
|
152
|
+
* scrape is already counted by the scrape summary.
|
|
153
|
+
*
|
|
154
|
+
* A *missing* reranker is not the same as nothing to rank. `rerankerType:
|
|
155
|
+
* 'none'` is a schema-valid opt-out, and `createReranker` returns `undefined`
|
|
156
|
+
* for it by design. Returning `undefined` here as well would leave the source
|
|
157
|
+
* without highlights, and `expandHighlights` then strips its content — the
|
|
158
|
+
* search would answer with links and no text at all. So the chunks are handed
|
|
159
|
+
* on in their original order via `getDefaultRanking`, exactly what every
|
|
160
|
+
* reranker's own failure path already does. */
|
|
150
161
|
const getHighlights = async ({
|
|
151
162
|
query,
|
|
152
163
|
content,
|
|
@@ -164,7 +175,7 @@ const getHighlights = async ({
|
|
|
164
175
|
maxContentLength?: number;
|
|
165
176
|
chunkOptions?: { chunkSize: number; chunkOverlap: number };
|
|
166
177
|
}): Promise<t.Highlight[] | undefined> => {
|
|
167
|
-
if (!content
|
|
178
|
+
if (!content) {
|
|
168
179
|
return;
|
|
169
180
|
}
|
|
170
181
|
|
|
@@ -181,7 +192,7 @@ const getHighlights = async ({
|
|
|
181
192
|
);
|
|
182
193
|
} catch (error) {
|
|
183
194
|
metrics.recordRerank({
|
|
184
|
-
provider: reranker
|
|
195
|
+
provider: reranker?.provider ?? NO_RERANKER_PROVIDER,
|
|
185
196
|
chunks: 0,
|
|
186
197
|
results: 0,
|
|
187
198
|
durationMs: Date.now() - chunkStartedAt,
|
|
@@ -191,6 +202,20 @@ const getHighlights = async ({
|
|
|
191
202
|
return;
|
|
192
203
|
}
|
|
193
204
|
|
|
205
|
+
/** Reranking is switched off, so the chunks pass through unscored. Recorded
|
|
206
|
+
* like any other rerank so the search summary still accounts for the source;
|
|
207
|
+
* it carries no `reason`, because nothing failed. */
|
|
208
|
+
if (!reranker) {
|
|
209
|
+
const highlights = getDefaultRanking(documents, topResults);
|
|
210
|
+
metrics.recordRerank({
|
|
211
|
+
provider: NO_RERANKER_PROVIDER,
|
|
212
|
+
chunks: documents.length,
|
|
213
|
+
results: highlights.length,
|
|
214
|
+
durationMs: Date.now() - chunkStartedAt,
|
|
215
|
+
});
|
|
216
|
+
return highlights;
|
|
217
|
+
}
|
|
218
|
+
|
|
194
219
|
const rerankStartedAt = Date.now();
|
|
195
220
|
try {
|
|
196
221
|
return await reranker.rerank(query, documents, topResults, metrics);
|
package/src/types/tools.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ToolErrorData,
|
|
12
12
|
} from './stream';
|
|
13
13
|
import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
|
|
14
|
+
import type { PreparedSubagents } from '@/tools/preparedSubagents';
|
|
14
15
|
import type { RunBreakerScope } from '@/llm/streamLimits';
|
|
15
16
|
import type { HumanInTheLoopConfig } from './hitl';
|
|
16
17
|
import type { LangfuseConfig } from './graph';
|
|
@@ -56,6 +57,13 @@ export type EagerEventToolExecutionConfig = {
|
|
|
56
57
|
* execution with final args.
|
|
57
58
|
*/
|
|
58
59
|
excludeToolNames?: string[];
|
|
60
|
+
/**
|
|
61
|
+
* Maximum retained early foreground subagent invocations per graph. Defaults
|
|
62
|
+
* to 4; 0 disables them. Checkpoints, parent tool hooks, background calls and
|
|
63
|
+
* control-flow graph tools retain normal batch execution. Once a child has
|
|
64
|
+
* started, a failed/revised model attempt fails instead of automatically retrying.
|
|
65
|
+
*/
|
|
66
|
+
maxPendingSubagents?: number;
|
|
59
67
|
};
|
|
60
68
|
|
|
61
69
|
export type EagerEventToolExecutionOutcome =
|
|
@@ -290,6 +298,8 @@ export type ToolNodeOptions = {
|
|
|
290
298
|
* controller.
|
|
291
299
|
*/
|
|
292
300
|
getRunScope?: () => RunBreakerScope;
|
|
301
|
+
/** Internal graph-owned prepared invocation registry. */
|
|
302
|
+
preparedSubagents?: PreparedSubagents;
|
|
293
303
|
/** SDK-owned checkpoint bridge for open run-step lifecycle state. */
|
|
294
304
|
restoreRunStepResumeState?: (
|
|
295
305
|
state?: RunStepResumeState,
|
|
@@ -1259,6 +1269,13 @@ export type ToolSearchArtifact = {
|
|
|
1259
1269
|
total_searched: number;
|
|
1260
1270
|
pattern: string;
|
|
1261
1271
|
error?: string;
|
|
1272
|
+
/** Servers that had tools to search, emitted when a server filter matched
|
|
1273
|
+
* nothing so the caller can see what it could have asked for. */
|
|
1274
|
+
available_mcp_servers?: string[];
|
|
1275
|
+
/** Requested servers that matched no registered server. */
|
|
1276
|
+
unmatched_mcp_servers?: string[];
|
|
1277
|
+
/** Requested servers that exist but had no tools left to search. */
|
|
1278
|
+
idle_mcp_servers?: string[];
|
|
1262
1279
|
};
|
|
1263
1280
|
};
|
|
1264
1281
|
|