@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 InfinityI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
# engine-lib
|
|
2
|
+
|
|
3
|
+
**Agent infrastructure for TypeScript, built on [`@infinityi/forge`](https://github.com/tqcuong2k/forge).**
|
|
4
|
+
|
|
5
|
+
[](https://bun.sh)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Project Goal
|
|
12
|
+
|
|
13
|
+
Every project that wants to "add an agent" ends up rebuilding the same scaffolding:
|
|
14
|
+
a provider client, a message/content model, a tool-calling loop, session state,
|
|
15
|
+
context injection, and a way to observe what the agent is doing. It is tedious,
|
|
16
|
+
error-prone, and almost never the interesting part of the product.
|
|
17
|
+
|
|
18
|
+
**engine-lib** is the reusable layer that owns that scaffolding once. It provides a
|
|
19
|
+
provider-agnostic way to define agents and tools, run them against any major LLM
|
|
20
|
+
provider, manage their conversation state, and observe their behavior — so that a
|
|
21
|
+
host application (a coding terminal, a server-management tool, an incident
|
|
22
|
+
responder) can import it and start shipping agent features without re-implementing
|
|
23
|
+
the plumbing.
|
|
24
|
+
|
|
25
|
+
It is built on **Forge**, inheriting its config, telemetry, resilience, and
|
|
26
|
+
lifecycle primitives rather than reinventing them. Where Forge is "the boring
|
|
27
|
+
infrastructure layer your business logic deserves," engine-lib is the boring
|
|
28
|
+
*agent* infrastructure layer your product deserves.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Target Users
|
|
33
|
+
|
|
34
|
+
engine-lib is for **developers building agent-integrated products** — including
|
|
35
|
+
the author's own future projects. You are the audience if you are:
|
|
36
|
+
|
|
37
|
+
- Adding an agent to a **coding terminal / dev tool** that needs to read files, run
|
|
38
|
+
commands, and stream responses.
|
|
39
|
+
- Building an **operations / server-management tool** that launches an agent to
|
|
40
|
+
triage incidents, inspect logs, and propose remediations.
|
|
41
|
+
- Writing **any backend service** that wants LLM-driven behavior with structured
|
|
42
|
+
tool calls, durable sessions, and first-class observability.
|
|
43
|
+
|
|
44
|
+
You are **not** the audience if you want a no-code agent builder, a hosted agent
|
|
45
|
+
runtime, or an opinionated framework that decides *what* your agent should do.
|
|
46
|
+
engine-lib gives you the contracts and the runtime; the behavior is yours.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Core Concepts
|
|
51
|
+
|
|
52
|
+
| Concept | What it is |
|
|
53
|
+
| :--- | :--- |
|
|
54
|
+
| **Provider** | A normalized adapter over an LLM API (OpenAI, Anthropic, Google, OpenAI-compatible). It exposes one shape for chat completion, streaming, tool calling, and usage — and declares its capabilities so callers can degrade gracefully. |
|
|
55
|
+
| **Message & Content** | A provider-neutral representation of a conversation. Messages carry roles (`system`/`user`/`assistant`/`tool`) and typed content parts (text, tool calls, tool results, images), so history is portable across providers. |
|
|
56
|
+
| **Agent** | A declarative definition: which model/provider to use, system instructions, the tools it may call, default generation settings, and lifecycle hooks. An agent is *data*, not a class hierarchy — you describe it, the runtime executes it. |
|
|
57
|
+
| **Tool** | A named capability the model can invoke: a parameter schema (validated before execution), an `execute` function, and a typed, structured **tool result**. Tools are how the agent affects the world. |
|
|
58
|
+
| **Execution** | The provider-native run loop. The model decides when to call tools using the provider's own tool-calling protocol; the runtime dispatches the calls, validates arguments, feeds results back, and repeats until the model produces a final answer. No hand-rolled ReAct prompt. |
|
|
59
|
+
| **Session** | The durable home of a conversation: ordered message history plus metadata. A session can be in-memory (ephemeral) or backed by a store, and is responsible for staying within the model's context window. |
|
|
60
|
+
| **Context** | Information injected into a run at execution time — system facts, retrieved documents, environment state — supplied by **context providers** that the host wires in. engine-lib injects context; it does not decide *what* the context is. |
|
|
61
|
+
| **Event Emitter** | A typed stream of everything that happens during a run (`run.start`, `message`, `tool.call`, `tool.result`, `token`, `run.finish`, `error`). External subscribers use it for UI streaming, logging, metrics, and auditing without coupling to the runtime internals. |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Design Principles
|
|
66
|
+
|
|
67
|
+
1. **Provider-native execution, no custom loop.** We do not invent a prompting
|
|
68
|
+
strategy. We use each provider's first-class tool-calling / function-calling
|
|
69
|
+
protocol and let the model drive. This keeps behavior aligned with how the
|
|
70
|
+
model was trained and avoids brittle "Thought/Action/Observation" string
|
|
71
|
+
parsing.
|
|
72
|
+
2. **Forge-backed, not reinvented.** Configuration and secrets come from
|
|
73
|
+
`forge/config`, observability from `forge/telemetry`, provider-call hardening
|
|
74
|
+
(timeout, retry, circuit breaking) from `forge/resilience`, and process
|
|
75
|
+
wiring from `forge/lifecycle`. engine-lib adds the agent layer; Forge owns the
|
|
76
|
+
infrastructure.
|
|
77
|
+
3. **Contracts over implementations.** `Provider`, `Tool`, `Agent`, `SessionStore`,
|
|
78
|
+
and `ContextProvider` are interfaces first. Every interface ships with an
|
|
79
|
+
in-memory double so consumers can test agents deterministically, mirroring
|
|
80
|
+
Forge's "interfaces first" philosophy.
|
|
81
|
+
4. **Schema-validated boundaries, fail-fast.** Tool parameters and structured
|
|
82
|
+
outputs are described by schemas and validated at the boundary. Bad arguments
|
|
83
|
+
become a typed tool error fed back to the model — not an unhandled exception.
|
|
84
|
+
5. **Observable by default.** Every run, every provider call, and every tool
|
|
85
|
+
invocation emits events and telemetry spans. You can reconstruct exactly what
|
|
86
|
+
an agent did and why.
|
|
87
|
+
6. **Composable, not monolithic.** Use the provider layer without sessions. Use
|
|
88
|
+
sessions without multi-agent coordination. There are no forced peer
|
|
89
|
+
dependencies between sub-modules.
|
|
90
|
+
7. **Zero magic.** No global agent registry by default, no decorator-based DI, no
|
|
91
|
+
hidden network calls. Wiring is explicit; the host stays in control.
|
|
92
|
+
8. **Developer ergonomics.** The common path (define a provider, define a tool,
|
|
93
|
+
define an agent, run it) should be a handful of well-typed function calls with
|
|
94
|
+
inferred types end-to-end.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Constraints / What Is Not Shipped
|
|
99
|
+
|
|
100
|
+
To stay a *library* and not a framework, engine-lib explicitly does **not** include:
|
|
101
|
+
|
|
102
|
+
- **No UI or frontend.** No chat widgets, no terminal renderer. We emit events;
|
|
103
|
+
rendering is the host's job.
|
|
104
|
+
- **No opinionated agent logic.** No built-in "researcher" or "coder" personas, no
|
|
105
|
+
default system prompts. You define behavior.
|
|
106
|
+
- **No built-in tools.** No web search, no code execution, no file system access
|
|
107
|
+
shipped in the box. Tools are yours to define (the library validates and runs
|
|
108
|
+
them).
|
|
109
|
+
- **No prompt template engine.** No templating DSL or prompt library. Instructions
|
|
110
|
+
are plain strings/functions you own.
|
|
111
|
+
- **No custom reasoning loop.** No ReAct/CoT/Tree-of-Thought framework — execution
|
|
112
|
+
is provider-native tool calling.
|
|
113
|
+
- **No RAG / vector store / embeddings engine.** Context injection is a hook;
|
|
114
|
+
retrieval, chunking, and embedding storage are out of scope. Plug your own in.
|
|
115
|
+
- **No model hosting or inference.** engine-lib calls external provider APIs; it
|
|
116
|
+
does not run models.
|
|
117
|
+
- **No fine-tuning, training, or evaluation harness.** Out of scope.
|
|
118
|
+
|
|
119
|
+
If you need one of these, build it *on top of* engine-lib using the provided
|
|
120
|
+
contracts.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Getting Started
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
bun install # install dependencies
|
|
128
|
+
bun run check # type-check (tsc --noEmit)
|
|
129
|
+
bun test # run the test suite
|
|
130
|
+
bun run build # emit dist/ (JS + .d.ts)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Public entry points:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { s, user, system, AgentError, createOpenAI, defineTool, defineAgent, runAgent, createSession, staticContext, createAgentRegistry, asTool } from "@infinityi/engine-lib";
|
|
137
|
+
// or via subpaths:
|
|
138
|
+
import { s } from "@infinityi/engine-lib/schema";
|
|
139
|
+
import { user } from "@infinityi/engine-lib/messages";
|
|
140
|
+
import { AgentError } from "@infinityi/engine-lib/errors";
|
|
141
|
+
import { resolveSecret } from "@infinityi/engine-lib/runtime";
|
|
142
|
+
import { createOpenAI } from "@infinityi/engine-lib/providers";
|
|
143
|
+
import { defineTool } from "@infinityi/engine-lib/tools";
|
|
144
|
+
import { defineAgent, createAgentRegistry, asTool } from "@infinityi/engine-lib/agent";
|
|
145
|
+
import { runAgent } from "@infinityi/engine-lib/execution";
|
|
146
|
+
import { createSession } from "@infinityi/engine-lib/session";
|
|
147
|
+
import { staticContext, truncateOldest } from "@infinityi/engine-lib/context";
|
|
148
|
+
import { createEventHub, loggingSubscriber, messageBusSubscriber } from "@infinityi/engine-lib/events";
|
|
149
|
+
import { agentRuntimeComponent } from "@infinityi/engine-lib/lifecycle";
|
|
150
|
+
// test-only doubles + the cross-provider conformance battery:
|
|
151
|
+
import { mockProvider, scriptedProvider, textResult, toolCallResult } from "@infinityi/engine-lib/testing";
|
|
152
|
+
import { runProviderConformance } from "@infinityi/engine-lib/testing/conformance";
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Stable public API
|
|
156
|
+
|
|
157
|
+
For application code, prefer the root import or the domain subpaths above. The
|
|
158
|
+
root barrel is intentionally focused on the stable, ergonomic surface:
|
|
159
|
+
|
|
160
|
+
- schema, message, and error helpers (`s`, `user`, `system`, `AgentError`, ...)
|
|
161
|
+
- provider factories (`createOpenAI`, `createAnthropic`, `createGoogle`,
|
|
162
|
+
`createOpenAICompatible`)
|
|
163
|
+
- agent/tool/run/session/context helpers (`defineTool`, `defineAgent`,
|
|
164
|
+
`runAgent`, `createSession`, `staticContext`, `dynamicContext`)
|
|
165
|
+
- multi-agent helpers (`createAgentRegistry`, `asTool`)
|
|
166
|
+
- run-event subscribers (`createEventHub`, `loggingSubscriber`,
|
|
167
|
+
`messageBusSubscriber`)
|
|
168
|
+
|
|
169
|
+
Advanced adapter plumbing remains available from subpaths for custom providers
|
|
170
|
+
and tests. For example, `@infinityi/engine-lib/providers` exports `createProvider`,
|
|
171
|
+
`createProviderHttp`, `parseSse`, and stream accumulation helpers, while
|
|
172
|
+
`@infinityi/engine-lib/events` exports event projection and telemetry helpers. Treat those
|
|
173
|
+
as lower-level extension APIs rather than the common application surface.
|
|
174
|
+
|
|
175
|
+
### Providers
|
|
176
|
+
|
|
177
|
+
Use the built-in factories for application code:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
const openai = createOpenAI({ apiKey: config.openaiApiKey, model: "gpt-5" });
|
|
181
|
+
const anthropic = createAnthropic({ apiKey: config.anthropicApiKey, model: "claude-opus-4-7" });
|
|
182
|
+
const google = createGoogle({ apiKey: config.googleApiKey, model: "gemini-2.5-pro" });
|
|
183
|
+
const local = createOpenAICompatible({ baseUrl: "http://localhost:1234/v1", model: "local-model" });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Provider API keys accept raw strings or Forge `Secret<string>` values. Factory
|
|
187
|
+
`model` options become the provider's `defaultModel`; `CompletionRequest.model`
|
|
188
|
+
can override the model per request.
|
|
189
|
+
|
|
190
|
+
The stable provider contract is `Provider`, `CompletionRequest`,
|
|
191
|
+
`CompletionResult`, `StreamEvent`, `Usage`, `ProviderCapabilities`, and the
|
|
192
|
+
factory option types. `CompletionRequest` contains normalized generation fields
|
|
193
|
+
(`temperature`, `topP`, `maxOutputTokens`, `stopSequences`), tool fields
|
|
194
|
+
(`tools`, `toolChoice`, `responseSchema`), optional `metadata`, and
|
|
195
|
+
`providerOptions` for vendor-specific request body fields that are not yet
|
|
196
|
+
first-classed.
|
|
197
|
+
|
|
198
|
+
`CompletionResult.raw` intentionally remains `unknown`: engine-lib keeps the
|
|
199
|
+
portable normalized fields stable, while adapter-aware consumers may narrow the
|
|
200
|
+
native response themselves. `ProviderCapabilities` should be treated as adapter
|
|
201
|
+
truth; callers can degrade based on those flags, and the conformance suite
|
|
202
|
+
checks the built-in adapters for capability honesty.
|
|
203
|
+
|
|
204
|
+
`@infinityi/engine-lib/providers` also exports advanced extension helpers such as
|
|
205
|
+
`createProvider`, `AdapterSpec`, HTTP/SSE utilities, `StreamAccumulator`, and
|
|
206
|
+
`collectStream`. Use those for custom adapters and conformance tests, not for
|
|
207
|
+
ordinary application wiring.
|
|
208
|
+
|
|
209
|
+
Runnable, offline examples live in [`examples/`](./examples/) (`bun examples/incident-analysis.ts`),
|
|
210
|
+
and the generated API reference is described in [`docs/`](./docs/README.md) (`bun run docs`).
|
|
211
|
+
|
|
212
|
+
`runAgent` drives the provider-native tool-calling loop: it sends the
|
|
213
|
+
conversation + tool schemas to the provider, validates and dispatches tool calls
|
|
214
|
+
(in parallel, with per-call error isolation), feeds results back, and repeats
|
|
215
|
+
until a final answer, the step budget (`MaxStepsExceededError`), or cancellation
|
|
216
|
+
(`CancelledError`). It runs buffered (`await runAgent(agent, { input })` →
|
|
217
|
+
`RunResult`) or streaming (`runAgent(agent, { input, stream: true })` → an
|
|
218
|
+
async-iterable of `RunEvent`s).
|
|
219
|
+
|
|
220
|
+
`runAgent` has three stable call shapes:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
const result = await runAgent(agent, { input: "go" });
|
|
224
|
+
// Promise<RunResult>
|
|
225
|
+
|
|
226
|
+
const handle = runAgent(agent, { input: "go", stream: true });
|
|
227
|
+
// RunHandle: AsyncIterable<RunEvent> & { completed: Promise<RunResult> }
|
|
228
|
+
|
|
229
|
+
const maybeStream: boolean = shouldStream();
|
|
230
|
+
const resultOrHandle = runAgent(agent, { input: "go", stream: maybeStream });
|
|
231
|
+
// Promise<RunResult> | RunHandle
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Streaming consumers should either drain the async iterable or await
|
|
235
|
+
`handle.completed`. Successful streams emit a final `run.finish` event and then
|
|
236
|
+
`completed` resolves with the same `RunResult`. If iteration throws,
|
|
237
|
+
`completed` rejects with the same error; if iteration is abandoned early,
|
|
238
|
+
`completed` rejects with `CancelledError`.
|
|
239
|
+
|
|
240
|
+
Run events are ordered. Every run starts with `run.start`. Provider assistant
|
|
241
|
+
turns are emitted as `message`; streaming text arrives as `token` before the
|
|
242
|
+
assistant `message` that contains the accumulated text. Tool calls emit
|
|
243
|
+
`tool.call`, then `tool.result`, then the tool-result `message`. Successful runs
|
|
244
|
+
end with `run.finish`; failed runs emit `error` and then reject or throw the same
|
|
245
|
+
`AgentError`. Tool validation failures, unknown tools, and thrown tool
|
|
246
|
+
implementations are isolated as `{ ok: false }` tool results so the model can
|
|
247
|
+
recover; provider failures, context/session failures, max-step/max-handoff
|
|
248
|
+
limits, and cancellation fail the run.
|
|
249
|
+
|
|
250
|
+
### Tools and schemas
|
|
251
|
+
|
|
252
|
+
Use `defineTool()` for application tools. It infers the `execute(args)` type from
|
|
253
|
+
the parameter schema and keeps the definition shape stable for future releases:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const readFile = defineTool({
|
|
257
|
+
name: "read_file",
|
|
258
|
+
description: "Read a file from the workspace.",
|
|
259
|
+
parameters: s.object({
|
|
260
|
+
path: s.string(),
|
|
261
|
+
maxBytes: s.optional(s.number({ int: true })),
|
|
262
|
+
}),
|
|
263
|
+
execute: async ({ path, maxBytes }) => {
|
|
264
|
+
const content = await workspace.read(path, { maxBytes });
|
|
265
|
+
return { ok: true, content };
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Tool results are deliberately small: return `{ ok: true, content }` for success
|
|
271
|
+
and `{ ok: false, error }` for expected/domain failures such as missing files or
|
|
272
|
+
permission denials. Throw only for unexpected implementation faults; `runAgent`
|
|
273
|
+
catches thrown tool errors and feeds them back to the model as recoverable
|
|
274
|
+
tool-result errors.
|
|
275
|
+
|
|
276
|
+
Tool content is rendered predictably for the model. Strings pass through,
|
|
277
|
+
`null`/`undefined` become empty text, and non-string values are JSON-encoded.
|
|
278
|
+
`error` is a string in the stable contract; use clear, user-actionable messages.
|
|
279
|
+
|
|
280
|
+
The built-in `s` schema builder covers the JSON-Schema subset engine-lib
|
|
281
|
+
validates and providers need for tool parameters. `s.object()` is strict by
|
|
282
|
+
default (`additionalProperties: false`), required keys are derived from the
|
|
283
|
+
shape, and `s.optional(...)` makes an object key optional in both TypeScript and
|
|
284
|
+
runtime validation. Use `asSchema()` / `fromJsonSchema()` when adapting an
|
|
285
|
+
external schema library or raw JSON Schema.
|
|
286
|
+
|
|
287
|
+
### Agents and composition
|
|
288
|
+
|
|
289
|
+
Use `defineAgent()` for application agents. An agent definition is plain data:
|
|
290
|
+
the provider to call, optional instructions, tools, default generation settings,
|
|
291
|
+
hooks, and optional handoff targets. Construction validates the agent name and
|
|
292
|
+
duplicate tool names, but does not call the provider or resolve handoff targets.
|
|
293
|
+
|
|
294
|
+
Instructions may be a string or a function:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
const coder = defineAgent({
|
|
298
|
+
name: "coder",
|
|
299
|
+
provider,
|
|
300
|
+
instructions: (ctx) => `You are ${ctx.agent.name}. Be precise.`,
|
|
301
|
+
tools: [readFile],
|
|
302
|
+
generation: { temperature: 0.2 },
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Resolved instructions are injected into the provider request as system context.
|
|
307
|
+
They are rebuilt every run and are not persisted to sessions.
|
|
308
|
+
|
|
309
|
+
Hooks are awaited and receive the shared engine context. Public run events are
|
|
310
|
+
emitted before the corresponding hook is invoked. Hook failures fail the run and
|
|
311
|
+
flow through `onError`; if `onError` throws, the original run failure is
|
|
312
|
+
preserved.
|
|
313
|
+
|
|
314
|
+
Agent registries are explicit and host-owned. There is no global registry.
|
|
315
|
+
String-named handoff targets require passing `registry` to `runAgent`; direct
|
|
316
|
+
`AgentDefinition` handoff targets do not. Each handoff target is advertised as a
|
|
317
|
+
synthetic `transfer_to_<agent>` tool, and a real tool with the same name is a
|
|
318
|
+
configuration error.
|
|
319
|
+
|
|
320
|
+
`asTool(agent)` wraps a child agent as a normal tool. The child runs to
|
|
321
|
+
completion, its output is returned as the tool result, its usage is folded into
|
|
322
|
+
the parent run, and its events surface as `agent.child` events. A failing child
|
|
323
|
+
run becomes a tool error so the parent model can recover.
|
|
324
|
+
|
|
325
|
+
Durable conversation state and run-time context injection are wired into the
|
|
326
|
+
loop. Pass `session` (from `createSession({ id })`, backed by a `SessionStore` —
|
|
327
|
+
`InMemorySessionStore` ships built-in) to resume a conversation: prior history is
|
|
328
|
+
read before the run and the new turn is appended after. Pass `context`
|
|
329
|
+
(`staticContext` / `dynamicContext` providers) to inject run-time facts into the
|
|
330
|
+
system layer — injected context and instructions are rebuilt every run and never
|
|
331
|
+
persisted. Pass `contextWindow: { maxTokens, strategy }` to keep the request
|
|
332
|
+
within budget via `truncateOldest()` or `summarizeOldest()`, raising
|
|
333
|
+
`ContextWindowError` only when history is irreducible; trimming never mutates the
|
|
334
|
+
persisted/returned history.
|
|
335
|
+
|
|
336
|
+
### Sessions and context
|
|
337
|
+
|
|
338
|
+
`createSession()` is synchronous. If no `id` is supplied it generates one; if an
|
|
339
|
+
`id` is supplied with a shared store, the session resumes that history lazily on
|
|
340
|
+
first use. Seed `messages` are written once and only when the backing store has
|
|
341
|
+
no existing history for that id. `messages()` returns a snapshot, `append()`
|
|
342
|
+
adds to the tail, and `clear()` deletes the history and prevents the seed from
|
|
343
|
+
being re-applied.
|
|
344
|
+
|
|
345
|
+
`SessionStore` is the durable persistence contract: `load`, `append`, `save`,
|
|
346
|
+
and `delete`. Store implementations must preserve message order, treat
|
|
347
|
+
`append()` as an atomic tail add, and avoid exposing mutable internal arrays by
|
|
348
|
+
reference.
|
|
349
|
+
|
|
350
|
+
Successful runs append only conversation messages: user input, assistant turns,
|
|
351
|
+
and tool-result messages. Instructions, injected context, and handoff-injected
|
|
352
|
+
instructions are request-time system messages and are never persisted. Failed
|
|
353
|
+
runs do not append new messages.
|
|
354
|
+
|
|
355
|
+
Context providers resolve once per run before the first provider call.
|
|
356
|
+
`staticContext()` injects fixed content; `dynamicContext()` computes content from
|
|
357
|
+
the engine context. All provider output is folded into a system message for that
|
|
358
|
+
request only and is never persisted.
|
|
359
|
+
|
|
360
|
+
`contextWindow` applies only to the provider request view. It never mutates the
|
|
361
|
+
canonical history returned from `runAgent` or stored in the session.
|
|
362
|
+
`truncateOldest()` is the default stable strategy and drops oldest non-system
|
|
363
|
+
messages while retaining system messages. `summarizeOldest()` is public, but it
|
|
364
|
+
performs an additional provider call and should be chosen deliberately.
|
|
365
|
+
|
|
366
|
+
### Events, subscribers, and telemetry
|
|
367
|
+
|
|
368
|
+
Use `onEvent` for a single callback and `subscribers` for fan-out:
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
await runAgent(agent, {
|
|
372
|
+
input: "go",
|
|
373
|
+
onEvent: (event) => ui.observe(event),
|
|
374
|
+
subscribers: [loggingSubscriber(logger), messageBusSubscriber(messageBus)],
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
`onEvent` is registered first, followed by `subscribers` in array order. Each
|
|
379
|
+
subscriber may be sync or async; subscribers are awaited in order, so slow sinks
|
|
380
|
+
apply back-pressure. Subscriber failures are isolated: a thrown/rejected
|
|
381
|
+
subscriber is reported to the hub's error reporter/logger and does not abort the
|
|
382
|
+
run or prevent later subscribers from seeing the event. Undefined subscriber
|
|
383
|
+
slots are ignored.
|
|
384
|
+
|
|
385
|
+
`loggingSubscriber()` writes compact fields from `eventFields()`.
|
|
386
|
+
`messageBusSubscriber()` publishes a serializable `eventPayload()` projection.
|
|
387
|
+
Those projection helpers are stable on `@infinityi/engine-lib/events` for custom
|
|
388
|
+
subscribers, but they are intentionally not root exports.
|
|
389
|
+
|
|
390
|
+
For telemetry, the stable application path is passing a Forge telemetry handle
|
|
391
|
+
to `runAgent`. That enables `agent.run`, `agent.provider.call`, and
|
|
392
|
+
`agent.tool.execute` spans plus `agent.run.duration`, `agent.tool.duration`,
|
|
393
|
+
`agent.tokens`, and `agent.runs` metrics. `createRunTelemetry()` and the span
|
|
394
|
+
constants are available from `@infinityi/engine-lib/events` for advanced integrations and
|
|
395
|
+
tests.
|
|
396
|
+
|
|
397
|
+
### Multi-agent coordination
|
|
398
|
+
|
|
399
|
+
Compose agents in two complementary ways. Both reuse the same `runAgent` loop —
|
|
400
|
+
no separate orchestrator.
|
|
401
|
+
|
|
402
|
+
**Handoff / delegation.** Declare `handoffs` on an agent and each target becomes
|
|
403
|
+
a synthetic `transfer_to_<name>` tool the model can call to transfer the running
|
|
404
|
+
conversation to a specialist. The message history is preserved across the switch;
|
|
405
|
+
the new agent's instructions are injected as an additional system message. The
|
|
406
|
+
`RunResult` reports the agent that produced the final answer (`result.agent`) and
|
|
407
|
+
the ordered `result.handoffs` trail. A `maxHandoffs` cap (default 8) bounds
|
|
408
|
+
triage↔specialist ping-pong with `MaxHandoffsExceededError`. Targets may be given
|
|
409
|
+
directly as an `AgentDefinition`, or by name as a `string` resolved through an
|
|
410
|
+
`AgentRegistry` passed in `RunOptions.registry`.
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
import { defineAgent, runAgent, createAgentRegistry } from "@infinityi/engine-lib";
|
|
414
|
+
|
|
415
|
+
const billing = defineAgent({ name: "billing", provider, instructions: "Handle billing." });
|
|
416
|
+
const triage = defineAgent({
|
|
417
|
+
name: "triage",
|
|
418
|
+
provider,
|
|
419
|
+
instructions: "Route the user to the right specialist.",
|
|
420
|
+
handoffs: [billing], // → exposes a `transfer_to_billing` tool
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const result = await runAgent(triage, { input: "I want a refund" });
|
|
424
|
+
result.agent; // "billing" — the specialist answered
|
|
425
|
+
result.handoffs; // ["billing"] — the transfer trail
|
|
426
|
+
|
|
427
|
+
// String-named targets resolve via a registry (no global state):
|
|
428
|
+
const registry = createAgentRegistry([billing]);
|
|
429
|
+
const router = defineAgent({ name: "router", provider, handoffs: ["billing"] });
|
|
430
|
+
await runAgent(router, { input: "…", registry });
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Sub-agent-as-tool.** Wrap an agent as a `ToolDefinition` with `asTool(agent)`
|
|
434
|
+
so a parent invokes it through the normal tool-calling path. The child runs to
|
|
435
|
+
completion and its output is fed back as the tool result; the child's token usage
|
|
436
|
+
is folded into the parent's total and its events surface to the parent as
|
|
437
|
+
`agent.child` events (with `depth` tracking nesting).
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { asTool, defineAgent } from "@infinityi/engine-lib";
|
|
441
|
+
|
|
442
|
+
const researcher = defineAgent({ name: "researcher", provider, instructions: "Research deeply." });
|
|
443
|
+
const lead = defineAgent({
|
|
444
|
+
name: "lead",
|
|
445
|
+
provider,
|
|
446
|
+
tools: [asTool(researcher, { description: "Delegate research to a specialist." })],
|
|
447
|
+
});
|
|
448
|
+
// lead's model calls the "researcher" tool → child run executes → output fed back.
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Developer experience & conformance
|
|
452
|
+
|
|
453
|
+
Three things make the library trustworthy to adopt:
|
|
454
|
+
|
|
455
|
+
- **Provider conformance suite** — a fixture-driven battery, shipped from
|
|
456
|
+
`@infinityi/engine-lib/testing/conformance`, that every adapter must pass (buffered
|
|
457
|
+
completion, streaming, tool calling, usage, capability honesty, error
|
|
458
|
+
mapping). Each adapter supplies its native wire fixtures plus the canonical
|
|
459
|
+
normalized values; the battery drives the public `Provider` seam through an
|
|
460
|
+
injected fake `fetch`, so third-party adapters can prove parity with the
|
|
461
|
+
in-house ones. Pass Bun's `{ describe, expect, it }` as `testApi` when
|
|
462
|
+
registering the battery. All four built-in adapters are wired through it.
|
|
463
|
+
- **Lifecycle adapter** — `agentRuntimeComponent()` (from `@infinityi/engine-lib/lifecycle`)
|
|
464
|
+
adapts the runtime to a `forge/lifecycle` `Component`: `start()` fail-fast
|
|
465
|
+
validates providers (and optionally probes them so a bad deploy rolls back in
|
|
466
|
+
`forge.boot`), `healthcheck()` maps provider probes to a forge `HealthResult`,
|
|
467
|
+
and `stop()` runs an `onStop` hook (e.g. flush/close a durable session store).
|
|
468
|
+
- **Test doubles & examples** — network-free helpers in `@infinityi/engine-lib/testing`
|
|
469
|
+
(`mockProvider`, `scriptedProvider`, `textResult`/`toolCallResult`,
|
|
470
|
+
`jsonFetch`/`sseFetch`, `inMemorySessionStore`) let consumers unit-test agents
|
|
471
|
+
deterministically, and [`examples/`](./examples/) holds runnable versions of
|
|
472
|
+
the scenarios above.
|
|
473
|
+
|
|
474
|
+
```ts
|
|
475
|
+
import { boot } from "@infinityi/forge/lifecycle";
|
|
476
|
+
import { agentRuntimeComponent } from "@infinityi/engine-lib/lifecycle";
|
|
477
|
+
|
|
478
|
+
const app = await boot({
|
|
479
|
+
components: [agentRuntimeComponent({ providers: [provider], sessionStore, probeOnStart: true })],
|
|
480
|
+
});
|
|
481
|
+
// app.ready === true; app.stop() drains it (running the optional onStop hook).
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
(An optional `forge/data`-backed `SessionStore` is deferred to a later change.)
|
|
485
|
+
|
|
486
|
+
## License
|
|
487
|
+
|
|
488
|
+
MIT — see [`LICENSE`](./LICENSE).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An opt-in, named-lookup registry for {@link AgentDefinition}s.
|
|
3
|
+
*
|
|
4
|
+
* Multi-agent coordination (Phase 7) needs a way to resolve an agent *by name* —
|
|
5
|
+
* a string-named {@link AgentDefinition.handoffs handoff} target, or a host that
|
|
6
|
+
* discovers agents dynamically. An {@link AgentRegistry} provides that lookup
|
|
7
|
+
* without introducing global state: it is created explicitly and held by the
|
|
8
|
+
* host (Design Principle 7, "no global agent registry by default"). It mirrors
|
|
9
|
+
* the per-agent {@link ToolRegistry}: built eagerly so duplicate agent names
|
|
10
|
+
* fail fast (a configuration bug) rather than surfacing as a confusing
|
|
11
|
+
* resolution error later.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import type { AgentDefinition } from "./types";
|
|
16
|
+
/** Name-based lookup over a set of {@link AgentDefinition}s. */
|
|
17
|
+
export interface AgentRegistry {
|
|
18
|
+
/** Number of registered agents. */
|
|
19
|
+
readonly size: number;
|
|
20
|
+
/** Whether an agent with this name is registered. */
|
|
21
|
+
has(name: string): boolean;
|
|
22
|
+
/** Get an agent by name, or `undefined` if absent. */
|
|
23
|
+
get(name: string): AgentDefinition | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Get an agent by name, throwing if it is not registered.
|
|
26
|
+
*
|
|
27
|
+
* @throws {ExecutionError} if no agent with `name` is registered.
|
|
28
|
+
*/
|
|
29
|
+
resolve(name: string): AgentDefinition;
|
|
30
|
+
/** A snapshot of every registered agent, in registration order. */
|
|
31
|
+
list(): readonly AgentDefinition[];
|
|
32
|
+
/**
|
|
33
|
+
* Register an additional agent.
|
|
34
|
+
*
|
|
35
|
+
* @throws {ExecutionError} if an agent with the same name is already registered.
|
|
36
|
+
*/
|
|
37
|
+
register(agent: AgentDefinition): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build an explicit, host-owned {@link AgentRegistry}, optionally seeded with
|
|
41
|
+
* `agents`. There is no global registry; pass this value to `runAgent` when an
|
|
42
|
+
* agent declares string-named handoff targets.
|
|
43
|
+
*
|
|
44
|
+
* @throws {ExecutionError} if two agents share a name.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createAgentRegistry(agents?: readonly AgentDefinition[]): AgentRegistry;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `asTool` — wrap an {@link AgentDefinition} as a {@link ToolDefinition} so a
|
|
3
|
+
* parent agent can invoke it through the normal provider-native tool-calling
|
|
4
|
+
* path (the "sub-agent-as-tool" pattern, Phase 7).
|
|
5
|
+
*
|
|
6
|
+
* The returned tool runs the child agent with {@link runAgent} (buffered) and
|
|
7
|
+
* returns its final text as the tool result. When dispatched inside a run, the
|
|
8
|
+
* child's events and token usage are propagated to the parent run through
|
|
9
|
+
* the {@link ToolContext.run} bridge: each child {@link RunEvent} is re-emitted
|
|
10
|
+
* as an `agent.child` event, and the child's usage is folded into the parent's
|
|
11
|
+
* aggregate. A failing child run (a thrown {@link AgentError}) becomes a tool
|
|
12
|
+
* error fed back to the parent model — never an unhandled throw.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { asTool, defineAgent } from "@infinityi/engine-lib/agent";
|
|
17
|
+
*
|
|
18
|
+
* const researcher = defineAgent({ name: "researcher", provider, instructions: "…" });
|
|
19
|
+
* const lead = defineAgent({
|
|
20
|
+
* name: "lead",
|
|
21
|
+
* provider,
|
|
22
|
+
* tools: [asTool(researcher, { description: "Delegate research to a specialist." })],
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import type { RunInput } from "../execution/types";
|
|
29
|
+
import type { Schema } from "../schema/types";
|
|
30
|
+
import type { ToolDefinition } from "../tools/types";
|
|
31
|
+
import type { AgentDefinition } from "./types";
|
|
32
|
+
/** Options for {@link asTool}. */
|
|
33
|
+
export interface AsToolOptions<TArgs = {
|
|
34
|
+
input: string;
|
|
35
|
+
}> {
|
|
36
|
+
/** Tool name advertised to the parent model. Defaults to the agent's name. */
|
|
37
|
+
readonly name?: string;
|
|
38
|
+
/** Tool description. Defaults to a generic "delegate to <agent>" line. */
|
|
39
|
+
readonly description?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Parameter schema for the tool. Defaults to `{ input: string }`. When you
|
|
42
|
+
* supply a custom schema, also supply {@link AsToolOptions.toInput} to map the
|
|
43
|
+
* validated arguments into the child run's input.
|
|
44
|
+
*/
|
|
45
|
+
readonly parameters?: Schema<TArgs>;
|
|
46
|
+
/**
|
|
47
|
+
* Map the validated tool arguments to the child run's {@link RunInput}.
|
|
48
|
+
* Defaults to using `args.input` (matching the default schema).
|
|
49
|
+
*/
|
|
50
|
+
readonly toInput?: (args: TArgs) => RunInput;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Wrap `agent` as a {@link ToolDefinition} the parent agent can call.
|
|
54
|
+
*
|
|
55
|
+
* The tool's `execute` runs `agent` to completion, threading the parent's
|
|
56
|
+
* telemetry / logger / abort signal through, propagating the child's events and
|
|
57
|
+
* usage to the parent run, and returning the child's final output as content.
|
|
58
|
+
* The default tool schema is `{ input: string }`; custom schemas should provide
|
|
59
|
+
* `toInput` so validated tool arguments can be converted into the child run's
|
|
60
|
+
* `RunInput`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function asTool<TArgs = {
|
|
63
|
+
input: string;
|
|
64
|
+
}>(agent: AgentDefinition, opts?: AsToolOptions<TArgs>): ToolDefinition<TArgs>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineAgent` — the ergonomic constructor for {@link AgentDefinition}s.
|
|
3
|
+
*
|
|
4
|
+
* Application code should prefer this constructor over hand-writing
|
|
5
|
+
* `AgentDefinition` objects. Pure and fail-fast: it validates the agent's
|
|
6
|
+
* `name` and eagerly builds a {@link ToolRegistry} so duplicate tool names are
|
|
7
|
+
* caught at definition time (an `ExecutionError`) rather than mid-run. It
|
|
8
|
+
* returns the definition as data; running it is `runAgent`'s job.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { defineAgent } from "@infinityi/engine-lib/agent";
|
|
13
|
+
*
|
|
14
|
+
* const coder = defineAgent({
|
|
15
|
+
* name: "terminal-coder",
|
|
16
|
+
* provider,
|
|
17
|
+
* instructions: "You are a coding assistant operating inside the user's terminal.",
|
|
18
|
+
* tools: [readFile, runCommand],
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import type { AgentDefinition } from "./types";
|
|
25
|
+
/**
|
|
26
|
+
* Define an agent, validating its name and tool-name uniqueness.
|
|
27
|
+
*
|
|
28
|
+
* Instructions, hooks, handoffs, and generation settings are stored as data and
|
|
29
|
+
* resolved by the run loop. No provider calls, context resolution, or registry
|
|
30
|
+
* lookup happens here.
|
|
31
|
+
*
|
|
32
|
+
* @throws {TypeError} if `name` is missing/empty.
|
|
33
|
+
* @throws {ExecutionError} if two tools share a name.
|
|
34
|
+
*/
|
|
35
|
+
export declare function defineAgent(def: AgentDefinition): AgentDefinition;
|