@intx/agent 0.1.2 → 0.3.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.
Files changed (60) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +80 -5
  3. package/dist/agent.d.ts +116 -0
  4. package/dist/agent.js +682 -0
  5. package/dist/canonicalize.d.ts +15 -0
  6. package/dist/canonicalize.js +160 -0
  7. package/dist/default-director.d.ts +24 -0
  8. package/dist/default-director.js +45 -0
  9. package/dist/definition.d.ts +139 -0
  10. package/dist/definition.js +40 -0
  11. package/dist/director-registry.d.ts +47 -0
  12. package/dist/director-registry.js +87 -0
  13. package/dist/director-types.d.ts +80 -0
  14. package/dist/director-types.js +13 -0
  15. package/dist/director.d.ts +70 -0
  16. package/dist/director.js +131 -0
  17. package/dist/env-validation.d.ts +59 -0
  18. package/dist/env-validation.js +180 -0
  19. package/dist/env.d.ts +160 -0
  20. package/dist/env.js +53 -0
  21. package/dist/index.d.ts +16 -0
  22. package/dist/index.js +23 -0
  23. package/dist/internal-fixtures/mail.d.ts +39 -0
  24. package/dist/internal-fixtures/mail.js +86 -0
  25. package/dist/internal-fixtures/planner.d.ts +19 -0
  26. package/dist/internal-fixtures/planner.js +49 -0
  27. package/dist/lock.d.ts +16 -0
  28. package/dist/lock.js +47 -0
  29. package/dist/namespace.d.ts +12 -0
  30. package/dist/namespace.js +39 -0
  31. package/dist/send-queue.d.ts +25 -0
  32. package/dist/send-queue.js +147 -0
  33. package/dist/source.d.ts +43 -0
  34. package/dist/source.js +118 -0
  35. package/dist/stream.d.ts +16 -0
  36. package/dist/stream.js +115 -0
  37. package/dist/testing/audit-noop.d.ts +7 -0
  38. package/dist/testing/audit-noop.js +25 -0
  39. package/dist/testing/authorize-allow.d.ts +8 -0
  40. package/dist/testing/authorize-allow.js +19 -0
  41. package/dist/testing/index.d.ts +2 -0
  42. package/dist/testing/index.js +17 -0
  43. package/dist/tool.d.ts +238 -0
  44. package/dist/tool.js +244 -0
  45. package/package.json +26 -7
  46. package/src/agent.test.ts +0 -46
  47. package/src/agent.ts +0 -494
  48. package/src/index.ts +0 -38
  49. package/src/lock.test.ts +0 -93
  50. package/src/lock.ts +0 -57
  51. package/src/send-queue.test.ts +0 -207
  52. package/src/send-queue.ts +0 -200
  53. package/src/source.test.ts +0 -171
  54. package/src/source.ts +0 -93
  55. package/src/stream.test.ts +0 -167
  56. package/src/stream.ts +0 -142
  57. package/src/tool.test.ts +0 -217
  58. package/src/tool.ts +0 -148
  59. package/tsconfig.json +0 -4
  60. package/tsconfig.tsbuildinfo +0 -1
