@aragon-agent/core 0.1.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/CHANGELOG.md +126 -0
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/engine/agent-loop.d.ts +52 -0
- package/dist/engine/agent-loop.js +260 -0
- package/dist/engine/agent.d.ts +129 -0
- package/dist/engine/agent.js +283 -0
- package/dist/engine/message-manager.d.ts +26 -0
- package/dist/engine/message-manager.js +39 -0
- package/dist/engine/steering.d.ts +27 -0
- package/dist/engine/steering.js +51 -0
- package/dist/engine/watchdog.d.ts +24 -0
- package/dist/engine/watchdog.js +50 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +63 -0
- package/dist/llm/model-registry.d.ts +51 -0
- package/dist/llm/model-registry.js +277 -0
- package/dist/llm/provider.d.ts +72 -0
- package/dist/llm/provider.js +77 -0
- package/dist/llm/providers/anthropic.d.ts +23 -0
- package/dist/llm/providers/anthropic.js +420 -0
- package/dist/llm/providers/google.d.ts +22 -0
- package/dist/llm/providers/google.js +336 -0
- package/dist/llm/providers/index.d.ts +67 -0
- package/dist/llm/providers/index.js +127 -0
- package/dist/llm/providers/openai.d.ts +22 -0
- package/dist/llm/providers/openai.js +353 -0
- package/dist/llm/stream-utils.d.ts +33 -0
- package/dist/llm/stream-utils.js +132 -0
- package/dist/llm/types.d.ts +129 -0
- package/dist/llm/types.js +17 -0
- package/dist/sandbox/console-capture.d.ts +29 -0
- package/dist/sandbox/console-capture.js +56 -0
- package/dist/sandbox/http-bridge.d.ts +47 -0
- package/dist/sandbox/http-bridge.js +143 -0
- package/dist/sandbox/isolated-vm-sandbox.d.ts +50 -0
- package/dist/sandbox/isolated-vm-sandbox.js +226 -0
- package/dist/sandbox/tool-bridge.d.ts +42 -0
- package/dist/sandbox/tool-bridge.js +98 -0
- package/dist/sandbox/types.d.ts +84 -0
- package/dist/sandbox/types.js +8 -0
- package/dist/skills/constants.d.ts +122 -0
- package/dist/skills/constants.js +179 -0
- package/dist/skills/disclosure.d.ts +146 -0
- package/dist/skills/disclosure.js +788 -0
- package/dist/skills/frontmatter.d.ts +27 -0
- package/dist/skills/frontmatter.js +177 -0
- package/dist/skills/index.d.ts +21 -0
- package/dist/skills/index.js +18 -0
- package/dist/skills/skill-find-tool.d.ts +41 -0
- package/dist/skills/skill-find-tool.js +117 -0
- package/dist/skills/skill-registry.d.ts +96 -0
- package/dist/skills/skill-registry.js +208 -0
- package/dist/skills/skill-tool.d.ts +44 -0
- package/dist/skills/skill-tool.js +96 -0
- package/dist/skills/tool-policy.d.ts +91 -0
- package/dist/skills/tool-policy.js +272 -0
- package/dist/skills/types.d.ts +271 -0
- package/dist/skills/types.js +8 -0
- package/dist/skills/validate.d.ts +37 -0
- package/dist/skills/validate.js +264 -0
- package/dist/tools/executor.d.ts +54 -0
- package/dist/tools/executor.js +206 -0
- package/dist/tools/helpers.d.ts +69 -0
- package/dist/tools/helpers.js +87 -0
- package/dist/tools/registry.d.ts +58 -0
- package/dist/tools/registry.js +119 -0
- package/dist/tools/types.d.ts +140 -0
- package/dist/tools/types.js +7 -0
- package/dist/tools/validator.d.ts +46 -0
- package/dist/tools/validator.js +210 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +7 -0
- package/package.json +115 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool System — Tool Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages dynamic registration and lookup of AgentTools.
|
|
5
|
+
* Supports change notification so consumers (e.g. the Agent engine)
|
|
6
|
+
* can react when tools are added or removed at runtime.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentTool, ToolDefinition, ToolRegistryEvent } from './types.js';
|
|
9
|
+
/** Listener callback type for registry change events. */
|
|
10
|
+
export type ToolRegistryListener = (event: ToolRegistryEvent) => void;
|
|
11
|
+
/**
|
|
12
|
+
* A registry that holds AgentTool instances indexed by name.
|
|
13
|
+
* Provides lookup, filtering, and LLM tool-definition export.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ToolRegistry {
|
|
16
|
+
/** Internal tool storage, keyed by tool name. */
|
|
17
|
+
private readonly tools;
|
|
18
|
+
/** Registered change listeners. */
|
|
19
|
+
private readonly listeners;
|
|
20
|
+
/**
|
|
21
|
+
* Register a single tool. Throws if a tool with the same name
|
|
22
|
+
* is already registered.
|
|
23
|
+
*/
|
|
24
|
+
register(tool: AgentTool): void;
|
|
25
|
+
/**
|
|
26
|
+
* Register multiple tools at once. Throws on the first duplicate
|
|
27
|
+
* name encountered (tools registered before the error remain).
|
|
28
|
+
*/
|
|
29
|
+
registerAll(tools: AgentTool[]): void;
|
|
30
|
+
/**
|
|
31
|
+
* Remove a tool by name.
|
|
32
|
+
* @returns `true` if the tool existed and was removed; `false` otherwise.
|
|
33
|
+
*/
|
|
34
|
+
unregister(name: string): boolean;
|
|
35
|
+
/** Retrieve a tool by name, or `undefined` if not found. */
|
|
36
|
+
get(name: string): AgentTool | undefined;
|
|
37
|
+
/** Return all registered tools as an array (snapshot). */
|
|
38
|
+
getAll(): AgentTool[];
|
|
39
|
+
/** Return tools that include the specified tag. */
|
|
40
|
+
getByTag(tag: string): AgentTool[];
|
|
41
|
+
/** Return tools marked as bridgeable (for CodeAct sandbox). */
|
|
42
|
+
getBridgeable(): AgentTool[];
|
|
43
|
+
/** Check whether a tool with the given name is registered. */
|
|
44
|
+
has(name: string): boolean;
|
|
45
|
+
/** Number of registered tools. */
|
|
46
|
+
get size(): number;
|
|
47
|
+
/**
|
|
48
|
+
* Export all registered tools as provider-agnostic ToolDefinitions
|
|
49
|
+
* suitable for passing to any LLM provider adapter.
|
|
50
|
+
*/
|
|
51
|
+
toToolDefinitions(): ToolDefinition[];
|
|
52
|
+
/**
|
|
53
|
+
* Subscribe to registry change events.
|
|
54
|
+
* @returns An unsubscribe function.
|
|
55
|
+
*/
|
|
56
|
+
onChange(listener: ToolRegistryListener): () => void;
|
|
57
|
+
private emit;
|
|
58
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool System — Tool Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages dynamic registration and lookup of AgentTools.
|
|
5
|
+
* Supports change notification so consumers (e.g. the Agent engine)
|
|
6
|
+
* can react when tools are added or removed at runtime.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A registry that holds AgentTool instances indexed by name.
|
|
10
|
+
* Provides lookup, filtering, and LLM tool-definition export.
|
|
11
|
+
*/
|
|
12
|
+
export class ToolRegistry {
|
|
13
|
+
/** Internal tool storage, keyed by tool name. */
|
|
14
|
+
tools = new Map();
|
|
15
|
+
/** Registered change listeners. */
|
|
16
|
+
listeners = new Set();
|
|
17
|
+
// -----------------------------------------------------------------------
|
|
18
|
+
// Registration
|
|
19
|
+
// -----------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Register a single tool. Throws if a tool with the same name
|
|
22
|
+
* is already registered.
|
|
23
|
+
*/
|
|
24
|
+
register(tool) {
|
|
25
|
+
if (this.tools.has(tool.name)) {
|
|
26
|
+
throw new Error(`ToolRegistry: tool "${tool.name}" is already registered. ` +
|
|
27
|
+
'Unregister it first or use a different name.');
|
|
28
|
+
}
|
|
29
|
+
this.tools.set(tool.name, tool);
|
|
30
|
+
this.emit({ type: 'registered', tool });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Register multiple tools at once. Throws on the first duplicate
|
|
34
|
+
* name encountered (tools registered before the error remain).
|
|
35
|
+
*/
|
|
36
|
+
registerAll(tools) {
|
|
37
|
+
for (const tool of tools) {
|
|
38
|
+
this.register(tool);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Remove a tool by name.
|
|
43
|
+
* @returns `true` if the tool existed and was removed; `false` otherwise.
|
|
44
|
+
*/
|
|
45
|
+
unregister(name) {
|
|
46
|
+
const existed = this.tools.delete(name);
|
|
47
|
+
if (existed) {
|
|
48
|
+
this.emit({ type: 'unregistered', name });
|
|
49
|
+
}
|
|
50
|
+
return existed;
|
|
51
|
+
}
|
|
52
|
+
// -----------------------------------------------------------------------
|
|
53
|
+
// Lookup
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
/** Retrieve a tool by name, or `undefined` if not found. */
|
|
56
|
+
get(name) {
|
|
57
|
+
return this.tools.get(name);
|
|
58
|
+
}
|
|
59
|
+
/** Return all registered tools as an array (snapshot). */
|
|
60
|
+
getAll() {
|
|
61
|
+
return Array.from(this.tools.values());
|
|
62
|
+
}
|
|
63
|
+
/** Return tools that include the specified tag. */
|
|
64
|
+
getByTag(tag) {
|
|
65
|
+
return this.getAll().filter((t) => t.tags !== undefined && t.tags.includes(tag));
|
|
66
|
+
}
|
|
67
|
+
/** Return tools marked as bridgeable (for CodeAct sandbox). */
|
|
68
|
+
getBridgeable() {
|
|
69
|
+
return this.getAll().filter((t) => t.bridgeable === true);
|
|
70
|
+
}
|
|
71
|
+
/** Check whether a tool with the given name is registered. */
|
|
72
|
+
has(name) {
|
|
73
|
+
return this.tools.has(name);
|
|
74
|
+
}
|
|
75
|
+
/** Number of registered tools. */
|
|
76
|
+
get size() {
|
|
77
|
+
return this.tools.size;
|
|
78
|
+
}
|
|
79
|
+
// -----------------------------------------------------------------------
|
|
80
|
+
// Export
|
|
81
|
+
// -----------------------------------------------------------------------
|
|
82
|
+
/**
|
|
83
|
+
* Export all registered tools as provider-agnostic ToolDefinitions
|
|
84
|
+
* suitable for passing to any LLM provider adapter.
|
|
85
|
+
*/
|
|
86
|
+
toToolDefinitions() {
|
|
87
|
+
return this.getAll().map((tool) => ({
|
|
88
|
+
name: tool.name,
|
|
89
|
+
description: tool.description,
|
|
90
|
+
parameters: tool.parameters,
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
// -----------------------------------------------------------------------
|
|
94
|
+
// Change Notification
|
|
95
|
+
// -----------------------------------------------------------------------
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to registry change events.
|
|
98
|
+
* @returns An unsubscribe function.
|
|
99
|
+
*/
|
|
100
|
+
onChange(listener) {
|
|
101
|
+
this.listeners.add(listener);
|
|
102
|
+
return () => {
|
|
103
|
+
this.listeners.delete(listener);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// -----------------------------------------------------------------------
|
|
107
|
+
// Internal
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
emit(event) {
|
|
110
|
+
for (const listener of this.listeners) {
|
|
111
|
+
try {
|
|
112
|
+
listener(event);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Swallow listener errors to avoid disrupting the registry.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool System — Core Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Defines the AgentTool interface and related types.
|
|
5
|
+
* Parameters use standard JSON Schema 7.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A JSON Schema 7 object used to describe tool parameters.
|
|
9
|
+
*
|
|
10
|
+
* This is intentionally a structural subset — it covers the shapes
|
|
11
|
+
* actually used in tool parameter definitions (object schemas with
|
|
12
|
+
* typed properties) while remaining compatible with full JSON Schema 7
|
|
13
|
+
* validators such as ajv.
|
|
14
|
+
*/
|
|
15
|
+
export interface JSONSchema7 {
|
|
16
|
+
type?: string | string[];
|
|
17
|
+
description?: string;
|
|
18
|
+
properties?: Record<string, JSONSchema7>;
|
|
19
|
+
required?: string[];
|
|
20
|
+
items?: JSONSchema7 | JSONSchema7[];
|
|
21
|
+
enum?: unknown[];
|
|
22
|
+
const?: unknown;
|
|
23
|
+
default?: unknown;
|
|
24
|
+
additionalProperties?: boolean | JSONSchema7;
|
|
25
|
+
minLength?: number;
|
|
26
|
+
maxLength?: number;
|
|
27
|
+
minimum?: number;
|
|
28
|
+
maximum?: number;
|
|
29
|
+
pattern?: string;
|
|
30
|
+
format?: string;
|
|
31
|
+
oneOf?: JSONSchema7[];
|
|
32
|
+
anyOf?: JSONSchema7[];
|
|
33
|
+
allOf?: JSONSchema7[];
|
|
34
|
+
not?: JSONSchema7;
|
|
35
|
+
if?: JSONSchema7;
|
|
36
|
+
then?: JSONSchema7;
|
|
37
|
+
else?: JSONSchema7;
|
|
38
|
+
/** Allow arbitrary additional keywords for full JSON Schema 7 compat. */
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
/** A single content block within a tool result. */
|
|
42
|
+
export type ToolResultContent = {
|
|
43
|
+
type: 'text';
|
|
44
|
+
text: string;
|
|
45
|
+
} | {
|
|
46
|
+
type: 'image';
|
|
47
|
+
mediaType: string;
|
|
48
|
+
data: string;
|
|
49
|
+
};
|
|
50
|
+
/** The result returned by a tool execution. */
|
|
51
|
+
export interface ToolResult {
|
|
52
|
+
content: ToolResultContent[];
|
|
53
|
+
isError?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** Progress information emitted by long-running tools. */
|
|
56
|
+
export interface ToolProgressUpdate {
|
|
57
|
+
message?: string;
|
|
58
|
+
percentage?: number;
|
|
59
|
+
detail?: unknown;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Execution context passed to every tool invocation.
|
|
63
|
+
* Provides cancellation signaling and progress reporting.
|
|
64
|
+
*/
|
|
65
|
+
export interface ToolExecutionContext {
|
|
66
|
+
/** AbortSignal for cooperative cancellation (timeout + external abort). */
|
|
67
|
+
signal?: AbortSignal;
|
|
68
|
+
/** Optional callback for reporting incremental progress. */
|
|
69
|
+
onProgress?: (update: ToolProgressUpdate) => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The function signature every tool must implement.
|
|
73
|
+
*
|
|
74
|
+
* @param toolCallId - Unique identifier for this tool invocation (from the LLM).
|
|
75
|
+
* @param params - Validated parameters matching the tool's JSON Schema.
|
|
76
|
+
* @param context - Execution context with signal and progress callback.
|
|
77
|
+
*/
|
|
78
|
+
export type ToolExecuteFn<TParams = Record<string, unknown>> = (toolCallId: string, params: TParams, context: ToolExecutionContext) => Promise<ToolResult>;
|
|
79
|
+
/**
|
|
80
|
+
* A self-contained tool that can be registered in the ToolRegistry,
|
|
81
|
+
* exposed to LLMs as a callable function, and optionally bridged
|
|
82
|
+
* into the CodeAct sandbox.
|
|
83
|
+
*/
|
|
84
|
+
export interface AgentTool<TParams = Record<string, unknown>> {
|
|
85
|
+
/** Unique tool identifier (used in LLM tool_use calls). */
|
|
86
|
+
name: string;
|
|
87
|
+
/** Human-readable display label (UI and logs). */
|
|
88
|
+
label: string;
|
|
89
|
+
/** Description injected into the LLM's tool definition. */
|
|
90
|
+
description: string;
|
|
91
|
+
/** JSON Schema 7 describing the tool's parameters. */
|
|
92
|
+
parameters: JSONSchema7;
|
|
93
|
+
/** The tool's execution function. */
|
|
94
|
+
execute: ToolExecuteFn<TParams>;
|
|
95
|
+
/** Optional classification tags for filtering (e.g. 'filesystem', 'network'). */
|
|
96
|
+
tags?: string[];
|
|
97
|
+
/**
|
|
98
|
+
* Whether this tool should be exposed as a bridged async function
|
|
99
|
+
* inside the CodeAct JS sandbox. Defaults to false.
|
|
100
|
+
*/
|
|
101
|
+
bridgeable?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* The function name used in the CodeAct sandbox when bridged.
|
|
104
|
+
* Defaults to the tool's `name` if not specified.
|
|
105
|
+
* Example: name='read_file', bridgeName='readFile'
|
|
106
|
+
*/
|
|
107
|
+
bridgeName?: string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A minimal, provider-agnostic tool definition suitable for passing
|
|
111
|
+
* to any LLM provider adapter.
|
|
112
|
+
*/
|
|
113
|
+
export interface ToolDefinition {
|
|
114
|
+
name: string;
|
|
115
|
+
description: string;
|
|
116
|
+
parameters: JSONSchema7;
|
|
117
|
+
}
|
|
118
|
+
/** Events emitted by ToolRegistry when tools are added or removed. */
|
|
119
|
+
export type ToolRegistryEvent = {
|
|
120
|
+
type: 'registered';
|
|
121
|
+
tool: AgentTool;
|
|
122
|
+
} | {
|
|
123
|
+
type: 'unregistered';
|
|
124
|
+
name: string;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Extended result from ToolExecutor.execute() that includes metadata
|
|
128
|
+
* about the execution (timing, tool identification, error status).
|
|
129
|
+
*/
|
|
130
|
+
export interface ToolExecutionResult {
|
|
131
|
+
result: ToolResult;
|
|
132
|
+
/** Wall-clock duration in milliseconds. */
|
|
133
|
+
duration: number;
|
|
134
|
+
/** Whether the execution resulted in an error. */
|
|
135
|
+
isError: boolean;
|
|
136
|
+
/** The name of the tool that was executed. */
|
|
137
|
+
toolName: string;
|
|
138
|
+
/** The tool call ID from the LLM. */
|
|
139
|
+
toolCallId: string;
|
|
140
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool System — Parameter Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates tool parameters against their JSON Schema 7 definitions.
|
|
5
|
+
* Uses `ajv` when available for full JSON Schema validation; falls back
|
|
6
|
+
* to a minimal inline validator for basic type checking when ajv is
|
|
7
|
+
* not yet installed.
|
|
8
|
+
*/
|
|
9
|
+
import type { JSONSchema7 } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Structured validation error with a JSON pointer path and message.
|
|
12
|
+
*/
|
|
13
|
+
export interface ValidationError {
|
|
14
|
+
path: string;
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Error thrown when parameter validation fails.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ToolValidationError extends Error {
|
|
21
|
+
readonly errors: ValidationError[];
|
|
22
|
+
constructor(errors: ValidationError[]);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validates tool call parameters against a JSON Schema 7 definition.
|
|
26
|
+
*
|
|
27
|
+
* Attempts to use `ajv` for full schema validation. If ajv is not
|
|
28
|
+
* available at runtime (not yet installed), falls back to a lightweight
|
|
29
|
+
* inline validator that covers basic type and required-field checks.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ToolParamValidator {
|
|
32
|
+
private strategy;
|
|
33
|
+
private strategyReady;
|
|
34
|
+
/**
|
|
35
|
+
* Validate `params` against the given JSON Schema.
|
|
36
|
+
*
|
|
37
|
+
* @returns The validated (and potentially coerced) parameters.
|
|
38
|
+
* @throws {ToolValidationError} if validation fails.
|
|
39
|
+
*/
|
|
40
|
+
validate(schema: JSONSchema7, params: unknown): Promise<Record<string, unknown>>;
|
|
41
|
+
private getStrategy;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Format validation errors into a single human-readable string.
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatValidationErrors(errors: ValidationError[]): string;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool System — Parameter Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates tool parameters against their JSON Schema 7 definitions.
|
|
5
|
+
* Uses `ajv` when available for full JSON Schema validation; falls back
|
|
6
|
+
* to a minimal inline validator for basic type checking when ajv is
|
|
7
|
+
* not yet installed.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when parameter validation fails.
|
|
11
|
+
*/
|
|
12
|
+
export class ToolValidationError extends Error {
|
|
13
|
+
errors;
|
|
14
|
+
constructor(errors) {
|
|
15
|
+
super(`Parameter validation failed: ${formatValidationErrors(errors)}`);
|
|
16
|
+
this.errors = errors;
|
|
17
|
+
this.name = 'ToolValidationError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// ToolParamValidator
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Validates tool call parameters against a JSON Schema 7 definition.
|
|
25
|
+
*
|
|
26
|
+
* Attempts to use `ajv` for full schema validation. If ajv is not
|
|
27
|
+
* available at runtime (not yet installed), falls back to a lightweight
|
|
28
|
+
* inline validator that covers basic type and required-field checks.
|
|
29
|
+
*/
|
|
30
|
+
export class ToolParamValidator {
|
|
31
|
+
strategy = null;
|
|
32
|
+
strategyReady = null;
|
|
33
|
+
/**
|
|
34
|
+
* Validate `params` against the given JSON Schema.
|
|
35
|
+
*
|
|
36
|
+
* @returns The validated (and potentially coerced) parameters.
|
|
37
|
+
* @throws {ToolValidationError} if validation fails.
|
|
38
|
+
*/
|
|
39
|
+
async validate(schema, params) {
|
|
40
|
+
const strategy = await this.getStrategy();
|
|
41
|
+
return strategy.validate(schema, params);
|
|
42
|
+
}
|
|
43
|
+
// -----------------------------------------------------------------------
|
|
44
|
+
// Strategy Resolution
|
|
45
|
+
// -----------------------------------------------------------------------
|
|
46
|
+
getStrategy() {
|
|
47
|
+
if (this.strategyReady) {
|
|
48
|
+
return this.strategyReady;
|
|
49
|
+
}
|
|
50
|
+
this.strategyReady = (async () => {
|
|
51
|
+
try {
|
|
52
|
+
// Dynamic import for ESM compatibility.
|
|
53
|
+
const AjvModule = await import('ajv');
|
|
54
|
+
const Ajv = AjvModule.default ?? AjvModule;
|
|
55
|
+
this.strategy = new AjvStrategy(Ajv);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// ajv not installed — use the fallback.
|
|
59
|
+
this.strategy = new FallbackStrategy();
|
|
60
|
+
}
|
|
61
|
+
return this.strategy;
|
|
62
|
+
})();
|
|
63
|
+
return this.strategyReady;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Ajv-based Strategy
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
class AjvStrategy {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
ajvInstance;
|
|
72
|
+
/** Cache of compiled validators keyed by a stable schema fingerprint. */
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
+
compiledCache = new Map();
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
constructor(Ajv) {
|
|
77
|
+
this.ajvInstance = new Ajv({
|
|
78
|
+
allErrors: true,
|
|
79
|
+
coerceTypes: true,
|
|
80
|
+
useDefaults: true,
|
|
81
|
+
strict: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
validate(schema, params) {
|
|
85
|
+
// Ensure we validate an object even if params is null/undefined.
|
|
86
|
+
const data = params !== null && typeof params === 'object' && !Array.isArray(params)
|
|
87
|
+
? { ...params }
|
|
88
|
+
: {};
|
|
89
|
+
const validateFn = this.getOrCompile(schema);
|
|
90
|
+
const valid = validateFn(data);
|
|
91
|
+
if (!valid) {
|
|
92
|
+
const errors = (validateFn.errors ?? []).map((err) => ({
|
|
93
|
+
path: err.instancePath || '/',
|
|
94
|
+
message: err.message ?? 'unknown error',
|
|
95
|
+
}));
|
|
96
|
+
throw new ToolValidationError(errors);
|
|
97
|
+
}
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
getOrCompile(schema) {
|
|
102
|
+
const key = JSON.stringify(schema);
|
|
103
|
+
let fn = this.compiledCache.get(key);
|
|
104
|
+
if (!fn) {
|
|
105
|
+
fn = this.ajvInstance.compile(schema);
|
|
106
|
+
this.compiledCache.set(key, fn);
|
|
107
|
+
}
|
|
108
|
+
return fn;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Fallback Strategy (no ajv)
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* A lightweight validator that covers the most common schema shapes
|
|
116
|
+
* used in tool parameter definitions:
|
|
117
|
+
* - top-level `type: 'object'` with `properties` and `required`
|
|
118
|
+
* - per-property primitive type checks (string, number, boolean, array, object)
|
|
119
|
+
*
|
|
120
|
+
* This is NOT a full JSON Schema 7 implementation — it exists solely
|
|
121
|
+
* as a safety net until ajv is installed.
|
|
122
|
+
*/
|
|
123
|
+
class FallbackStrategy {
|
|
124
|
+
validate(schema, params) {
|
|
125
|
+
const data = params !== null && typeof params === 'object' && !Array.isArray(params)
|
|
126
|
+
? { ...params }
|
|
127
|
+
: {};
|
|
128
|
+
const errors = [];
|
|
129
|
+
// Check required fields.
|
|
130
|
+
if (Array.isArray(schema.required)) {
|
|
131
|
+
for (const key of schema.required) {
|
|
132
|
+
if (data[key] === undefined || data[key] === null) {
|
|
133
|
+
errors.push({
|
|
134
|
+
path: `/${key}`,
|
|
135
|
+
message: `required property '${key}' is missing`,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Check property types.
|
|
141
|
+
if (schema.properties) {
|
|
142
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
143
|
+
const value = data[key];
|
|
144
|
+
if (value === undefined || value === null) {
|
|
145
|
+
// Apply defaults if specified.
|
|
146
|
+
if (propSchema.default !== undefined) {
|
|
147
|
+
data[key] = propSchema.default;
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const typeError = checkPrimitiveType(key, value, propSchema);
|
|
152
|
+
if (typeError) {
|
|
153
|
+
errors.push(typeError);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (errors.length > 0) {
|
|
158
|
+
throw new ToolValidationError(errors);
|
|
159
|
+
}
|
|
160
|
+
return data;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Helpers
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
function checkPrimitiveType(key, value, schema) {
|
|
167
|
+
const expectedType = schema.type;
|
|
168
|
+
if (!expectedType || typeof expectedType !== 'string') {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
172
|
+
switch (expectedType) {
|
|
173
|
+
case 'string':
|
|
174
|
+
if (typeof value !== 'string') {
|
|
175
|
+
return { path: `/${key}`, message: `expected string, got ${actualType}` };
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
case 'number':
|
|
179
|
+
case 'integer':
|
|
180
|
+
if (typeof value !== 'number') {
|
|
181
|
+
return { path: `/${key}`, message: `expected number, got ${actualType}` };
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
case 'boolean':
|
|
185
|
+
if (typeof value !== 'boolean') {
|
|
186
|
+
return { path: `/${key}`, message: `expected boolean, got ${actualType}` };
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
case 'array':
|
|
190
|
+
if (!Array.isArray(value)) {
|
|
191
|
+
return { path: `/${key}`, message: `expected array, got ${actualType}` };
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
case 'object':
|
|
195
|
+
if (typeof value !== 'object' || Array.isArray(value) || value === null) {
|
|
196
|
+
return { path: `/${key}`, message: `expected object, got ${actualType}` };
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
// 'null' and unknown types are not checked — let them pass.
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Format validation errors into a single human-readable string.
|
|
205
|
+
*/
|
|
206
|
+
export function formatValidationErrors(errors) {
|
|
207
|
+
return errors
|
|
208
|
+
.map((e) => `${e.path}: ${e.message}`)
|
|
209
|
+
.join('; ');
|
|
210
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Agent event types.
|
|
3
|
+
*
|
|
4
|
+
* These events are emitted by the Agent engine during execution and consumed
|
|
5
|
+
* by subscribers (e.g. SelfAgentService) to drive UI updates and persistence.
|
|
6
|
+
*/
|
|
7
|
+
import type { AssistantMessage, Message, StreamEvent, TokenUsage } from './llm/types.js';
|
|
8
|
+
import type { ToolResult } from './tools/types.js';
|
|
9
|
+
export interface AgentStartEvent {
|
|
10
|
+
type: 'agent_start';
|
|
11
|
+
}
|
|
12
|
+
export interface AgentEndEvent {
|
|
13
|
+
type: 'agent_end';
|
|
14
|
+
messages: Message[];
|
|
15
|
+
}
|
|
16
|
+
export interface TurnStartEvent {
|
|
17
|
+
type: 'turn_start';
|
|
18
|
+
}
|
|
19
|
+
export interface TurnEndEvent {
|
|
20
|
+
type: 'turn_end';
|
|
21
|
+
message: AssistantMessage;
|
|
22
|
+
usage: TokenUsage;
|
|
23
|
+
}
|
|
24
|
+
export interface MessageUpdateEvent {
|
|
25
|
+
type: 'message_update';
|
|
26
|
+
streamEvent: StreamEvent;
|
|
27
|
+
}
|
|
28
|
+
export interface ToolExecutionStartEvent {
|
|
29
|
+
type: 'tool_execution_start';
|
|
30
|
+
toolCallId: string;
|
|
31
|
+
toolName: string;
|
|
32
|
+
args: unknown;
|
|
33
|
+
}
|
|
34
|
+
export interface ToolExecutionEndEvent {
|
|
35
|
+
type: 'tool_execution_end';
|
|
36
|
+
toolCallId: string;
|
|
37
|
+
toolName: string;
|
|
38
|
+
result: ToolResult;
|
|
39
|
+
isError: boolean;
|
|
40
|
+
duration: number;
|
|
41
|
+
}
|
|
42
|
+
export interface CodeExecutionStartEvent {
|
|
43
|
+
type: 'code_execution_start';
|
|
44
|
+
code: string;
|
|
45
|
+
language: string;
|
|
46
|
+
}
|
|
47
|
+
export interface CodeExecutionEndEvent {
|
|
48
|
+
type: 'code_execution_end';
|
|
49
|
+
output: string;
|
|
50
|
+
error?: string;
|
|
51
|
+
duration: number;
|
|
52
|
+
}
|
|
53
|
+
/** Union of all events emitted by the Agent engine. */
|
|
54
|
+
export type AgentEvent = AgentStartEvent | AgentEndEvent | TurnStartEvent | TurnEndEvent | MessageUpdateEvent | ToolExecutionStartEvent | ToolExecutionEndEvent | CodeExecutionStartEvent | CodeExecutionEndEvent;
|
|
55
|
+
/** Callback signature for Agent event listeners. */
|
|
56
|
+
export type AgentEventListener = (event: AgentEvent) => void;
|