@modelprofile.com/flexharness 10.12.0 → 10.14.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.codesandbox.d.ts +39 -0
- package/dist_ts/classes.codesandbox.js +539 -0
- package/dist_ts/classes.flexharness.d.ts +17 -0
- package/dist_ts/classes.flexharness.js +222 -8
- package/dist_ts/classes.toolcatalog.d.ts +90 -0
- package/dist_ts/classes.toolcatalog.js +1398 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/interfaces.d.ts +237 -3
- package/dist_ts/interfaces.js +50 -1
- package/dist_ts/plugins.d.ts +66 -1
- package/dist_ts/plugins.js +19 -1
- package/dist_ts/utils.harnessoptions.d.ts +7 -1
- package/dist_ts/utils.harnessoptions.js +48 -2
- package/dist_ts/utils.json.js +3 -1
- package/dist_ts/utils.schemadeclarations.d.ts +53 -0
- package/dist_ts/utils.schemadeclarations.js +315 -0
- package/dist_ts/utils.tooloutput.d.ts +2 -0
- package/dist_ts/utils.tooloutput.js +11 -1
- package/dist_ts_compaction/plugins.d.ts +3 -3
- package/dist_ts_compaction/plugins.js +3 -3
- package/dist_ts_compaction/smartagent.compaction.d.ts +10 -1
- package/dist_ts_compaction/smartagent.compaction.js +79 -38
- package/package.json +17 -4
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.codesandbox.ts +565 -0
- package/ts/classes.flexharness.ts +242 -7
- package/ts/classes.toolcatalog.ts +1512 -0
- package/ts/index.ts +1 -0
- package/ts/interfaces.ts +285 -3
- package/ts/plugins.ts +86 -0
- package/ts/readme.md +1 -1
- package/ts/utils.harnessoptions.ts +55 -0
- package/ts/utils.json.ts +2 -0
- package/ts/utils.schemadeclarations.ts +322 -0
- package/ts/utils.tooloutput.ts +9 -0
- package/ts_compaction/plugins.ts +3 -3
- package/ts_compaction/smartagent.compaction.ts +90 -34
|
@@ -28,8 +28,16 @@ import {
|
|
|
28
28
|
FLEX_REVERSION_REFERENCE_MAX_BYTES,
|
|
29
29
|
FLEX_REVERSION_WORKSPACE_ID_MAX_BYTES,
|
|
30
30
|
FLEX_REVERSION_WORKSPACE_LABEL_MAX_BYTES,
|
|
31
|
+
FLEX_CODE_MODE_TOOL_NAME,
|
|
31
32
|
FLEX_SESSION_IMPORT_MAX_DEPTH,
|
|
33
|
+
FLEX_TOOL_CATALOG_TOOL_NAMES,
|
|
32
34
|
} from './interfaces.js';
|
|
35
|
+
import {
|
|
36
|
+
FlexToolCatalog,
|
|
37
|
+
assertNoCatalogCollisions,
|
|
38
|
+
catalogToolInputSchema,
|
|
39
|
+
} from './classes.toolcatalog.js';
|
|
40
|
+
import { FlexCodeSandbox, type IFlexCodeHost } from './classes.codesandbox.js';
|
|
33
41
|
import type {
|
|
34
42
|
IFlexAgentContextCompactionOptions,
|
|
35
43
|
IFlexAgentContextInvocation,
|
|
@@ -112,6 +120,10 @@ import type {
|
|
|
112
120
|
IFlexSubagentDefinition,
|
|
113
121
|
IFlexSubagentProvenance,
|
|
114
122
|
IFlexTerminalProjection,
|
|
123
|
+
IFlexCatalogDescribeInput,
|
|
124
|
+
IFlexCatalogProposal,
|
|
125
|
+
IFlexCatalogSearchInput,
|
|
126
|
+
IFlexToolCatalogOptions,
|
|
115
127
|
IFlexToolHandle,
|
|
116
128
|
IFlexToolMessagePart,
|
|
117
129
|
IFlexToolProviderContext,
|
|
@@ -168,6 +180,7 @@ import {
|
|
|
168
180
|
normalizeAgentSessionPolicy,
|
|
169
181
|
normalizeBuiltInTools,
|
|
170
182
|
normalizeSlashCommands,
|
|
183
|
+
normalizeToolCatalogOptions,
|
|
171
184
|
normalizeSubagents,
|
|
172
185
|
requireHarnessStores,
|
|
173
186
|
resolveBoundedPositiveInteger,
|
|
@@ -212,7 +225,7 @@ import {
|
|
|
212
225
|
validateSessionImportHandle,
|
|
213
226
|
type INormalizedSessionImportOptions,
|
|
214
227
|
} from './utils.sessionimport.js';
|
|
215
|
-
import { wrapToolSet } from './utils.tooloutput.js';
|
|
228
|
+
import { settleToolOutput, wrapToolSet } from './utils.tooloutput.js';
|
|
216
229
|
import {
|
|
217
230
|
validateCompactSessionOptions,
|
|
218
231
|
validateCreateProjectTaskInput,
|
|
@@ -431,6 +444,12 @@ interface IActiveRun {
|
|
|
431
444
|
reservedUserMessage?: IFlexMessage;
|
|
432
445
|
reservedAssistantMessage?: IFlexMessage;
|
|
433
446
|
modelResolution?: IFlexResolvedModel;
|
|
447
|
+
/** The tool-provider context of the run, passed to catalog methods. */
|
|
448
|
+
toolProviderContext?: Readonly<IFlexToolProviderContext<unknown>>;
|
|
449
|
+
/** The catalog methods the run offers through search_tools, describe_tools and execute. */
|
|
450
|
+
toolCatalog?: FlexToolCatalog<unknown>;
|
|
451
|
+
/** The sandbox `run_code` programs run in, loaded when the catalog has code mode. */
|
|
452
|
+
codeSandbox?: FlexCodeSandbox;
|
|
434
453
|
subagents: ReadonlyMap<string, Readonly<IFlexSubagentDefinition>>;
|
|
435
454
|
allowedTools?: readonly string[];
|
|
436
455
|
agentAllowedTools?: readonly string[];
|
|
@@ -851,6 +870,7 @@ export class FlexHarness<TScope = unknown> {
|
|
|
851
870
|
private readonly externalErrorProjector?: TFlexExternalErrorProjector;
|
|
852
871
|
private readonly subagents: ReadonlyMap<string, Readonly<IFlexSubagentDefinition>>;
|
|
853
872
|
private readonly builtInTools: INormalizedBuiltInTools;
|
|
873
|
+
private readonly toolCatalogOptions: Readonly<IFlexToolCatalogOptions>;
|
|
854
874
|
private readonly slashCommands: ReadonlyMap<string, TRegisteredSlashCommand<TScope>>;
|
|
855
875
|
private readonly maxSubagentDepth: number;
|
|
856
876
|
private readonly maxSubagentCallsPerRun: number;
|
|
@@ -937,6 +957,11 @@ export class FlexHarness<TScope = unknown> {
|
|
|
937
957
|
this.externalErrorProjector = options.externalErrorProjector;
|
|
938
958
|
this.subagents = normalizeSubagents(options.subagents);
|
|
939
959
|
this.builtInTools = normalizeBuiltInTools(options.builtInTools);
|
|
960
|
+
const toolCatalogOptions = normalizeToolCatalogOptions(options.toolCatalog);
|
|
961
|
+
this.toolCatalogOptions = Object.freeze({
|
|
962
|
+
declarations: toolCatalogOptions.declarations,
|
|
963
|
+
...(toolCatalogOptions.codeMode ? { codeMode: { limits: toolCatalogOptions.codeMode } } : {}),
|
|
964
|
+
});
|
|
940
965
|
this.slashCommands = normalizeSlashCommands(options.slashCommands);
|
|
941
966
|
this.maxSubagentDepth = resolveBoundedPositiveInteger(
|
|
942
967
|
options.maxSubagentDepth,
|
|
@@ -5823,18 +5848,38 @@ export class FlexHarness<TScope = unknown> {
|
|
|
5823
5848
|
}
|
|
5824
5849
|
signal.throwIfAborted();
|
|
5825
5850
|
run.modelResolution = model!;
|
|
5851
|
+
run.toolProviderContext = toolProviderContext as Readonly<IFlexToolProviderContext<unknown>>;
|
|
5852
|
+
let catalog: FlexToolCatalog<unknown> | undefined;
|
|
5853
|
+
if (toolHandle?.catalog) {
|
|
5854
|
+
try {
|
|
5855
|
+
catalog = await FlexToolCatalog.create(toolHandle.catalog, this.toolCatalogOptions);
|
|
5856
|
+
if (catalog.codeMode) run.codeSandbox = await FlexCodeSandbox.load();
|
|
5857
|
+
} catch (error) {
|
|
5858
|
+
throw this.projectExternalError(run, error, 'toolProvider');
|
|
5859
|
+
}
|
|
5860
|
+
signal.throwIfAborted();
|
|
5861
|
+
}
|
|
5826
5862
|
let tools: TFlexAgentToolSet | undefined;
|
|
5827
5863
|
try {
|
|
5828
5864
|
const providedTools = toolHandle?.tools;
|
|
5829
5865
|
const combinedTools: Record<string, unknown> = { ...(providedTools ?? {}) };
|
|
5830
5866
|
Object.assign(combinedTools, this.createBuiltInTools(run));
|
|
5867
|
+
if (catalog) assertNoCatalogCollisions(catalog, combinedTools);
|
|
5831
5868
|
const globalNames = run.allowedTools === undefined ? undefined : new Set(run.allowedTools);
|
|
5832
5869
|
const agentNames = run.agentAllowedTools === undefined ? undefined : new Set(run.agentAllowedTools);
|
|
5870
|
+
const admitted = (name: string): boolean =>
|
|
5871
|
+
(globalNames === undefined || globalNames.has(name))
|
|
5872
|
+
&& (agentNames === undefined || agentNames.has(name));
|
|
5833
5873
|
const admittedTools = globalNames === undefined && agentNames === undefined
|
|
5834
5874
|
? combinedTools
|
|
5835
|
-
: Object.fromEntries(Object.entries(combinedTools).filter(([name]) =>
|
|
5836
|
-
|
|
5837
|
-
|
|
5875
|
+
: Object.fromEntries(Object.entries(combinedTools).filter(([name]) => admitted(name)));
|
|
5876
|
+
const admittedCatalog = catalog && (globalNames === undefined && agentNames === undefined
|
|
5877
|
+
? catalog
|
|
5878
|
+
: catalog.restrict(catalog.methods.filter(admitted)));
|
|
5879
|
+
if (admittedCatalog && admittedCatalog.methods.length > 0) {
|
|
5880
|
+
run.toolCatalog = admittedCatalog;
|
|
5881
|
+
Object.assign(admittedTools, this.createCatalogTools(run, admittedCatalog));
|
|
5882
|
+
}
|
|
5838
5883
|
tools = Object.keys(admittedTools).length > 0
|
|
5839
5884
|
? wrapToolSet(
|
|
5840
5885
|
admittedTools as TFlexAgentToolSet,
|
|
@@ -5913,6 +5958,180 @@ export class FlexHarness<TScope = unknown> {
|
|
|
5913
5958
|
}
|
|
5914
5959
|
}
|
|
5915
5960
|
|
|
5961
|
+
/**
|
|
5962
|
+
* search_tools, describe_tools and execute. `execute` validates the call and runs the method
|
|
5963
|
+
* inside this tool call, so the tool wrapper bounds its output and projects its errors, and the
|
|
5964
|
+
* call's part, events and permission requests carry the method's path.
|
|
5965
|
+
*/
|
|
5966
|
+
private createCatalogTools(run: IActiveRun, catalog: FlexToolCatalog<unknown>): Record<string, unknown> {
|
|
5967
|
+
const names = FLEX_TOOL_CATALOG_TOOL_NAMES;
|
|
5968
|
+
const definitions = catalog.toolDefinitions();
|
|
5969
|
+
const describeBytes = Math.max(1024, this.toolOutputLimits.maxBytes - 1024);
|
|
5970
|
+
return {
|
|
5971
|
+
[names.search]: plugins.tool({
|
|
5972
|
+
description: definitions[names.search].description,
|
|
5973
|
+
strict: false,
|
|
5974
|
+
inputSchema: catalogToolInputSchema<IFlexCatalogSearchInput>(definitions[names.search]),
|
|
5975
|
+
execute: async (input: IFlexCatalogSearchInput) => catalog.search(input),
|
|
5976
|
+
}),
|
|
5977
|
+
[names.describe]: plugins.tool({
|
|
5978
|
+
description: definitions[names.describe].description,
|
|
5979
|
+
strict: false,
|
|
5980
|
+
inputSchema: catalogToolInputSchema<IFlexCatalogDescribeInput>(definitions[names.describe]),
|
|
5981
|
+
execute: async (input: IFlexCatalogDescribeInput, options?: { messages?: plugins.ModelMessage[] }) =>
|
|
5982
|
+
catalog.describe(input, {
|
|
5983
|
+
declaredTypes: catalog.declaredTypesIn(options?.messages),
|
|
5984
|
+
maxBytes: describeBytes,
|
|
5985
|
+
}).output,
|
|
5986
|
+
}),
|
|
5987
|
+
[names.execute]: plugins.tool({
|
|
5988
|
+
description: definitions[names.execute].description,
|
|
5989
|
+
strict: false,
|
|
5990
|
+
inputSchema: catalogToolInputSchema<{ method: string; params?: Record<string, unknown> }>(
|
|
5991
|
+
definitions[names.execute],
|
|
5992
|
+
),
|
|
5993
|
+
execute: async (
|
|
5994
|
+
input: { method: string; params?: Record<string, unknown> },
|
|
5995
|
+
options?: { toolCallId?: string; abortSignal?: AbortSignal },
|
|
5996
|
+
) => {
|
|
5997
|
+
const toolCallId = options?.toolCallId ?? '';
|
|
5998
|
+
const abortSignal = options?.abortSignal ?? run.controller.signal;
|
|
5999
|
+
const call = await catalog.prepareCall(input.method, input.params);
|
|
6000
|
+
if (!call.success) throw this.catalogCallError(run, toolCallId, call.error);
|
|
6001
|
+
const context = { method: call.method, toolCallId, abortSignal, provider: run.toolProviderContext! };
|
|
6002
|
+
const decision = await call.decide(context);
|
|
6003
|
+
if (decision === 'run') return call.run(context);
|
|
6004
|
+
if (decision === 'propose') {
|
|
6005
|
+
return catalog.propose(
|
|
6006
|
+
[{ id: toolCallId, method: call.method, params: call.params }],
|
|
6007
|
+
{ toolCallId, abortSignal, provider: run.toolProviderContext! },
|
|
6008
|
+
);
|
|
6009
|
+
}
|
|
6010
|
+
throw this.catalogCallError(run, toolCallId, { error: 'denied', method: call.method, reason: decision.deny });
|
|
6011
|
+
},
|
|
6012
|
+
}),
|
|
6013
|
+
...(catalog.codeMode && run.codeSandbox ? {
|
|
6014
|
+
[FLEX_CODE_MODE_TOOL_NAME]: plugins.tool({
|
|
6015
|
+
description: definitions[FLEX_CODE_MODE_TOOL_NAME].description,
|
|
6016
|
+
strict: false,
|
|
6017
|
+
inputSchema: catalogToolInputSchema<{ code: string }>(definitions[FLEX_CODE_MODE_TOOL_NAME]),
|
|
6018
|
+
execute: async (input: { code: string }, options?: { toolCallId?: string; abortSignal?: AbortSignal }) =>
|
|
6019
|
+
this.runCatalogProgram(run, catalog, input.code, options?.toolCallId ?? '', options?.abortSignal ?? run.controller.signal),
|
|
6020
|
+
}),
|
|
6021
|
+
} : {}),
|
|
6022
|
+
};
|
|
6023
|
+
}
|
|
6024
|
+
|
|
6025
|
+
/** A refused catalog call as the trusted error the model and the tool part show. */
|
|
6026
|
+
private catalogCallError(run: IActiveRun, toolCallId: string, payload: unknown): Error {
|
|
6027
|
+
const error = this.trustInternalError(new FlexHarnessValidationError(JSON.stringify(payload)));
|
|
6028
|
+
if (toolCallId) run.trustedToolExecutionErrors.set(toolCallId, error);
|
|
6029
|
+
return error;
|
|
6030
|
+
}
|
|
6031
|
+
|
|
6032
|
+
/**
|
|
6033
|
+
* Runs a `run_code` program. Every catalog call it makes is dispatched like an `execute`
|
|
6034
|
+
* (validation, `onCall`, the method's run with its permission requests, the output limits, error
|
|
6035
|
+
* projection) and recorded as a tool part of its own, named by the method, with the `run_code`
|
|
6036
|
+
* call as its parent. Proposals go to `onProposals` as one batch once the program finished; a
|
|
6037
|
+
* program that fails proposes nothing.
|
|
6038
|
+
*/
|
|
6039
|
+
private async runCatalogProgram(
|
|
6040
|
+
run: IActiveRun,
|
|
6041
|
+
catalog: FlexToolCatalog<unknown>,
|
|
6042
|
+
code: string,
|
|
6043
|
+
toolCallId: string,
|
|
6044
|
+
abortSignal: AbortSignal,
|
|
6045
|
+
): Promise<unknown> {
|
|
6046
|
+
const proposals: IFlexCatalogProposal[] = [];
|
|
6047
|
+
const base = run.toolProviderContext!;
|
|
6048
|
+
// Waiting for a permission decision is the user's time, not the program's.
|
|
6049
|
+
let waitedMs = 0;
|
|
6050
|
+
let waiting = 0;
|
|
6051
|
+
let waitingSince = 0;
|
|
6052
|
+
/** The provider context of one call: its permission waits are timed, and end when the program stops. */
|
|
6053
|
+
const providerFor = (signal: AbortSignal): Readonly<IFlexToolProviderContext<unknown>> => Object.freeze({
|
|
6054
|
+
...base,
|
|
6055
|
+
requestPermission: async (request: IFlexPermissionRequestInput) => {
|
|
6056
|
+
signal.throwIfAborted();
|
|
6057
|
+
if (waiting++ === 0) waitingSince = performance.now();
|
|
6058
|
+
let onAbort: (() => void) | undefined;
|
|
6059
|
+
try {
|
|
6060
|
+
await Promise.race([
|
|
6061
|
+
base.requestPermission(request),
|
|
6062
|
+
new Promise<never>((_resolve, reject) => {
|
|
6063
|
+
onAbort = () => reject(signal.reason);
|
|
6064
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
6065
|
+
}),
|
|
6066
|
+
]);
|
|
6067
|
+
} finally {
|
|
6068
|
+
if (onAbort) signal.removeEventListener('abort', onAbort);
|
|
6069
|
+
if (--waiting === 0) waitedMs += performance.now() - waitingSince;
|
|
6070
|
+
}
|
|
6071
|
+
},
|
|
6072
|
+
});
|
|
6073
|
+
const stopped = (method: string) => ({ ok: false as const, error: { error: 'aborted', method } });
|
|
6074
|
+
let callNumber = 0;
|
|
6075
|
+
const finish = (id: string, method: string, input: unknown, outcome: { output: unknown } | { error: Error }) => {
|
|
6076
|
+
if ('error' in outcome) run.trustedToolExecutionErrors.set(id, outcome.error);
|
|
6077
|
+
this.onToolFinish(run.state, run.sessionId, 'error' in outcome
|
|
6078
|
+
? { toolCallId: id, toolName: method, input, success: false, error: outcome.error.message }
|
|
6079
|
+
: { toolCallId: id, toolName: method, input, success: true, output: outcome.output });
|
|
6080
|
+
};
|
|
6081
|
+
const host: IFlexCodeHost = {
|
|
6082
|
+
waitedMs: () => waitedMs + (waiting > 0 ? performance.now() - waitingSince : 0),
|
|
6083
|
+
call: async (method, params, signal) => {
|
|
6084
|
+
if (signal.aborted) return stopped(method);
|
|
6085
|
+
const id = `${toolCallId}.${++callNumber}`;
|
|
6086
|
+
const call = await catalog.prepareCall(method, params);
|
|
6087
|
+
if (!call.success) {
|
|
6088
|
+
if (catalog.has(method)) {
|
|
6089
|
+
this.onToolStart(run.state, run.sessionId, { toolCallId: id, toolName: method, input: params ?? {}, parentToolCallId: toolCallId });
|
|
6090
|
+
finish(id, method, params, { error: this.catalogCallError(run, id, call.error) });
|
|
6091
|
+
}
|
|
6092
|
+
return { ok: false, error: call.error as unknown as TJsonValue };
|
|
6093
|
+
}
|
|
6094
|
+
if (signal.aborted) return stopped(call.method);
|
|
6095
|
+
this.onToolStart(run.state, run.sessionId, {
|
|
6096
|
+
toolCallId: id, toolName: call.method, input: call.params, parentToolCallId: toolCallId,
|
|
6097
|
+
});
|
|
6098
|
+
const context = { method: call.method, toolCallId: id, abortSignal: signal, provider: providerFor(signal) };
|
|
6099
|
+
try {
|
|
6100
|
+
const decision = await call.decide(context);
|
|
6101
|
+
signal.throwIfAborted();
|
|
6102
|
+
if (decision === 'propose') {
|
|
6103
|
+
proposals.push({ id, method: call.method, params: call.params });
|
|
6104
|
+
const value = { proposed: true, id };
|
|
6105
|
+
finish(id, call.method, call.params, { output: value });
|
|
6106
|
+
return { ok: true, value };
|
|
6107
|
+
}
|
|
6108
|
+
if (decision !== 'run') {
|
|
6109
|
+
const payload = { error: 'denied' as const, method: call.method, reason: decision.deny };
|
|
6110
|
+
finish(id, call.method, call.params, { error: this.catalogCallError(run, id, payload) });
|
|
6111
|
+
return { ok: false, error: payload };
|
|
6112
|
+
}
|
|
6113
|
+
const value = normalizeJsonValue(await settleToolOutput(call.run(context)), this.toolOutputLimits);
|
|
6114
|
+
finish(id, call.method, call.params, { output: value });
|
|
6115
|
+
return { ok: true, value };
|
|
6116
|
+
} catch (error) {
|
|
6117
|
+
const projected = this.projectExternalError(run, error, 'toolExecution');
|
|
6118
|
+
finish(id, call.method, call.params, { error: projected });
|
|
6119
|
+
return { ok: false, error: { error: 'failed', method: call.method, message: projected.message } };
|
|
6120
|
+
}
|
|
6121
|
+
},
|
|
6122
|
+
};
|
|
6123
|
+
const result = await run.codeSandbox!.run(code, host, catalog.codeMode!, abortSignal);
|
|
6124
|
+
if (!result.ok) throw this.catalogCallError(run, toolCallId, result.failure);
|
|
6125
|
+
return {
|
|
6126
|
+
result: result.value,
|
|
6127
|
+
calls: result.calls,
|
|
6128
|
+
...(result.logs ? { logs: result.logs } : {}),
|
|
6129
|
+
...(proposals.length > 0
|
|
6130
|
+
? { proposals: await catalog.propose(proposals, { toolCallId, abortSignal, provider: base }) }
|
|
6131
|
+
: {}),
|
|
6132
|
+
};
|
|
6133
|
+
}
|
|
6134
|
+
|
|
5916
6135
|
private createBuiltInTools(run: IActiveRun): Record<string, unknown> {
|
|
5917
6136
|
const tools: Record<string, unknown> = {};
|
|
5918
6137
|
if (run.subagents.size > 0 && (run.stored.session.depth ?? 0) < this.maxSubagentDepth) {
|
|
@@ -8417,20 +8636,27 @@ export class FlexHarness<TScope = unknown> {
|
|
|
8417
8636
|
private onToolStart(
|
|
8418
8637
|
state: IStorageState,
|
|
8419
8638
|
sessionId: string,
|
|
8420
|
-
event: { toolCallId: string; toolName: string; input: unknown },
|
|
8639
|
+
event: { toolCallId: string; toolName: string; input: unknown; parentToolCallId?: string },
|
|
8421
8640
|
): void {
|
|
8422
8641
|
const run = this.callbackRun(state, sessionId);
|
|
8423
8642
|
if (!run || run.toolPartIds.has(event.toolCallId)) return;
|
|
8424
8643
|
try {
|
|
8425
|
-
|
|
8644
|
+
// An execute call is the call of the catalog method it names.
|
|
8645
|
+
const target = event.toolName === FLEX_TOOL_CATALOG_TOOL_NAMES.execute
|
|
8646
|
+
? run.toolCatalog?.callTarget(event.input)
|
|
8647
|
+
: undefined;
|
|
8648
|
+
const input = normalizeJsonValue(target ? target.params : event.input, this.toolOutputLimits);
|
|
8426
8649
|
if (!this.reserveCallbackCapacity(run, 1, jsonBytes(input), 1)) return;
|
|
8427
8650
|
const part: IFlexToolMessagePart = {
|
|
8428
8651
|
partId: plugins.crypto.randomUUID(),
|
|
8429
8652
|
type: 'tool',
|
|
8430
8653
|
toolCallId: this.boundedIdentifier(event.toolCallId, 'toolCallId'),
|
|
8431
|
-
toolName: this.boundedIdentifier(event.toolName, 'toolName'),
|
|
8654
|
+
toolName: this.boundedIdentifier(target ? target.method : event.toolName, 'toolName'),
|
|
8432
8655
|
status: 'running',
|
|
8433
8656
|
input,
|
|
8657
|
+
...(event.parentToolCallId === undefined
|
|
8658
|
+
? {}
|
|
8659
|
+
: { parentToolCallId: this.boundedIdentifier(event.parentToolCallId, 'parentToolCallId') }),
|
|
8434
8660
|
startedAt: new Date().toISOString(),
|
|
8435
8661
|
};
|
|
8436
8662
|
run.toolPartIds.set(event.toolCallId, part.partId);
|
|
@@ -9129,9 +9355,14 @@ export class FlexHarness<TScope = unknown> {
|
|
|
9129
9355
|
}
|
|
9130
9356
|
const tools = handle.tools;
|
|
9131
9357
|
this.assertNoBuiltInToolCollisions(run, tools);
|
|
9358
|
+
const catalog = handle.catalog;
|
|
9359
|
+
if (catalog !== undefined && (typeof catalog !== 'object' || catalog === null)) {
|
|
9360
|
+
throw new FlexHarnessValidationError('Tool handle catalog must be an object.');
|
|
9361
|
+
}
|
|
9132
9362
|
const closeHandle = close;
|
|
9133
9363
|
return {
|
|
9134
9364
|
tools,
|
|
9365
|
+
...(catalog === undefined ? {} : { catalog }),
|
|
9135
9366
|
...(closeHandle ? { close: () => closeHandle.call(handle) } : {}),
|
|
9136
9367
|
};
|
|
9137
9368
|
} catch (error) {
|
|
@@ -9218,6 +9449,9 @@ export class FlexHarness<TScope = unknown> {
|
|
|
9218
9449
|
);
|
|
9219
9450
|
if (!handle) continue;
|
|
9220
9451
|
owned.push({ handle, closed: false });
|
|
9452
|
+
if (handle.catalog !== undefined) {
|
|
9453
|
+
throw new FlexHarnessValidationError('Resource tool providers cannot supply a tool catalog.');
|
|
9454
|
+
}
|
|
9221
9455
|
for (const [toolName, tool] of Object.entries(handle.tools)) {
|
|
9222
9456
|
const exposedName = createFlexResourceToolName(namespace, toolName);
|
|
9223
9457
|
if (Object.prototype.hasOwnProperty.call(tools, exposedName)) {
|
|
@@ -9229,6 +9463,7 @@ export class FlexHarness<TScope = unknown> {
|
|
|
9229
9463
|
if (owned.length === 0 && Object.keys(tools).length === 0) return undefined;
|
|
9230
9464
|
return {
|
|
9231
9465
|
tools: tools as TFlexAgentToolSet,
|
|
9466
|
+
...(applicationHandle?.catalog === undefined ? {} : { catalog: applicationHandle.catalog }),
|
|
9232
9467
|
...(owned.some((entry) => entry.handle.close) ? { close: closeOwned } : {}),
|
|
9233
9468
|
};
|
|
9234
9469
|
} catch (error) {
|