@infinityi/engine-lib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +488 -0
- package/dist/agent/agent-registry.d.ts +46 -0
- package/dist/agent/as-tool.d.ts +64 -0
- package/dist/agent/define.d.ts +35 -0
- package/dist/agent/handoff.d.ts +39 -0
- package/dist/agent/index.d.ts +20 -0
- package/dist/agent/index.js +38 -0
- package/dist/agent/registry.d.ts +27 -0
- package/dist/agent/types.d.ts +109 -0
- package/dist/context/index.d.ts +11 -0
- package/dist/context/index.js +21 -0
- package/dist/context/providers.d.ts +25 -0
- package/dist/context/types.d.ts +63 -0
- package/dist/context/window.d.ts +41 -0
- package/dist/errors.d.ts +93 -0
- package/dist/errors.js +24 -0
- package/dist/events/hub.d.ts +15 -0
- package/dist/events/index.d.ts +26 -0
- package/dist/events/index.js +24 -0
- package/dist/events/subscribers.d.ts +57 -0
- package/dist/events/telemetry.d.ts +61 -0
- package/dist/events/types.d.ts +39 -0
- package/dist/execution/index.d.ts +11 -0
- package/dist/execution/index.js +22 -0
- package/dist/execution/run.d.ts +35 -0
- package/dist/execution/types.d.ts +203 -0
- package/dist/execution/usage.d.ts +14 -0
- package/dist/index-02s1fjxr.js +226 -0
- package/dist/index-19pwq79t.js +0 -0
- package/dist/index-1p6mb2vz.js +32 -0
- package/dist/index-64tt9696.js +1796 -0
- package/dist/index-7690reng.js +96 -0
- package/dist/index-bqg01r42.js +354 -0
- package/dist/index-d4xz3abn.js +0 -0
- package/dist/index-dexgmwg6.js +148 -0
- package/dist/index-fkr3rcq9.js +97 -0
- package/dist/index-jg19te9v.js +0 -0
- package/dist/index-jp2b31xs.js +101 -0
- package/dist/index-jxgj4z08.js +68 -0
- package/dist/index-kte2h4k2.js +0 -0
- package/dist/index-pwr8179t.js +492 -0
- package/dist/index-rentvdpp.js +27 -0
- package/dist/index-vnby35rm.js +84 -0
- package/dist/index-w34cbktd.js +14 -0
- package/dist/index-xsv43c5j.js +39 -0
- package/dist/index-yrqrxwjt.js +148 -0
- package/dist/index-zfgr4xx3.js +90 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +117 -0
- package/dist/lifecycle/component.d.ts +74 -0
- package/dist/lifecycle/index.d.ts +12 -0
- package/dist/lifecycle/index.js +72 -0
- package/dist/messages/factory.d.ts +24 -0
- package/dist/messages/index.d.ts +8 -0
- package/dist/messages/index.js +17 -0
- package/dist/messages/types.d.ts +52 -0
- package/dist/providers/adapter.d.ts +42 -0
- package/dist/providers/anthropic/index.d.ts +31 -0
- package/dist/providers/anthropic/map.d.ts +12 -0
- package/dist/providers/anthropic/stream.d.ts +9 -0
- package/dist/providers/google/index.d.ts +29 -0
- package/dist/providers/google/map.d.ts +13 -0
- package/dist/providers/google/stream.d.ts +11 -0
- package/dist/providers/http.d.ts +61 -0
- package/dist/providers/index.d.ts +32 -0
- package/dist/providers/index.js +35 -0
- package/dist/providers/openai/index.d.ts +34 -0
- package/dist/providers/openai/map.d.ts +10 -0
- package/dist/providers/openai/stream.d.ts +9 -0
- package/dist/providers/openai-compatible/index.d.ts +37 -0
- package/dist/providers/openai-compatible/map.d.ts +13 -0
- package/dist/providers/openai-compatible/stream.d.ts +11 -0
- package/dist/providers/shared.d.ts +34 -0
- package/dist/providers/sse.d.ts +19 -0
- package/dist/providers/stream.d.ts +69 -0
- package/dist/providers/types.d.ts +137 -0
- package/dist/runtime/index.d.ts +11 -0
- package/dist/runtime/index.js +11 -0
- package/dist/runtime/secret.d.ts +12 -0
- package/dist/runtime/types.d.ts +27 -0
- package/dist/schema/builder.d.ts +70 -0
- package/dist/schema/index.d.ts +13 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/json-schema.d.ts +19 -0
- package/dist/schema/types.d.ts +70 -0
- package/dist/schema/validate.d.ts +19 -0
- package/dist/session/index.d.ts +11 -0
- package/dist/session/index.js +8 -0
- package/dist/session/session.d.ts +31 -0
- package/dist/session/store.d.ts +20 -0
- package/dist/session/types.d.ts +55 -0
- package/dist/testing/conformance.d.ts +106 -0
- package/dist/testing/conformance.js +132 -0
- package/dist/testing/index.d.ts +84 -0
- package/dist/testing/index.js +31 -0
- package/dist/tools/define.d.ts +42 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/result.d.ts +36 -0
- package/dist/tools/types.d.ts +85 -0
- package/docs/README.md +36 -0
- package/examples/README.md +24 -0
- package/examples/incident-analysis.ts +100 -0
- package/examples/lifecycle.ts +53 -0
- package/examples/multi-agent.ts +93 -0
- package/examples/terminal-coder.ts +80 -0
- package/package.json +114 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `forge/telemetry` bridge for agent runs.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the run loop in spans and metrics expressed entirely in terms of
|
|
5
|
+
* forge's `Telemetry` handle — engine-lib never invents its own observability
|
|
6
|
+
* surface. The stable application path is passing `telemetry` to `runAgent`.
|
|
7
|
+
* `createRunTelemetry`, span constants, and telemetry handle types are exported
|
|
8
|
+
* from `@infinityi/engine-lib/events` for advanced integrations and tests. Everything here
|
|
9
|
+
* is a no-op when no telemetry handle (or no tracer/meter) is supplied, so the
|
|
10
|
+
* library stays usable with zero wiring.
|
|
11
|
+
*
|
|
12
|
+
* Spans:
|
|
13
|
+
* - {@link SPAN_RUN} `agent.run` — one per {@link runAgent} call.
|
|
14
|
+
* - {@link SPAN_PROVIDER} `agent.provider.call` — one per provider turn.
|
|
15
|
+
* - {@link SPAN_TOOL} `agent.tool.execute` — one per tool execution.
|
|
16
|
+
*
|
|
17
|
+
* In buffered mode the run span is opened with `tracer.withSpan`, so it is the
|
|
18
|
+
* active context for the whole loop and the provider/tool spans (and the
|
|
19
|
+
* provider's own `tracedFetch` HTTP span) nest underneath it. In streaming mode
|
|
20
|
+
* the run span is opened with `tracer.startSpan` and ended on completion;
|
|
21
|
+
* child spans are still emitted but not nested under it.
|
|
22
|
+
*
|
|
23
|
+
* Metric names emitted by this bridge: `agent.run.duration` /
|
|
24
|
+
* `agent.tool.duration` (histograms, ms), `agent.tokens` (counter, tagged
|
|
25
|
+
* `token.type`), and `agent.runs` (counter, tagged outcome).
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import type { Usage } from "../providers/types";
|
|
30
|
+
import type { TelemetryHandle } from "../runtime/types";
|
|
31
|
+
/** Span name for the whole run. */
|
|
32
|
+
export declare const SPAN_RUN = "agent.run";
|
|
33
|
+
/** Span name for a single provider turn. */
|
|
34
|
+
export declare const SPAN_PROVIDER = "agent.provider.call";
|
|
35
|
+
/** Span name for a single tool execution. */
|
|
36
|
+
export declare const SPAN_TOOL = "agent.tool.execute";
|
|
37
|
+
/** Attribute bag accepted by spans and metrics (forge's common subset). */
|
|
38
|
+
export type Attrs = Record<string, string | number | boolean>;
|
|
39
|
+
/** A started span the loop ends manually; a no-op when telemetry is absent. */
|
|
40
|
+
export interface SpanHandle {
|
|
41
|
+
setAttributes(attrs: Attrs): void;
|
|
42
|
+
ok(): void;
|
|
43
|
+
fail(message: string): void;
|
|
44
|
+
end(): void;
|
|
45
|
+
}
|
|
46
|
+
/** The telemetry surface the run loop drives. All methods are no-op safe. */
|
|
47
|
+
export interface RunTelemetry {
|
|
48
|
+
/** Run `fn` inside `name` as the active context (nests children). */
|
|
49
|
+
withSpan<T>(name: string, attrs: Attrs, fn: (span: SpanHandle) => Promise<T>): Promise<T>;
|
|
50
|
+
/** Start `name`, parented to the current active span; caller ends it. */
|
|
51
|
+
startSpan(name: string, attrs: Attrs): SpanHandle;
|
|
52
|
+
/** Record run-level duration, outcome, and token usage. */
|
|
53
|
+
recordRun(attrs: Attrs, durationMs: number, usage: Usage): void;
|
|
54
|
+
/** Record one tool execution's duration. */
|
|
55
|
+
recordTool(attrs: Attrs, durationMs: number): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build the {@link RunTelemetry} bridge from a forge telemetry handle.
|
|
59
|
+
* Returns a fully no-op implementation when no tracer/meter is available.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createRunTelemetry(telemetry?: TelemetryHandle): RunTelemetry;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-system contracts (Phase 6).
|
|
3
|
+
*
|
|
4
|
+
* A run already emits a {@link RunEvent} stream (Phase 4). Phase 6 adds the
|
|
5
|
+
* ability to attach **multiple independent subscribers** — UI streaming, an
|
|
6
|
+
* audit log, a metrics sink, a `forge/messaging` bridge — to a single run
|
|
7
|
+
* without coupling them to the runtime internals or to each other.
|
|
8
|
+
*
|
|
9
|
+
* A {@link RunSubscriber} is just a function of a {@link RunEvent}. Subscribers
|
|
10
|
+
* are dispatched by an {@link EventHub} (see {@link createEventHub}) in
|
|
11
|
+
* declaration order, awaited, and isolated: a subscriber that throws or rejects
|
|
12
|
+
* is reported but never aborts the run nor starves the other subscribers.
|
|
13
|
+
* `runAgent` registers `onEvent` first, then entries from `subscribers`.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import type { RunEvent } from "../execution/types";
|
|
18
|
+
/**
|
|
19
|
+
* An external observer of a run. May be sync or async; the hub awaits it so a
|
|
20
|
+
* slow subscriber applies back-pressure to the run (offload heavy work yourself
|
|
21
|
+
* if you don't want that).
|
|
22
|
+
*/
|
|
23
|
+
export type RunSubscriber = (event: RunEvent) => void | Promise<void>;
|
|
24
|
+
/** Fans one {@link RunEvent} out to every registered {@link RunSubscriber}. */
|
|
25
|
+
export interface EventHub {
|
|
26
|
+
/** Deliver `event` to all subscribers, in order, awaiting each. */
|
|
27
|
+
emit(event: RunEvent): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/** Options for {@link createEventHub}. */
|
|
30
|
+
export interface EventHubOptions {
|
|
31
|
+
/** Subscribers, dispatched in array order. `undefined` entries are ignored. */
|
|
32
|
+
readonly subscribers?: ReadonlyArray<RunSubscriber | undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* Invoked when a subscriber throws/rejects (the run continues regardless).
|
|
35
|
+
* If this callback throws, that failure is swallowed so isolation is
|
|
36
|
+
* preserved.
|
|
37
|
+
*/
|
|
38
|
+
readonly onSubscriberError?: (error: unknown, event: RunEvent, index: number) => void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@infinityi/engine-lib/execution` — the agent run loop.
|
|
3
|
+
*
|
|
4
|
+
* {@link runAgent} executes an {@link AgentDefinition} against its provider
|
|
5
|
+
* using provider-native tool calling, in buffered or streaming mode.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
export { DEFAULT_MAX_HANDOFFS, DEFAULT_MAX_STEPS, runAgent } from "./run";
|
|
10
|
+
export { addUsage, emptyUsage } from "./usage";
|
|
11
|
+
export type { AnyRunOptions, BufferedRunOptions, RunBridge, RunEvent, RunHandle, RunInput, RunOptions, RunResult, StreamingRunOptions, } from "./types";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import"../index-kte2h4k2.js";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_MAX_HANDOFFS,
|
|
4
|
+
DEFAULT_MAX_STEPS,
|
|
5
|
+
addUsage,
|
|
6
|
+
emptyUsage,
|
|
7
|
+
runAgent
|
|
8
|
+
} from "../index-pwr8179t.js";
|
|
9
|
+
import"../index-yrqrxwjt.js";
|
|
10
|
+
import"../index-fkr3rcq9.js";
|
|
11
|
+
import"../index-02s1fjxr.js";
|
|
12
|
+
import"../index-zfgr4xx3.js";
|
|
13
|
+
import"../index-7690reng.js";
|
|
14
|
+
import"../index-rentvdpp.js";
|
|
15
|
+
import"../index-1p6mb2vz.js";
|
|
16
|
+
export {
|
|
17
|
+
runAgent,
|
|
18
|
+
emptyUsage,
|
|
19
|
+
addUsage,
|
|
20
|
+
DEFAULT_MAX_STEPS,
|
|
21
|
+
DEFAULT_MAX_HANDOFFS
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `runAgent` — the provider-native agent execution loop.
|
|
3
|
+
*
|
|
4
|
+
* Each step sends the conversation + the agent's tool schemas to the provider,
|
|
5
|
+
* appends the assistant turn, and — if the model requested tools — validates
|
|
6
|
+
* each call's arguments against its Phase-1 schema, dispatches the tools **in
|
|
7
|
+
* parallel** with per-call error isolation, appends the results, and loops.
|
|
8
|
+
* The loop ends when the model answers without tool calls, the step budget is
|
|
9
|
+
* exhausted (`MaxStepsExceededError`), or the run is cancelled
|
|
10
|
+
* (`CancelledError`).
|
|
11
|
+
*
|
|
12
|
+
* Buffered and streaming modes share one core async generator
|
|
13
|
+
* ({@link executeAgent}); the buffered path drives it to completion, the
|
|
14
|
+
* streaming path exposes it as a {@link RunHandle}.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import type { AgentDefinition } from "../agent/types";
|
|
19
|
+
import type { AnyRunOptions, BufferedRunOptions, RunHandle, RunResult, StreamingRunOptions } from "./types";
|
|
20
|
+
/** Default cap on provider turns when {@link RunOptions.maxSteps} is omitted. */
|
|
21
|
+
export declare const DEFAULT_MAX_STEPS = 16;
|
|
22
|
+
/** Default cap on agent handoffs when {@link RunOptions.maxHandoffs} is omitted. */
|
|
23
|
+
export declare const DEFAULT_MAX_HANDOFFS = 8;
|
|
24
|
+
/**
|
|
25
|
+
* Run an agent to completion using the provider-native tool-calling loop.
|
|
26
|
+
*
|
|
27
|
+
* Buffered mode (default, or `stream: false`) resolves with a {@link RunResult}.
|
|
28
|
+
* Streaming mode (`stream: true`) returns a {@link RunHandle} you can
|
|
29
|
+
* `for await` over for {@link RunEvent}s, with `handle.completed` resolving to
|
|
30
|
+
* the final result. When `stream` is a non-literal boolean, the return type is
|
|
31
|
+
* `Promise<RunResult> | RunHandle`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runAgent(agent: AgentDefinition, opts?: BufferedRunOptions): Promise<RunResult>;
|
|
34
|
+
export declare function runAgent(agent: AgentDefinition, opts: StreamingRunOptions): RunHandle;
|
|
35
|
+
export declare function runAgent(agent: AgentDefinition, opts: AnyRunOptions): Promise<RunResult> | RunHandle;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the agent execution flow.
|
|
3
|
+
*
|
|
4
|
+
* {@link runAgent} drives the provider-native tool-calling loop. It runs in two
|
|
5
|
+
* shapes that share one core: *buffered* (returns a {@link RunResult}) and
|
|
6
|
+
* *streaming* (returns a {@link RunHandle} — an async-iterable of
|
|
7
|
+
* {@link RunEvent}s plus a `completed` promise). Both modes also feed
|
|
8
|
+
* {@link RunOptions.onEvent} and {@link RunOptions.subscribers}.
|
|
9
|
+
*
|
|
10
|
+
* `RunEvent["type"]` values are part of the public contract. Consumers should
|
|
11
|
+
* handle unknown future variants defensively if they want minor-version
|
|
12
|
+
* compatibility with newly added events.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import type { AgentError } from "../errors";
|
|
17
|
+
import type { Message } from "../messages/types";
|
|
18
|
+
import type { FinishReason, Usage } from "../providers/types";
|
|
19
|
+
import type { Logger, TelemetryHandle } from "../runtime/types";
|
|
20
|
+
import type { GenerationSettings } from "../agent/types";
|
|
21
|
+
import type { AgentRegistry } from "../agent/agent-registry";
|
|
22
|
+
import type { ContextProvider, ContextWindowOptions } from "../context/types";
|
|
23
|
+
import type { Session } from "../session/types";
|
|
24
|
+
import type { ToolResult } from "../tools/types";
|
|
25
|
+
import type { RunSubscriber } from "../events/types";
|
|
26
|
+
/** New input for a run: raw text (→ a user message), a single message, or several. */
|
|
27
|
+
export type RunInput = string | Message | Message[];
|
|
28
|
+
/**
|
|
29
|
+
* A single event emitted over the course of a run.
|
|
30
|
+
*
|
|
31
|
+
* Ordering guarantees:
|
|
32
|
+
*
|
|
33
|
+
* - Every run starts with `run.start`.
|
|
34
|
+
* - Provider assistant turns are emitted as `message` events.
|
|
35
|
+
* - Streaming text is emitted as `token` before the assistant `message` that
|
|
36
|
+
* contains the accumulated text.
|
|
37
|
+
* - Tool calls emit `tool.call`, then `tool.result`, then the corresponding
|
|
38
|
+
* tool-result `message`.
|
|
39
|
+
* - Successful runs end with `run.finish`; failed runs emit `error` and then
|
|
40
|
+
* reject/throw the same `AgentError`.
|
|
41
|
+
* - `agent.child` wraps events forwarded from sub-agent tools, and
|
|
42
|
+
* `agent.handoff` marks a control transfer between agents.
|
|
43
|
+
*/
|
|
44
|
+
export type RunEvent = {
|
|
45
|
+
readonly type: "run.start";
|
|
46
|
+
readonly agent: string;
|
|
47
|
+
} | {
|
|
48
|
+
readonly type: "message";
|
|
49
|
+
readonly message: Message;
|
|
50
|
+
} | {
|
|
51
|
+
readonly type: "token";
|
|
52
|
+
readonly delta: string;
|
|
53
|
+
} | {
|
|
54
|
+
readonly type: "tool.call";
|
|
55
|
+
readonly id: string;
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly arguments: unknown;
|
|
58
|
+
} | {
|
|
59
|
+
readonly type: "tool.result";
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly name: string;
|
|
62
|
+
readonly result: ToolResult;
|
|
63
|
+
} | {
|
|
64
|
+
readonly type: "run.finish";
|
|
65
|
+
readonly result: RunResult;
|
|
66
|
+
} | {
|
|
67
|
+
readonly type: "error";
|
|
68
|
+
readonly error: AgentError;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* An event from a nested child run (e.g. a sub-agent invoked through
|
|
72
|
+
* {@link RunBridge}). `agent` is the child's name and `depth` is its nesting
|
|
73
|
+
* level relative to this run (1 for a direct child). Parent subscribers that
|
|
74
|
+
* don't care about nesting can ignore this variant; those that do can recurse
|
|
75
|
+
* into `event`.
|
|
76
|
+
*/
|
|
77
|
+
| {
|
|
78
|
+
readonly type: "agent.child";
|
|
79
|
+
readonly agent: string;
|
|
80
|
+
readonly depth: number;
|
|
81
|
+
readonly event: RunEvent;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The active agent handed the run off to another agent (Phase 7). `from` and
|
|
85
|
+
* `to` are agent names. Emitted at the switch point; the message history is
|
|
86
|
+
* preserved across the handoff.
|
|
87
|
+
*/
|
|
88
|
+
| {
|
|
89
|
+
readonly type: "agent.handoff";
|
|
90
|
+
readonly from: string;
|
|
91
|
+
readonly to: string;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* A handle the run loop hands to a tool so it can participate in its parent run:
|
|
95
|
+
* forward nested {@link RunEvent}s onto the parent's event stream and fold
|
|
96
|
+
* nested token {@link Usage} into the parent run's aggregate. Used by
|
|
97
|
+
* sub-agent-as-tool (`asTool`) to propagate a child run's events and usage
|
|
98
|
+
* upward. Tools that ignore it behave exactly as before.
|
|
99
|
+
*/
|
|
100
|
+
export interface RunBridge {
|
|
101
|
+
/** Forward a nested event onto the parent run's event stream. */
|
|
102
|
+
emit(event: RunEvent): void;
|
|
103
|
+
/** Add token usage from nested work into the parent run's running total. */
|
|
104
|
+
reportUsage(usage: Usage): void;
|
|
105
|
+
}
|
|
106
|
+
/** Options for a single {@link runAgent} call. */
|
|
107
|
+
export interface RunOptions {
|
|
108
|
+
/** New input for this run. */
|
|
109
|
+
readonly input?: RunInput;
|
|
110
|
+
/** Prior conversation prepended before `input`. Ignored when `session` is set (history comes from the session). */
|
|
111
|
+
readonly messages?: Message[];
|
|
112
|
+
/** Durable conversation: history is read before the run and new messages appended after it. */
|
|
113
|
+
readonly session?: Session;
|
|
114
|
+
/** Context providers injected into the system layer at run time (after instructions, before history). */
|
|
115
|
+
readonly context?: readonly ContextProvider[];
|
|
116
|
+
/** Token budgeting for the messages sent to the provider (does not affect persisted/returned history). */
|
|
117
|
+
readonly contextWindow?: ContextWindowOptions;
|
|
118
|
+
/** Per-run generation overrides, merged over the agent's `generation` defaults. */
|
|
119
|
+
readonly generation?: GenerationSettings;
|
|
120
|
+
/** Cap on provider turns (model→tools→model cycles). Defaults to {@link DEFAULT_MAX_STEPS}. */
|
|
121
|
+
readonly maxSteps?: number;
|
|
122
|
+
/** Stream tokens + tool lifecycle. Omitted/`false` → buffered `Promise<RunResult>`. */
|
|
123
|
+
readonly stream?: boolean;
|
|
124
|
+
/** Event sink invoked for every {@link RunEvent}, in both buffered and streaming modes. */
|
|
125
|
+
readonly onEvent?: (event: RunEvent) => void;
|
|
126
|
+
/**
|
|
127
|
+
* Additional independent event subscribers (UI streaming, audit log, metrics,
|
|
128
|
+
* a `forge/messaging` bridge). Dispatched after {@link RunOptions.onEvent}, in
|
|
129
|
+
* order, awaited per event. A subscriber that throws/rejects is isolated — it
|
|
130
|
+
* neither aborts the run nor starves the others.
|
|
131
|
+
*/
|
|
132
|
+
readonly subscribers?: readonly RunSubscriber[];
|
|
133
|
+
/** Forge telemetry handle, threaded to provider calls / tools / hooks. */
|
|
134
|
+
readonly telemetry?: TelemetryHandle;
|
|
135
|
+
/** Structured logger (defaults to `telemetry.log`). */
|
|
136
|
+
readonly logger?: Logger;
|
|
137
|
+
/** Cancellation signal; halts the loop and in-flight provider/tool calls. */
|
|
138
|
+
readonly signal?: AbortSignal;
|
|
139
|
+
/**
|
|
140
|
+
* Cap on agent handoffs in a single run, to bound triage↔specialist
|
|
141
|
+
* ping-pong (Phase 7). Defaults to {@link DEFAULT_MAX_HANDOFFS}. Exceeding it
|
|
142
|
+
* throws {@link MaxHandoffsExceededError}.
|
|
143
|
+
*/
|
|
144
|
+
readonly maxHandoffs?: number;
|
|
145
|
+
/**
|
|
146
|
+
* Registry used to resolve string-named {@link AgentDefinition.handoffs}
|
|
147
|
+
* targets (Phase 7). Not needed when every handoff target is given directly
|
|
148
|
+
* as an {@link AgentDefinition}.
|
|
149
|
+
*/
|
|
150
|
+
readonly registry?: AgentRegistry;
|
|
151
|
+
}
|
|
152
|
+
/** Options for buffered mode. This is the default when `stream` is omitted. */
|
|
153
|
+
export type BufferedRunOptions = Omit<RunOptions, "stream"> & {
|
|
154
|
+
readonly stream?: false | undefined;
|
|
155
|
+
};
|
|
156
|
+
/** Options for streaming mode. */
|
|
157
|
+
export type StreamingRunOptions = Omit<RunOptions, "stream"> & {
|
|
158
|
+
readonly stream: true;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Options for callers that decide streaming dynamically. Passing this shape to
|
|
162
|
+
* `runAgent` returns `Promise<RunResult> | RunHandle`.
|
|
163
|
+
*/
|
|
164
|
+
export type AnyRunOptions = Omit<RunOptions, "stream"> & {
|
|
165
|
+
readonly stream?: boolean;
|
|
166
|
+
};
|
|
167
|
+
/** The buffered result of a completed run. */
|
|
168
|
+
export interface RunResult {
|
|
169
|
+
/** Concatenated text of the final assistant message. */
|
|
170
|
+
readonly output: string;
|
|
171
|
+
/** The final assistant message. */
|
|
172
|
+
readonly finalMessage: Message;
|
|
173
|
+
/** Full history: prior messages + input + every assistant turn + tool results. */
|
|
174
|
+
readonly messages: Message[];
|
|
175
|
+
/** Why the loop ended. */
|
|
176
|
+
readonly finishReason: FinishReason;
|
|
177
|
+
/** Number of provider turns taken. */
|
|
178
|
+
readonly steps: number;
|
|
179
|
+
/** Token usage aggregated across all turns (zeros when unreported). */
|
|
180
|
+
readonly usage: Usage;
|
|
181
|
+
/**
|
|
182
|
+
* Name of the agent that produced the final answer (Phase 7). Equals the
|
|
183
|
+
* agent passed to {@link runAgent} unless the run handed off to another.
|
|
184
|
+
*/
|
|
185
|
+
readonly agent: string;
|
|
186
|
+
/**
|
|
187
|
+
* Ordered trail of agent names the run handed off to (Phase 7). Empty when no
|
|
188
|
+
* handoff occurred; the final entry equals {@link RunResult.agent}.
|
|
189
|
+
*/
|
|
190
|
+
readonly handoffs: readonly string[];
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* A streaming run: an async-iterable of {@link RunEvent}s, plus `completed`
|
|
194
|
+
* which resolves with the final {@link RunResult} once the stream is fully
|
|
195
|
+
* consumed (the same result is also delivered as the `run.finish` event).
|
|
196
|
+
*
|
|
197
|
+
* If iteration throws, `completed` rejects with the same error. If the consumer
|
|
198
|
+
* abandons iteration before a terminal event, `completed` rejects with
|
|
199
|
+
* `CancelledError`.
|
|
200
|
+
*/
|
|
201
|
+
export type RunHandle = AsyncIterable<RunEvent> & {
|
|
202
|
+
readonly completed: Promise<RunResult>;
|
|
203
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage aggregation across the turns of a run.
|
|
3
|
+
*
|
|
4
|
+
* Provider {@link Usage} is reported per turn (and may be absent); the run loop
|
|
5
|
+
* folds each turn's usage into a running total so {@link RunResult.usage}
|
|
6
|
+
* reflects the whole run.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { Usage } from "../providers/types";
|
|
11
|
+
/** A zeroed {@link Usage} accumulator. */
|
|
12
|
+
export declare function emptyUsage(): Usage;
|
|
13
|
+
/** Field-wise sum of two {@link Usage} values; optional fields appear only when present. */
|
|
14
|
+
export declare function addUsage(a: Usage, b?: Usage): Usage;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SchemaValidationError
|
|
3
|
+
} from "./index-7690reng.js";
|
|
4
|
+
|
|
5
|
+
// src/schema/validate.ts
|
|
6
|
+
function issue(path, message) {
|
|
7
|
+
return { path: [...path], message };
|
|
8
|
+
}
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function validateJsonSchema(node, input, path = []) {
|
|
13
|
+
const issues = [];
|
|
14
|
+
if (node.enum !== undefined) {
|
|
15
|
+
if (!node.enum.includes(input)) {
|
|
16
|
+
issues.push(issue(path, `expected one of ${JSON.stringify(node.enum)}`));
|
|
17
|
+
}
|
|
18
|
+
return issues;
|
|
19
|
+
}
|
|
20
|
+
switch (node.type) {
|
|
21
|
+
case "string":
|
|
22
|
+
if (typeof input !== "string")
|
|
23
|
+
issues.push(issue(path, "expected string"));
|
|
24
|
+
break;
|
|
25
|
+
case "boolean":
|
|
26
|
+
if (typeof input !== "boolean")
|
|
27
|
+
issues.push(issue(path, "expected boolean"));
|
|
28
|
+
break;
|
|
29
|
+
case "null":
|
|
30
|
+
if (input !== null)
|
|
31
|
+
issues.push(issue(path, "expected null"));
|
|
32
|
+
break;
|
|
33
|
+
case "number":
|
|
34
|
+
if (typeof input !== "number" || Number.isNaN(input)) {
|
|
35
|
+
issues.push(issue(path, "expected number"));
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case "integer":
|
|
39
|
+
if (typeof input !== "number" || !Number.isInteger(input)) {
|
|
40
|
+
issues.push(issue(path, "expected integer"));
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case "array": {
|
|
44
|
+
if (!Array.isArray(input)) {
|
|
45
|
+
issues.push(issue(path, "expected array"));
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (node.items !== undefined) {
|
|
49
|
+
input.forEach((element, index) => {
|
|
50
|
+
issues.push(...validateJsonSchema(node.items, element, [...path, index]));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case "object": {
|
|
56
|
+
if (!isPlainObject(input)) {
|
|
57
|
+
issues.push(issue(path, "expected object"));
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
const properties = node.properties ?? {};
|
|
61
|
+
const required = node.required ?? [];
|
|
62
|
+
for (const key of required) {
|
|
63
|
+
if (input[key] === undefined) {
|
|
64
|
+
issues.push(issue([...path, key], "required"));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
68
|
+
const value = input[key];
|
|
69
|
+
if (value === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
issues.push(...validateJsonSchema(propSchema, value, [...path, key]));
|
|
72
|
+
}
|
|
73
|
+
if (node.additionalProperties === false) {
|
|
74
|
+
for (const key of Object.keys(input)) {
|
|
75
|
+
if (!(key in properties)) {
|
|
76
|
+
issues.push(issue([...path, key], "unexpected property"));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
return issues;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/schema/builder.ts
|
|
89
|
+
var optionalSchemas = new WeakSet;
|
|
90
|
+
function makeSchema(jsonSchema) {
|
|
91
|
+
return {
|
|
92
|
+
jsonSchema,
|
|
93
|
+
safeParse(input) {
|
|
94
|
+
const issues = validateJsonSchema(jsonSchema, input);
|
|
95
|
+
if (issues.length === 0)
|
|
96
|
+
return { success: true, data: input };
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: new SchemaValidationError("schema validation failed", { issues })
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
parse(input) {
|
|
103
|
+
const result = this.safeParse(input);
|
|
104
|
+
if (!result.success)
|
|
105
|
+
throw result.error;
|
|
106
|
+
return result.data;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
var s = {
|
|
111
|
+
string(opts) {
|
|
112
|
+
const node = { type: "string" };
|
|
113
|
+
if (opts?.description !== undefined)
|
|
114
|
+
node.description = opts.description;
|
|
115
|
+
if (opts?.enum !== undefined)
|
|
116
|
+
node.enum = opts.enum;
|
|
117
|
+
return makeSchema(node);
|
|
118
|
+
},
|
|
119
|
+
number(opts) {
|
|
120
|
+
const node = { type: opts?.int ? "integer" : "number" };
|
|
121
|
+
if (opts?.description !== undefined)
|
|
122
|
+
node.description = opts.description;
|
|
123
|
+
return makeSchema(node);
|
|
124
|
+
},
|
|
125
|
+
boolean(opts) {
|
|
126
|
+
const node = { type: "boolean" };
|
|
127
|
+
if (opts?.description !== undefined)
|
|
128
|
+
node.description = opts.description;
|
|
129
|
+
return makeSchema(node);
|
|
130
|
+
},
|
|
131
|
+
enum(values, opts) {
|
|
132
|
+
const node = { type: "string", enum: values };
|
|
133
|
+
if (opts?.description !== undefined)
|
|
134
|
+
node.description = opts.description;
|
|
135
|
+
return makeSchema(node);
|
|
136
|
+
},
|
|
137
|
+
array(item, opts) {
|
|
138
|
+
const node = { type: "array", items: item.jsonSchema };
|
|
139
|
+
if (opts?.description !== undefined)
|
|
140
|
+
node.description = opts.description;
|
|
141
|
+
return makeSchema(node);
|
|
142
|
+
},
|
|
143
|
+
object(props, opts) {
|
|
144
|
+
const properties = {};
|
|
145
|
+
const required = [];
|
|
146
|
+
for (const [key, prop] of Object.entries(props)) {
|
|
147
|
+
properties[key] = prop.jsonSchema;
|
|
148
|
+
if (!optionalSchemas.has(prop))
|
|
149
|
+
required.push(key);
|
|
150
|
+
}
|
|
151
|
+
const node = {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties,
|
|
154
|
+
additionalProperties: false
|
|
155
|
+
};
|
|
156
|
+
if (required.length > 0)
|
|
157
|
+
node.required = required;
|
|
158
|
+
if (opts?.description !== undefined)
|
|
159
|
+
node.description = opts.description;
|
|
160
|
+
return makeSchema(node);
|
|
161
|
+
},
|
|
162
|
+
optional(inner) {
|
|
163
|
+
const schema = {
|
|
164
|
+
jsonSchema: inner.jsonSchema,
|
|
165
|
+
safeParse(input) {
|
|
166
|
+
if (input === undefined)
|
|
167
|
+
return { success: true, data: undefined };
|
|
168
|
+
return inner.safeParse(input);
|
|
169
|
+
},
|
|
170
|
+
parse(input) {
|
|
171
|
+
const result = this.safeParse(input);
|
|
172
|
+
if (!result.success)
|
|
173
|
+
throw result.error;
|
|
174
|
+
return result.data;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
optionalSchemas.add(schema);
|
|
178
|
+
return schema;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// src/schema/json-schema.ts
|
|
182
|
+
function toJsonSchema(schema) {
|
|
183
|
+
return schema.jsonSchema;
|
|
184
|
+
}
|
|
185
|
+
function asSchema(impl) {
|
|
186
|
+
return {
|
|
187
|
+
jsonSchema: impl.jsonSchema,
|
|
188
|
+
parse: impl.parse.bind(impl),
|
|
189
|
+
safeParse: impl.safeParse?.bind(impl) ?? ((input) => {
|
|
190
|
+
try {
|
|
191
|
+
return { success: true, data: impl.parse(input) };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (error instanceof SchemaValidationError)
|
|
194
|
+
return { success: false, error };
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
error: new SchemaValidationError("schema validation failed", {
|
|
198
|
+
cause: error,
|
|
199
|
+
issues: [{ path: [], message: String(error) }]
|
|
200
|
+
})
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function fromJsonSchema(jsonSchema) {
|
|
207
|
+
return {
|
|
208
|
+
jsonSchema,
|
|
209
|
+
safeParse(input) {
|
|
210
|
+
const issues = validateJsonSchema(jsonSchema, input);
|
|
211
|
+
if (issues.length === 0)
|
|
212
|
+
return { success: true, data: input };
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
error: new SchemaValidationError("schema validation failed", { issues })
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
parse(input) {
|
|
219
|
+
const result = this.safeParse(input);
|
|
220
|
+
if (!result.success)
|
|
221
|
+
throw result.error;
|
|
222
|
+
return result.data;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
export { validateJsonSchema, s, toJsonSchema, asSchema, fromJsonSchema };
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/messages/factory.ts
|
|
2
|
+
function text(value) {
|
|
3
|
+
return { type: "text", text: value };
|
|
4
|
+
}
|
|
5
|
+
function normalizeContent(content) {
|
|
6
|
+
return typeof content === "string" ? [text(content)] : content;
|
|
7
|
+
}
|
|
8
|
+
function system(content) {
|
|
9
|
+
return { role: "system", content: normalizeContent(content) };
|
|
10
|
+
}
|
|
11
|
+
function user(content) {
|
|
12
|
+
return { role: "user", content: normalizeContent(content) };
|
|
13
|
+
}
|
|
14
|
+
function assistant(content) {
|
|
15
|
+
return { role: "assistant", content: normalizeContent(content) };
|
|
16
|
+
}
|
|
17
|
+
function toolResult(toolCallId, output, opts) {
|
|
18
|
+
const content = typeof output === "string" ? [text(output)] : output;
|
|
19
|
+
return {
|
|
20
|
+
role: "tool",
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "tool_result",
|
|
24
|
+
toolCallId,
|
|
25
|
+
content,
|
|
26
|
+
...opts?.isError !== undefined ? { isError: opts.isError } : {}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { text, normalizeContent, system, user, assistant, toolResult };
|