@@ -1,167 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
-
3
- import type { ReactorEmittedEvent } from "@intx/inference";
4
-
5
- import { createStreamConsumer, StreamBackpressureError } from "./stream";
6
-
7
- /**
8
- * Build a minimal ReactorEmittedEvent suitable for fan-out testing. The
9
- * event's structural details do not matter — the stream consumer treats
10
- * events opaquely — so we use `reactor.done`, which has an empty `data`.
11
- */
12
- function makeEvent(seq: number): ReactorEmittedEvent {
13
- return { type: "reactor.done", seq, data: {} };
14
- }
15
-
16
- async function collect(
17
- it: AsyncIterableIterator<ReactorEmittedEvent>,
18
- n: number,
19
- ): Promise<ReactorEmittedEvent[]> {
20
- const out: ReactorEmittedEvent[] = [];
21
- for (let i = 0; i < n; i++) {
22
- const r = await it.next();
23
- if (r.done === true) break;
24
- out.push(r.value);
25
- }
26
- return out;
27
- }
28
-
29
- describe("createStreamConsumer", () => {
30
- test("delivers buffered events to a later iterator read", async () => {
31
- const c = createStreamConsumer(8);
32
- const it = c.iterator();
33
- c.push(makeEvent(1));
34
- c.push(makeEvent(2));
35
- const got = await collect(it, 2);
36
- expect(got.map((e) => e.seq)).toEqual([1, 2]);
37
- });
38
-
39
- test("delivers events directly to a waiting iterator", async () => {
40
- const c = createStreamConsumer(8);
41
- const it = c.iterator();
42
- const pending = it.next();
43
- c.push(makeEvent(42));
44
- const r = await pending;
45
- expect(r.done).toBe(false);
46
- if (r.done !== true) expect(r.value.seq).toBe(42);
47
- });
48
-
49
- test("close terminates pending and subsequent reads with done", async () => {
50
- const c = createStreamConsumer(8);
51
- const it = c.iterator();
52
- const pending = it.next();
53
- c.close();
54
- const r1 = await pending;
55
- expect(r1.done).toBe(true);
56
- const r2 = await it.next();
57
- expect(r2.done).toBe(true);
58
- });
59
-
60
- test("close after buffered events still drains them before done", async () => {
61
- const c = createStreamConsumer(8);
62
- const it = c.iterator();
63
- c.push(makeEvent(1));
64
- c.push(makeEvent(2));
65
- c.close();
66
- const r1 = await it.next();
67
- expect(r1.done).toBe(false);
68
- const r2 = await it.next();
69
- expect(r2.done).toBe(false);
70
- const r3 = await it.next();
71
- expect(r3.done).toBe(true);
72
- });
73
-
74
- test("overflow throws StreamBackpressureError on next read", async () => {
75
- const c = createStreamConsumer(3);
76
- const it = c.iterator();
77
- c.push(makeEvent(1));
78
- c.push(makeEvent(2));
79
- c.push(makeEvent(3));
80
- c.push(makeEvent(4));
81
-
82
- // Buffered events drain first.
83
- const r1 = await it.next();
84
- expect(r1.done).toBe(false);
85
- const r2 = await it.next();
86
- expect(r2.done).toBe(false);
87
- const r3 = await it.next();
88
- expect(r3.done).toBe(false);
89
-
90
- // Next read sees the overflow.
91
- await expect(it.next()).rejects.toBeInstanceOf(StreamBackpressureError);
92
- });
93
-
94
- test("overflow rejects a pending waiter", async () => {
95
- const c = createStreamConsumer(2);
96
- const it = c.iterator();
97
- const pending = it.next();
98
- // Direct delivery to the waiter does NOT increase the buffer.
99
- c.push(makeEvent(1));
100
- const r1 = await pending;
101
- expect(r1.done).toBe(false);
102
-
103
- // Now buffer 2 events (capacity), then a 3rd while another waiter is
104
- // pending — wait, an immediate waiter would consume the 3rd directly.
105
- // Instead saturate the buffer first.
106
- c.push(makeEvent(2));
107
- c.push(makeEvent(3));
108
- // Saturated. A pending waiter at this point will be served from the
109
- // buffer; the overflow only fires on a push that has no waiter and a
110
- // full buffer.
111
- c.push(makeEvent(4));
112
-
113
- // Drain.
114
- const r2 = await it.next();
115
- expect(r2.done).toBe(false);
116
- const r3 = await it.next();
117
- expect(r3.done).toBe(false);
118
-
119
- await expect(it.next()).rejects.toBeInstanceOf(StreamBackpressureError);
120
- });
121
-
122
- test("multiple consumers buffer independently", async () => {
123
- const a = createStreamConsumer(8);
124
- const b = createStreamConsumer(8);
125
- const ai = a.iterator();
126
- const bi = b.iterator();
127
-
128
- a.push(makeEvent(1));
129
- b.push(makeEvent(1));
130
- a.push(makeEvent(2));
131
- b.push(makeEvent(2));
132
-
133
- const aGot = await collect(ai, 2);
134
- const bGot = await collect(bi, 2);
135
- expect(aGot.map((e) => e.seq)).toEqual([1, 2]);
136
- expect(bGot.map((e) => e.seq)).toEqual([1, 2]);
137
- });
138
-
139
- test("iterator.return() closes the consumer", async () => {
140
- const c = createStreamConsumer(8);
141
- const it = c.iterator();
142
- expect(c.closed).toBe(false);
143
- await it.return?.();
144
- expect(c.closed).toBe(true);
145
- const r = await it.next();
146
- expect(r.done).toBe(true);
147
- });
148
-
149
- test("push after close is ignored", async () => {
150
- const c = createStreamConsumer(8);
151
- const it = c.iterator();
152
- c.close();
153
- c.push(makeEvent(1));
154
- const r = await it.next();
155
- expect(r.done).toBe(true);
156
- });
157
-
158
- test("Symbol.asyncIterator returns the iterator itself", async () => {
159
- const c = createStreamConsumer(8);
160
- const it = c.iterator();
161
- expect(it[Symbol.asyncIterator]()).toBe(it);
162
- });
163
-
164
- test("rejects maxBuffer < 1", () => {
165
- expect(() => createStreamConsumer(0)).toThrow();
166
- });
167
- });
package/src/stream.ts DELETED
@@ -1,142 +0,0 @@
1
- // Bounded per-consumer fan-out for the agent's reactor event stream.
2
- //
3
- // Each call to `agent.stream()` creates a fresh `StreamConsumer`. The
4
- // agent feeds every reactor event to every consumer; consumers buffer
5
- // independently. If a consumer falls more than `maxBuffer` events behind
6
- // it is poisoned with `StreamBackpressureError` and its iterator throws
7
- // on the next read — the consumer is removed but other consumers keep
8
- // running.
9
- //
10
- // Loud failure matches the defensive-coding rule: silently dropping
11
- // events would hide consumer bugs, and unbounded buffering would let a
12
- // stalled consumer balloon the agent's memory. The cap is configurable
13
- // via `streamBufferMax` on `AgentConfig`.
14
-
15
- import type { ReactorEmittedEvent } from "@intx/inference";
16
-
17
- export class StreamBackpressureError extends Error {
18
- readonly maxBuffer: number;
19
-
20
- constructor(maxBuffer: number) {
21
- super(`stream consumer fell more than ${String(maxBuffer)} events behind`);
22
- this.name = "StreamBackpressureError";
23
- this.maxBuffer = maxBuffer;
24
- }
25
- }
26
-
27
- type Waiter = {
28
- resolve: (value: IteratorResult<ReactorEmittedEvent>) => void;
29
- reject: (reason: unknown) => void;
30
- };
31
-
32
- export type StreamConsumer = {
33
- /** Deliver an event to this consumer's buffer. */
34
- push(event: ReactorEmittedEvent): void;
35
- /** Cleanly terminate the iterator with `done: true`. */
36
- close(): void;
37
- /** True once close() or an overflow has poisoned the consumer. */
38
- readonly closed: boolean;
39
- /** Iterator handed back to the caller of `stream()`. */
40
- iterator(): AsyncIterableIterator<ReactorEmittedEvent>;
41
- };
42
-
43
- export function createStreamConsumer(maxBuffer: number): StreamConsumer {
44
- if (maxBuffer < 1) {
45
- throw new Error(`streamBufferMax must be >= 1, got ${String(maxBuffer)}`);
46
- }
47
-
48
- const buffer: ReactorEmittedEvent[] = [];
49
- const waiters: Waiter[] = [];
50
- let overflow: StreamBackpressureError | undefined;
51
- let done = false;
52
-
53
- function settleOverflowedWaiters(err: StreamBackpressureError): void {
54
- while (waiters.length > 0) {
55
- const w = waiters.shift();
56
- if (w === undefined) return;
57
- w.reject(err);
58
- }
59
- }
60
-
61
- function settleDoneWaiters(): void {
62
- while (waiters.length > 0) {
63
- const w = waiters.shift();
64
- if (w === undefined) return;
65
- w.resolve({ value: undefined, done: true });
66
- }
67
- }
68
-
69
- function push(event: ReactorEmittedEvent): void {
70
- if (done || overflow !== undefined) return;
71
-
72
- if (waiters.length > 0) {
73
- const w = waiters.shift();
74
- if (w === undefined) return;
75
- w.resolve({ value: event, done: false });
76
- return;
77
- }
78
-
79
- if (buffer.length >= maxBuffer) {
80
- overflow = new StreamBackpressureError(maxBuffer);
81
- settleOverflowedWaiters(overflow);
82
- return;
83
- }
84
-
85
- buffer.push(event);
86
- }
87
-
88
- function close(): void {
89
- if (done) return;
90
- done = true;
91
- settleDoneWaiters();
92
- }
93
-
94
- function nextResult(): Promise<IteratorResult<ReactorEmittedEvent>> {
95
- if (overflow !== undefined) {
96
- // Drain any buffered events before throwing so the caller sees
97
- // every event up to the overflow point.
98
- if (buffer.length > 0) {
99
- const ev = buffer.shift();
100
- if (ev !== undefined) {
101
- return Promise.resolve({ value: ev, done: false });
102
- }
103
- }
104
- return Promise.reject(overflow);
105
- }
106
- if (buffer.length > 0) {
107
- const ev = buffer.shift();
108
- if (ev !== undefined) {
109
- return Promise.resolve({ value: ev, done: false });
110
- }
111
- }
112
- if (done) {
113
- return Promise.resolve({ value: undefined, done: true });
114
- }
115
- return new Promise((resolve, reject) => {
116
- waiters.push({ resolve, reject });
117
- });
118
- }
119
-
120
- function iterator(): AsyncIterableIterator<ReactorEmittedEvent> {
121
- const it: AsyncIterableIterator<ReactorEmittedEvent> = {
122
- next: nextResult,
123
- async return() {
124
- close();
125
- return { value: undefined, done: true };
126
- },
127
- [Symbol.asyncIterator]() {
128
- return it;
129
- },
130
- };
131
- return it;
132
- }
133
-
134
- return {
135
- push,
136
- close,
137
- get closed() {
138
- return done || overflow !== undefined;
139
- },
140
- iterator,
141
- };
142
- }
package/src/tool.test.ts DELETED
@@ -1,217 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
-
3
- import type { ToolDefinition } from "@intx/types/runtime";
4
-
5
- import {
6
- createToolRunner,
7
- DuplicateToolError,
8
- fromToolRunner,
9
- stringTool,
10
- tool,
11
- } from "./tool";
12
-
13
- const DEF_A: ToolDefinition = {
14
- name: "a",
15
- description: "tool a",
16
- inputSchema: { type: "object" },
17
- };
18
-
19
- const DEF_B: ToolDefinition = {
20
- name: "b",
21
- description: "tool b",
22
- inputSchema: { type: "object" },
23
- };
24
-
25
- describe("createToolRunner", () => {
26
- test("dispatches to a full handler by tool name", async () => {
27
- const runner = createToolRunner([
28
- tool({
29
- definition: DEF_A,
30
- handler: async (call) => ({
31
- callId: call.id,
32
- content: `got ${String(call.arguments.x)}`,
33
- }),
34
- }),
35
- ]);
36
-
37
- const result = await runner.run(
38
- { id: "c1", name: "a", arguments: { x: 5 } },
39
- new AbortController().signal,
40
- );
41
-
42
- expect(result).toEqual({ callId: "c1", content: "got 5" });
43
- });
44
-
45
- test("lifts the string handler return into a ToolResult", async () => {
46
- const runner = createToolRunner([
47
- stringTool({
48
- definition: DEF_A,
49
- handler: async (args) => `hello ${String(args.name)}`,
50
- }),
51
- ]);
52
-
53
- const result = await runner.run(
54
- { id: "c2", name: "a", arguments: { name: "world" } },
55
- new AbortController().signal,
56
- );
57
-
58
- expect(result).toEqual({ callId: "c2", content: "hello world" });
59
- });
60
-
61
- test("returns an error ToolResult for an unknown tool name", async () => {
62
- const runner = createToolRunner([
63
- tool({
64
- definition: DEF_A,
65
- handler: async (call) => ({ callId: call.id, content: "" }),
66
- }),
67
- ]);
68
-
69
- const result = await runner.run(
70
- { id: "c3", name: "nope", arguments: {} },
71
- new AbortController().signal,
72
- );
73
-
74
- expect(result.isError).toBe(true);
75
- expect(result.callId).toBe("c3");
76
- expect(result.content).toBe("unknown tool: nope");
77
- });
78
-
79
- test("wraps a thrown error from a full handler into an error ToolResult", async () => {
80
- const runner = createToolRunner([
81
- tool({
82
- definition: DEF_A,
83
- handler: async () => {
84
- throw new Error("boom");
85
- },
86
- }),
87
- ]);
88
-
89
- const result = await runner.run(
90
- { id: "c4", name: "a", arguments: {} },
91
- new AbortController().signal,
92
- );
93
-
94
- expect(result).toEqual({ callId: "c4", content: "boom", isError: true });
95
- });
96
-
97
- test("wraps a thrown error from a string handler into an error ToolResult", async () => {
98
- const runner = createToolRunner([
99
- stringTool({
100
- definition: DEF_A,
101
- handler: async () => {
102
- throw new Error("bad input");
103
- },
104
- }),
105
- ]);
106
-
107
- const result = await runner.run(
108
- { id: "c5", name: "a", arguments: {} },
109
- new AbortController().signal,
110
- );
111
-
112
- expect(result).toEqual({
113
- callId: "c5",
114
- content: "bad input",
115
- isError: true,
116
- });
117
- });
118
-
119
- test("exposes definitions in registration order", () => {
120
- const runner = createToolRunner([
121
- tool({
122
- definition: DEF_A,
123
- handler: async (call) => ({ callId: call.id, content: "" }),
124
- }),
125
- stringTool({ definition: DEF_B, handler: async () => "x" }),
126
- ]);
127
-
128
- expect(runner.definitions.map((d) => d.name)).toEqual(["a", "b"]);
129
- });
130
-
131
- test("throws DuplicateToolError at construction on duplicate names", () => {
132
- expect(() =>
133
- createToolRunner([
134
- tool({
135
- definition: DEF_A,
136
- handler: async (call) => ({ callId: call.id, content: "" }),
137
- }),
138
- stringTool({ definition: DEF_A, handler: async () => "x" }),
139
- ]),
140
- ).toThrow(DuplicateToolError);
141
- });
142
-
143
- test("propagates the AbortSignal to the handler", async () => {
144
- let received: AbortSignal | undefined;
145
- const runner = createToolRunner([
146
- tool({
147
- definition: DEF_A,
148
- handler: async (call, signal) => {
149
- received = signal;
150
- return { callId: call.id, content: "ok" };
151
- },
152
- }),
153
- ]);
154
-
155
- const ctl = new AbortController();
156
- await runner.run({ id: "c6", name: "a", arguments: {} }, ctl.signal);
157
-
158
- expect(received).toBe(ctl.signal);
159
- });
160
-
161
- test("fromToolRunner wraps each definition as a full-handler AgentTool", async () => {
162
- const calls: string[] = [];
163
- const stubRunner = {
164
- definitions: [DEF_A, DEF_B] as const,
165
- run: async (call: {
166
- id: string;
167
- name: string;
168
- arguments: Record<string, unknown>;
169
- }) => {
170
- calls.push(call.name);
171
- return Promise.resolve({
172
- callId: call.id,
173
- content: `ran ${call.name}`,
174
- });
175
- },
176
- };
177
-
178
- const tools = fromToolRunner(stubRunner);
179
- const runner = createToolRunner(tools);
180
-
181
- expect(runner.definitions.map((d) => d.name)).toEqual(["a", "b"]);
182
-
183
- const ra = await runner.run(
184
- { id: "c8", name: "a", arguments: {} },
185
- new AbortController().signal,
186
- );
187
- expect(ra).toEqual({ callId: "c8", content: "ran a" });
188
-
189
- const rb = await runner.run(
190
- { id: "c9", name: "b", arguments: {} },
191
- new AbortController().signal,
192
- );
193
- expect(rb).toEqual({ callId: "c9", content: "ran b" });
194
-
195
- expect(calls).toEqual(["a", "b"]);
196
- });
197
-
198
- test("string handler receives the parsed arguments object", async () => {
199
- let received: Record<string, unknown> | undefined;
200
- const runner = createToolRunner([
201
- stringTool({
202
- definition: DEF_A,
203
- handler: async (args) => {
204
- received = args;
205
- return "ok";
206
- },
207
- }),
208
- ]);
209
-
210
- await runner.run(
211
- { id: "c7", name: "a", arguments: { foo: 1, bar: "x" } },
212
- new AbortController().signal,
213
- );
214
-
215
- expect(received).toEqual({ foo: 1, bar: "x" });
216
- });
217
- });
package/src/tool.ts DELETED
@@ -1,148 +0,0 @@
1
- // Tool registration and dispatch.
2
- //
3
- // Two registration shapes are supported:
4
- //
5
- // `tool({ definition, handler })` — handler receives the full
6
- // ToolCall and returns the full
7
- // ToolResult. Use when the
8
- // handler needs the callId or
9
- // wants to set isError/detail/
10
- // pendingMarker.
11
- //
12
- // `stringTool({ definition, handler })` — sugar for the common case of
13
- // "compute a string from the
14
- // parsed arguments." The callId
15
- // is filled in from the
16
- // surrounding ToolCall, and
17
- // isError is false unless the
18
- // handler throws.
19
- //
20
- // `createToolRunner(tools)` builds a `ToolRunner` that dispatches by tool
21
- // name. Per the ToolRunner contract (packages/types/src/runtime.ts), `run`
22
- // must not throw — unknown tool names and handler exceptions are surfaced
23
- // as `ToolResult` with `isError: true` so the model sees them and can
24
- // recover.
25
-
26
- import type {
27
- ToolCall,
28
- ToolDefinition,
29
- ToolResult,
30
- ToolRunner,
31
- } from "@intx/types/runtime";
32
-
33
- export type ToolHandler = (
34
- call: ToolCall,
35
- signal: AbortSignal,
36
- ) => Promise<ToolResult>;
37
-
38
- export type StringToolHandler = (
39
- args: Record<string, unknown>,
40
- signal: AbortSignal,
41
- ) => Promise<string>;
42
-
43
- export type AgentTool =
44
- | { kind: "full"; definition: ToolDefinition; handler: ToolHandler }
45
- | {
46
- kind: "string";
47
- definition: ToolDefinition;
48
- handler: StringToolHandler;
49
- };
50
-
51
- export function tool(args: {
52
- definition: ToolDefinition;
53
- handler: ToolHandler;
54
- }): AgentTool {
55
- return { kind: "full", definition: args.definition, handler: args.handler };
56
- }
57
-
58
- export function stringTool(args: {
59
- definition: ToolDefinition;
60
- handler: StringToolHandler;
61
- }): AgentTool {
62
- return {
63
- kind: "string",
64
- definition: args.definition,
65
- handler: args.handler,
66
- };
67
- }
68
-
69
- /**
70
- * Adapt a pre-built ToolRunner (e.g. the one returned by
71
- * `createPosixTools`) into a list of AgentTools that can be passed to
72
- * `createAgent({ tools })`. Each definition becomes a full-handler
73
- * AgentTool that delegates to the runner's `run`.
74
- *
75
- * Use this when integrating tool packages whose public surface is a
76
- * single ToolRunner rather than individual handlers.
77
- */
78
- export function fromToolRunner(runner: {
79
- readonly definitions: readonly ToolDefinition[];
80
- run: ToolRunner["run"];
81
- }): AgentTool[] {
82
- return runner.definitions.map((definition) => ({
83
- kind: "full",
84
- definition,
85
- handler: (call, signal) => runner.run(call, signal),
86
- }));
87
- }
88
-
89
- export class DuplicateToolError extends Error {
90
- readonly toolName: string;
91
-
92
- constructor(toolName: string) {
93
- super(`duplicate tool name: ${toolName}`);
94
- this.name = "DuplicateToolError";
95
- this.toolName = toolName;
96
- }
97
- }
98
-
99
- export type AgentToolRunner = ToolRunner & {
100
- readonly definitions: readonly ToolDefinition[];
101
- };
102
-
103
- /**
104
- * Build a `ToolRunner` that dispatches by tool name. Throws
105
- * `DuplicateToolError` at construction if any two tools share a name.
106
- *
107
- * At call time, unknown tool names and exceptions from handlers are
108
- * converted to `ToolResult { isError: true }` so the contract on
109
- * `ToolRunner.run` ("must not throw") is upheld.
110
- */
111
- export function createToolRunner(tools: AgentTool[]): AgentToolRunner {
112
- const byName = new Map<string, AgentTool>();
113
- for (const t of tools) {
114
- if (byName.has(t.definition.name)) {
115
- throw new DuplicateToolError(t.definition.name);
116
- }
117
- byName.set(t.definition.name, t);
118
- }
119
-
120
- const definitions: readonly ToolDefinition[] = tools.map((t) => t.definition);
121
-
122
- return {
123
- definitions,
124
- async run(call, signal): Promise<ToolResult> {
125
- const found = byName.get(call.name);
126
- if (found === undefined) {
127
- return {
128
- callId: call.id,
129
- content: `unknown tool: ${call.name}`,
130
- isError: true,
131
- };
132
- }
133
- try {
134
- if (found.kind === "full") {
135
- return await found.handler(call, signal);
136
- }
137
- const text = await found.handler(call.arguments, signal);
138
- return { callId: call.id, content: text };
139
- } catch (err) {
140
- return {
141
- callId: call.id,
142
- content: err instanceof Error ? err.message : String(err),
143
- isError: true,
144
- };
145
- }
146
- },
147
- };
148
- }
package/tsconfig.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "include": ["src/**/*.ts"]
4
- }