@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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +488 -0
  3. package/dist/agent/agent-registry.d.ts +46 -0
  4. package/dist/agent/as-tool.d.ts +64 -0
  5. package/dist/agent/define.d.ts +35 -0
  6. package/dist/agent/handoff.d.ts +39 -0
  7. package/dist/agent/index.d.ts +20 -0
  8. package/dist/agent/index.js +38 -0
  9. package/dist/agent/registry.d.ts +27 -0
  10. package/dist/agent/types.d.ts +109 -0
  11. package/dist/context/index.d.ts +11 -0
  12. package/dist/context/index.js +21 -0
  13. package/dist/context/providers.d.ts +25 -0
  14. package/dist/context/types.d.ts +63 -0
  15. package/dist/context/window.d.ts +41 -0
  16. package/dist/errors.d.ts +93 -0
  17. package/dist/errors.js +24 -0
  18. package/dist/events/hub.d.ts +15 -0
  19. package/dist/events/index.d.ts +26 -0
  20. package/dist/events/index.js +24 -0
  21. package/dist/events/subscribers.d.ts +57 -0
  22. package/dist/events/telemetry.d.ts +61 -0
  23. package/dist/events/types.d.ts +39 -0
  24. package/dist/execution/index.d.ts +11 -0
  25. package/dist/execution/index.js +22 -0
  26. package/dist/execution/run.d.ts +35 -0
  27. package/dist/execution/types.d.ts +203 -0
  28. package/dist/execution/usage.d.ts +14 -0
  29. package/dist/index-02s1fjxr.js +226 -0
  30. package/dist/index-19pwq79t.js +0 -0
  31. package/dist/index-1p6mb2vz.js +32 -0
  32. package/dist/index-64tt9696.js +1796 -0
  33. package/dist/index-7690reng.js +96 -0
  34. package/dist/index-bqg01r42.js +354 -0
  35. package/dist/index-d4xz3abn.js +0 -0
  36. package/dist/index-dexgmwg6.js +148 -0
  37. package/dist/index-fkr3rcq9.js +97 -0
  38. package/dist/index-jg19te9v.js +0 -0
  39. package/dist/index-jp2b31xs.js +101 -0
  40. package/dist/index-jxgj4z08.js +68 -0
  41. package/dist/index-kte2h4k2.js +0 -0
  42. package/dist/index-pwr8179t.js +492 -0
  43. package/dist/index-rentvdpp.js +27 -0
  44. package/dist/index-vnby35rm.js +84 -0
  45. package/dist/index-w34cbktd.js +14 -0
  46. package/dist/index-xsv43c5j.js +39 -0
  47. package/dist/index-yrqrxwjt.js +148 -0
  48. package/dist/index-zfgr4xx3.js +90 -0
  49. package/dist/index.d.ts +45 -0
  50. package/dist/index.js +117 -0
  51. package/dist/lifecycle/component.d.ts +74 -0
  52. package/dist/lifecycle/index.d.ts +12 -0
  53. package/dist/lifecycle/index.js +72 -0
  54. package/dist/messages/factory.d.ts +24 -0
  55. package/dist/messages/index.d.ts +8 -0
  56. package/dist/messages/index.js +17 -0
  57. package/dist/messages/types.d.ts +52 -0
  58. package/dist/providers/adapter.d.ts +42 -0
  59. package/dist/providers/anthropic/index.d.ts +31 -0
  60. package/dist/providers/anthropic/map.d.ts +12 -0
  61. package/dist/providers/anthropic/stream.d.ts +9 -0
  62. package/dist/providers/google/index.d.ts +29 -0
  63. package/dist/providers/google/map.d.ts +13 -0
  64. package/dist/providers/google/stream.d.ts +11 -0
  65. package/dist/providers/http.d.ts +61 -0
  66. package/dist/providers/index.d.ts +32 -0
  67. package/dist/providers/index.js +35 -0
  68. package/dist/providers/openai/index.d.ts +34 -0
  69. package/dist/providers/openai/map.d.ts +10 -0
  70. package/dist/providers/openai/stream.d.ts +9 -0
  71. package/dist/providers/openai-compatible/index.d.ts +37 -0
  72. package/dist/providers/openai-compatible/map.d.ts +13 -0
  73. package/dist/providers/openai-compatible/stream.d.ts +11 -0
  74. package/dist/providers/shared.d.ts +34 -0
  75. package/dist/providers/sse.d.ts +19 -0
  76. package/dist/providers/stream.d.ts +69 -0
  77. package/dist/providers/types.d.ts +137 -0
  78. package/dist/runtime/index.d.ts +11 -0
  79. package/dist/runtime/index.js +11 -0
  80. package/dist/runtime/secret.d.ts +12 -0
  81. package/dist/runtime/types.d.ts +27 -0
  82. package/dist/schema/builder.d.ts +70 -0
  83. package/dist/schema/index.d.ts +13 -0
  84. package/dist/schema/index.js +15 -0
  85. package/dist/schema/json-schema.d.ts +19 -0
  86. package/dist/schema/types.d.ts +70 -0
  87. package/dist/schema/validate.d.ts +19 -0
  88. package/dist/session/index.d.ts +11 -0
  89. package/dist/session/index.js +8 -0
  90. package/dist/session/session.d.ts +31 -0
  91. package/dist/session/store.d.ts +20 -0
  92. package/dist/session/types.d.ts +55 -0
  93. package/dist/testing/conformance.d.ts +106 -0
  94. package/dist/testing/conformance.js +132 -0
  95. package/dist/testing/index.d.ts +84 -0
  96. package/dist/testing/index.js +31 -0
  97. package/dist/tools/define.d.ts +42 -0
  98. package/dist/tools/index.d.ts +11 -0
  99. package/dist/tools/index.js +15 -0
  100. package/dist/tools/result.d.ts +36 -0
  101. package/dist/tools/types.d.ts +85 -0
  102. package/docs/README.md +36 -0
  103. package/examples/README.md +24 -0
  104. package/examples/incident-analysis.ts +100 -0
  105. package/examples/lifecycle.ts +53 -0
  106. package/examples/multi-agent.ts +93 -0
  107. package/examples/terminal-coder.ts +80 -0
  108. package/package.json +114 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Provider conformance suite (Phase 8).
