@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,96 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
class AgentError extends Error {
|
|
3
|
+
usage;
|
|
4
|
+
constructor(message, options) {
|
|
5
|
+
super(message, options);
|
|
6
|
+
this.name = "AgentError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class ProviderError extends AgentError {
|
|
11
|
+
provider;
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.name = "ProviderError";
|
|
15
|
+
if (options?.provider !== undefined)
|
|
16
|
+
this.provider = options.provider;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class ToolError extends AgentError {
|
|
21
|
+
toolName;
|
|
22
|
+
constructor(message, options) {
|
|
23
|
+
super(message, options);
|
|
24
|
+
this.name = "ToolError";
|
|
25
|
+
if (options?.toolName !== undefined)
|
|
26
|
+
this.toolName = options.toolName;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class SchemaValidationError extends AgentError {
|
|
31
|
+
issues;
|
|
32
|
+
constructor(message, options) {
|
|
33
|
+
super(message, options);
|
|
34
|
+
this.name = "SchemaValidationError";
|
|
35
|
+
this.issues = options.issues;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class ToolValidationError extends SchemaValidationError {
|
|
40
|
+
toolName;
|
|
41
|
+
constructor(message, options) {
|
|
42
|
+
super(message, options);
|
|
43
|
+
this.name = "ToolValidationError";
|
|
44
|
+
if (options?.toolName !== undefined)
|
|
45
|
+
this.toolName = options.toolName;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class ExecutionError extends AgentError {
|
|
50
|
+
constructor(message, options) {
|
|
51
|
+
super(message, options);
|
|
52
|
+
this.name = "ExecutionError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class MaxStepsExceededError extends ExecutionError {
|
|
57
|
+
steps;
|
|
58
|
+
constructor(message, options) {
|
|
59
|
+
super(message, options);
|
|
60
|
+
this.name = "MaxStepsExceededError";
|
|
61
|
+
if (options?.steps !== undefined)
|
|
62
|
+
this.steps = options.steps;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class MaxHandoffsExceededError extends ExecutionError {
|
|
67
|
+
handoffs;
|
|
68
|
+
constructor(message, options) {
|
|
69
|
+
super(message, options);
|
|
70
|
+
this.name = "MaxHandoffsExceededError";
|
|
71
|
+
if (options?.handoffs !== undefined)
|
|
72
|
+
this.handoffs = options.handoffs;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class CancelledError extends AgentError {
|
|
77
|
+
constructor(message, options) {
|
|
78
|
+
super(message, options);
|
|
79
|
+
this.name = "CancelledError";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class ContextWindowError extends AgentError {
|
|
84
|
+
tokens;
|
|
85
|
+
limit;
|
|
86
|
+
constructor(message, options) {
|
|
87
|
+
super(message, options);
|
|
88
|
+
this.name = "ContextWindowError";
|
|
89
|
+
if (options?.tokens !== undefined)
|
|
90
|
+
this.tokens = options.tokens;
|
|
91
|
+
if (options?.limit !== undefined)
|
|
92
|
+
this.limit = options.limit;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { AgentError, ProviderError, ToolError, SchemaValidationError, ToolValidationError, ExecutionError, MaxStepsExceededError, MaxHandoffsExceededError, CancelledError, ContextWindowError };
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
// node_modules/@infinityi/forge/dist/index-ft2g12w9.js
|
|
2
|
+
var realClock = {
|
|
3
|
+
now() {
|
|
4
|
+
return Date.now();
|
|
5
|
+
},
|
|
6
|
+
sleep(ms, signal) {
|
|
7
|
+
if (signal?.aborted) {
|
|
8
|
+
return Promise.reject(signal.reason);
|
|
9
|
+
}
|
|
10
|
+
if (ms <= 0) {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const timer = setTimeout(() => {
|
|
15
|
+
signal?.removeEventListener("abort", onAbort);
|
|
16
|
+
resolve();
|
|
17
|
+
}, ms);
|
|
18
|
+
const onAbort = () => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
reject(signal.reason);
|
|
21
|
+
};
|
|
22
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// node_modules/@infinityi/forge/dist/index-sw7jeh00.js
|
|
28
|
+
class ResilienceError extends Error {
|
|
29
|
+
constructor(message, options) {
|
|
30
|
+
super(message, options);
|
|
31
|
+
this.name = "ResilienceError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// node_modules/@infinityi/forge/dist/index-f41wfqnm.js
|
|
36
|
+
function buildInstruments(telemetry) {
|
|
37
|
+
const meter = telemetry?.meter;
|
|
38
|
+
const tracer = telemetry?.tracer;
|
|
39
|
+
let attempts;
|
|
40
|
+
let retries;
|
|
41
|
+
let timeouts;
|
|
42
|
+
let circuitState;
|
|
43
|
+
let bulkheadQueueSize;
|
|
44
|
+
return {
|
|
45
|
+
attempts() {
|
|
46
|
+
if (!meter)
|
|
47
|
+
return;
|
|
48
|
+
attempts ??= meter.createCounter("forge_resilience_attempts_total", {
|
|
49
|
+
description: "Total execution attempts (including retries).",
|
|
50
|
+
unit: "1"
|
|
51
|
+
});
|
|
52
|
+
return attempts;
|
|
53
|
+
},
|
|
54
|
+
retries() {
|
|
55
|
+
if (!meter)
|
|
56
|
+
return;
|
|
57
|
+
retries ??= meter.createCounter("forge_resilience_retries_total", {
|
|
58
|
+
description: "Total retry attempts triggered.",
|
|
59
|
+
unit: "1"
|
|
60
|
+
});
|
|
61
|
+
return retries;
|
|
62
|
+
},
|
|
63
|
+
timeouts() {
|
|
64
|
+
if (!meter)
|
|
65
|
+
return;
|
|
66
|
+
timeouts ??= meter.createCounter("forge_resilience_timeout_total", {
|
|
67
|
+
description: "Total operations aborted due to timeout.",
|
|
68
|
+
unit: "1"
|
|
69
|
+
});
|
|
70
|
+
return timeouts;
|
|
71
|
+
},
|
|
72
|
+
circuitState() {
|
|
73
|
+
if (!meter)
|
|
74
|
+
return;
|
|
75
|
+
circuitState ??= meter.createGauge("forge_resilience_circuit_state", {
|
|
76
|
+
description: "Current circuit-breaker state (0=Closed, 1=HalfOpen, 2=Open).",
|
|
77
|
+
unit: "1"
|
|
78
|
+
});
|
|
79
|
+
return circuitState;
|
|
80
|
+
},
|
|
81
|
+
bulkheadQueueSize() {
|
|
82
|
+
if (!meter)
|
|
83
|
+
return;
|
|
84
|
+
bulkheadQueueSize ??= meter.createGauge("forge_resilience_bulkhead_queue_size", {
|
|
85
|
+
description: "Current number of tasks waiting in a bulkhead queue.",
|
|
86
|
+
unit: "1"
|
|
87
|
+
});
|
|
88
|
+
return bulkheadQueueSize;
|
|
89
|
+
},
|
|
90
|
+
addEvent(name, attributes) {
|
|
91
|
+
if (!tracer)
|
|
92
|
+
return;
|
|
93
|
+
const span = tracer.startSpan(name, attributes ? { attributes } : undefined);
|
|
94
|
+
span.end();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// node_modules/@infinityi/forge/dist/index-yh6c6ebj.js
|
|
100
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
101
|
+
var INVALID_TRACE_ID = "0".repeat(32);
|
|
102
|
+
var INVALID_SPAN_ID = "0".repeat(16);
|
|
103
|
+
var contextStorage = new AsyncLocalStorage;
|
|
104
|
+
function currentContext() {
|
|
105
|
+
return contextStorage.getStore();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// node_modules/@infinityi/forge/dist/index-0jqvt0fs.js
|
|
109
|
+
function buildRootContext() {
|
|
110
|
+
const controller = new AbortController;
|
|
111
|
+
const telemetry = currentContext();
|
|
112
|
+
const context = telemetry ? { signal: controller.signal, attempt: 1, context: telemetry } : { signal: controller.signal, attempt: 1 };
|
|
113
|
+
return { context, controller };
|
|
114
|
+
}
|
|
115
|
+
function withExecutionContext(base, overrides) {
|
|
116
|
+
const next = {
|
|
117
|
+
signal: overrides.signal ?? base.signal,
|
|
118
|
+
attempt: overrides.attempt ?? base.attempt
|
|
119
|
+
};
|
|
120
|
+
const telemetry = "context" in overrides ? overrides.context : base.context;
|
|
121
|
+
if (telemetry !== undefined) {
|
|
122
|
+
next.context = telemetry;
|
|
123
|
+
}
|
|
124
|
+
return next;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// node_modules/@infinityi/forge/dist/index-br4e2rnq.js
|
|
128
|
+
function ok(value) {
|
|
129
|
+
return {
|
|
130
|
+
ok: true,
|
|
131
|
+
value,
|
|
132
|
+
isOk() {
|
|
133
|
+
return true;
|
|
134
|
+
},
|
|
135
|
+
isErr() {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function err(error) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
error,
|
|
144
|
+
isOk() {
|
|
145
|
+
return false;
|
|
146
|
+
},
|
|
147
|
+
isErr() {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function combine(...policies) {
|
|
153
|
+
return createPipeline(policies);
|
|
154
|
+
}
|
|
155
|
+
function createPipeline(policies) {
|
|
156
|
+
function buildChain(op) {
|
|
157
|
+
let chain = op;
|
|
158
|
+
for (let i = policies.length - 1;i >= 0; i--) {
|
|
159
|
+
const policy = policies[i];
|
|
160
|
+
const inner = chain;
|
|
161
|
+
chain = (ctx) => policy.execute(inner, ctx);
|
|
162
|
+
}
|
|
163
|
+
return chain;
|
|
164
|
+
}
|
|
165
|
+
async function execute(op) {
|
|
166
|
+
const { context } = buildRootContext();
|
|
167
|
+
return buildChain(op)(context);
|
|
168
|
+
}
|
|
169
|
+
async function executeResult(op) {
|
|
170
|
+
try {
|
|
171
|
+
const value = await execute(op);
|
|
172
|
+
return ok(value);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error instanceof ResilienceError)
|
|
175
|
+
return err(error);
|
|
176
|
+
return err(new ResilienceError("operation failed", { cause: error }));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { execute, executeResult };
|
|
180
|
+
}
|
|
181
|
+
function constantBackoff(delayMs) {
|
|
182
|
+
const ms = Math.max(0, delayMs);
|
|
183
|
+
return { delay: () => ms };
|
|
184
|
+
}
|
|
185
|
+
function exponentialBackoff(options) {
|
|
186
|
+
const initial = Math.max(0, options.initial);
|
|
187
|
+
const max = options.max !== undefined ? Math.max(initial, options.max) : Number.POSITIVE_INFINITY;
|
|
188
|
+
const factor = options.factor ?? 2;
|
|
189
|
+
const jitter = options.jitter ?? true;
|
|
190
|
+
return {
|
|
191
|
+
delay(attempt) {
|
|
192
|
+
const exponent = Math.max(0, attempt - 1);
|
|
193
|
+
const raw = Math.min(max, initial * Math.pow(factor, exponent));
|
|
194
|
+
return jitter ? applyFullJitter(raw) : raw;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function applyFullJitter(base) {
|
|
199
|
+
if (base <= 0)
|
|
200
|
+
return 0;
|
|
201
|
+
return Math.random() * base;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
class RetryExhaustedError extends ResilienceError {
|
|
205
|
+
attempts;
|
|
206
|
+
constructor(message, options) {
|
|
207
|
+
super(message, options);
|
|
208
|
+
this.name = "RetryExhaustedError";
|
|
209
|
+
this.attempts = options.attempts;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function retry(options) {
|
|
213
|
+
if (!Number.isInteger(options.maxAttempts) || options.maxAttempts < 1) {
|
|
214
|
+
throw new RangeError(`retry: maxAttempts must be an integer >= 1, got ${options.maxAttempts}`);
|
|
215
|
+
}
|
|
216
|
+
const maxAttempts = options.maxAttempts;
|
|
217
|
+
const backoff = options.backoff ?? constantBackoff(0);
|
|
218
|
+
const shouldRetry = options.shouldRetry ?? (() => true);
|
|
219
|
+
const retryOn = options.retryOn;
|
|
220
|
+
const clock = options.clock ?? realClock;
|
|
221
|
+
const instruments = buildInstruments(options.telemetry);
|
|
222
|
+
async function execute(next, ctx) {
|
|
223
|
+
let lastError;
|
|
224
|
+
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
225
|
+
if (ctx.signal.aborted) {
|
|
226
|
+
throw ctx.signal.reason;
|
|
227
|
+
}
|
|
228
|
+
const attemptCtx = attempt === ctx.attempt ? ctx : withExecutionContext(ctx, { attempt });
|
|
229
|
+
instruments.attempts()?.add(1, { policy: "retry" });
|
|
230
|
+
try {
|
|
231
|
+
const value = await next(attemptCtx);
|
|
232
|
+
if (retryOn && retryOn(value, attempt)) {
|
|
233
|
+
const synthetic = new Error("operation returned a retryable value");
|
|
234
|
+
synthetic.cause = value;
|
|
235
|
+
lastError = synthetic;
|
|
236
|
+
} else {
|
|
237
|
+
return value;
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
lastError = error;
|
|
241
|
+
if (!shouldRetry(error, attempt))
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
if (attempt >= maxAttempts)
|
|
245
|
+
break;
|
|
246
|
+
const delayMs = Math.max(0, backoff.delay(attempt));
|
|
247
|
+
instruments.retries()?.add(1, { policy: "retry" });
|
|
248
|
+
instruments.addEvent("resilience.retry.attempt", {
|
|
249
|
+
attempt_number: attempt,
|
|
250
|
+
delay_ms: delayMs,
|
|
251
|
+
error_message: lastError instanceof Error ? lastError.message : String(lastError)
|
|
252
|
+
});
|
|
253
|
+
if (delayMs > 0) {
|
|
254
|
+
await clock.sleep(delayMs, ctx.signal);
|
|
255
|
+
} else if (ctx.signal.aborted) {
|
|
256
|
+
throw ctx.signal.reason;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
throw new RetryExhaustedError(`retry: exhausted ${maxAttempts} attempt(s)`, { attempts: maxAttempts, cause: lastError });
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
name: "retry",
|
|
263
|
+
maxAttempts,
|
|
264
|
+
execute
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
class TimeoutError extends ResilienceError {
|
|
269
|
+
timeoutMs;
|
|
270
|
+
strategy;
|
|
271
|
+
constructor(message, options) {
|
|
272
|
+
super(message, options);
|
|
273
|
+
this.name = "TimeoutError";
|
|
274
|
+
this.timeoutMs = options.timeoutMs;
|
|
275
|
+
this.strategy = options.strategy;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function timeout(options) {
|
|
279
|
+
if (!Number.isFinite(options.ms) || options.ms < 0) {
|
|
280
|
+
throw new RangeError(`timeout: ms must be a finite non-negative number, got ${options.ms}`);
|
|
281
|
+
}
|
|
282
|
+
const ms = options.ms;
|
|
283
|
+
const strategy = options.strategy ?? "optimistic";
|
|
284
|
+
const clock = options.clock ?? realClock;
|
|
285
|
+
const instruments = buildInstruments(options.telemetry);
|
|
286
|
+
async function execute(next, ctx) {
|
|
287
|
+
const inner = new AbortController;
|
|
288
|
+
const onParentAbort = () => {
|
|
289
|
+
inner.abort(ctx.signal.reason);
|
|
290
|
+
};
|
|
291
|
+
if (ctx.signal.aborted) {
|
|
292
|
+
throw ctx.signal.reason;
|
|
293
|
+
}
|
|
294
|
+
ctx.signal.addEventListener("abort", onParentAbort, { once: true });
|
|
295
|
+
const childCtx = withExecutionContext(ctx, { signal: inner.signal });
|
|
296
|
+
const timerController = new AbortController;
|
|
297
|
+
let timedOut = false;
|
|
298
|
+
const timerPromise = clock.sleep(ms, timerController.signal).then(() => {
|
|
299
|
+
timedOut = true;
|
|
300
|
+
const err2 = new TimeoutError(`timeout: operation exceeded ${ms}ms`, { timeoutMs: ms, strategy });
|
|
301
|
+
inner.abort(err2);
|
|
302
|
+
instruments.timeouts()?.add(1, { policy: "timeout", strategy });
|
|
303
|
+
instruments.addEvent("resilience.timeout.triggered", {
|
|
304
|
+
timeout_ms: ms,
|
|
305
|
+
strategy
|
|
306
|
+
});
|
|
307
|
+
return err2;
|
|
308
|
+
}, (reason) => {
|
|
309
|
+
throw reason;
|
|
310
|
+
});
|
|
311
|
+
let opSettled = false;
|
|
312
|
+
const opPromise = (async () => {
|
|
313
|
+
try {
|
|
314
|
+
const value = await next(childCtx);
|
|
315
|
+
opSettled = true;
|
|
316
|
+
timerController.abort();
|
|
317
|
+
return value;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
opSettled = true;
|
|
320
|
+
timerController.abort();
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
})();
|
|
324
|
+
try {
|
|
325
|
+
const winner = await Promise.race([
|
|
326
|
+
opPromise.then((value) => ({ kind: "ok", value })),
|
|
327
|
+
timerPromise.then((err2) => ({ kind: "timeout", err: err2 }))
|
|
328
|
+
]);
|
|
329
|
+
if (winner.kind === "ok") {
|
|
330
|
+
return winner.value;
|
|
331
|
+
}
|
|
332
|
+
if (strategy === "pessimistic") {
|
|
333
|
+
try {
|
|
334
|
+
await opPromise;
|
|
335
|
+
} catch {}
|
|
336
|
+
} else {
|
|
337
|
+
opPromise.catch(() => {});
|
|
338
|
+
}
|
|
339
|
+
throw winner.err;
|
|
340
|
+
} finally {
|
|
341
|
+
ctx.signal.removeEventListener("abort", onParentAbort);
|
|
342
|
+
if (!opSettled && !timedOut) {
|
|
343
|
+
timerController.abort();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
name: "timeout",
|
|
349
|
+
ms,
|
|
350
|
+
strategy,
|
|
351
|
+
execute
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
export { currentContext, combine, exponentialBackoff, retry, timeout };
|
|
File without changes
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InMemorySessionStore
|
|
3
|
+
} from "./index-vnby35rm.js";
|
|
4
|
+
import {
|
|
5
|
+
collectStream
|
|
6
|
+
} from "./index-zfgr4xx3.js";
|
|
7
|
+
|
|
8
|
+
// src/testing/index.ts
|
|
9
|
+
function conversation(...messages) {
|
|
10
|
+
return messages;
|
|
11
|
+
}
|
|
12
|
+
var MOCK_CAPABILITIES = {
|
|
13
|
+
tools: true,
|
|
14
|
+
streaming: true,
|
|
15
|
+
multimodalInput: true,
|
|
16
|
+
parallelToolCalls: true,
|
|
17
|
+
structuredOutput: true
|
|
18
|
+
};
|
|
19
|
+
function mockProvider(opts = {}) {
|
|
20
|
+
const name = opts.name ?? "mock";
|
|
21
|
+
const defaultModel = opts.defaultModel ?? "mock-model";
|
|
22
|
+
const resultFor = (req) => {
|
|
23
|
+
if (typeof opts.result === "function")
|
|
24
|
+
return opts.result(req);
|
|
25
|
+
if (opts.result !== undefined)
|
|
26
|
+
return opts.result;
|
|
27
|
+
return {
|
|
28
|
+
message: { role: "assistant", content: [{ type: "text", text: "ok" }] },
|
|
29
|
+
toolCalls: [],
|
|
30
|
+
finishReason: "stop",
|
|
31
|
+
model: req.model ?? defaultModel,
|
|
32
|
+
raw: {}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const eventsFor = (req) => {
|
|
36
|
+
if (typeof opts.events === "function")
|
|
37
|
+
return opts.events(req);
|
|
38
|
+
if (opts.events !== undefined)
|
|
39
|
+
return opts.events;
|
|
40
|
+
const result = resultFor(req);
|
|
41
|
+
const text = result.message.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
42
|
+
const events = [{ type: "message_start", model: result.model }];
|
|
43
|
+
if (text !== "")
|
|
44
|
+
events.push({ type: "text_delta", text });
|
|
45
|
+
result.toolCalls.forEach((call, index) => {
|
|
46
|
+
const argumentsText = call.argumentsText ?? (call.arguments === undefined ? "" : JSON.stringify(call.arguments) ?? "");
|
|
47
|
+
events.push({ type: "tool_call_start", index, id: call.id, name: call.name });
|
|
48
|
+
if (argumentsText !== "")
|
|
49
|
+
events.push({ type: "tool_call_delta", index, argumentsTextDelta: argumentsText });
|
|
50
|
+
events.push({ type: "tool_call_end", index });
|
|
51
|
+
});
|
|
52
|
+
events.push({
|
|
53
|
+
type: "finish",
|
|
54
|
+
finishReason: result.finishReason,
|
|
55
|
+
...result.usage !== undefined ? { usage: result.usage } : {}
|
|
56
|
+
});
|
|
57
|
+
return events;
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
name,
|
|
61
|
+
defaultModel,
|
|
62
|
+
capabilities: { ...MOCK_CAPABILITIES, ...opts.capabilities },
|
|
63
|
+
async complete(req, ctx) {
|
|
64
|
+
opts.onRequest?.(req, ctx);
|
|
65
|
+
return resultFor(req);
|
|
66
|
+
},
|
|
67
|
+
async* stream(req, ctx) {
|
|
68
|
+
opts.onRequest?.(req, ctx);
|
|
69
|
+
for (const event of eventsFor(req))
|
|
70
|
+
yield event;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function collectProviderStream(provider, req, ctx) {
|
|
75
|
+
return collectStream(provider.stream(req, ctx), req.model ?? provider.defaultModel);
|
|
76
|
+
}
|
|
77
|
+
function expectValid(schema, input) {
|
|
78
|
+
const result = schema.safeParse(input);
|
|
79
|
+
if (result.success)
|
|
80
|
+
return result.data;
|
|
81
|
+
const detail = result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
|
|
82
|
+
throw new Error(`expected input to be valid, but: ${detail}`);
|
|
83
|
+
}
|
|
84
|
+
function byteStreamOf(...chunks) {
|
|
85
|
+
const encoder = new TextEncoder;
|
|
86
|
+
return new ReadableStream({
|
|
87
|
+
start(controller) {
|
|
88
|
+
for (const chunk of chunks)
|
|
89
|
+
controller.enqueue(encoder.encode(chunk));
|
|
90
|
+
controller.close();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function jsonFetch(body, init) {
|
|
95
|
+
const calls = [];
|
|
96
|
+
const fetchImpl = async (input, requestInit) => {
|
|
97
|
+
calls.push({ url: String(input), init: requestInit });
|
|
98
|
+
return new Response(JSON.stringify(body), {
|
|
99
|
+
status: init?.status ?? 200,
|
|
100
|
+
headers: { "content-type": "application/json" }
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
return { fetch: fetchImpl, calls };
|
|
104
|
+
}
|
|
105
|
+
function sseFetch(sse) {
|
|
106
|
+
const calls = [];
|
|
107
|
+
const fetchImpl = async (input, requestInit) => {
|
|
108
|
+
calls.push({ url: String(input), init: requestInit });
|
|
109
|
+
return new Response(byteStreamOf(sse), {
|
|
110
|
+
status: 200,
|
|
111
|
+
headers: { "content-type": "text/event-stream" }
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
return { fetch: fetchImpl, calls };
|
|
115
|
+
}
|
|
116
|
+
var SCRIPTED_MODEL = "mock-model";
|
|
117
|
+
function textResult(text, usage) {
|
|
118
|
+
return {
|
|
119
|
+
message: { role: "assistant", content: [{ type: "text", text }] },
|
|
120
|
+
toolCalls: [],
|
|
121
|
+
finishReason: "stop",
|
|
122
|
+
model: SCRIPTED_MODEL,
|
|
123
|
+
raw: {},
|
|
124
|
+
...usage !== undefined ? { usage } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function toolCallResult(calls, usage) {
|
|
128
|
+
return {
|
|
129
|
+
message: {
|
|
130
|
+
role: "assistant",
|
|
131
|
+
content: calls.map((c) => ({ type: "tool_call", id: c.id, name: c.name, arguments: c.arguments }))
|
|
132
|
+
},
|
|
133
|
+
toolCalls: calls,
|
|
134
|
+
finishReason: "tool_calls",
|
|
135
|
+
model: SCRIPTED_MODEL,
|
|
136
|
+
raw: {},
|
|
137
|
+
...usage !== undefined ? { usage } : {}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function scriptedProvider(results, opts) {
|
|
141
|
+
let i = 0;
|
|
142
|
+
return mockProvider({ ...opts, result: () => results[Math.min(i++, results.length - 1)] });
|
|
143
|
+
}
|
|
144
|
+
function inMemorySessionStore() {
|
|
145
|
+
return new InMemorySessionStore;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export { conversation, mockProvider, collectProviderStream, expectValid, byteStreamOf, jsonFetch, sseFetch, textResult, toolCallResult, scriptedProvider, inMemorySessionStore };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/events/hub.ts
|
|
2
|
+
function createEventHub(opts = {}) {
|
|
3
|
+
const subscribers = (opts.subscribers ?? []).filter((s) => s !== undefined);
|
|
4
|
+
const onSubscriberError = opts.onSubscriberError;
|
|
5
|
+
return {
|
|
6
|
+
async emit(event) {
|
|
7
|
+
for (let i = 0;i < subscribers.length; i++) {
|
|
8
|
+
try {
|
|
9
|
+
await subscribers[i](event);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
try {
|
|
12
|
+
onSubscriberError?.(error, event, i);
|
|
13
|
+
} catch {}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/events/telemetry.ts
|
|
21
|
+
var SPAN_RUN = "agent.run";
|
|
22
|
+
var SPAN_PROVIDER = "agent.provider.call";
|
|
23
|
+
var SPAN_TOOL = "agent.tool.execute";
|
|
24
|
+
var NOOP_SPAN = {
|
|
25
|
+
setAttributes() {},
|
|
26
|
+
ok() {},
|
|
27
|
+
fail() {},
|
|
28
|
+
end() {}
|
|
29
|
+
};
|
|
30
|
+
function wrapSpan(span) {
|
|
31
|
+
return {
|
|
32
|
+
setAttributes(attrs) {
|
|
33
|
+
span.setAttributes(attrs);
|
|
34
|
+
},
|
|
35
|
+
ok() {
|
|
36
|
+
span.setStatus({ code: "ok" });
|
|
37
|
+
},
|
|
38
|
+
fail(message) {
|
|
39
|
+
span.setStatus({ code: "error", message });
|
|
40
|
+
},
|
|
41
|
+
end() {
|
|
42
|
+
span.end();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function tokenTotal(usage) {
|
|
47
|
+
return usage.totalTokens + usage.inputTokens + usage.outputTokens;
|
|
48
|
+
}
|
|
49
|
+
function createRunTelemetry(telemetry) {
|
|
50
|
+
const tracer = telemetry?.tracer;
|
|
51
|
+
const meter = telemetry?.meter;
|
|
52
|
+
const runDuration = meter?.createHistogram("agent.run.duration", {
|
|
53
|
+
description: "Wall-clock duration of an agent run.",
|
|
54
|
+
unit: "ms"
|
|
55
|
+
});
|
|
56
|
+
const toolDuration = meter?.createHistogram("agent.tool.duration", {
|
|
57
|
+
description: "Wall-clock duration of a single tool execution.",
|
|
58
|
+
unit: "ms"
|
|
59
|
+
});
|
|
60
|
+
const tokens = meter?.createCounter("agent.tokens", {
|
|
61
|
+
description: "LLM tokens consumed by agent runs.",
|
|
62
|
+
unit: "1"
|
|
63
|
+
});
|
|
64
|
+
const runs = meter?.createCounter("agent.runs", {
|
|
65
|
+
description: "Completed agent runs, tagged by outcome.",
|
|
66
|
+
unit: "1"
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
async withSpan(name, attrs, fn) {
|
|
70
|
+
if (tracer === undefined)
|
|
71
|
+
return fn(NOOP_SPAN);
|
|
72
|
+
return tracer.withSpan(name, (span) => {
|
|
73
|
+
span.setAttributes(attrs);
|
|
74
|
+
return fn(wrapSpan(span));
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
startSpan(name, attrs) {
|
|
78
|
+
if (tracer === undefined)
|
|
79
|
+
return NOOP_SPAN;
|
|
80
|
+
const span = tracer.startSpan(name, { attributes: attrs });
|
|
81
|
+
return wrapSpan(span);
|
|
82
|
+
},
|
|
83
|
+
recordRun(attrs, durationMs, usage) {
|
|
84
|
+
runDuration?.record(durationMs, attrs);
|
|
85
|
+
runs?.add(1, attrs);
|
|
86
|
+
if (tokenTotal(usage) > 0) {
|
|
87
|
+
tokens?.add(usage.inputTokens, { ...attrs, "token.type": "input" });
|
|
88
|
+
tokens?.add(usage.outputTokens, { ...attrs, "token.type": "output" });
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
recordTool(attrs, durationMs) {
|
|
92
|
+
toolDuration?.record(durationMs, attrs);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { createEventHub, SPAN_RUN, SPAN_PROVIDER, SPAN_TOOL, createRunTelemetry };
|
|
File without changes
|