@cuylabs/agent-runtime 0.5.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/dist/index.js ADDED
@@ -0,0 +1,408 @@
1
+ import {
2
+ InMemoryRuntimeDriver
3
+ } from "./chunk-DBQSJ476.js";
4
+ import {
5
+ AgentOrchestrator,
6
+ AgentRuntime,
7
+ InMemoryOrchestratorRunStore,
8
+ createAgentOrchestrator,
9
+ createAgentRuntime,
10
+ createConsoleRuntimeLogger,
11
+ isTerminalRunStatus,
12
+ silentRuntimeLogger
13
+ } from "./chunk-H6BHDIUX.js";
14
+ import {
15
+ computeNextRunAtMs,
16
+ normalizeSchedule
17
+ } from "./chunk-ZVISWF7S.js";
18
+
19
+ // src/metrics.ts
20
+ var DEFAULT_METRIC_PREFIX = "agent_runtime";
21
+ var DEFAULT_DURATION_BUCKETS_MS = [
22
+ 10,
23
+ 50,
24
+ 100,
25
+ 250,
26
+ 500,
27
+ 1e3,
28
+ 2500,
29
+ 5e3,
30
+ 1e4,
31
+ 3e4,
32
+ 6e4
33
+ ];
34
+ var PROMETHEUS_TEXT_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
35
+ function normalizeMetricPrefix(value) {
36
+ const normalized = (value ?? DEFAULT_METRIC_PREFIX).trim().replace(/[^a-zA-Z0-9_:]/g, "_").replace(/^[^a-zA-Z_:]+/, "");
37
+ if (!normalized) {
38
+ throw new Error("metricPrefix must contain at least one valid Prometheus metric character");
39
+ }
40
+ return normalized;
41
+ }
42
+ function normalizeDurationBuckets(value) {
43
+ const buckets = [...value ?? DEFAULT_DURATION_BUCKETS_MS].map((bucket) => Math.floor(bucket)).filter((bucket) => Number.isFinite(bucket) && bucket > 0).sort((left, right) => left - right);
44
+ if (buckets.length === 0) {
45
+ throw new Error("durationBucketsMs must include at least one positive bucket");
46
+ }
47
+ return [...new Set(buckets)];
48
+ }
49
+ function escapeMetricHelp(text) {
50
+ return text.replace(/\\/g, "\\\\").replace(/\n/g, "\\n");
51
+ }
52
+ function escapeLabelValue(value) {
53
+ return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, '\\"');
54
+ }
55
+ function cloneLabels(labels) {
56
+ return Object.fromEntries(
57
+ Object.entries(labels).filter(([, value]) => value !== void 0).sort(([left], [right]) => left.localeCompare(right))
58
+ );
59
+ }
60
+ function serializeLabels(labels) {
61
+ const entries = Object.entries(labels).sort(([left], [right]) => left.localeCompare(right));
62
+ return entries.map(([key, value]) => `${key}="${escapeLabelValue(value)}"`).join(",");
63
+ }
64
+ function labelsKey(labels) {
65
+ return serializeLabels(labels);
66
+ }
67
+ function mergeLabels(defaultLabels, extra) {
68
+ const labels = { ...defaultLabels };
69
+ for (const [key, value] of Object.entries(extra)) {
70
+ if (value === void 0) {
71
+ continue;
72
+ }
73
+ labels[key] = String(value);
74
+ }
75
+ return cloneLabels(labels);
76
+ }
77
+ function getOrCreateMetricEntry(store, labels) {
78
+ const key = labelsKey(labels);
79
+ const existing = store.get(key);
80
+ if (existing) {
81
+ return existing;
82
+ }
83
+ const entry = {
84
+ labels,
85
+ value: 0
86
+ };
87
+ store.set(key, entry);
88
+ return entry;
89
+ }
90
+ function getOrCreateHistogramEntry(store, labels, bucketCount) {
91
+ const key = labelsKey(labels);
92
+ const existing = store.get(key);
93
+ if (existing) {
94
+ return existing;
95
+ }
96
+ const entry = {
97
+ labels,
98
+ count: 0,
99
+ sum: 0,
100
+ bucketCounts: new Array(bucketCount).fill(0)
101
+ };
102
+ store.set(key, entry);
103
+ return entry;
104
+ }
105
+ function sortMetricEntries(entries) {
106
+ return [...entries].sort(
107
+ (left, right) => labelsKey(left.labels).localeCompare(labelsKey(right.labels))
108
+ );
109
+ }
110
+ function createPrometheusRuntimeMetrics(options = {}) {
111
+ const metricPrefix = normalizeMetricPrefix(options.metricPrefix);
112
+ const defaultLabels = cloneLabels(options.defaultLabels ?? {});
113
+ const durationBucketsMs = normalizeDurationBuckets(options.durationBucketsMs);
114
+ const counters = /* @__PURE__ */ new Map();
115
+ const gauges = /* @__PURE__ */ new Map();
116
+ const histograms = /* @__PURE__ */ new Map();
117
+ const counter = (name, help) => {
118
+ const fullName = `${metricPrefix}_${name}`;
119
+ const existing = counters.get(fullName);
120
+ if (existing) {
121
+ return existing.entries;
122
+ }
123
+ const entries = /* @__PURE__ */ new Map();
124
+ counters.set(fullName, { help, type: "counter", entries });
125
+ return entries;
126
+ };
127
+ const gauge = (name, help) => {
128
+ const fullName = `${metricPrefix}_${name}`;
129
+ const existing = gauges.get(fullName);
130
+ if (existing) {
131
+ return existing.entries;
132
+ }
133
+ const entries = /* @__PURE__ */ new Map();
134
+ gauges.set(fullName, { help, type: "gauge", entries });
135
+ return entries;
136
+ };
137
+ const histogram = (name, help) => {
138
+ const fullName = `${metricPrefix}_${name}`;
139
+ const existing = histograms.get(fullName);
140
+ if (existing) {
141
+ return existing.entries;
142
+ }
143
+ const entries = /* @__PURE__ */ new Map();
144
+ histograms.set(fullName, { help, type: "histogram", entries });
145
+ return entries;
146
+ };
147
+ const runtimeStartCounter = counter(
148
+ "starts_total",
149
+ "Total number of runtime start events."
150
+ );
151
+ const runtimeStopCounter = counter(
152
+ "stops_total",
153
+ "Total number of runtime stop events."
154
+ );
155
+ const dispatchQueuedCounter = counter(
156
+ "dispatch_queued_total",
157
+ "Total number of dispatches accepted into the runtime queue."
158
+ );
159
+ const dispatchDroppedCounter = counter(
160
+ "dispatch_dropped_total",
161
+ "Total number of dispatches dropped before execution."
162
+ );
163
+ const runStartedCounter = counter(
164
+ "run_started_total",
165
+ "Total number of runtime runs started."
166
+ );
167
+ const runRetriedCounter = counter(
168
+ "run_retried_total",
169
+ "Total number of runtime retries scheduled."
170
+ );
171
+ const runCompletedCounter = counter(
172
+ "run_completed_total",
173
+ "Total number of runtime runs completed."
174
+ );
175
+ const deadLetterCounter = counter(
176
+ "dead_letter_total",
177
+ "Total number of runtime runs that exhausted retries and were dead-lettered."
178
+ );
179
+ const queueDepthGauge = gauge(
180
+ "queue_depth",
181
+ "Current number of queued dispatches waiting for capacity."
182
+ );
183
+ const inFlightGauge = gauge(
184
+ "in_flight_runs",
185
+ "Current number of runtime runs in flight."
186
+ );
187
+ const runDurationHistogram = histogram(
188
+ "run_duration_ms",
189
+ "Observed runtime run duration in milliseconds."
190
+ );
191
+ const baseGaugeLabels = cloneLabels(defaultLabels);
192
+ getOrCreateMetricEntry(queueDepthGauge, baseGaugeLabels).value = 0;
193
+ getOrCreateMetricEntry(inFlightGauge, baseGaugeLabels).value = 0;
194
+ const incrementCounter = (store, labels, amount = 1) => {
195
+ getOrCreateMetricEntry(store, labels).value += amount;
196
+ };
197
+ const setGauge = (store, labels, value) => {
198
+ getOrCreateMetricEntry(store, labels).value = value;
199
+ };
200
+ const adjustGauge = (store, labels, delta) => {
201
+ const entry = getOrCreateMetricEntry(store, labels);
202
+ entry.value = Math.max(0, entry.value + delta);
203
+ };
204
+ const observeRunDuration = (status, trigger, durationMs) => {
205
+ const labels = mergeLabels(defaultLabels, { status, trigger });
206
+ const entry = getOrCreateHistogramEntry(
207
+ runDurationHistogram,
208
+ labels,
209
+ durationBucketsMs.length
210
+ );
211
+ const normalizedDuration = Math.max(0, durationMs);
212
+ entry.count += 1;
213
+ entry.sum += normalizedDuration;
214
+ durationBucketsMs.forEach((bucket, index) => {
215
+ if (normalizedDuration <= bucket) {
216
+ entry.bucketCounts[index] += 1;
217
+ }
218
+ });
219
+ };
220
+ const observer = {
221
+ notifyRuntimeStart: async (event) => {
222
+ incrementCounter(
223
+ runtimeStartCounter,
224
+ mergeLabels(defaultLabels, { driver: event.driver })
225
+ );
226
+ },
227
+ notifyRuntimeStop: async (event) => {
228
+ incrementCounter(
229
+ runtimeStopCounter,
230
+ mergeLabels(defaultLabels, { driver: event.driver })
231
+ );
232
+ setGauge(queueDepthGauge, baseGaugeLabels, 0);
233
+ setGauge(inFlightGauge, baseGaugeLabels, 0);
234
+ },
235
+ notifyDispatchQueued: async (event) => {
236
+ incrementCounter(
237
+ dispatchQueuedCounter,
238
+ mergeLabels(defaultLabels, { trigger: event.trigger })
239
+ );
240
+ setGauge(queueDepthGauge, baseGaugeLabels, event.queueLength);
241
+ },
242
+ notifyDispatchDropped: async (event) => {
243
+ incrementCounter(
244
+ dispatchDroppedCounter,
245
+ mergeLabels(defaultLabels, {
246
+ trigger: event.trigger,
247
+ reason: event.reason
248
+ })
249
+ );
250
+ setGauge(queueDepthGauge, baseGaugeLabels, event.queueLength);
251
+ },
252
+ notifyRunStart: async (event) => {
253
+ incrementCounter(
254
+ runStartedCounter,
255
+ mergeLabels(defaultLabels, { trigger: event.trigger })
256
+ );
257
+ adjustGauge(inFlightGauge, baseGaugeLabels, 1);
258
+ const queueDepth = getOrCreateMetricEntry(queueDepthGauge, baseGaugeLabels);
259
+ if (queueDepth.value > 0) {
260
+ queueDepth.value -= 1;
261
+ }
262
+ },
263
+ notifyRunRetry: async (event) => {
264
+ incrementCounter(
265
+ runRetriedCounter,
266
+ mergeLabels(defaultLabels, { trigger: event.trigger })
267
+ );
268
+ },
269
+ notifyRunComplete: async (event) => {
270
+ incrementCounter(
271
+ runCompletedCounter,
272
+ mergeLabels(defaultLabels, {
273
+ trigger: event.trigger,
274
+ status: event.status
275
+ })
276
+ );
277
+ observeRunDuration(
278
+ event.status,
279
+ event.trigger,
280
+ Math.max(0, event.endedAt - event.startedAt)
281
+ );
282
+ adjustGauge(inFlightGauge, baseGaugeLabels, -1);
283
+ },
284
+ notifyDeadLetter: async (event) => {
285
+ incrementCounter(
286
+ deadLetterCounter,
287
+ mergeLabels(defaultLabels, { trigger: event.trigger })
288
+ );
289
+ }
290
+ };
291
+ const snapshot = () => {
292
+ const counterSnapshot = Object.fromEntries(
293
+ [...counters.entries()].map(([name, store]) => [
294
+ name,
295
+ sortMetricEntries(store.entries.values()).map((entry) => ({
296
+ labels: cloneLabels(entry.labels),
297
+ value: entry.value
298
+ }))
299
+ ])
300
+ );
301
+ const gaugeSnapshot = Object.fromEntries(
302
+ [...gauges.entries()].map(([name, store]) => [
303
+ name,
304
+ sortMetricEntries(store.entries.values()).map((entry) => ({
305
+ labels: cloneLabels(entry.labels),
306
+ value: entry.value
307
+ }))
308
+ ])
309
+ );
310
+ const histogramSnapshot = Object.fromEntries(
311
+ [...histograms.entries()].map(([name, store]) => [
312
+ name,
313
+ sortMetricEntries(store.entries.values()).map((entry) => ({
314
+ labels: cloneLabels(entry.labels),
315
+ count: entry.count,
316
+ sum: entry.sum,
317
+ buckets: durationBucketsMs.map((bucket, index) => ({
318
+ le: bucket,
319
+ value: entry.bucketCounts[index]
320
+ }))
321
+ }))
322
+ ])
323
+ );
324
+ return {
325
+ counters: counterSnapshot,
326
+ gauges: gaugeSnapshot,
327
+ histograms: histogramSnapshot
328
+ };
329
+ };
330
+ const render = () => {
331
+ const lines = [];
332
+ for (const [name, store] of [...counters.entries()].sort(([left], [right]) => left.localeCompare(right))) {
333
+ lines.push(`# HELP ${name} ${escapeMetricHelp(store.help)}`);
334
+ lines.push(`# TYPE ${name} ${store.type}`);
335
+ for (const entry of sortMetricEntries(store.entries.values())) {
336
+ const labels = serializeLabels(entry.labels);
337
+ lines.push(`${name}${labels ? `{${labels}}` : ""} ${entry.value}`);
338
+ }
339
+ }
340
+ for (const [name, store] of [...gauges.entries()].sort(([left], [right]) => left.localeCompare(right))) {
341
+ lines.push(`# HELP ${name} ${escapeMetricHelp(store.help)}`);
342
+ lines.push(`# TYPE ${name} ${store.type}`);
343
+ for (const entry of sortMetricEntries(store.entries.values())) {
344
+ const labels = serializeLabels(entry.labels);
345
+ lines.push(`${name}${labels ? `{${labels}}` : ""} ${entry.value}`);
346
+ }
347
+ }
348
+ for (const [name, store] of [...histograms.entries()].sort(([left], [right]) => left.localeCompare(right))) {
349
+ lines.push(`# HELP ${name} ${escapeMetricHelp(store.help)}`);
350
+ lines.push(`# TYPE ${name} ${store.type}`);
351
+ for (const entry of sortMetricEntries(store.entries.values())) {
352
+ const baseLabels = cloneLabels(entry.labels);
353
+ durationBucketsMs.forEach((bucket, index) => {
354
+ const labels = serializeLabels({
355
+ ...baseLabels,
356
+ le: String(bucket)
357
+ });
358
+ lines.push(`${name}_bucket{${labels}} ${entry.bucketCounts[index]}`);
359
+ });
360
+ const infiniteLabels = serializeLabels({
361
+ ...baseLabels,
362
+ le: "+Inf"
363
+ });
364
+ const seriesLabels = serializeLabels(baseLabels);
365
+ lines.push(`${name}_bucket{${infiniteLabels}} ${entry.count}`);
366
+ lines.push(`${name}_sum${seriesLabels ? `{${seriesLabels}}` : ""} ${entry.sum}`);
367
+ lines.push(`${name}_count${seriesLabels ? `{${seriesLabels}}` : ""} ${entry.count}`);
368
+ }
369
+ }
370
+ return `${lines.join("\n")}
371
+ `;
372
+ };
373
+ const reset = () => {
374
+ for (const store of counters.values()) {
375
+ store.entries.clear();
376
+ }
377
+ for (const store of gauges.values()) {
378
+ store.entries.clear();
379
+ }
380
+ for (const store of histograms.values()) {
381
+ store.entries.clear();
382
+ }
383
+ getOrCreateMetricEntry(queueDepthGauge, baseGaugeLabels).value = 0;
384
+ getOrCreateMetricEntry(inFlightGauge, baseGaugeLabels).value = 0;
385
+ };
386
+ return {
387
+ observer,
388
+ contentType: PROMETHEUS_TEXT_CONTENT_TYPE,
389
+ snapshot,
390
+ render,
391
+ reset
392
+ };
393
+ }
394
+ export {
395
+ AgentOrchestrator,
396
+ AgentRuntime,
397
+ InMemoryOrchestratorRunStore,
398
+ InMemoryRuntimeDriver,
399
+ PROMETHEUS_TEXT_CONTENT_TYPE,
400
+ computeNextRunAtMs,
401
+ createAgentOrchestrator,
402
+ createAgentRuntime,
403
+ createConsoleRuntimeLogger,
404
+ createPrometheusRuntimeMetrics,
405
+ isTerminalRunStatus,
406
+ normalizeSchedule,
407
+ silentRuntimeLogger
408
+ };
@@ -0,0 +1,2 @@
1
+ export { A as AgentOrchestrator, a as AgentOrchestratorOptions, I as InMemoryOrchestratorRunStore, O as OrchestratorCloseOptions, d as OrchestratorExecutionContext, e as OrchestratorExecutor, f as OrchestratorGuideInput, g as OrchestratorGuidedInputResolver, h as OrchestratorInvokeInput, i as OrchestratorInvokeResult, j as OrchestratorJobPayload, k as OrchestratorListOptions, l as OrchestratorRunMode, m as OrchestratorRunRecord, n as OrchestratorRunState, o as OrchestratorRunStatus, p as OrchestratorRunStore, q as OrchestratorWaitOptions, x as createAgentOrchestrator, z as isTerminalRunStatus } from '../index-CbLKNFMq.js';
2
+ import '../driver-KdyMlXQq.js';
@@ -0,0 +1,13 @@
1
+ import {
2
+ AgentOrchestrator,
3
+ InMemoryOrchestratorRunStore,
4
+ createAgentOrchestrator,
5
+ isTerminalRunStatus
6
+ } from "../chunk-H6BHDIUX.js";
7
+ import "../chunk-ZVISWF7S.js";
8
+ export {
9
+ AgentOrchestrator,
10
+ InMemoryOrchestratorRunStore,
11
+ createAgentOrchestrator,
12
+ isTerminalRunStatus
13
+ };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@cuylabs/agent-runtime",
3
+ "version": "0.5.0",
4
+ "description": "Agent runtime orchestration layer - scheduling, execution, and pluggable runtime drivers",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./orchestration": {
15
+ "types": "./dist/orchestration/index.d.ts",
16
+ "import": "./dist/orchestration/index.js",
17
+ "default": "./dist/orchestration/index.js"
18
+ },
19
+ "./drivers/in-memory": {
20
+ "types": "./dist/drivers/in-memory.d.ts",
21
+ "import": "./dist/drivers/in-memory.js",
22
+ "default": "./dist/drivers/in-memory.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md"
28
+ ],
29
+ "dependencies": {
30
+ "croner": "^10.0.1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "tsup": "^8.0.0",
35
+ "typescript": "^5.7.0",
36
+ "vitest": "^4.0.18"
37
+ },
38
+ "keywords": [
39
+ "agent",
40
+ "runtime",
41
+ "scheduler",
42
+ "cron",
43
+ "orchestration"
44
+ ],
45
+ "author": "cuylabs",
46
+ "license": "Apache-2.0",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/cuylabs-ai/agents-ts.git",
50
+ "directory": "packages/agent-runtime"
51
+ },
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup src/index.ts src/orchestration/index.ts src/drivers/in-memory.ts --format esm --dts --clean",
60
+ "dev": "tsup src/index.ts src/orchestration/index.ts src/drivers/in-memory.ts --format esm --dts --watch",
61
+ "typecheck": "tsc --noEmit",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
64
+ "clean": "rm -rf dist"
65
+ }
66
+ }