3
+ *
4
+ * A shared, fixture-driven test battery that **every** {@link Provider} adapter
5
+ * must pass — the in-house OpenAI / Anthropic / Google / OpenAI-compatible
6
+ * adapters as well as any third-party adapter. It guarantees cross-provider
7
+ * parity on the parts of the contract that callers above the provider layer
8
+ * rely on: buffered completion, streaming, tool calling, usage reporting,
9
+ * capability honesty, and error mapping.
10
+ *
11
+ * The battery is provider-agnostic: each adapter supplies its **native wire
12
+ * fixtures** (the JSON / SSE bytes its vendor would return) plus the canonical
13
+ * normalized values those fixtures should decode to. The battery drives the
14
+ * public `Provider` seam through an injected fake `fetch` and asserts the
15
+ * normalized {@link CompletionResult} / {@link StreamEvent} shape — never the
16
+ * vendor wire format.
17
+ *
18
+ * Shipped from its own subpath (`@infinityi/engine-lib/testing/conformance`)
19
+ * rather than the main `@infinityi/engine-lib/testing` barrel. It registers
20
+ * tests against Bun's test-runner API when called, while remaining safe to
21
+ * import in non-Bun runtimes.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { describe, expect, it } from "bun:test";
26
+ * import { runProviderConformance } from "@infinityi/engine-lib/testing/conformance";
27
+ * import { createOpenAICompatible } from "@infinityi/engine-lib/providers";
28
+ *
29
+ * runProviderConformance("openai-compatible", {
30
+ * testApi: { describe, expect, it },
31
+ * makeProvider: ({ fetch }) => createOpenAICompatible({ baseUrl: "https://h/v1", model: "m", fetch }),
32
+ * expectPath: "/chat/completions",
33
+ * fixtures: {
34
+ * text: { body: { choices: [{ finish_reason: "stop", message: { content: "hi" } }] }, expectText: "hi" },
35
+ * // …toolCall, usage, stream
36
+ * },
37
+ * });
38
+ * ```
39
+ *
40
+ * @module
41
+ */
42
+ import type { describe as bunDescribe, expect as bunExpect, it as bunIt } from "bun:test";
43
+ import type { PipelineLike } from "@infinityi/forge/http/client";
44
+ import type { Provider, Usage } from "../providers/types";
45
+ /** A `fetch`-compatible function. */
46
+ type FetchFn = typeof globalThis.fetch;
47
+ export interface ConformanceTestApi {
48
+ readonly describe: typeof bunDescribe;
49
+ readonly expect: typeof bunExpect;
50
+ readonly it: typeof bunIt;
51
+ }
52
+ /** The transport seams the battery injects into the adapter under test. */
53
+ export interface ProviderIO {
54
+ /** Fake `fetch` returning the fixture bytes. */
55
+ readonly fetch: FetchFn;
56
+ /** Optional resilience override (the battery passes a no-retry pipeline
57
+ * for the error-mapping case so it doesn't pay the default retry backoff). */
58
+ readonly resilience?: PipelineLike;
59
+ }
60
+ /** Build a fresh adapter wired to the supplied transport seams. */
61
+ export type MakeProvider = (io: ProviderIO) => Provider;
62
+ /** Native wire fixtures plus the canonical normalized values they decode to. */
63
+ export interface ConformanceFixtures {
64
+ /** A buffered completion whose body decodes to assistant text. */
65
+ readonly text: {
66
+ readonly body: unknown;
67
+ readonly expectText: string;
68
+ };
69
+ /** A buffered completion whose body decodes to a single tool call. */
70
+ readonly toolCall: {
71
+ readonly body: unknown;
72
+ readonly expectName: string;
73
+ readonly expectArgs?: Record<string, unknown>;
74
+ };
75
+ /** A buffered completion whose body reports token usage. */
76
+ readonly usage: {
77
+ readonly body: unknown;
78
+ readonly expect: Usage;
79
+ };
80
+ /**
81
+ * A streaming response (raw `text/event-stream` bytes) that yields text
82
+ * deltas and a final finish. Provide this iff the adapter declares
83
+ * `capabilities.streaming` — the battery cross-checks the two.
84
+ */
85
+ readonly stream?: {
86
+ readonly sse: string;
87
+ readonly expectText: string;
88
+ };
89
+ }
90
+ /** Options for {@link runProviderConformance}. */
91
+ export interface ConformanceOptions {
92
+ /** Factory building the adapter under test from an injected fake `fetch`. */
93
+ readonly makeProvider: MakeProvider;
94
+ /** The native wire fixtures for this provider. */
95
+ readonly fixtures: ConformanceFixtures;
96
+ /** Substring asserted to appear in the request URL (e.g. `/messages`). */
97
+ readonly expectPath?: string;
98
+ /** Test-runner API. Pass Bun's `{ describe, expect, it }` from a test file. */
99
+ readonly testApi?: ConformanceTestApi;
100
+ }
101
+ /**
102
+ * Register a `describe()` block of shared assertions the `Provider` adapter
103
+ * `name` must satisfy. Call it from a `*.test.ts` file (it uses `bun:test`).
104
+ */
105
+ export declare function runProviderConformance(name: string, opts: ConformanceOptions): void;
106
+ export {};
@@ -0,0 +1,132 @@
1
+ import {
2
+ jsonFetch,
3
+ sseFetch
4
+ } from "../index-dexgmwg6.js";
5
+ import"../index-vnby35rm.js";
6
+ import"../index-19pwq79t.js";
7
+ import {
8
+ combine
9
+ } from "../index-bqg01r42.js";
10
+ import"../index-zfgr4xx3.js";
11
+ import {
12
+ ProviderError
13
+ } from "../index-7690reng.js";
14
+ import {
15
+ user
16
+ } from "../index-1p6mb2vz.js";
17
+
18
+ // src/testing/conformance.ts
19
+ function getTestApi() {
20
+ const globals = globalThis;
21
+ if (typeof globals.describe === "function" && typeof globals.expect === "function" && typeof globals.it === "function") {
22
+ return {
23
+ describe: globals.describe,
24
+ expect: globals.expect,
25
+ it: globals.it
26
+ };
27
+ }
28
+ try {
29
+ const requireFn = (0, eval)("require");
30
+ const api = typeof requireFn === "function" ? requireFn("bun:test") : undefined;
31
+ if (typeof api?.describe === "function" && typeof api.expect === "function" && typeof api.it === "function") {
32
+ return {
33
+ describe: api.describe,
34
+ expect: api.expect,
35
+ it: api.it
36
+ };
37
+ }
38
+ } catch {}
39
+ throw new Error("runProviderConformance requires Bun's test runner; call it from a Bun test file.");
40
+ }
41
+ var NO_RETRY = combine();
42
+ var REQUEST = { messages: [user("hi")] };
43
+ async function collect(events) {
44
+ const out = [];
45
+ for await (const event of events)
46
+ out.push(event);
47
+ return out;
48
+ }
49
+ function runProviderConformance(name, opts) {
50
+ const { describe, expect, it } = opts.testApi ?? getTestApi();
51
+ const { makeProvider, fixtures, expectPath } = opts;
52
+ describe(`provider conformance — ${name}`, () => {
53
+ it("declares honest, well-formed capabilities and identity", () => {
54
+ const provider = makeProvider({
55
+ fetch: jsonFetch(fixtures.text.body).fetch
56
+ });
57
+ expect(typeof provider.name).toBe("string");
58
+ expect(provider.name.length).toBeGreaterThan(0);
59
+ expect(typeof provider.defaultModel).toBe("string");
60
+ expect(provider.defaultModel.length).toBeGreaterThan(0);
61
+ const caps = provider.capabilities;
62
+ for (const flag of [
63
+ caps.tools,
64
+ caps.streaming,
65
+ caps.multimodalInput,
66
+ caps.parallelToolCalls,
67
+ caps.structuredOutput
68
+ ]) {
69
+ expect(typeof flag).toBe("boolean");
70
+ }
71
+ if (fixtures.stream !== undefined)
72
+ expect(caps.streaming).toBe(true);
73
+ });
74
+ it("completes a buffered turn into normalized text", async () => {
75
+ const { fetch, calls } = jsonFetch(fixtures.text.body);
76
+ const result = await makeProvider({ fetch }).complete(REQUEST);
77
+ expect(calls.length).toBeGreaterThan(0);
78
+ if (expectPath !== undefined)
79
+ expect(calls[0]?.url).toContain(expectPath);
80
+ expect(result.finishReason).toBe("stop");
81
+ expect(typeof result.model).toBe("string");
82
+ const text = result.message.content.filter((p) => p.type === "text").map((p) => p.text).join("");
83
+ expect(text).toBe(fixtures.text.expectText);
84
+ });
85
+ it("surfaces a tool call with an id, name, and parsed arguments", async () => {
86
+ const result = await makeProvider({
87
+ fetch: jsonFetch(fixtures.toolCall.body).fetch
88
+ }).complete(REQUEST);
89
+ expect(result.finishReason).toBe("tool_calls");
90
+ expect(result.toolCalls.length).toBeGreaterThan(0);
91
+ const call = result.toolCalls[0];
92
+ expect(typeof call.id).toBe("string");
93
+ expect(call.id.length).toBeGreaterThan(0);
94
+ expect(call.name).toBe(fixtures.toolCall.expectName);
95
+ if (fixtures.toolCall.expectArgs !== undefined) {
96
+ expect(call.arguments).toEqual(fixtures.toolCall.expectArgs);
97
+ }
98
+ expect(result.message.content.some((p) => p.type === "tool_call")).toBe(true);
99
+ });
100
+ it("normalizes token usage", async () => {
101
+ const result = await makeProvider({
102
+ fetch: jsonFetch(fixtures.usage.body).fetch
103
+ }).complete(REQUEST);
104
+ expect(result.usage).toBeDefined();
105
+ expect(result.usage?.inputTokens).toBe(fixtures.usage.expect.inputTokens);
106
+ expect(result.usage?.outputTokens).toBe(fixtures.usage.expect.outputTokens);
107
+ expect(result.usage?.totalTokens).toBe(fixtures.usage.expect.totalTokens);
108
+ });
109
+ it("maps a non-2xx response to a ProviderError", async () => {
110
+ const provider = makeProvider({
111
+ fetch: jsonFetch({ error: "boom" }, { status: 500 }).fetch,
112
+ resilience: NO_RETRY
113
+ });
114
+ await expect(provider.complete(REQUEST)).rejects.toBeInstanceOf(ProviderError);
115
+ });
116
+ const streamFixture = fixtures.stream;
117
+ if (streamFixture !== undefined) {
118
+ it("streams text deltas bracketed by message_start and finish", async () => {
119
+ const events = await collect(makeProvider({ fetch: sseFetch(streamFixture.sse).fetch }).stream(REQUEST));
120
+ expect(events[0]?.type).toBe("message_start");
121
+ expect(events[events.length - 1]?.type).toBe("finish");
122
+ const text = events.filter((e) => e.type === "text_delta").map((e) => e.text).join("");
123
+ expect(text).toBe(streamFixture.expectText);
124
+ });
125
+ } else {
126
+ it.skip("streaming (no stream fixture provided)", () => {});
127
+ }
128
+ });
129
+ }
130
+ export {
131
+ runProviderConformance
132
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `@infinityi/engine-lib/testing` — in-memory helpers and assertions for tests.
3
+ *
4
+ * Phase 1 ships only the helpers relevant to the foundation layer;
5
+ * provider and session doubles arrive with Phases 2 and 5.
6
+ *
7
+ * @module
8
+ */
9
+ import type { Message } from "../messages/types";
10
+ import type { Schema } from "../schema/types";
11
+ import type { EngineContext } from "../runtime/types";
12
+ import type { CompletionRequest, CompletionResult, Provider, ProviderCapabilities, ToolCall, Usage } from "../providers/types";
13
+ import { InMemorySessionStore } from "../session/index";
14
+ import type { StreamEvent } from "../providers/stream";
15
+ /** Build a `Message[]` from arguments, for readable test fixtures. */
16
+ export declare function conversation(...messages: Message[]): Message[];
17
+ /** Options for {@link mockProvider}. */
18
+ export interface MockProviderOptions {
19
+ readonly name?: string;
20
+ readonly defaultModel?: string;
21
+ readonly capabilities?: Partial<ProviderCapabilities>;
22
+ /** Scripted buffered result (or a function of the request). */
23
+ readonly result?: CompletionResult | ((req: CompletionRequest) => CompletionResult);
24
+ /** Scripted stream events (or a function of the request). When omitted, a
25
+ * single text/finish stream is derived from {@link MockProviderOptions.result}. */
26
+ readonly events?: StreamEvent[] | ((req: CompletionRequest) => StreamEvent[]);
27
+ /** Called with every request, so tests can assert on what was sent. */
28
+ readonly onRequest?: (req: CompletionRequest, ctx?: EngineContext) => void;
29
+ }
30
+ /**
31
+ * A network-free {@link Provider} for contract tests and Phase-4 run-loop
32
+ * tests. Returns scripted results/streams and records every request.
33
+ */
34
+ export declare function mockProvider(opts?: MockProviderOptions): Provider;
35
+ /** Drain a {@link Provider.stream} into a {@link CompletionResult} for assertions. */
36
+ export declare function collectProviderStream(provider: Provider, req: CompletionRequest, ctx?: EngineContext): Promise<CompletionResult>;
37
+ /**
38
+ * Parse `input` with `schema`, returning the typed value. Throws (failing
39
+ * the test) with a readable message listing all issues if invalid.
40
+ */
41
+ export declare function expectValid<T>(schema: Schema<T>, input: unknown): T;
42
+ /** A recorded outbound call captured by a {@link RecordingFetch}. */
43
+ export interface RecordedCall {
44
+ readonly url: string;
45
+ readonly init?: RequestInit;
46
+ }
47
+ /** A fake `fetch` plus the list of calls it recorded. */
48
+ export interface RecordingFetch {
49
+ readonly fetch: typeof fetch;
50
+ readonly calls: RecordedCall[];
51
+ }
52
+ /** A `ReadableStream<Uint8Array>` that emits `chunks` (one enqueue each). */
53
+ export declare function byteStreamOf(...chunks: string[]): ReadableStream<Uint8Array>;
54
+ /**
55
+ * A `fetch` that returns a fixed JSON `body` / `status` and records every call,
56
+ * for driving a {@link Provider} adapter without a network.
57
+ */
58
+ export declare function jsonFetch(body: unknown, init?: {
59
+ status?: number;
60
+ }): RecordingFetch;
61
+ /**
62
+ * A `fetch` that returns a fixed `text/event-stream` body and records every
63
+ * call, for driving a {@link Provider} adapter's `stream()` without a network.
64
+ */
65
+ export declare function sseFetch(sse: string): RecordingFetch;
66
+ /**
67
+ * Build a buffered text {@link CompletionResult} (`finishReason: "stop"`) for
68
+ * scripting a {@link mockProvider} or {@link scriptedProvider} turn.
69
+ */
70
+ export declare function textResult(text: string, usage?: Usage): CompletionResult;
71
+ /**
72
+ * Build a tool-call {@link CompletionResult} (`finishReason: "tool_calls"`),
73
+ * mirroring each {@link ToolCall} as a `tool_call` message part.
74
+ */
75
+ export declare function toolCallResult(calls: ToolCall[], usage?: Usage): CompletionResult;
76
+ /**
77
+ * A {@link Provider} that returns each scripted result in turn; the last
78
+ * result repeats once the script is exhausted. Handy for multi-turn run-loop
79
+ * tests (e.g. a tool call followed by a final answer).
80
+ */
81
+ export declare function scriptedProvider(results: readonly CompletionResult[], opts?: MockProviderOptions): Provider;
82
+ export { InMemorySessionStore } from "../session/index";
83
+ /** Construct a fresh in-memory {@link SessionStore} double. */
84
+ export declare function inMemorySessionStore(): InMemorySessionStore;
@@ -0,0 +1,31 @@
1
+ import {
2
+ byteStreamOf,
3
+ collectProviderStream,
4
+ conversation,
5
+ expectValid,
6
+ inMemorySessionStore,
7
+ jsonFetch,
8
+ mockProvider,
9
+ scriptedProvider,
10
+ sseFetch,
11
+ textResult,
12
+ toolCallResult
13
+ } from "../index-dexgmwg6.js";
14
+ import {
15
+ InMemorySessionStore
16
+ } from "../index-vnby35rm.js";
17
+ import"../index-zfgr4xx3.js";
18
+ export {
19
+ toolCallResult,
20
+ textResult,
21
+ sseFetch,
22
+ scriptedProvider,
23
+ mockProvider,
24
+ jsonFetch,
25
+ inMemorySessionStore,
26
+ expectValid,
27
+ conversation,
28
+ collectProviderStream,
29
+ byteStreamOf,
30
+ InMemorySessionStore
31
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `defineTool` — the ergonomic constructor for {@link ToolDefinition}s.
3
+ *
4
+ * Application code should prefer this constructor over hand-writing
5
+ * `ToolDefinition` objects. The argument type `TArgs` is inferred from the
6
+ * parameter schema (`parameters: Schema<TArgs>`), so `execute` receives
7
+ * fully-typed, already-validated arguments with zero annotations.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { s } from "@infinityi/engine-lib/schema";
12
+ * import { defineTool } from "@infinityi/engine-lib/tools";
13
+ *
14
+ * const readFile = defineTool({
15
+ * name: "read_file",
16
+ * description: "Read a file from the workspace.",
17
+ * parameters: s.object({ path: s.string() }),
18
+ * execute: async ({ path }) => ({ ok: true, content: await workspace.read(path) }),
19
+ * });
20
+ * ```
21
+ *
22
+ * @module
23
+ */
24
+ import type { Schema } from "../schema/types";
25
+ import type { ToolContext, ToolDefinition, ToolResult } from "./types";
26
+ /** The shape passed to {@link defineTool}, with `execute` typed against the schema. */
27
+ export interface ToolSpec<TArgs> {
28
+ readonly name: string;
29
+ readonly description?: string;
30
+ readonly parameters: Schema<TArgs>;
31
+ execute(args: TArgs, ctx: ToolContext): ToolResult | Promise<ToolResult>;
32
+ }
33
+ /**
34
+ * Define a tool with argument types inferred from its parameter schema.
35
+ *
36
+ * Pure: validates that `name` is non-empty and returns a frozen
37
+ * {@link ToolDefinition}. No execution or registration happens here. Return
38
+ * `{ ok: false, error }` for expected/domain failures; reserve thrown errors
39
+ * for unexpected implementation faults. `runAgent` isolates both forms as tool
40
+ * results so one bad tool call does not crash the whole run.
41
+ */
42
+ export declare function defineTool<TArgs>(spec: ToolSpec<TArgs>): ToolDefinition<TArgs>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `@infinityi/engine-lib/tools` — the tool contract: declarative tool definitions, the
3
+ * structured tool-result model, and the mappers that connect tools to the
4
+ * conversation model (Phase 1) and the provider toolset (Phase 2).
5
+ *
6
+ * @module
7
+ */
8
+ export { defineTool } from "./define";
9
+ export type { ToolSpec } from "./define";
10
+ export { renderToolContent, toProviderTool, toToolResultMessage } from "./result";
11
+ export type { ToolContext, ToolDefinition, ToolFailure, ToolResult, ToolSuccess, } from "./types";
@@ -0,0 +1,15 @@
1
+ import {
2
+ defineTool
3
+ } from "../index-w34cbktd.js";
4
+ import {
5
+ renderToolContent,
6
+ toProviderTool,
7
+ toToolResultMessage
8
+ } from "../index-rentvdpp.js";
9
+ import"../index-1p6mb2vz.js";
10
+ export {
11
+ toToolResultMessage,
12
+ toProviderTool,
13
+ renderToolContent,
14
+ defineTool
15
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Normalization between the Phase-3 tool layer and its neighbours.
3
+ *
4
+ * Two directions:
5
+ * - {@link renderToolContent} / {@link toToolResultMessage} map a
6
+ * {@link ToolResult} *down* into the Phase-1 conversation model (a
7
+ * `role: "tool"` {@link Message}), reusing the `toolResult` factory.
8
+ * - {@link toProviderTool} maps a {@link ToolDefinition} *across* into the
9
+ * Phase-2 {@link ProviderTool} the provider advertises to the model.
10
+ *
11
+ * @module
12
+ */
13
+ import type { Message, TextPart } from "../messages/types";
14
+ import type { ProviderTool } from "../providers/types";
15
+ import type { ToolDefinition, ToolResult } from "./types";
16
+ /**
17
+ * Render a {@link ToolResult}'s payload into `TextPart[]`.
18
+ *
19
+ * This rendering is part of the stable tool contract: strings pass through
20
+ * unchanged; `undefined`/`null` become empty text; any other value is
21
+ * JSON-encoded so structured results survive into the text-only Phase-1
22
+ * tool-result part.
23
+ */
24
+ export declare function renderToolContent(result: ToolResult): TextPart[];
25
+ /**
26
+ * Map a {@link ToolResult} into a Phase-1 tool-result {@link Message}.
27
+ *
28
+ * A failure is marked `isError: true` so the model sees a recoverable tool
29
+ * error rather than a crashed run.
30
+ */
31
+ export declare function toToolResultMessage(toolCallId: string, result: ToolResult): Message;
32
+ /**
33
+ * Project a {@link ToolDefinition} into the provider-facing {@link ProviderTool}
34
+ * (name + optional description + JSON-Schema parameters).
35
+ */
36
+ export declare function toProviderTool(tool: ToolDefinition): ProviderTool;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The tool contract — a named capability the model can invoke.
3
+ *
4
+ * A {@link ToolDefinition} couples a Phase-1 parameter {@link Schema} (used to
5
+ * advertise the tool to a provider and to validate model-supplied arguments at
6
+ * the boundary in Phase 4) with a typed `execute` function. Execution returns a
7
+ * structured {@link ToolResult}: a *successful* outcome carries content fed back
8
+ * to the model, while a *failed* outcome is surfaced to the model as a tool
9
+ * error rather than thrown — bad arguments and tool failures become data the
10
+ * model can recover from, not unhandled exceptions. The stable application
11
+ * path is {@link defineTool}; hand-written {@link ToolDefinition} objects are
12
+ * mainly useful for adapters and tests.
13
+ *
14
+ * @module
15
+ */
16
+ import type { EngineContext } from "../runtime/types";
17
+ import type { Schema } from "../schema/types";
18
+ import type { RunBridge } from "../execution/types";
19
+ /**
20
+ * Per-invocation context handed to a tool's {@link ToolDefinition.execute}.
21
+ *
22
+ * Extends the shared {@link EngineContext} (telemetry / logger / cancellation
23
+ * signal) with call-correlation metadata. Only the owning agent's *name* is
24
+ * exposed — not the agent itself — so tools never carry a forward dependency on
25
+ * the agent module.
26
+ */
27
+ export interface ToolContext extends EngineContext {
28
+ /** Correlates this invocation to the model's `ToolCall.id`. */
29
+ readonly toolCallId: string;
30
+ /** Name of the agent that owns this tool, when run inside one. */
31
+ readonly agentName?: string;
32
+ /**
33
+ * Bridge to the surrounding run, present when the tool is dispatched by the
34
+ * Phase-4 loop. Lets a tool forward nested events and report token usage to
35
+ * the parent run (used by sub-agent-as-tool); absent when a tool is invoked
36
+ * outside `runAgent`.
37
+ */
38
+ readonly run?: RunBridge;
39
+ }
40
+ /**
41
+ * A successful tool outcome.
42
+ *
43
+ * `content` is rendered to text for the model: strings pass through unchanged;
44
+ * any other value is JSON-encoded (see `renderToolContent`).
45
+ */
46
+ export interface ToolSuccess {
47
+ readonly ok: true;
48
+ readonly content: unknown;
49
+ }
50
+ /**
51
+ * A failed tool outcome for expected/domain failures such as "file not found"
52
+ * or "permission denied". Surfaced to the model as a tool error (an `isError`
53
+ * tool-result message), never thrown out of the run loop.
54
+ *
55
+ * `error` is intentionally a string in the stable contract. If a tool needs to
56
+ * return structured diagnostics, encode them in the string or return a
57
+ * structured success payload with an application-level status.
58
+ */
59
+ export interface ToolFailure {
60
+ readonly ok: false;
61
+ readonly error: string;
62
+ }
63
+ /** The structured result of a tool invocation, discriminated on `ok`. */
64
+ export type ToolResult = ToolSuccess | ToolFailure;
65
+ /**
66
+ * A named capability the model can invoke.
67
+ *
68
+ * `TArgs` is the validated argument type; with {@link defineTool} it is inferred
69
+ * from `parameters` so `execute` receives fully-typed arguments.
70
+ */
71
+ export interface ToolDefinition<TArgs = unknown> {
72
+ readonly name: string;
73
+ readonly description?: string;
74
+ /** Parameter schema: `.jsonSchema` is advertised to the provider, `.parse` validates args. */
75
+ readonly parameters: Schema<TArgs>;
76
+ /**
77
+ * Run the tool with already-validated arguments.
78
+ *
79
+ * Return `{ ok: true, content }` for success and `{ ok: false, error }` for
80
+ * expected/domain failures. Throw only for unexpected implementation faults;
81
+ * `runAgent` catches thrown errors and feeds them back to the model as
82
+ * recoverable tool-result errors.
83
+ */
84
+ execute(args: TArgs, ctx: ToolContext): ToolResult | Promise<ToolResult>;
85
+ }
package/docs/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # engine-lib documentation
2
+
3
+ - **Guide & concepts:** [`../README.md`](../README.md) - project goal, design principles, and annotated usage scenarios.
4
+ - **Runnable examples:** [`../examples/`](../examples/) - small, offline programs you can run with `bun`.
5
+
6
+ ## API reference
7
+
8
+ The full API reference is generated from source doc-comments with
9
+ [TypeDoc](https://typedoc.org):
10
+
11
+ ```bash
12
+ bun run docs # writes HTML to docs/api/ (git-ignored)
13
+ ```
14
+
15
+ Then open `docs/api/index.html`. Configuration lives in `typedoc.json` in the
16
+ source repository; the documented entry points are the public import surfaces:
17
+
18
+ | Import | Module |
19
+ | --- | --- |
20
+ | `@infinityi/engine-lib` | stable root barrel: schemas, messages, errors, provider factories, tools, agents, execution, sessions, context helpers, event subscribers |
21
+ | `@infinityi/engine-lib/schema` | schema builder, JSON Schema export, and validation helpers |
22
+ | `@infinityi/engine-lib/messages` | provider-neutral message and content helpers |
23
+ | `@infinityi/engine-lib/errors` | public error taxonomy |
24
+ | `@infinityi/engine-lib/runtime` | Forge secret and telemetry integration helpers |
25
+ | `@infinityi/engine-lib/providers` | provider contracts, built-in provider factories, and advanced adapter/HTTP/SSE helpers |
26
+ | `@infinityi/engine-lib/tools` | tool definitions and tool-result mapping helpers |
27
+ | `@infinityi/engine-lib/agent` | agent definitions, registries, handoffs, and sub-agent-as-tool helpers |
28
+ | `@infinityi/engine-lib/execution` | `runAgent` and run result/event types |
29
+ | `@infinityi/engine-lib/session` | session handles and session store contract |
30
+ | `@infinityi/engine-lib/context` | context providers and context-window strategies |
31
+ | `@infinityi/engine-lib/events` | event hub, subscribers, event projection helpers, and telemetry bridge |
32
+ | `@infinityi/engine-lib/lifecycle` | Forge lifecycle adapter (`agentRuntimeComponent`) |
33
+ | `@infinityi/engine-lib/testing` | network-free test doubles (`mockProvider`, `scriptedProvider`, `textResult`, `toolCallResult`, `jsonFetch`/`sseFetch`, `inMemorySessionStore`) |
34
+ | `@infinityi/engine-lib/testing/conformance` | the provider conformance battery (`runProviderConformance`) |
35
+
36
+ The output is intentionally not committed; regenerate it locally or in CI.
@@ -0,0 +1,24 @@
1
+ # Examples
2
+
3
+ Small, self-contained programs demonstrating the engine-lib API. Each one runs
4
+ **offline** with `bun` — they use the network-free test doubles from
5
+ `@infinityi/engine-lib/testing` (scripted providers) so no API key is required.
6
+
7
+ ```bash
8
+ bun examples/incident-analysis.ts # context injection + a read-only tool
9
+ bun examples/terminal-coder.ts # streaming tokens + a persisted session
10
+ bun examples/multi-agent.ts # handoff/delegation + sub-agent-as-tool
11
+ bun examples/lifecycle.ts # forge.boot with agentRuntimeComponent
12
+ ```
13
+
14
+ | File | Demonstrates |
15
+ | --- | --- |
16
+ | [`incident-analysis.ts`](./incident-analysis.ts) | `defineAgent` + `defineTool`, `staticContext` injection, `onEvent` tool-call observation |
17
+ | [`terminal-coder.ts`](./terminal-coder.ts) | streaming (`stream: true`) `token` events, `createSession` history persistence |
18
+ | [`multi-agent.ts`](./multi-agent.ts) | Phase 7 — `handoffs` / `transfer_to_<name>` and `asTool(agent)` |
19
+ | [`lifecycle.ts`](./lifecycle.ts) | Phase 8 — `agentRuntimeComponent` start/healthcheck/stop under `forge.boot` |
20
+
21
+ > The examples import from `@infinityi/engine-lib`, so they work both inside this
22
+ > repository and from the published package. In your own application, pass a real
23
+ > provider, e.g. `createOpenAI({ apiKey, model })` or
24
+ > `createAnthropic({ apiKey, model })`.