@lssm/lib.ai-agent 0.0.0-canary-20251217063201 → 0.0.0-canary-20251217072406
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/agent/agent-factory.js +102 -1
- package/dist/agent/contract-spec-agent.js +147 -1
- package/dist/agent/index.js +4 -1
- package/dist/approval/index.js +3 -1
- package/dist/approval/workflow.js +159 -1
- package/dist/index.js +21 -1
- package/dist/knowledge/index.js +3 -1
- package/dist/knowledge/injector.js +49 -5
- package/dist/memory/in-memory.js +47 -2
- package/dist/memory/index.js +4 -1
- package/dist/memory/manager.js +79 -1
- package/dist/schema/index.js +4 -1
- package/dist/schema/json-schema-to-zod.js +123 -1
- package/dist/schema/schema-output.js +64 -1
- package/dist/session/index.js +3 -1
- package/dist/session/store.js +78 -1
- package/dist/spec/index.js +4 -1
- package/dist/spec/registry.js +116 -1
- package/dist/spec/spec.js +29 -1
- package/dist/telemetry/adapter.js +102 -1
- package/dist/telemetry/index.js +3 -1
- package/dist/tools/index.js +6 -1
- package/dist/tools/knowledge-tool.js +50 -6
- package/dist/tools/mcp-client.js +57 -1
- package/dist/tools/mcp-server.js +68 -1
- package/dist/tools/tool-adapter.js +79 -1
- package/package.json +7 -6
|
@@ -1 +1,123 @@
|
|
|
1
|
-
import{z
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/schema/json-schema-to-zod.ts
|
|
4
|
+
/**
|
|
5
|
+
* Convert a JSON Schema to a Zod schema.
|
|
6
|
+
*
|
|
7
|
+
* Supports common JSON Schema types and constraints:
|
|
8
|
+
* - string, number, integer, boolean, null
|
|
9
|
+
* - object with properties and required
|
|
10
|
+
* - array with items
|
|
11
|
+
* - enum and const
|
|
12
|
+
* - anyOf, oneOf, allOf
|
|
13
|
+
* - format constraints (email, uri, uuid, date-time)
|
|
14
|
+
* - numeric constraints (minimum, maximum)
|
|
15
|
+
* - string constraints (minLength, maxLength, pattern)
|
|
16
|
+
*
|
|
17
|
+
* @param schema - JSON Schema object
|
|
18
|
+
* @returns Zod schema
|
|
19
|
+
*/
|
|
20
|
+
function jsonSchemaToZod(schema) {
|
|
21
|
+
const s = schema;
|
|
22
|
+
const makeNullable = (zodSchema) => {
|
|
23
|
+
return s.nullable ? z.union([zodSchema, z.null()]) : zodSchema;
|
|
24
|
+
};
|
|
25
|
+
if (s.const !== void 0) return z.literal(s.const);
|
|
26
|
+
if (s.enum) {
|
|
27
|
+
const values = s.enum;
|
|
28
|
+
return makeNullable(z.enum(values.map(String)));
|
|
29
|
+
}
|
|
30
|
+
if (s.anyOf && s.anyOf.length > 0) {
|
|
31
|
+
const schemas = s.anyOf.map((sub) => jsonSchemaToZod(sub));
|
|
32
|
+
if (schemas.length === 1) return schemas[0];
|
|
33
|
+
return z.union([
|
|
34
|
+
schemas[0],
|
|
35
|
+
schemas[1],
|
|
36
|
+
...schemas.slice(2)
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
if (s.oneOf && s.oneOf.length > 0) {
|
|
40
|
+
const schemas = s.oneOf.map((sub) => jsonSchemaToZod(sub));
|
|
41
|
+
if (schemas.length === 1) return schemas[0];
|
|
42
|
+
return z.union([
|
|
43
|
+
schemas[0],
|
|
44
|
+
schemas[1],
|
|
45
|
+
...schemas.slice(2)
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
if (s.allOf && s.allOf.length > 0) return s.allOf.map((sub) => jsonSchemaToZod(sub)).reduce((acc, curr) => z.intersection(acc, curr));
|
|
49
|
+
switch (s.type) {
|
|
50
|
+
case "string": return makeNullable(buildStringSchema(s));
|
|
51
|
+
case "number":
|
|
52
|
+
case "integer": return makeNullable(buildNumberSchema(s));
|
|
53
|
+
case "boolean": return makeNullable(z.boolean());
|
|
54
|
+
case "null": return z.null();
|
|
55
|
+
case "array": return makeNullable(buildArraySchema(s));
|
|
56
|
+
case "object": return makeNullable(buildObjectSchema(s));
|
|
57
|
+
default: return z.unknown();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function buildStringSchema(schema) {
|
|
61
|
+
let zodSchema = z.string();
|
|
62
|
+
if (schema.description) zodSchema = zodSchema.describe(schema.description);
|
|
63
|
+
switch (schema.format) {
|
|
64
|
+
case "email":
|
|
65
|
+
zodSchema = zodSchema.email();
|
|
66
|
+
break;
|
|
67
|
+
case "uri":
|
|
68
|
+
case "url":
|
|
69
|
+
zodSchema = zodSchema.url();
|
|
70
|
+
break;
|
|
71
|
+
case "uuid":
|
|
72
|
+
zodSchema = zodSchema.uuid();
|
|
73
|
+
break;
|
|
74
|
+
case "date-time":
|
|
75
|
+
zodSchema = zodSchema.datetime();
|
|
76
|
+
break;
|
|
77
|
+
case "date":
|
|
78
|
+
zodSchema = zodSchema.date();
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (schema.minLength !== void 0) zodSchema = zodSchema.min(schema.minLength);
|
|
82
|
+
if (schema.maxLength !== void 0) zodSchema = zodSchema.max(schema.maxLength);
|
|
83
|
+
if (schema.pattern) zodSchema = zodSchema.regex(new RegExp(schema.pattern));
|
|
84
|
+
return zodSchema;
|
|
85
|
+
}
|
|
86
|
+
function buildNumberSchema(schema) {
|
|
87
|
+
let zodSchema = schema.type === "integer" ? z.number().int() : z.number();
|
|
88
|
+
if (schema.description) zodSchema = zodSchema.describe(schema.description);
|
|
89
|
+
if (schema.minimum !== void 0) zodSchema = zodSchema.min(schema.minimum);
|
|
90
|
+
if (schema.maximum !== void 0) zodSchema = zodSchema.max(schema.maximum);
|
|
91
|
+
return zodSchema;
|
|
92
|
+
}
|
|
93
|
+
function buildArraySchema(schema) {
|
|
94
|
+
const itemsSchema = schema.items ? jsonSchemaToZod(schema.items) : z.unknown();
|
|
95
|
+
let zodSchema = z.array(itemsSchema);
|
|
96
|
+
if (schema.description) zodSchema = zodSchema.describe(schema.description);
|
|
97
|
+
return zodSchema;
|
|
98
|
+
}
|
|
99
|
+
function buildObjectSchema(schema) {
|
|
100
|
+
const properties = schema.properties ?? {};
|
|
101
|
+
const required = new Set(schema.required ?? []);
|
|
102
|
+
const shape = {};
|
|
103
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
104
|
+
const zodProp = jsonSchemaToZod(propSchema);
|
|
105
|
+
shape[key] = required.has(key) ? zodProp : zodProp.optional();
|
|
106
|
+
}
|
|
107
|
+
let zodSchema = z.object(shape);
|
|
108
|
+
if (schema.description) zodSchema = zodSchema.describe(schema.description);
|
|
109
|
+
return zodSchema;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Convert a JSON Schema to a Zod schema with a default empty object fallback.
|
|
113
|
+
*
|
|
114
|
+
* @param schema - Optional JSON Schema object
|
|
115
|
+
* @returns Zod schema (defaults to empty object schema)
|
|
116
|
+
*/
|
|
117
|
+
function jsonSchemaToZodSafe(schema) {
|
|
118
|
+
if (!schema || Object.keys(schema).length === 0) return z.object({});
|
|
119
|
+
return jsonSchemaToZod(schema);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
export { jsonSchemaToZod, jsonSchemaToZodSafe };
|
|
@@ -1 +1,64 @@
|
|
|
1
|
-
import{jsonSchemaToZod
|
|
1
|
+
import { jsonSchemaToZod } from "./json-schema-to-zod.js";
|
|
2
|
+
import { Output } from "ai";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/schema/schema-output.ts
|
|
6
|
+
/**
|
|
7
|
+
* Create an AI SDK Output.object from a JSON Schema.
|
|
8
|
+
*
|
|
9
|
+
* @param schema - JSON Schema object
|
|
10
|
+
* @returns AI SDK Output configuration
|
|
11
|
+
*/
|
|
12
|
+
function jsonSchemaToOutput(schema) {
|
|
13
|
+
const zodSchema = jsonSchemaToZod(schema);
|
|
14
|
+
return Output.object({ schema: zodSchema });
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create an AI SDK Output.array from a JSON Schema items definition.
|
|
18
|
+
*
|
|
19
|
+
* @param itemSchema - JSON Schema for array items
|
|
20
|
+
* @returns AI SDK Output configuration
|
|
21
|
+
*/
|
|
22
|
+
function jsonSchemaToArrayOutput(itemSchema) {
|
|
23
|
+
const zodSchema = jsonSchemaToZod(itemSchema);
|
|
24
|
+
return Output.array({ element: zodSchema });
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create an AI SDK Output.choice from enum values.
|
|
28
|
+
*
|
|
29
|
+
* @param choices - Array of choice values
|
|
30
|
+
* @returns AI SDK Output configuration
|
|
31
|
+
*/
|
|
32
|
+
function enumToChoiceOutput(choices) {
|
|
33
|
+
return Output.choice({ options: choices });
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create an AI SDK Output from a Zod schema directly.
|
|
37
|
+
*
|
|
38
|
+
* @param schema - Zod schema
|
|
39
|
+
* @returns AI SDK Output configuration
|
|
40
|
+
*/
|
|
41
|
+
function zodToOutput(schema) {
|
|
42
|
+
return Output.object({ schema });
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a simple text output configuration.
|
|
46
|
+
*
|
|
47
|
+
* @returns AI SDK Output configuration for text
|
|
48
|
+
*/
|
|
49
|
+
function textOutput() {
|
|
50
|
+
return Output.text();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Output builder that can be used fluently.
|
|
54
|
+
*/
|
|
55
|
+
const SchemaOutput = {
|
|
56
|
+
fromJsonSchema: jsonSchemaToOutput,
|
|
57
|
+
arrayFromJsonSchema: jsonSchemaToArrayOutput,
|
|
58
|
+
fromEnum: enumToChoiceOutput,
|
|
59
|
+
fromZod: zodToOutput,
|
|
60
|
+
text: textOutput
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { SchemaOutput, enumToChoiceOutput, jsonSchemaToArrayOutput, jsonSchemaToOutput, textOutput, zodToOutput };
|
package/dist/session/index.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
import{InMemorySessionStore
|
|
1
|
+
import { InMemorySessionStore, createInMemorySessionStore, generateSessionId } from "./store.js";
|
|
2
|
+
|
|
3
|
+
export { InMemorySessionStore, createInMemorySessionStore, generateSessionId };
|
package/dist/session/store.js
CHANGED
|
@@ -1 +1,78 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/session/store.ts
|
|
2
|
+
/**
|
|
3
|
+
* In-memory session store for development and testing.
|
|
4
|
+
*/
|
|
5
|
+
var InMemorySessionStore = class {
|
|
6
|
+
sessions = /* @__PURE__ */ new Map();
|
|
7
|
+
async get(sessionId) {
|
|
8
|
+
return this.sessions.get(sessionId) ?? null;
|
|
9
|
+
}
|
|
10
|
+
async create(session) {
|
|
11
|
+
const now = /* @__PURE__ */ new Date();
|
|
12
|
+
const fullSession = {
|
|
13
|
+
...session,
|
|
14
|
+
createdAt: now,
|
|
15
|
+
updatedAt: now
|
|
16
|
+
};
|
|
17
|
+
this.sessions.set(session.sessionId, fullSession);
|
|
18
|
+
return fullSession;
|
|
19
|
+
}
|
|
20
|
+
async appendStep(sessionId, step) {
|
|
21
|
+
const session = this.sessions.get(sessionId);
|
|
22
|
+
if (session) {
|
|
23
|
+
session.steps.push(step);
|
|
24
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async appendMessage(sessionId, message) {
|
|
28
|
+
const session = this.sessions.get(sessionId);
|
|
29
|
+
if (session) {
|
|
30
|
+
session.messages.push(message);
|
|
31
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async update(sessionId, updates) {
|
|
35
|
+
const session = this.sessions.get(sessionId);
|
|
36
|
+
if (session) Object.assign(session, updates, { updatedAt: /* @__PURE__ */ new Date() });
|
|
37
|
+
}
|
|
38
|
+
async delete(sessionId) {
|
|
39
|
+
return this.sessions.delete(sessionId);
|
|
40
|
+
}
|
|
41
|
+
async listByAgent(agentId, limit = 100) {
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const session of this.sessions.values()) if (session.agentId === agentId) {
|
|
44
|
+
results.push(session);
|
|
45
|
+
if (results.length >= limit) break;
|
|
46
|
+
}
|
|
47
|
+
return results.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
48
|
+
}
|
|
49
|
+
async listByTenant(tenantId, limit = 100) {
|
|
50
|
+
const results = [];
|
|
51
|
+
for (const session of this.sessions.values()) if (session.tenantId === tenantId) {
|
|
52
|
+
results.push(session);
|
|
53
|
+
if (results.length >= limit) break;
|
|
54
|
+
}
|
|
55
|
+
return results.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Clear all sessions (for testing).
|
|
59
|
+
*/
|
|
60
|
+
clear() {
|
|
61
|
+
this.sessions.clear();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Create an in-memory session store.
|
|
66
|
+
*/
|
|
67
|
+
function createInMemorySessionStore() {
|
|
68
|
+
return new InMemorySessionStore();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Generate a unique session ID.
|
|
72
|
+
*/
|
|
73
|
+
function generateSessionId() {
|
|
74
|
+
return `sess_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
export { InMemorySessionStore, createInMemorySessionStore, generateSessionId };
|
package/dist/spec/index.js
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
import{agentKey
|
|
1
|
+
import { agentKey, defineAgent } from "./spec.js";
|
|
2
|
+
import { AgentRegistry, createAgentRegistry } from "./registry.js";
|
|
3
|
+
|
|
4
|
+
export { AgentRegistry, agentKey, createAgentRegistry, defineAgent };
|
package/dist/spec/registry.js
CHANGED
|
@@ -1 +1,116 @@
|
|
|
1
|
-
import{agentKey
|
|
1
|
+
import { agentKey } from "./spec.js";
|
|
2
|
+
|
|
3
|
+
//#region src/spec/registry.ts
|
|
4
|
+
/**
|
|
5
|
+
* Registry for managing agent specifications.
|
|
6
|
+
*
|
|
7
|
+
* Provides registration, lookup, and version management for agent specs.
|
|
8
|
+
*/
|
|
9
|
+
var AgentRegistry = class {
|
|
10
|
+
specs = /* @__PURE__ */ new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Register an agent specification.
|
|
13
|
+
*
|
|
14
|
+
* @param spec - The agent specification to register
|
|
15
|
+
* @returns This registry for chaining
|
|
16
|
+
* @throws Error if the spec is already registered
|
|
17
|
+
*/
|
|
18
|
+
register(spec) {
|
|
19
|
+
const key = agentKey(spec.meta);
|
|
20
|
+
if (this.specs.has(key)) throw new Error(`Duplicate agent spec registered for ${key}`);
|
|
21
|
+
this.specs.set(key, spec);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Unregister an agent specification.
|
|
26
|
+
*
|
|
27
|
+
* @param name - Agent name
|
|
28
|
+
* @param version - Agent version
|
|
29
|
+
* @returns True if the spec was removed
|
|
30
|
+
*/
|
|
31
|
+
unregister(name, version) {
|
|
32
|
+
return this.specs.delete(`${name}.v${version}`);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* List all registered agent specifications.
|
|
36
|
+
*/
|
|
37
|
+
list() {
|
|
38
|
+
return [...this.specs.values()];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* List all unique agent names (without versions).
|
|
42
|
+
*/
|
|
43
|
+
listNames() {
|
|
44
|
+
const names = /* @__PURE__ */ new Set();
|
|
45
|
+
for (const spec of this.specs.values()) names.add(spec.meta.name);
|
|
46
|
+
return [...names];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get an agent specification by name and optional version.
|
|
50
|
+
*
|
|
51
|
+
* @param name - Agent name
|
|
52
|
+
* @param version - Optional version. If omitted, returns the latest version.
|
|
53
|
+
* @returns The agent spec or undefined if not found
|
|
54
|
+
*/
|
|
55
|
+
get(name, version) {
|
|
56
|
+
if (version != null) return this.specs.get(`${name}.v${version}`);
|
|
57
|
+
let latest;
|
|
58
|
+
let maxVersion = -Infinity;
|
|
59
|
+
for (const spec of this.specs.values()) {
|
|
60
|
+
if (spec.meta.name !== name) continue;
|
|
61
|
+
if (spec.meta.version > maxVersion) {
|
|
62
|
+
latest = spec;
|
|
63
|
+
maxVersion = spec.meta.version;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return latest;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get an agent specification or throw if not found.
|
|
70
|
+
*
|
|
71
|
+
* @param name - Agent name
|
|
72
|
+
* @param version - Optional version
|
|
73
|
+
* @returns The agent spec
|
|
74
|
+
* @throws Error if the spec is not found
|
|
75
|
+
*/
|
|
76
|
+
require(name, version) {
|
|
77
|
+
const spec = this.get(name, version);
|
|
78
|
+
if (!spec) throw new Error(`Agent spec not found for ${name}${version != null ? `.v${version}` : ""}`);
|
|
79
|
+
return spec;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check if an agent is registered.
|
|
83
|
+
*
|
|
84
|
+
* @param name - Agent name
|
|
85
|
+
* @param version - Optional version
|
|
86
|
+
*/
|
|
87
|
+
has(name, version) {
|
|
88
|
+
return this.get(name, version) !== void 0;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all versions of an agent.
|
|
92
|
+
*
|
|
93
|
+
* @param name - Agent name
|
|
94
|
+
* @returns Array of specs sorted by version (ascending)
|
|
95
|
+
*/
|
|
96
|
+
getVersions(name) {
|
|
97
|
+
const versions = [];
|
|
98
|
+
for (const spec of this.specs.values()) if (spec.meta.name === name) versions.push(spec);
|
|
99
|
+
return versions.sort((a, b) => a.meta.version - b.meta.version);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Clear all registered specs.
|
|
103
|
+
*/
|
|
104
|
+
clear() {
|
|
105
|
+
this.specs.clear();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Create a new agent registry.
|
|
110
|
+
*/
|
|
111
|
+
function createAgentRegistry() {
|
|
112
|
+
return new AgentRegistry();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
//#endregion
|
|
116
|
+
export { AgentRegistry, createAgentRegistry };
|
package/dist/spec/spec.js
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/spec/spec.ts
|
|
2
|
+
/**
|
|
3
|
+
* Define and validate an agent specification.
|
|
4
|
+
*
|
|
5
|
+
* @param spec - The agent specification
|
|
6
|
+
* @returns The frozen, validated specification
|
|
7
|
+
* @throws Error if the specification is invalid
|
|
8
|
+
*/
|
|
9
|
+
function defineAgent(spec) {
|
|
10
|
+
if (!spec.meta?.name) throw new Error("Agent name is required");
|
|
11
|
+
if (!Number.isFinite(spec.meta.version)) throw new Error(`Agent ${spec.meta.name} is missing a numeric version`);
|
|
12
|
+
if (!spec.instructions?.trim()) throw new Error(`Agent ${spec.meta.name} requires instructions`);
|
|
13
|
+
if (!spec.tools?.length) throw new Error(`Agent ${spec.meta.name} must expose at least one tool`);
|
|
14
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
15
|
+
for (const tool of spec.tools) {
|
|
16
|
+
if (toolNames.has(tool.name)) throw new Error(`Agent ${spec.meta.name} has duplicate tool name: ${tool.name}`);
|
|
17
|
+
toolNames.add(tool.name);
|
|
18
|
+
}
|
|
19
|
+
return Object.freeze(spec);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate a unique key for an agent spec.
|
|
23
|
+
*/
|
|
24
|
+
function agentKey(meta) {
|
|
25
|
+
return `${meta.name}.v${meta.version}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { agentKey, defineAgent };
|
|
@@ -1 +1,102 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/telemetry/adapter.ts
|
|
2
|
+
/**
|
|
3
|
+
* Parse agent ID into name and version.
|
|
4
|
+
*/
|
|
5
|
+
function parseAgentId(agentId) {
|
|
6
|
+
const match = agentId.match(/^(.+)\.v(\d+)$/);
|
|
7
|
+
if (match) return {
|
|
8
|
+
name: match[1],
|
|
9
|
+
version: parseInt(match[2], 10)
|
|
10
|
+
};
|
|
11
|
+
return {
|
|
12
|
+
name: agentId,
|
|
13
|
+
version: 1
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Track an agent step for telemetry.
|
|
18
|
+
*
|
|
19
|
+
* Called from ContractSpecAgent.onStepFinish to feed metrics
|
|
20
|
+
* to the evolution engine.
|
|
21
|
+
*
|
|
22
|
+
* @param collector - Telemetry collector
|
|
23
|
+
* @param agentId - Agent identifier (e.g., "support.bot.v1")
|
|
24
|
+
* @param step - AI SDK step result
|
|
25
|
+
* @param durationMs - Optional step duration in milliseconds
|
|
26
|
+
*/
|
|
27
|
+
async function trackAgentStep(collector, agentId, step, durationMs) {
|
|
28
|
+
const { name, version } = parseAgentId(agentId);
|
|
29
|
+
for (const toolCall of step.toolCalls ?? []) {
|
|
30
|
+
const toolSample = {
|
|
31
|
+
operation: {
|
|
32
|
+
name: `${name}.${toolCall.toolName}`,
|
|
33
|
+
version
|
|
34
|
+
},
|
|
35
|
+
durationMs: durationMs ?? 0,
|
|
36
|
+
success: step.toolResults?.some((r) => r.toolCallId === toolCall.toolCallId && r.output !== void 0) ?? false,
|
|
37
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
38
|
+
metadata: {
|
|
39
|
+
agentId,
|
|
40
|
+
toolName: toolCall.toolName,
|
|
41
|
+
finishReason: step.finishReason
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
await collector.collect(toolSample);
|
|
45
|
+
}
|
|
46
|
+
const stepSample = {
|
|
47
|
+
operation: {
|
|
48
|
+
name,
|
|
49
|
+
version
|
|
50
|
+
},
|
|
51
|
+
durationMs: durationMs ?? 0,
|
|
52
|
+
success: step.finishReason !== "error",
|
|
53
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
54
|
+
metadata: {
|
|
55
|
+
agentId,
|
|
56
|
+
finishReason: step.finishReason,
|
|
57
|
+
tokenUsage: step.usage,
|
|
58
|
+
toolCallCount: step.toolCalls?.length ?? 0
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
await collector.collect(stepSample);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* In-memory telemetry collector for testing.
|
|
65
|
+
*/
|
|
66
|
+
var InMemoryTelemetryCollector = class {
|
|
67
|
+
samples = [];
|
|
68
|
+
async collect(sample) {
|
|
69
|
+
this.samples.push(sample);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get all collected samples.
|
|
73
|
+
*/
|
|
74
|
+
getSamples() {
|
|
75
|
+
return [...this.samples];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get samples for a specific operation.
|
|
79
|
+
*/
|
|
80
|
+
getSamplesForOperation(operationName) {
|
|
81
|
+
return this.samples.filter((s) => s.operation.name === operationName);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Clear all samples.
|
|
85
|
+
*/
|
|
86
|
+
clear() {
|
|
87
|
+
this.samples.length = 0;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Create an in-memory telemetry collector.
|
|
92
|
+
*/
|
|
93
|
+
function createInMemoryTelemetryCollector() {
|
|
94
|
+
return new InMemoryTelemetryCollector();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* No-op telemetry collector that discards all metrics.
|
|
98
|
+
*/
|
|
99
|
+
const noopTelemetryCollector = { collect: async () => {} };
|
|
100
|
+
|
|
101
|
+
//#endregion
|
|
102
|
+
export { InMemoryTelemetryCollector, createInMemoryTelemetryCollector, noopTelemetryCollector, trackAgentStep };
|
package/dist/telemetry/index.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
import{InMemoryTelemetryCollector
|
|
1
|
+
import { InMemoryTelemetryCollector, createInMemoryTelemetryCollector, noopTelemetryCollector, trackAgentStep } from "./adapter.js";
|
|
2
|
+
|
|
3
|
+
export { InMemoryTelemetryCollector, createInMemoryTelemetryCollector, noopTelemetryCollector, trackAgentStep };
|
package/dist/tools/index.js
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
import{buildToolHandlers
|
|
1
|
+
import { buildToolHandlers, createToolHandler, specToolToAISDKTool, specToolsToAISDKTools } from "./tool-adapter.js";
|
|
2
|
+
import { createKnowledgeQueryTool } from "./knowledge-tool.js";
|
|
3
|
+
import { createMcpToolsets, mcpServerToTools } from "./mcp-client.js";
|
|
4
|
+
import { agentToMcpServer, createAgentMcpServer } from "./mcp-server.js";
|
|
5
|
+
|
|
6
|
+
export { agentToMcpServer, buildToolHandlers, createAgentMcpServer, createKnowledgeQueryTool, createMcpToolsets, createToolHandler, mcpServerToTools, specToolToAISDKTool, specToolsToAISDKTools };
|
|
@@ -1,9 +1,53 @@
|
|
|
1
|
-
import{tool
|
|
1
|
+
import { tool } from "ai";
|
|
2
|
+
import * as z$1 from "zod";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
//#region src/tools/knowledge-tool.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create a knowledge query tool for dynamic RAG.
|
|
7
|
+
*
|
|
8
|
+
* This tool allows the agent to query optional knowledge spaces
|
|
9
|
+
* at runtime. Required knowledge is injected statically via
|
|
10
|
+
* the knowledge injector.
|
|
11
|
+
*
|
|
12
|
+
* @param retriever - The knowledge retriever to use
|
|
13
|
+
* @param knowledgeRefs - Knowledge references from the agent spec
|
|
14
|
+
* @returns AI SDK CoreTool for knowledge queries
|
|
15
|
+
*/
|
|
16
|
+
function createKnowledgeQueryTool(retriever, knowledgeRefs) {
|
|
17
|
+
const optionalSpaces = knowledgeRefs.filter((k) => !k.required).map((k) => k.key).filter((key) => retriever.supportsSpace(key));
|
|
18
|
+
if (optionalSpaces.length === 0) return null;
|
|
19
|
+
return tool({
|
|
20
|
+
description: `Query knowledge bases for relevant information. Use this tool when you need to look up specific information that may not be in your context.
|
|
6
21
|
|
|
7
|
-
|
|
22
|
+
Available knowledge spaces:
|
|
23
|
+
${knowledgeRefs.filter((k) => !k.required && retriever.supportsSpace(k.key)).map((k) => `- ${k.key}: ${k.instructions ?? "Knowledge space"}`).join("\n")}`,
|
|
24
|
+
inputSchema: z$1.object({
|
|
25
|
+
query: z$1.string().describe("The question or search query to find relevant information"),
|
|
26
|
+
spaceKey: z$1.enum(optionalSpaces).optional().describe("Specific knowledge space to query. If omitted, searches all available spaces."),
|
|
27
|
+
topK: z$1.number().optional().default(5).describe("Maximum number of results to return")
|
|
28
|
+
}),
|
|
29
|
+
execute: async ({ query, spaceKey, topK }) => {
|
|
30
|
+
const spacesToSearch = spaceKey ? [spaceKey] : optionalSpaces;
|
|
31
|
+
const allResults = [];
|
|
32
|
+
for (const space of spacesToSearch) try {
|
|
33
|
+
const results = await retriever.retrieve(query, {
|
|
34
|
+
spaceKey: space,
|
|
35
|
+
topK: topK ?? 5
|
|
36
|
+
});
|
|
37
|
+
for (const result of results) allResults.push({
|
|
38
|
+
space,
|
|
39
|
+
content: result.content,
|
|
40
|
+
score: result.score
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.warn(`Failed to query knowledge space ${space}:`, error);
|
|
44
|
+
}
|
|
45
|
+
if (allResults.length === 0) return "No relevant information found in the knowledge bases.";
|
|
46
|
+
allResults.sort((a, b) => b.score - a.score);
|
|
47
|
+
return allResults.slice(0, topK ?? 5).map((r, i) => `[Source ${i + 1} - ${r.space}] (relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`).join("\n\n---\n\n");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
8
51
|
|
|
9
|
-
|
|
52
|
+
//#endregion
|
|
53
|
+
export { createKnowledgeQueryTool };
|
package/dist/tools/mcp-client.js
CHANGED
|
@@ -1 +1,57 @@
|
|
|
1
|
-
import{experimental_createMCPClient
|
|
1
|
+
import { experimental_createMCPClient } from "@ai-sdk/mcp";
|
|
2
|
+
import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
|
|
3
|
+
|
|
4
|
+
//#region src/tools/mcp-client.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create AI SDK tools from an MCP server.
|
|
7
|
+
*
|
|
8
|
+
* This adapter allows ContractSpec agents to consume tools
|
|
9
|
+
* from external MCP servers (e.g., filesystem, database, etc.).
|
|
10
|
+
*
|
|
11
|
+
* @param config - MCP server configuration
|
|
12
|
+
* @returns Tools and cleanup function
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const { tools, cleanup } = await mcpServerToTools({
|
|
17
|
+
* name: 'filesystem',
|
|
18
|
+
* command: 'npx',
|
|
19
|
+
* args: ['-y', '@modelcontextprotocol/server-filesystem', '/path'],
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Use tools in agent...
|
|
23
|
+
*
|
|
24
|
+
* await cleanup();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
async function mcpServerToTools(config) {
|
|
28
|
+
const client = await experimental_createMCPClient({ transport: new Experimental_StdioMCPTransport({
|
|
29
|
+
command: config.command,
|
|
30
|
+
args: config.args,
|
|
31
|
+
env: config.env
|
|
32
|
+
}) });
|
|
33
|
+
return {
|
|
34
|
+
tools: await client.tools(),
|
|
35
|
+
cleanup: () => client.close()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create multiple MCP tool sets from configurations.
|
|
40
|
+
*
|
|
41
|
+
* @param configs - Array of MCP server configurations
|
|
42
|
+
* @returns Combined tools and cleanup function
|
|
43
|
+
*/
|
|
44
|
+
async function createMcpToolsets(configs) {
|
|
45
|
+
const results = await Promise.all(configs.map(mcpServerToTools));
|
|
46
|
+
const combinedTools = {};
|
|
47
|
+
for (const result of results) Object.assign(combinedTools, result.tools);
|
|
48
|
+
return {
|
|
49
|
+
tools: combinedTools,
|
|
50
|
+
cleanup: async () => {
|
|
51
|
+
await Promise.all(results.map((r) => r.cleanup()));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { createMcpToolsets, mcpServerToTools };
|