@infinityi/engine-lib 1.0.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/LICENSE +21 -0
- package/README.md +488 -0
- package/dist/agent/agent-registry.d.ts +46 -0
- package/dist/agent/as-tool.d.ts +64 -0
- package/dist/agent/define.d.ts +35 -0
- package/dist/agent/handoff.d.ts +39 -0
- package/dist/agent/index.d.ts +20 -0
- package/dist/agent/index.js +38 -0
- package/dist/agent/registry.d.ts +27 -0
- package/dist/agent/types.d.ts +109 -0
- package/dist/context/index.d.ts +11 -0
- package/dist/context/index.js +21 -0
- package/dist/context/providers.d.ts +25 -0
- package/dist/context/types.d.ts +63 -0
- package/dist/context/window.d.ts +41 -0
- package/dist/errors.d.ts +93 -0
- package/dist/errors.js +24 -0
- package/dist/events/hub.d.ts +15 -0
- package/dist/events/index.d.ts +26 -0
- package/dist/events/index.js +24 -0
- package/dist/events/subscribers.d.ts +57 -0
- package/dist/events/telemetry.d.ts +61 -0
- package/dist/events/types.d.ts +39 -0
- package/dist/execution/index.d.ts +11 -0
- package/dist/execution/index.js +22 -0
- package/dist/execution/run.d.ts +35 -0
- package/dist/execution/types.d.ts +203 -0
- package/dist/execution/usage.d.ts +14 -0
- package/dist/index-02s1fjxr.js +226 -0
- package/dist/index-19pwq79t.js +0 -0
- package/dist/index-1p6mb2vz.js +32 -0
- package/dist/index-64tt9696.js +1796 -0
- package/dist/index-7690reng.js +96 -0
- package/dist/index-bqg01r42.js +354 -0
- package/dist/index-d4xz3abn.js +0 -0
- package/dist/index-dexgmwg6.js +148 -0
- package/dist/index-fkr3rcq9.js +97 -0
- package/dist/index-jg19te9v.js +0 -0
- package/dist/index-jp2b31xs.js +101 -0
- package/dist/index-jxgj4z08.js +68 -0
- package/dist/index-kte2h4k2.js +0 -0
- package/dist/index-pwr8179t.js +492 -0
- package/dist/index-rentvdpp.js +27 -0
- package/dist/index-vnby35rm.js +84 -0
- package/dist/index-w34cbktd.js +14 -0
- package/dist/index-xsv43c5j.js +39 -0
- package/dist/index-yrqrxwjt.js +148 -0
- package/dist/index-zfgr4xx3.js +90 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +117 -0
- package/dist/lifecycle/component.d.ts +74 -0
- package/dist/lifecycle/index.d.ts +12 -0
- package/dist/lifecycle/index.js +72 -0
- package/dist/messages/factory.d.ts +24 -0
- package/dist/messages/index.d.ts +8 -0
- package/dist/messages/index.js +17 -0
- package/dist/messages/types.d.ts +52 -0
- package/dist/providers/adapter.d.ts +42 -0
- package/dist/providers/anthropic/index.d.ts +31 -0
- package/dist/providers/anthropic/map.d.ts +12 -0
- package/dist/providers/anthropic/stream.d.ts +9 -0
- package/dist/providers/google/index.d.ts +29 -0
- package/dist/providers/google/map.d.ts +13 -0
- package/dist/providers/google/stream.d.ts +11 -0
- package/dist/providers/http.d.ts +61 -0
- package/dist/providers/index.d.ts +32 -0
- package/dist/providers/index.js +35 -0
- package/dist/providers/openai/index.d.ts +34 -0
- package/dist/providers/openai/map.d.ts +10 -0
- package/dist/providers/openai/stream.d.ts +9 -0
- package/dist/providers/openai-compatible/index.d.ts +37 -0
- package/dist/providers/openai-compatible/map.d.ts +13 -0
- package/dist/providers/openai-compatible/stream.d.ts +11 -0
- package/dist/providers/shared.d.ts +34 -0
- package/dist/providers/sse.d.ts +19 -0
- package/dist/providers/stream.d.ts +69 -0
- package/dist/providers/types.d.ts +137 -0
- package/dist/runtime/index.d.ts +11 -0
- package/dist/runtime/index.js +11 -0
- package/dist/runtime/secret.d.ts +12 -0
- package/dist/runtime/types.d.ts +27 -0
- package/dist/schema/builder.d.ts +70 -0
- package/dist/schema/index.d.ts +13 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/json-schema.d.ts +19 -0
- package/dist/schema/types.d.ts +70 -0
- package/dist/schema/validate.d.ts +19 -0
- package/dist/session/index.d.ts +11 -0
- package/dist/session/index.js +8 -0
- package/dist/session/session.d.ts +31 -0
- package/dist/session/store.d.ts +20 -0
- package/dist/session/types.d.ts +55 -0
- package/dist/testing/conformance.d.ts +106 -0
- package/dist/testing/conformance.js +132 -0
- package/dist/testing/index.d.ts +84 -0
- package/dist/testing/index.js +31 -0
- package/dist/tools/define.d.ts +42 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/result.d.ts +36 -0
- package/dist/tools/types.d.ts +85 -0
- package/docs/README.md +36 -0
- package/examples/README.md +24 -0
- package/examples/incident-analysis.ts +100 -0
- package/examples/lifecycle.ts +53 -0
- package/examples/multi-agent.ts +93 -0
- package/examples/terminal-coder.ts +80 -0
- package/package.json +114 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createToolRegistry,
|
|
3
|
+
runAgent
|
|
4
|
+
} from "./index-pwr8179t.js";
|
|
5
|
+
import {
|
|
6
|
+
s
|
|
7
|
+
} from "./index-02s1fjxr.js";
|
|
8
|
+
import {
|
|
9
|
+
AgentError,
|
|
10
|
+
ExecutionError
|
|
11
|
+
} from "./index-7690reng.js";
|
|
12
|
+
import {
|
|
13
|
+
defineTool
|
|
14
|
+
} from "./index-w34cbktd.js";
|
|
15
|
+
|
|
16
|
+
// src/agent/define.ts
|
|
17
|
+
function defineAgent(def) {
|
|
18
|
+
if (typeof def.name !== "string" || def.name.trim() === "") {
|
|
19
|
+
throw new TypeError("defineAgent: `name` must be a non-empty string");
|
|
20
|
+
}
|
|
21
|
+
createToolRegistry(def.tools ?? []);
|
|
22
|
+
return def;
|
|
23
|
+
}
|
|
24
|
+
// src/agent/agent-registry.ts
|
|
25
|
+
function createAgentRegistry(agents = []) {
|
|
26
|
+
const byName = new Map;
|
|
27
|
+
const ordered = [];
|
|
28
|
+
const register = (agent) => {
|
|
29
|
+
if (byName.has(agent.name)) {
|
|
30
|
+
throw new ExecutionError(`duplicate agent name: "${agent.name}"`);
|
|
31
|
+
}
|
|
32
|
+
byName.set(agent.name, agent);
|
|
33
|
+
ordered.push(agent);
|
|
34
|
+
};
|
|
35
|
+
for (const agent of agents)
|
|
36
|
+
register(agent);
|
|
37
|
+
return {
|
|
38
|
+
get size() {
|
|
39
|
+
return ordered.length;
|
|
40
|
+
},
|
|
41
|
+
has(name) {
|
|
42
|
+
return byName.has(name);
|
|
43
|
+
},
|
|
44
|
+
get(name) {
|
|
45
|
+
return byName.get(name);
|
|
46
|
+
},
|
|
47
|
+
resolve(name) {
|
|
48
|
+
const agent = byName.get(name);
|
|
49
|
+
if (agent === undefined) {
|
|
50
|
+
throw new ExecutionError(`unknown agent: "${name}"`);
|
|
51
|
+
}
|
|
52
|
+
return agent;
|
|
53
|
+
},
|
|
54
|
+
list() {
|
|
55
|
+
return [...ordered];
|
|
56
|
+
},
|
|
57
|
+
register
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// src/agent/as-tool.ts
|
|
61
|
+
var DEFAULT_PARAMETERS = s.object({ input: s.string() });
|
|
62
|
+
function childDepth(event) {
|
|
63
|
+
return event.type === "agent.child" ? event.depth + 1 : 1;
|
|
64
|
+
}
|
|
65
|
+
function asTool(agent, opts = {}) {
|
|
66
|
+
const parameters = opts.parameters ?? DEFAULT_PARAMETERS;
|
|
67
|
+
const toInput = opts.toInput ?? ((args) => args.input);
|
|
68
|
+
return defineTool({
|
|
69
|
+
name: opts.name ?? agent.name,
|
|
70
|
+
description: opts.description ?? `Delegate the task to the "${agent.name}" agent.`,
|
|
71
|
+
parameters,
|
|
72
|
+
execute: async (args, ctx) => {
|
|
73
|
+
const bridge = ctx.run;
|
|
74
|
+
try {
|
|
75
|
+
const result = await runAgent(agent, {
|
|
76
|
+
input: toInput(args),
|
|
77
|
+
...ctx.telemetry !== undefined ? { telemetry: ctx.telemetry } : {},
|
|
78
|
+
...ctx.logger !== undefined ? { logger: ctx.logger } : {},
|
|
79
|
+
...ctx.signal !== undefined ? { signal: ctx.signal } : {},
|
|
80
|
+
...bridge !== undefined ? {
|
|
81
|
+
onEvent: (event) => bridge.emit({
|
|
82
|
+
type: "agent.child",
|
|
83
|
+
agent: agent.name,
|
|
84
|
+
depth: childDepth(event),
|
|
85
|
+
event
|
|
86
|
+
})
|
|
87
|
+
} : {}
|
|
88
|
+
});
|
|
89
|
+
bridge?.reportUsage(result.usage);
|
|
90
|
+
return { ok: true, content: result.output };
|
|
91
|
+
} catch (err) {
|
|
92
|
+
if (err instanceof AgentError && err.usage !== undefined) {
|
|
93
|
+
bridge?.reportUsage(err.usage);
|
|
94
|
+
}
|
|
95
|
+
const message = err instanceof AgentError ? err.message : String(err);
|
|
96
|
+
return { ok: false, error: message };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export { defineAgent, createAgentRegistry, asTool };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/events/subscribers.ts
|
|
2
|
+
function loggingSubscriber(logger, opts = {}) {
|
|
3
|
+
const level = opts.level ?? "debug";
|
|
4
|
+
return (event) => {
|
|
5
|
+
logger[level](`agent.run ${event.type}`, eventFields(event));
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function messageBusSubscriber(bus, opts = {}) {
|
|
9
|
+
const prefix = opts.typePrefix ?? "agent.";
|
|
10
|
+
return async (event) => {
|
|
11
|
+
await bus.publish({ type: `${prefix}${event.type}`, payload: eventPayload(event) });
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function eventFields(event) {
|
|
15
|
+
switch (event.type) {
|
|
16
|
+
case "run.start":
|
|
17
|
+
return { agent: event.agent };
|
|
18
|
+
case "message":
|
|
19
|
+
return { role: event.message.role, parts: event.message.content.length };
|
|
20
|
+
case "token":
|
|
21
|
+
return { length: event.delta.length };
|
|
22
|
+
case "tool.call":
|
|
23
|
+
return { id: event.id, name: event.name };
|
|
24
|
+
case "tool.result":
|
|
25
|
+
return { id: event.id, name: event.name, ok: event.result.ok };
|
|
26
|
+
case "run.finish":
|
|
27
|
+
return {
|
|
28
|
+
finishReason: event.result.finishReason,
|
|
29
|
+
steps: event.result.steps,
|
|
30
|
+
totalTokens: event.result.usage.totalTokens
|
|
31
|
+
};
|
|
32
|
+
case "error":
|
|
33
|
+
return { name: event.error.name, message: event.error.message };
|
|
34
|
+
case "agent.child":
|
|
35
|
+
return { agent: event.agent, depth: event.depth, child: event.event.type };
|
|
36
|
+
case "agent.handoff":
|
|
37
|
+
return { from: event.from, to: event.to };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function eventPayload(event) {
|
|
41
|
+
switch (event.type) {
|
|
42
|
+
case "run.start":
|
|
43
|
+
return { agent: event.agent };
|
|
44
|
+
case "message":
|
|
45
|
+
return { message: event.message };
|
|
46
|
+
case "token":
|
|
47
|
+
return { delta: event.delta };
|
|
48
|
+
case "tool.call":
|
|
49
|
+
return { id: event.id, name: event.name, arguments: event.arguments };
|
|
50
|
+
case "tool.result":
|
|
51
|
+
return { id: event.id, name: event.name, result: event.result };
|
|
52
|
+
case "run.finish":
|
|
53
|
+
return {
|
|
54
|
+
output: event.result.output,
|
|
55
|
+
finishReason: event.result.finishReason,
|
|
56
|
+
steps: event.result.steps,
|
|
57
|
+
usage: event.result.usage,
|
|
58
|
+
messages: event.result.messages
|
|
59
|
+
};
|
|
60
|
+
case "error":
|
|
61
|
+
return { name: event.error.name, message: event.error.message };
|
|
62
|
+
case "agent.child":
|
|
63
|
+
return { agent: event.agent, depth: event.depth, event: eventPayload(event.event) };
|
|
64
|
+
case "agent.handoff":
|
|
65
|
+
return { from: event.from, to: event.to };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export { loggingSubscriber, messageBusSubscriber, eventFields, eventPayload };
|
|
File without changes
|
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyContextWindow,
|
|
3
|
+
resolveContext
|
|
4
|
+
} from "./index-yrqrxwjt.js";
|
|
5
|
+
import {
|
|
6
|
+
SPAN_PROVIDER,
|
|
7
|
+
SPAN_RUN,
|
|
8
|
+
SPAN_TOOL,
|
|
9
|
+
createEventHub,
|
|
10
|
+
createRunTelemetry
|
|
11
|
+
} from "./index-fkr3rcq9.js";
|
|
12
|
+
import {
|
|
13
|
+
s
|
|
14
|
+
} from "./index-02s1fjxr.js";
|
|
15
|
+
import {
|
|
16
|
+
StreamAccumulator
|
|
17
|
+
} from "./index-zfgr4xx3.js";
|
|
18
|
+
import {
|
|
19
|
+
AgentError,
|
|
20
|
+
CancelledError,
|
|
21
|
+
ExecutionError,
|
|
22
|
+
MaxHandoffsExceededError,
|
|
23
|
+
MaxStepsExceededError
|
|
24
|
+
} from "./index-7690reng.js";
|
|
25
|
+
import {
|
|
26
|
+
toProviderTool,
|
|
27
|
+
toToolResultMessage
|
|
28
|
+
} from "./index-rentvdpp.js";
|
|
29
|
+
import {
|
|
30
|
+
system,
|
|
31
|
+
user
|
|
32
|
+
} from "./index-1p6mb2vz.js";
|
|
33
|
+
|
|
34
|
+
// src/agent/registry.ts
|
|
35
|
+
function createToolRegistry(tools) {
|
|
36
|
+
const byName = new Map;
|
|
37
|
+
const ordered = [];
|
|
38
|
+
for (const tool of tools) {
|
|
39
|
+
if (byName.has(tool.name)) {
|
|
40
|
+
throw new ExecutionError(`duplicate tool name: "${tool.name}"`);
|
|
41
|
+
}
|
|
42
|
+
byName.set(tool.name, tool);
|
|
43
|
+
ordered.push(tool);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
get size() {
|
|
47
|
+
return ordered.length;
|
|
48
|
+
},
|
|
49
|
+
has(name) {
|
|
50
|
+
return byName.has(name);
|
|
51
|
+
},
|
|
52
|
+
get(name) {
|
|
53
|
+
return byName.get(name);
|
|
54
|
+
},
|
|
55
|
+
list() {
|
|
56
|
+
return ordered;
|
|
57
|
+
},
|
|
58
|
+
toProviderTools() {
|
|
59
|
+
return ordered.map(toProviderTool);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/agent/handoff.ts
|
|
65
|
+
var HANDOFF_TOOL_PREFIX = "transfer_to_";
|
|
66
|
+
function handoffToolName(agentName) {
|
|
67
|
+
return `${HANDOFF_TOOL_PREFIX}${agentName}`;
|
|
68
|
+
}
|
|
69
|
+
var HANDOFF_PARAMETERS = s.object({}).jsonSchema;
|
|
70
|
+
function resolveHandoffTargets(agent, registry) {
|
|
71
|
+
const targets = new Map;
|
|
72
|
+
for (const entry of agent.handoffs ?? []) {
|
|
73
|
+
const target = typeof entry === "string" ? resolveByName(agent, entry, registry) : entry;
|
|
74
|
+
const toolName = handoffToolName(target.name);
|
|
75
|
+
if (targets.has(toolName)) {
|
|
76
|
+
throw new ExecutionError(`agent "${agent.name}" declares duplicate handoff target "${target.name}"`);
|
|
77
|
+
}
|
|
78
|
+
targets.set(toolName, target);
|
|
79
|
+
}
|
|
80
|
+
return targets;
|
|
81
|
+
}
|
|
82
|
+
function resolveByName(agent, name, registry) {
|
|
83
|
+
if (registry === undefined) {
|
|
84
|
+
throw new ExecutionError(`agent "${agent.name}" declares a string handoff "${name}" but no registry was ` + `provided; pass { registry } to runAgent or use a direct AgentDefinition target`);
|
|
85
|
+
}
|
|
86
|
+
return registry.resolve(name);
|
|
87
|
+
}
|
|
88
|
+
function handoffProviderTools(targets) {
|
|
89
|
+
return [...targets.entries()].map(([toolName, target]) => ({
|
|
90
|
+
name: toolName,
|
|
91
|
+
description: `Transfer the conversation to the "${target.name}" agent. ` + `Use this when the request is better handled by that agent; ` + `the full message history is preserved.`,
|
|
92
|
+
parameters: HANDOFF_PARAMETERS
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/execution/usage.ts
|
|
97
|
+
function emptyUsage() {
|
|
98
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
99
|
+
}
|
|
100
|
+
function addUsage(a, b) {
|
|
101
|
+
if (b === undefined)
|
|
102
|
+
return a;
|
|
103
|
+
const reasoningTokens = a.reasoningTokens !== undefined || b.reasoningTokens !== undefined ? (a.reasoningTokens ?? 0) + (b.reasoningTokens ?? 0) : undefined;
|
|
104
|
+
const cachedInputTokens = a.cachedInputTokens !== undefined || b.cachedInputTokens !== undefined ? (a.cachedInputTokens ?? 0) + (b.cachedInputTokens ?? 0) : undefined;
|
|
105
|
+
return {
|
|
106
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
107
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
108
|
+
totalTokens: a.totalTokens + b.totalTokens,
|
|
109
|
+
...reasoningTokens !== undefined ? { reasoningTokens } : {},
|
|
110
|
+
...cachedInputTokens !== undefined ? { cachedInputTokens } : {}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/execution/run.ts
|
|
115
|
+
var DEFAULT_MAX_STEPS = 16;
|
|
116
|
+
var DEFAULT_MAX_HANDOFFS = 8;
|
|
117
|
+
function normalizeInput(input) {
|
|
118
|
+
if (input === undefined)
|
|
119
|
+
return [];
|
|
120
|
+
if (typeof input === "string")
|
|
121
|
+
return [user(input)];
|
|
122
|
+
if (Array.isArray(input))
|
|
123
|
+
return input;
|
|
124
|
+
return [input];
|
|
125
|
+
}
|
|
126
|
+
function extractText(message) {
|
|
127
|
+
return message.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
128
|
+
}
|
|
129
|
+
function toAgentError(err) {
|
|
130
|
+
if (err instanceof AgentError)
|
|
131
|
+
return err;
|
|
132
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
133
|
+
return new ExecutionError(message, err instanceof Error ? { cause: err } : undefined);
|
|
134
|
+
}
|
|
135
|
+
function buildEngineContext(opts) {
|
|
136
|
+
return {
|
|
137
|
+
...opts.telemetry !== undefined ? { telemetry: opts.telemetry } : {},
|
|
138
|
+
...opts.logger !== undefined ? { logger: opts.logger } : {},
|
|
139
|
+
...opts.signal !== undefined ? { signal: opts.signal } : {}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function buildRequest(agent, opts, providerTools, messages) {
|
|
143
|
+
const gen = { ...agent.generation, ...opts.generation };
|
|
144
|
+
return {
|
|
145
|
+
messages,
|
|
146
|
+
...gen.model !== undefined ? { model: gen.model } : {},
|
|
147
|
+
...providerTools !== undefined ? { tools: providerTools } : {},
|
|
148
|
+
...gen.toolChoice !== undefined ? { toolChoice: gen.toolChoice } : {},
|
|
149
|
+
...gen.temperature !== undefined ? { temperature: gen.temperature } : {},
|
|
150
|
+
...gen.topP !== undefined ? { topP: gen.topP } : {},
|
|
151
|
+
...gen.maxOutputTokens !== undefined ? { maxOutputTokens: gen.maxOutputTokens } : {},
|
|
152
|
+
...gen.stopSequences !== undefined ? { stopSequences: gen.stopSequences } : {}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function resolveInstructions(agent, engineCtx) {
|
|
156
|
+
const instr = agent.instructions;
|
|
157
|
+
if (instr === undefined)
|
|
158
|
+
return;
|
|
159
|
+
if (typeof instr === "string")
|
|
160
|
+
return instr;
|
|
161
|
+
const ctx = { ...engineCtx, agent };
|
|
162
|
+
return instr(ctx);
|
|
163
|
+
}
|
|
164
|
+
function throwIfAborted(signal) {
|
|
165
|
+
if (signal?.aborted)
|
|
166
|
+
throw new CancelledError("run cancelled");
|
|
167
|
+
}
|
|
168
|
+
async function* executeAgent(agent, opts, tel) {
|
|
169
|
+
const maxSteps = opts.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
170
|
+
const maxHandoffs = opts.maxHandoffs ?? DEFAULT_MAX_HANDOFFS;
|
|
171
|
+
const stream = opts.stream === true;
|
|
172
|
+
const engineCtx = buildEngineContext(opts);
|
|
173
|
+
const activate = (agentDef) => {
|
|
174
|
+
const registry = createToolRegistry(agentDef.tools ?? []);
|
|
175
|
+
const handoffs = resolveHandoffTargets(agentDef, opts.registry);
|
|
176
|
+
for (const toolName of handoffs.keys()) {
|
|
177
|
+
if (registry.has(toolName)) {
|
|
178
|
+
throw new ExecutionError(`agent "${agentDef.name}" has a tool named "${toolName}" that collides with a handoff target`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const advertised = [...registry.toProviderTools(), ...handoffProviderTools(handoffs)];
|
|
182
|
+
return {
|
|
183
|
+
agent: agentDef,
|
|
184
|
+
registry,
|
|
185
|
+
handoffs,
|
|
186
|
+
providerTools: advertised.length > 0 ? advertised : undefined
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
let active = activate(agent);
|
|
190
|
+
const runOneTool = async (call) => {
|
|
191
|
+
const events = [];
|
|
192
|
+
let usage2 = emptyUsage();
|
|
193
|
+
const bridge = {
|
|
194
|
+
emit: (event) => events.push(event),
|
|
195
|
+
reportUsage: (u) => {
|
|
196
|
+
usage2 = addUsage(usage2, u);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const tool = active.registry.get(call.name);
|
|
200
|
+
if (tool === undefined) {
|
|
201
|
+
return { result: { ok: false, error: `unknown tool: "${call.name}"` }, events, usage: usage2 };
|
|
202
|
+
}
|
|
203
|
+
const parsed = tool.parameters.safeParse(call.arguments);
|
|
204
|
+
if (!parsed.success) {
|
|
205
|
+
const detail = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
|
|
206
|
+
return {
|
|
207
|
+
result: { ok: false, error: `invalid arguments for "${call.name}": ${detail}` },
|
|
208
|
+
events,
|
|
209
|
+
usage: usage2
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const toolCtx = {
|
|
213
|
+
...engineCtx,
|
|
214
|
+
toolCallId: call.id,
|
|
215
|
+
agentName: active.agent.name,
|
|
216
|
+
run: bridge
|
|
217
|
+
};
|
|
218
|
+
const span = tel.startSpan(SPAN_TOOL, { "tool.name": call.name, "tool.call_id": call.id });
|
|
219
|
+
const startedAt = Date.now();
|
|
220
|
+
try {
|
|
221
|
+
const result = await tool.execute(parsed.data, toolCtx);
|
|
222
|
+
span.setAttributes({ "tool.ok": result.ok });
|
|
223
|
+
span.ok();
|
|
224
|
+
return { result, events, usage: usage2 };
|
|
225
|
+
} catch (err) {
|
|
226
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
227
|
+
span.fail(message);
|
|
228
|
+
return { result: { ok: false, error: message }, events, usage: usage2 };
|
|
229
|
+
} finally {
|
|
230
|
+
span.end();
|
|
231
|
+
tel.recordTool({ "tool.name": call.name, "agent.name": active.agent.name }, Date.now() - startedAt);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
let usage = emptyUsage();
|
|
235
|
+
try {
|
|
236
|
+
throwIfAborted(opts.signal);
|
|
237
|
+
yield { type: "run.start", agent: agent.name };
|
|
238
|
+
const instructions = await resolveInstructions(agent, engineCtx);
|
|
239
|
+
const contextMessages = await resolveContext(opts.context, engineCtx);
|
|
240
|
+
const prior = opts.session !== undefined ? await opts.session.messages() : opts.messages ?? [];
|
|
241
|
+
const inputMessages = normalizeInput(opts.input);
|
|
242
|
+
const messages = [];
|
|
243
|
+
if (instructions !== undefined && instructions !== "")
|
|
244
|
+
messages.push(system(instructions));
|
|
245
|
+
messages.push(...contextMessages);
|
|
246
|
+
messages.push(...prior);
|
|
247
|
+
messages.push(...inputMessages);
|
|
248
|
+
const newMessages = [...inputMessages];
|
|
249
|
+
await active.agent.hooks?.onStart?.({ agent: active.agent, messages: [...messages] }, engineCtx);
|
|
250
|
+
let finishReason = "stop";
|
|
251
|
+
let finalMessage = { role: "assistant", content: [] };
|
|
252
|
+
let steps = 0;
|
|
253
|
+
const handoffTrail = [];
|
|
254
|
+
while (steps < maxSteps) {
|
|
255
|
+
throwIfAborted(opts.signal);
|
|
256
|
+
steps++;
|
|
257
|
+
const provider = active.agent.provider;
|
|
258
|
+
const model = opts.generation?.model ?? active.agent.generation?.model ?? provider.defaultModel;
|
|
259
|
+
const requestMessages = await applyContextWindow(messages, opts.contextWindow, {
|
|
260
|
+
provider,
|
|
261
|
+
model,
|
|
262
|
+
engine: engineCtx
|
|
263
|
+
});
|
|
264
|
+
const req = buildRequest(active.agent, opts, active.providerTools, requestMessages);
|
|
265
|
+
const useStreaming = stream && provider.capabilities.streaming;
|
|
266
|
+
const providerSpan = tel.startSpan(SPAN_PROVIDER, {
|
|
267
|
+
"provider.name": provider.name,
|
|
268
|
+
"provider.model": model,
|
|
269
|
+
"provider.mode": useStreaming ? "stream" : "complete",
|
|
270
|
+
"agent.step": steps
|
|
271
|
+
});
|
|
272
|
+
let result;
|
|
273
|
+
try {
|
|
274
|
+
if (useStreaming) {
|
|
275
|
+
const acc = new StreamAccumulator;
|
|
276
|
+
let streamError;
|
|
277
|
+
for await (const event of provider.stream(req, engineCtx)) {
|
|
278
|
+
acc.push(event);
|
|
279
|
+
if (event.type === "text_delta" && event.text !== "") {
|
|
280
|
+
yield { type: "token", delta: event.text };
|
|
281
|
+
} else if (event.type === "error") {
|
|
282
|
+
streamError = event.error;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (streamError !== undefined)
|
|
286
|
+
throw streamError;
|
|
287
|
+
result = acc.result(model);
|
|
288
|
+
} else {
|
|
289
|
+
result = await provider.complete(req, engineCtx);
|
|
290
|
+
if (stream) {
|
|
291
|
+
const buffered = extractText(result.message);
|
|
292
|
+
if (buffered !== "")
|
|
293
|
+
yield { type: "token", delta: buffered };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
providerSpan.setAttributes({ "provider.finish_reason": result.finishReason });
|
|
297
|
+
providerSpan.ok();
|
|
298
|
+
} catch (err) {
|
|
299
|
+
providerSpan.fail(err instanceof Error ? err.message : String(err));
|
|
300
|
+
throw err;
|
|
301
|
+
} finally {
|
|
302
|
+
providerSpan.end();
|
|
303
|
+
}
|
|
304
|
+
usage = addUsage(usage, result.usage);
|
|
305
|
+
finishReason = result.finishReason;
|
|
306
|
+
finalMessage = result.message;
|
|
307
|
+
messages.push(result.message);
|
|
308
|
+
newMessages.push(result.message);
|
|
309
|
+
yield { type: "message", message: result.message };
|
|
310
|
+
await active.agent.hooks?.onStep?.({ step: steps, result }, engineCtx);
|
|
311
|
+
const calls = result.toolCalls;
|
|
312
|
+
if (calls.length === 0) {
|
|
313
|
+
const output = extractText(finalMessage);
|
|
314
|
+
const runResult = {
|
|
315
|
+
output,
|
|
316
|
+
finalMessage,
|
|
317
|
+
messages: [...messages],
|
|
318
|
+
finishReason,
|
|
319
|
+
steps,
|
|
320
|
+
usage,
|
|
321
|
+
agent: active.agent.name,
|
|
322
|
+
handoffs: [...handoffTrail]
|
|
323
|
+
};
|
|
324
|
+
await opts.session?.append(newMessages);
|
|
325
|
+
yield { type: "run.finish", result: runResult };
|
|
326
|
+
await active.agent.hooks?.onFinish?.({ output, usage }, engineCtx);
|
|
327
|
+
return runResult;
|
|
328
|
+
}
|
|
329
|
+
const handoffTargets = active.handoffs;
|
|
330
|
+
const regularCalls = calls.filter((call) => !handoffTargets.has(call.name));
|
|
331
|
+
const handoffCalls = calls.filter((call) => handoffTargets.has(call.name));
|
|
332
|
+
for (const call of calls) {
|
|
333
|
+
yield { type: "tool.call", id: call.id, name: call.name, arguments: call.arguments };
|
|
334
|
+
const tool = active.registry.get(call.name);
|
|
335
|
+
if (tool !== undefined)
|
|
336
|
+
await active.agent.hooks?.onToolCall?.({ call, tool }, engineCtx);
|
|
337
|
+
}
|
|
338
|
+
const settled = await Promise.all(regularCalls.map(async (call) => ({ call, outcome: await runOneTool(call) })));
|
|
339
|
+
for (const { outcome } of settled)
|
|
340
|
+
usage = addUsage(usage, outcome.usage);
|
|
341
|
+
throwIfAborted(opts.signal);
|
|
342
|
+
for (const { call, outcome } of settled) {
|
|
343
|
+
for (const childEvent of outcome.events)
|
|
344
|
+
yield childEvent;
|
|
345
|
+
const toolResult = outcome.result;
|
|
346
|
+
yield { type: "tool.result", id: call.id, name: call.name, result: toolResult };
|
|
347
|
+
await active.agent.hooks?.onToolResult?.({ call, result: toolResult }, engineCtx);
|
|
348
|
+
}
|
|
349
|
+
for (const { call, outcome } of settled) {
|
|
350
|
+
const message = toToolResultMessage(call.id, outcome.result);
|
|
351
|
+
messages.push(message);
|
|
352
|
+
newMessages.push(message);
|
|
353
|
+
yield { type: "message", message };
|
|
354
|
+
}
|
|
355
|
+
for (const call of handoffCalls) {
|
|
356
|
+
const target = handoffTargets.get(call.name);
|
|
357
|
+
if (target === undefined)
|
|
358
|
+
continue;
|
|
359
|
+
if (handoffTrail.length >= maxHandoffs) {
|
|
360
|
+
throw new MaxHandoffsExceededError(`exceeded max handoffs (${maxHandoffs})`, {
|
|
361
|
+
handoffs: maxHandoffs
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const ack = { ok: true, content: `Transferred to "${target.name}".` };
|
|
365
|
+
const ackMessage = toToolResultMessage(call.id, ack);
|
|
366
|
+
messages.push(ackMessage);
|
|
367
|
+
newMessages.push(ackMessage);
|
|
368
|
+
yield { type: "tool.result", id: call.id, name: call.name, result: ack };
|
|
369
|
+
yield { type: "message", message: ackMessage };
|
|
370
|
+
const from = active.agent;
|
|
371
|
+
yield { type: "agent.handoff", from: from.name, to: target.name };
|
|
372
|
+
await from.hooks?.onHandoff?.({ from, to: target }, engineCtx);
|
|
373
|
+
active = activate(target);
|
|
374
|
+
handoffTrail.push(target.name);
|
|
375
|
+
const nextInstructions = await resolveInstructions(active.agent, engineCtx);
|
|
376
|
+
if (nextInstructions !== undefined && nextInstructions !== "") {
|
|
377
|
+
messages.push(system(nextInstructions));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
throw new MaxStepsExceededError(`exceeded max steps (${maxSteps})`, { steps: maxSteps });
|
|
382
|
+
} catch (err) {
|
|
383
|
+
const agentError = toAgentError(err);
|
|
384
|
+
if (agentError.usage === undefined)
|
|
385
|
+
agentError.usage = usage;
|
|
386
|
+
yield { type: "error", error: agentError };
|
|
387
|
+
try {
|
|
388
|
+
await active.agent.hooks?.onError?.({ error: agentError }, engineCtx);
|
|
389
|
+
} catch {}
|
|
390
|
+
throw agentError;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function usageOfError(err) {
|
|
394
|
+
return err instanceof AgentError && err.usage !== undefined ? err.usage : emptyUsage();
|
|
395
|
+
}
|
|
396
|
+
function runResultAttrs(result) {
|
|
397
|
+
return {
|
|
398
|
+
"agent.steps": result.steps,
|
|
399
|
+
"agent.finish_reason": result.finishReason,
|
|
400
|
+
"agent.usage.total_tokens": result.usage.totalTokens
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
async function driveToCompletion(gen, hub, tel, agentName) {
|
|
404
|
+
const startedAt = Date.now();
|
|
405
|
+
try {
|
|
406
|
+
const result = await tel.withSpan(SPAN_RUN, { "agent.name": agentName }, async (span) => {
|
|
407
|
+
let next = await gen.next();
|
|
408
|
+
while (!next.done) {
|
|
409
|
+
await hub.emit(next.value);
|
|
410
|
+
next = await gen.next();
|
|
411
|
+
}
|
|
412
|
+
span.setAttributes(runResultAttrs(next.value));
|
|
413
|
+
span.ok();
|
|
414
|
+
return next.value;
|
|
415
|
+
});
|
|
416
|
+
tel.recordRun({ "agent.name": agentName, "agent.outcome": "ok" }, Date.now() - startedAt, result.usage);
|
|
417
|
+
return result;
|
|
418
|
+
} catch (err) {
|
|
419
|
+
tel.recordRun({ "agent.name": agentName, "agent.outcome": "error" }, Date.now() - startedAt, usageOfError(err));
|
|
420
|
+
throw err;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function makeHandle(gen, hub, tel, agentName) {
|
|
424
|
+
let resolveCompleted;
|
|
425
|
+
let rejectCompleted;
|
|
426
|
+
const completed = new Promise((resolve, reject) => {
|
|
427
|
+
resolveCompleted = resolve;
|
|
428
|
+
rejectCompleted = reject;
|
|
429
|
+
});
|
|
430
|
+
completed.catch(() => {});
|
|
431
|
+
const span = tel.startSpan(SPAN_RUN, { "agent.name": agentName });
|
|
432
|
+
const startedAt = Date.now();
|
|
433
|
+
async function* iterate() {
|
|
434
|
+
let settled = false;
|
|
435
|
+
try {
|
|
436
|
+
let next = await gen.next();
|
|
437
|
+
while (!next.done) {
|
|
438
|
+
await hub.emit(next.value);
|
|
439
|
+
yield next.value;
|
|
440
|
+
next = await gen.next();
|
|
441
|
+
}
|
|
442
|
+
span.setAttributes(runResultAttrs(next.value));
|
|
443
|
+
span.ok();
|
|
444
|
+
settled = true;
|
|
445
|
+
tel.recordRun({ "agent.name": agentName, "agent.outcome": "ok" }, Date.now() - startedAt, next.value.usage);
|
|
446
|
+
resolveCompleted(next.value);
|
|
447
|
+
} catch (err) {
|
|
448
|
+
span.fail(err instanceof Error ? err.message : String(err));
|
|
449
|
+
settled = true;
|
|
450
|
+
tel.recordRun({ "agent.name": agentName, "agent.outcome": "error" }, Date.now() - startedAt, usageOfError(err));
|
|
451
|
+
rejectCompleted(err);
|
|
452
|
+
throw err;
|
|
453
|
+
} finally {
|
|
454
|
+
if (!settled) {
|
|
455
|
+
const cancelled = new CancelledError("run stream abandoned before completion");
|
|
456
|
+
await gen.return(undefined).catch(() => {});
|
|
457
|
+
span.fail(cancelled.message);
|
|
458
|
+
tel.recordRun({ "agent.name": agentName, "agent.outcome": "incomplete" }, Date.now() - startedAt, emptyUsage());
|
|
459
|
+
rejectCompleted(cancelled);
|
|
460
|
+
}
|
|
461
|
+
span.end();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const iterator = iterate();
|
|
465
|
+
return {
|
|
466
|
+
[Symbol.asyncIterator]: () => iterator,
|
|
467
|
+
completed
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function buildEventHub(opts) {
|
|
471
|
+
const logger = opts.logger ?? opts.telemetry?.log;
|
|
472
|
+
return createEventHub({
|
|
473
|
+
subscribers: [opts.onEvent, ...opts.subscribers ?? []],
|
|
474
|
+
onSubscriberError: (error, event, index) => {
|
|
475
|
+
logger?.warn("agent.run subscriber threw", {
|
|
476
|
+
event: event.type,
|
|
477
|
+
index,
|
|
478
|
+
error: error instanceof Error ? error.message : String(error)
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function runAgent(agent, opts = {}) {
|
|
484
|
+
const tel = createRunTelemetry(opts.telemetry);
|
|
485
|
+
const hub = buildEventHub(opts);
|
|
486
|
+
const gen = executeAgent(agent, opts, tel);
|
|
487
|
+
if (opts.stream === true)
|
|
488
|
+
return makeHandle(gen, hub, tel, agent.name);
|
|
489
|
+
return driveToCompletion(gen, hub, tel, agent.name);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export { createToolRegistry, handoffToolName, resolveHandoffTargets, handoffProviderTools, emptyUsage, addUsage, DEFAULT_MAX_STEPS, DEFAULT_MAX_HANDOFFS, runAgent };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
text,
|
|
3
|
+
toolResult
|
|
4
|
+
} from "./index-1p6mb2vz.js";
|
|
5
|
+
|
|
6
|
+
// src/tools/result.ts
|
|
7
|
+
function renderToolContent(result) {
|
|
8
|
+
const value = result.ok ? result.content : result.error;
|
|
9
|
+
if (typeof value === "string")
|
|
10
|
+
return [text(value)];
|
|
11
|
+
if (value === undefined || value === null)
|
|
12
|
+
return [text("")];
|
|
13
|
+
return [text(JSON.stringify(value))];
|
|
14
|
+
}
|
|
15
|
+
function toToolResultMessage(toolCallId, result) {
|
|
16
|
+
const content = renderToolContent(result);
|
|
17
|
+
return result.ok ? toolResult(toolCallId, content) : toolResult(toolCallId, content, { isError: true });
|
|
18
|
+
}
|
|
19
|
+
function toProviderTool(tool) {
|
|
20
|
+
return {
|
|
21
|
+
name: tool.name,
|
|
22
|
+
...tool.description !== undefined ? { description: tool.description } : {},
|
|
23
|
+
parameters: tool.parameters.jsonSchema
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { renderToolContent, toToolResultMessage, toProviderTool };
|