agentflow-core 0.1.2 → 0.1.4
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/chunk-TT5DLU73.js +435 -0
- package/dist/cli.cjs +544 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +142 -0
- package/dist/index.cjs +209 -0
- package/dist/index.d.cts +102 -1
- package/dist/index.d.ts +102 -1
- package/dist/index.js +9 -225
- package/package.json +6 -3
package/dist/index.cjs
CHANGED
|
@@ -33,7 +33,10 @@ __export(index_exports, {
|
|
|
33
33
|
getStats: () => getStats,
|
|
34
34
|
getSubtree: () => getSubtree,
|
|
35
35
|
getTraceTree: () => getTraceTree,
|
|
36
|
+
graphToJson: () => graphToJson,
|
|
36
37
|
groupByTraceId: () => groupByTraceId,
|
|
38
|
+
loadGraph: () => loadGraph,
|
|
39
|
+
runTraced: () => runTraced,
|
|
37
40
|
stitchTrace: () => stitchTrace
|
|
38
41
|
});
|
|
39
42
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -264,6 +267,209 @@ function createGraphBuilder(config) {
|
|
|
264
267
|
return builder;
|
|
265
268
|
}
|
|
266
269
|
|
|
270
|
+
// src/loader.ts
|
|
271
|
+
function toNodesMap(raw) {
|
|
272
|
+
if (raw instanceof Map) return raw;
|
|
273
|
+
if (Array.isArray(raw)) {
|
|
274
|
+
return new Map(raw);
|
|
275
|
+
}
|
|
276
|
+
if (raw !== null && typeof raw === "object") {
|
|
277
|
+
return new Map(Object.entries(raw));
|
|
278
|
+
}
|
|
279
|
+
return /* @__PURE__ */ new Map();
|
|
280
|
+
}
|
|
281
|
+
function loadGraph(input) {
|
|
282
|
+
const raw = typeof input === "string" ? JSON.parse(input) : input;
|
|
283
|
+
const nodes = toNodesMap(raw.nodes);
|
|
284
|
+
return {
|
|
285
|
+
id: raw.id ?? "",
|
|
286
|
+
rootNodeId: raw.rootNodeId ?? raw.rootId ?? "",
|
|
287
|
+
nodes,
|
|
288
|
+
edges: raw.edges ?? [],
|
|
289
|
+
startTime: raw.startTime ?? 0,
|
|
290
|
+
endTime: raw.endTime ?? null,
|
|
291
|
+
status: raw.status ?? "completed",
|
|
292
|
+
trigger: raw.trigger ?? "unknown",
|
|
293
|
+
agentId: raw.agentId ?? "unknown",
|
|
294
|
+
events: raw.events ?? [],
|
|
295
|
+
traceId: raw.traceId,
|
|
296
|
+
spanId: raw.spanId,
|
|
297
|
+
parentSpanId: raw.parentSpanId
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function graphToJson(graph) {
|
|
301
|
+
const nodesObj = {};
|
|
302
|
+
for (const [id, node] of graph.nodes) {
|
|
303
|
+
nodesObj[id] = node;
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
id: graph.id,
|
|
307
|
+
rootNodeId: graph.rootNodeId,
|
|
308
|
+
nodes: nodesObj,
|
|
309
|
+
edges: graph.edges,
|
|
310
|
+
startTime: graph.startTime,
|
|
311
|
+
endTime: graph.endTime,
|
|
312
|
+
status: graph.status,
|
|
313
|
+
trigger: graph.trigger,
|
|
314
|
+
agentId: graph.agentId,
|
|
315
|
+
events: graph.events,
|
|
316
|
+
traceId: graph.traceId,
|
|
317
|
+
spanId: graph.spanId,
|
|
318
|
+
parentSpanId: graph.parentSpanId
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/runner.ts
|
|
323
|
+
var import_node_child_process = require("child_process");
|
|
324
|
+
var import_node_fs = require("fs");
|
|
325
|
+
var import_node_path = require("path");
|
|
326
|
+
function globToRegex(pattern) {
|
|
327
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
328
|
+
return new RegExp(`^${escaped}$`);
|
|
329
|
+
}
|
|
330
|
+
function snapshotDir(dir, patterns) {
|
|
331
|
+
const result = /* @__PURE__ */ new Map();
|
|
332
|
+
if (!(0, import_node_fs.existsSync)(dir)) return result;
|
|
333
|
+
for (const entry of (0, import_node_fs.readdirSync)(dir)) {
|
|
334
|
+
if (!patterns.some((re) => re.test(entry))) continue;
|
|
335
|
+
const full = (0, import_node_path.join)(dir, entry);
|
|
336
|
+
try {
|
|
337
|
+
const stat = (0, import_node_fs.statSync)(full);
|
|
338
|
+
if (stat.isFile()) {
|
|
339
|
+
result.set(full, stat.mtimeMs);
|
|
340
|
+
}
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
function agentIdFromFilename(filePath) {
|
|
347
|
+
const base = (0, import_node_path.basename)(filePath, ".json");
|
|
348
|
+
const cleaned = base.replace(/-state$/, "");
|
|
349
|
+
return `alfred-${cleaned}`;
|
|
350
|
+
}
|
|
351
|
+
function deriveAgentId(command) {
|
|
352
|
+
return "orchestrator";
|
|
353
|
+
}
|
|
354
|
+
function fileTimestamp() {
|
|
355
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-").replace(/\.\d+Z$/, "");
|
|
356
|
+
}
|
|
357
|
+
async function runTraced(config) {
|
|
358
|
+
const {
|
|
359
|
+
command,
|
|
360
|
+
agentId = deriveAgentId(command),
|
|
361
|
+
trigger = "cli",
|
|
362
|
+
tracesDir = "./traces",
|
|
363
|
+
watchDirs = [],
|
|
364
|
+
watchPatterns = ["*.json"]
|
|
365
|
+
} = config;
|
|
366
|
+
if (command.length === 0) {
|
|
367
|
+
throw new Error("runTraced: command must not be empty");
|
|
368
|
+
}
|
|
369
|
+
const resolvedTracesDir = (0, import_node_path.resolve)(tracesDir);
|
|
370
|
+
const patterns = watchPatterns.map(globToRegex);
|
|
371
|
+
const orchestrator = createGraphBuilder({ agentId, trigger });
|
|
372
|
+
const { traceId, spanId } = orchestrator.traceContext;
|
|
373
|
+
const beforeSnapshots = /* @__PURE__ */ new Map();
|
|
374
|
+
for (const dir of watchDirs) {
|
|
375
|
+
beforeSnapshots.set(dir, snapshotDir(dir, patterns));
|
|
376
|
+
}
|
|
377
|
+
const rootId = orchestrator.startNode({ type: "agent", name: agentId });
|
|
378
|
+
const dispatchId = orchestrator.startNode({
|
|
379
|
+
type: "tool",
|
|
380
|
+
name: "dispatch-command",
|
|
381
|
+
parentId: rootId
|
|
382
|
+
});
|
|
383
|
+
orchestrator.updateState(dispatchId, { command: command.join(" ") });
|
|
384
|
+
const monitorId = orchestrator.startNode({
|
|
385
|
+
type: "tool",
|
|
386
|
+
name: "state-monitor",
|
|
387
|
+
parentId: rootId
|
|
388
|
+
});
|
|
389
|
+
orchestrator.updateState(monitorId, {
|
|
390
|
+
watchDirs,
|
|
391
|
+
watchPatterns
|
|
392
|
+
});
|
|
393
|
+
const startMs = Date.now();
|
|
394
|
+
const execCmd = command[0] ?? "";
|
|
395
|
+
const execArgs = command.slice(1);
|
|
396
|
+
process.env.AGENTFLOW_TRACE_ID = traceId;
|
|
397
|
+
process.env.AGENTFLOW_PARENT_SPAN_ID = spanId;
|
|
398
|
+
const result = (0, import_node_child_process.spawnSync)(execCmd, execArgs, { stdio: "inherit" });
|
|
399
|
+
delete process.env.AGENTFLOW_TRACE_ID;
|
|
400
|
+
delete process.env.AGENTFLOW_PARENT_SPAN_ID;
|
|
401
|
+
const exitCode = result.status ?? 1;
|
|
402
|
+
const duration = (Date.now() - startMs) / 1e3;
|
|
403
|
+
const stateChanges = [];
|
|
404
|
+
for (const dir of watchDirs) {
|
|
405
|
+
const before = beforeSnapshots.get(dir) ?? /* @__PURE__ */ new Map();
|
|
406
|
+
const after = snapshotDir(dir, patterns);
|
|
407
|
+
for (const [filePath, mtime] of after) {
|
|
408
|
+
const prevMtime = before.get(filePath);
|
|
409
|
+
if (prevMtime === void 0 || mtime > prevMtime) {
|
|
410
|
+
stateChanges.push(filePath);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
orchestrator.updateState(monitorId, { stateChanges });
|
|
415
|
+
orchestrator.endNode(monitorId);
|
|
416
|
+
if (exitCode === 0) {
|
|
417
|
+
orchestrator.endNode(dispatchId);
|
|
418
|
+
} else {
|
|
419
|
+
orchestrator.failNode(dispatchId, `Command exited with code ${exitCode}`);
|
|
420
|
+
}
|
|
421
|
+
orchestrator.updateState(rootId, {
|
|
422
|
+
exitCode,
|
|
423
|
+
duration,
|
|
424
|
+
stateChanges
|
|
425
|
+
});
|
|
426
|
+
if (exitCode === 0) {
|
|
427
|
+
orchestrator.endNode(rootId);
|
|
428
|
+
} else {
|
|
429
|
+
orchestrator.failNode(rootId, `Command exited with code ${exitCode}`);
|
|
430
|
+
}
|
|
431
|
+
const orchestratorGraph = orchestrator.build();
|
|
432
|
+
const allGraphs = [orchestratorGraph];
|
|
433
|
+
for (const filePath of stateChanges) {
|
|
434
|
+
const childAgentId = agentIdFromFilename(filePath);
|
|
435
|
+
const childBuilder = createGraphBuilder({
|
|
436
|
+
agentId: childAgentId,
|
|
437
|
+
trigger: "state-change",
|
|
438
|
+
traceId,
|
|
439
|
+
parentSpanId: spanId
|
|
440
|
+
});
|
|
441
|
+
const childRootId = childBuilder.startNode({
|
|
442
|
+
type: "agent",
|
|
443
|
+
name: childAgentId
|
|
444
|
+
});
|
|
445
|
+
childBuilder.updateState(childRootId, {
|
|
446
|
+
stateFile: filePath,
|
|
447
|
+
detectedBy: "runner-state-monitor"
|
|
448
|
+
});
|
|
449
|
+
childBuilder.endNode(childRootId);
|
|
450
|
+
allGraphs.push(childBuilder.build());
|
|
451
|
+
}
|
|
452
|
+
if (!(0, import_node_fs.existsSync)(resolvedTracesDir)) {
|
|
453
|
+
(0, import_node_fs.mkdirSync)(resolvedTracesDir, { recursive: true });
|
|
454
|
+
}
|
|
455
|
+
const ts = fileTimestamp();
|
|
456
|
+
const tracePaths = [];
|
|
457
|
+
for (const graph of allGraphs) {
|
|
458
|
+
const filename = `${graph.agentId}-${ts}.json`;
|
|
459
|
+
const outPath = (0, import_node_path.join)(resolvedTracesDir, filename);
|
|
460
|
+
(0, import_node_fs.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
|
|
461
|
+
tracePaths.push(outPath);
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
exitCode,
|
|
465
|
+
traceId,
|
|
466
|
+
spanId,
|
|
467
|
+
tracePaths,
|
|
468
|
+
stateChanges,
|
|
469
|
+
duration
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
267
473
|
// src/graph-stitch.ts
|
|
268
474
|
function groupByTraceId(graphs) {
|
|
269
475
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -481,6 +687,9 @@ function getStats(graph) {
|
|
|
481
687
|
getStats,
|
|
482
688
|
getSubtree,
|
|
483
689
|
getTraceTree,
|
|
690
|
+
graphToJson,
|
|
484
691
|
groupByTraceId,
|
|
692
|
+
loadGraph,
|
|
693
|
+
runTraced,
|
|
485
694
|
stitchTrace
|
|
486
695
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -276,6 +276,107 @@ interface MutableExecutionNode {
|
|
|
276
276
|
*/
|
|
277
277
|
declare function createGraphBuilder(config?: AgentFlowConfig): GraphBuilder;
|
|
278
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Load and deserialize execution graphs from JSON.
|
|
281
|
+
*
|
|
282
|
+
* Handles all serialization formats produced by the runner, graph-builder,
|
|
283
|
+
* and third-party tools:
|
|
284
|
+
* - `nodes` as a plain object `{ "node_001": { ... } }` (runner.ts output)
|
|
285
|
+
* - `nodes` as an array of `[id, node]` pairs (Map JSON serialization)
|
|
286
|
+
* - `nodes` already a Map (in-memory passthrough)
|
|
287
|
+
*
|
|
288
|
+
* @module
|
|
289
|
+
*/
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Deserialize a JSON object (or JSON string) into a valid `ExecutionGraph`.
|
|
293
|
+
*
|
|
294
|
+
* Use this whenever you read a trace file from disk or receive one over the
|
|
295
|
+
* network. It normalizes `nodes` into a proper `Map` regardless of the
|
|
296
|
+
* serialization format.
|
|
297
|
+
*
|
|
298
|
+
* @param input - A parsed JSON object, or a JSON string to be parsed.
|
|
299
|
+
* @returns A valid `ExecutionGraph` ready for use with query functions.
|
|
300
|
+
* @throws {Error} If the input cannot be parsed or is missing required fields.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* import { readFileSync } from 'fs';
|
|
305
|
+
* import { loadGraph, getStats } from 'agentflow-core';
|
|
306
|
+
*
|
|
307
|
+
* const graph = loadGraph(readFileSync('trace.json', 'utf8'));
|
|
308
|
+
* console.log(getStats(graph));
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
declare function loadGraph(input: string | Record<string, unknown>): ExecutionGraph;
|
|
312
|
+
/**
|
|
313
|
+
* Serialize an `ExecutionGraph` to a plain JSON-safe object.
|
|
314
|
+
*
|
|
315
|
+
* The inverse of `loadGraph`. `nodes` is written as a plain object keyed by
|
|
316
|
+
* node ID, which is the most readable format for trace files on disk.
|
|
317
|
+
*
|
|
318
|
+
* @param graph - The execution graph to serialize.
|
|
319
|
+
* @returns A plain object safe to pass to `JSON.stringify`.
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```ts
|
|
323
|
+
* import { writeFileSync } from 'fs';
|
|
324
|
+
* import { graphToJson } from 'agentflow-core';
|
|
325
|
+
*
|
|
326
|
+
* writeFileSync('trace.json', JSON.stringify(graphToJson(graph), null, 2));
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare function graphToJson(graph: ExecutionGraph): Record<string, unknown>;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* CLI runner that wraps any command with automatic AgentFlow tracing.
|
|
333
|
+
*
|
|
334
|
+
* No code changes needed in the target application — the runner sets
|
|
335
|
+
* AGENTFLOW_TRACE_ID and AGENTFLOW_PARENT_SPAN_ID env vars so any child
|
|
336
|
+
* process using AgentFlow auto-joins the distributed trace.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* const result = await runTraced({
|
|
341
|
+
* command: ['python', '-m', 'alfred', 'process'],
|
|
342
|
+
* watchDirs: ['/home/trader/.alfred/data'],
|
|
343
|
+
* });
|
|
344
|
+
* ```
|
|
345
|
+
* @module
|
|
346
|
+
*/
|
|
347
|
+
interface RunConfig {
|
|
348
|
+
/** Command to execute (e.g. ['python', '-m', 'alfred', 'process']). */
|
|
349
|
+
command: string[];
|
|
350
|
+
/** Agent ID for the orchestrator trace (default: derived from command). */
|
|
351
|
+
agentId?: string;
|
|
352
|
+
/** Trigger label (default: "cli"). */
|
|
353
|
+
trigger?: string;
|
|
354
|
+
/** Directory to save trace files (default: ./traces). */
|
|
355
|
+
tracesDir?: string;
|
|
356
|
+
/** Directories to watch for state file changes during execution. */
|
|
357
|
+
watchDirs?: string[];
|
|
358
|
+
/** File patterns to watch (default: ["*.json"]). */
|
|
359
|
+
watchPatterns?: string[];
|
|
360
|
+
}
|
|
361
|
+
interface RunResult {
|
|
362
|
+
exitCode: number;
|
|
363
|
+
traceId: string;
|
|
364
|
+
spanId: string;
|
|
365
|
+
tracePaths: string[];
|
|
366
|
+
stateChanges: string[];
|
|
367
|
+
duration: number;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Run a command with automatic AgentFlow tracing.
|
|
371
|
+
*
|
|
372
|
+
* 1. Creates an orchestrator (parent) trace.
|
|
373
|
+
* 2. Snapshots watched directories.
|
|
374
|
+
* 3. Spawns the command with trace env vars.
|
|
375
|
+
* 4. After exit, detects state file changes and creates child traces.
|
|
376
|
+
* 5. Saves all trace JSON files.
|
|
377
|
+
*/
|
|
378
|
+
declare function runTraced(config: RunConfig): Promise<RunResult>;
|
|
379
|
+
|
|
279
380
|
declare function groupByTraceId(graphs: ExecutionGraph[]): Map<string, ExecutionGraph[]>;
|
|
280
381
|
declare function stitchTrace(graphs: ExecutionGraph[]): DistributedTrace;
|
|
281
382
|
declare function getTraceTree(trace: DistributedTrace): ExecutionGraph[];
|
|
@@ -396,4 +497,4 @@ declare function getDepth(graph: ExecutionGraph): number;
|
|
|
396
497
|
*/
|
|
397
498
|
declare function getStats(graph: ExecutionGraph): GraphStats;
|
|
398
499
|
|
|
399
|
-
export { type Adapter, type AgentFlowConfig, type DistributedTrace, type EdgeType, type ExecutionEdge, type ExecutionGraph, type ExecutionNode, type GraphBuilder, type GraphStats, type GraphStatus, type MutableExecutionNode, type NodeStatus, type NodeType, type StartNodeOptions, type TraceEvent, type TraceEventType, type Writer, createGraphBuilder, findWaitingOn, getChildren, getCriticalPath, getDepth, getDuration, getFailures, getHungNodes, getNode, getParent, getStats, getSubtree, getTraceTree, groupByTraceId, stitchTrace };
|
|
500
|
+
export { type Adapter, type AgentFlowConfig, type DistributedTrace, type EdgeType, type ExecutionEdge, type ExecutionGraph, type ExecutionNode, type GraphBuilder, type GraphStats, type GraphStatus, type MutableExecutionNode, type NodeStatus, type NodeType, type RunConfig, type RunResult, type StartNodeOptions, type TraceEvent, type TraceEventType, type Writer, createGraphBuilder, findWaitingOn, getChildren, getCriticalPath, getDepth, getDuration, getFailures, getHungNodes, getNode, getParent, getStats, getSubtree, getTraceTree, graphToJson, groupByTraceId, loadGraph, runTraced, stitchTrace };
|
package/dist/index.d.ts
CHANGED
|
@@ -276,6 +276,107 @@ interface MutableExecutionNode {
|
|
|
276
276
|
*/
|
|
277
277
|
declare function createGraphBuilder(config?: AgentFlowConfig): GraphBuilder;
|
|
278
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Load and deserialize execution graphs from JSON.
|
|
281
|
+
*
|
|
282
|
+
* Handles all serialization formats produced by the runner, graph-builder,
|
|
283
|
+
* and third-party tools:
|
|
284
|
+
* - `nodes` as a plain object `{ "node_001": { ... } }` (runner.ts output)
|
|
285
|
+
* - `nodes` as an array of `[id, node]` pairs (Map JSON serialization)
|
|
286
|
+
* - `nodes` already a Map (in-memory passthrough)
|
|
287
|
+
*
|
|
288
|
+
* @module
|
|
289
|
+
*/
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Deserialize a JSON object (or JSON string) into a valid `ExecutionGraph`.
|
|
293
|
+
*
|
|
294
|
+
* Use this whenever you read a trace file from disk or receive one over the
|
|
295
|
+
* network. It normalizes `nodes` into a proper `Map` regardless of the
|
|
296
|
+
* serialization format.
|
|
297
|
+
*
|
|
298
|
+
* @param input - A parsed JSON object, or a JSON string to be parsed.
|
|
299
|
+
* @returns A valid `ExecutionGraph` ready for use with query functions.
|
|
300
|
+
* @throws {Error} If the input cannot be parsed or is missing required fields.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* import { readFileSync } from 'fs';
|
|
305
|
+
* import { loadGraph, getStats } from 'agentflow-core';
|
|
306
|
+
*
|
|
307
|
+
* const graph = loadGraph(readFileSync('trace.json', 'utf8'));
|
|
308
|
+
* console.log(getStats(graph));
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
declare function loadGraph(input: string | Record<string, unknown>): ExecutionGraph;
|
|
312
|
+
/**
|
|
313
|
+
* Serialize an `ExecutionGraph` to a plain JSON-safe object.
|
|
314
|
+
*
|
|
315
|
+
* The inverse of `loadGraph`. `nodes` is written as a plain object keyed by
|
|
316
|
+
* node ID, which is the most readable format for trace files on disk.
|
|
317
|
+
*
|
|
318
|
+
* @param graph - The execution graph to serialize.
|
|
319
|
+
* @returns A plain object safe to pass to `JSON.stringify`.
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```ts
|
|
323
|
+
* import { writeFileSync } from 'fs';
|
|
324
|
+
* import { graphToJson } from 'agentflow-core';
|
|
325
|
+
*
|
|
326
|
+
* writeFileSync('trace.json', JSON.stringify(graphToJson(graph), null, 2));
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare function graphToJson(graph: ExecutionGraph): Record<string, unknown>;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* CLI runner that wraps any command with automatic AgentFlow tracing.
|
|
333
|
+
*
|
|
334
|
+
* No code changes needed in the target application — the runner sets
|
|
335
|
+
* AGENTFLOW_TRACE_ID and AGENTFLOW_PARENT_SPAN_ID env vars so any child
|
|
336
|
+
* process using AgentFlow auto-joins the distributed trace.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* const result = await runTraced({
|
|
341
|
+
* command: ['python', '-m', 'alfred', 'process'],
|
|
342
|
+
* watchDirs: ['/home/trader/.alfred/data'],
|
|
343
|
+
* });
|
|
344
|
+
* ```
|
|
345
|
+
* @module
|
|
346
|
+
*/
|
|
347
|
+
interface RunConfig {
|
|
348
|
+
/** Command to execute (e.g. ['python', '-m', 'alfred', 'process']). */
|
|
349
|
+
command: string[];
|
|
350
|
+
/** Agent ID for the orchestrator trace (default: derived from command). */
|
|
351
|
+
agentId?: string;
|
|
352
|
+
/** Trigger label (default: "cli"). */
|
|
353
|
+
trigger?: string;
|
|
354
|
+
/** Directory to save trace files (default: ./traces). */
|
|
355
|
+
tracesDir?: string;
|
|
356
|
+
/** Directories to watch for state file changes during execution. */
|
|
357
|
+
watchDirs?: string[];
|
|
358
|
+
/** File patterns to watch (default: ["*.json"]). */
|
|
359
|
+
watchPatterns?: string[];
|
|
360
|
+
}
|
|
361
|
+
interface RunResult {
|
|
362
|
+
exitCode: number;
|
|
363
|
+
traceId: string;
|
|
364
|
+
spanId: string;
|
|
365
|
+
tracePaths: string[];
|
|
366
|
+
stateChanges: string[];
|
|
367
|
+
duration: number;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Run a command with automatic AgentFlow tracing.
|
|
371
|
+
*
|
|
372
|
+
* 1. Creates an orchestrator (parent) trace.
|
|
373
|
+
* 2. Snapshots watched directories.
|
|
374
|
+
* 3. Spawns the command with trace env vars.
|
|
375
|
+
* 4. After exit, detects state file changes and creates child traces.
|
|
376
|
+
* 5. Saves all trace JSON files.
|
|
377
|
+
*/
|
|
378
|
+
declare function runTraced(config: RunConfig): Promise<RunResult>;
|
|
379
|
+
|
|
279
380
|
declare function groupByTraceId(graphs: ExecutionGraph[]): Map<string, ExecutionGraph[]>;
|
|
280
381
|
declare function stitchTrace(graphs: ExecutionGraph[]): DistributedTrace;
|
|
281
382
|
declare function getTraceTree(trace: DistributedTrace): ExecutionGraph[];
|
|
@@ -396,4 +497,4 @@ declare function getDepth(graph: ExecutionGraph): number;
|
|
|
396
497
|
*/
|
|
397
498
|
declare function getStats(graph: ExecutionGraph): GraphStats;
|
|
398
499
|
|
|
399
|
-
export { type Adapter, type AgentFlowConfig, type DistributedTrace, type EdgeType, type ExecutionEdge, type ExecutionGraph, type ExecutionNode, type GraphBuilder, type GraphStats, type GraphStatus, type MutableExecutionNode, type NodeStatus, type NodeType, type StartNodeOptions, type TraceEvent, type TraceEventType, type Writer, createGraphBuilder, findWaitingOn, getChildren, getCriticalPath, getDepth, getDuration, getFailures, getHungNodes, getNode, getParent, getStats, getSubtree, getTraceTree, groupByTraceId, stitchTrace };
|
|
500
|
+
export { type Adapter, type AgentFlowConfig, type DistributedTrace, type EdgeType, type ExecutionEdge, type ExecutionGraph, type ExecutionNode, type GraphBuilder, type GraphStats, type GraphStatus, type MutableExecutionNode, type NodeStatus, type NodeType, type RunConfig, type RunResult, type StartNodeOptions, type TraceEvent, type TraceEventType, type Writer, createGraphBuilder, findWaitingOn, getChildren, getCriticalPath, getDepth, getDuration, getFailures, getHungNodes, getNode, getParent, getStats, getSubtree, getTraceTree, graphToJson, groupByTraceId, loadGraph, runTraced, stitchTrace };
|
package/dist/index.js
CHANGED
|
@@ -1,228 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
for (const value of obj.values()) {
|
|
8
|
-
deepFreeze(value);
|
|
9
|
-
}
|
|
10
|
-
return obj;
|
|
11
|
-
}
|
|
12
|
-
Object.freeze(obj);
|
|
13
|
-
const record = obj;
|
|
14
|
-
for (const key of Object.keys(record)) {
|
|
15
|
-
const value = record[key];
|
|
16
|
-
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
17
|
-
deepFreeze(value);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return obj;
|
|
21
|
-
}
|
|
22
|
-
function createCounterIdGenerator() {
|
|
23
|
-
let counter = 0;
|
|
24
|
-
return () => {
|
|
25
|
-
counter++;
|
|
26
|
-
return `node_${String(counter).padStart(3, "0")}`;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
function createGraphBuilder(config) {
|
|
30
|
-
const generateId = config?.idGenerator ?? createCounterIdGenerator();
|
|
31
|
-
const agentId = config?.agentId ?? "unknown";
|
|
32
|
-
const trigger = config?.trigger ?? "manual";
|
|
33
|
-
const spanId = randomUUID();
|
|
34
|
-
const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? randomUUID();
|
|
35
|
-
const parentSpanId = config?.parentSpanId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_PARENT_SPAN_ID : void 0) ?? null;
|
|
36
|
-
const graphId = generateId();
|
|
37
|
-
const startTime = Date.now();
|
|
38
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
39
|
-
const edges = [];
|
|
40
|
-
const events = [];
|
|
41
|
-
const parentStack = [];
|
|
42
|
-
let rootNodeId = null;
|
|
43
|
-
let built = false;
|
|
44
|
-
function assertNotBuilt() {
|
|
45
|
-
if (built) {
|
|
46
|
-
throw new Error("GraphBuilder: cannot mutate after build() has been called");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function getNode2(nodeId) {
|
|
50
|
-
const node = nodes.get(nodeId);
|
|
51
|
-
if (!node) {
|
|
52
|
-
throw new Error(`GraphBuilder: node "${nodeId}" does not exist`);
|
|
53
|
-
}
|
|
54
|
-
return node;
|
|
55
|
-
}
|
|
56
|
-
function recordEvent(nodeId, eventType, data = {}) {
|
|
57
|
-
events.push({
|
|
58
|
-
timestamp: Date.now(),
|
|
59
|
-
eventType,
|
|
60
|
-
nodeId,
|
|
61
|
-
data
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
function buildGraph() {
|
|
65
|
-
if (rootNodeId === null) {
|
|
66
|
-
throw new Error("GraphBuilder: cannot build a graph with no nodes");
|
|
67
|
-
}
|
|
68
|
-
let graphStatus = "completed";
|
|
69
|
-
for (const node of nodes.values()) {
|
|
70
|
-
if (node.status === "failed" || node.status === "timeout" || node.status === "hung") {
|
|
71
|
-
graphStatus = "failed";
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
if (node.status === "running") {
|
|
75
|
-
graphStatus = "running";
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const endTime = graphStatus === "running" ? null : Date.now();
|
|
79
|
-
const frozenNodes = new Map(
|
|
80
|
-
[...nodes.entries()].map(([id, mNode]) => [
|
|
81
|
-
id,
|
|
82
|
-
{
|
|
83
|
-
id: mNode.id,
|
|
84
|
-
type: mNode.type,
|
|
85
|
-
name: mNode.name,
|
|
86
|
-
startTime: mNode.startTime,
|
|
87
|
-
endTime: mNode.endTime,
|
|
88
|
-
status: mNode.status,
|
|
89
|
-
parentId: mNode.parentId,
|
|
90
|
-
children: [...mNode.children],
|
|
91
|
-
metadata: { ...mNode.metadata },
|
|
92
|
-
state: { ...mNode.state }
|
|
93
|
-
}
|
|
94
|
-
])
|
|
95
|
-
);
|
|
96
|
-
const graph = {
|
|
97
|
-
id: graphId,
|
|
98
|
-
rootNodeId,
|
|
99
|
-
nodes: frozenNodes,
|
|
100
|
-
edges: [...edges],
|
|
101
|
-
startTime,
|
|
102
|
-
endTime,
|
|
103
|
-
status: graphStatus,
|
|
104
|
-
trigger,
|
|
105
|
-
agentId,
|
|
106
|
-
events: [...events],
|
|
107
|
-
traceId,
|
|
108
|
-
spanId,
|
|
109
|
-
parentSpanId
|
|
110
|
-
};
|
|
111
|
-
return deepFreeze(graph);
|
|
112
|
-
}
|
|
113
|
-
const builder = {
|
|
114
|
-
get graphId() {
|
|
115
|
-
return graphId;
|
|
116
|
-
},
|
|
117
|
-
get traceContext() {
|
|
118
|
-
return { traceId, spanId };
|
|
119
|
-
},
|
|
120
|
-
startNode(opts) {
|
|
121
|
-
assertNotBuilt();
|
|
122
|
-
const id = generateId();
|
|
123
|
-
const parentId = opts.parentId ?? parentStack[parentStack.length - 1] ?? null;
|
|
124
|
-
if (parentId !== null && !nodes.has(parentId)) {
|
|
125
|
-
throw new Error(`GraphBuilder: parent node "${parentId}" does not exist`);
|
|
126
|
-
}
|
|
127
|
-
const node = {
|
|
128
|
-
id,
|
|
129
|
-
type: opts.type,
|
|
130
|
-
name: opts.name,
|
|
131
|
-
startTime: Date.now(),
|
|
132
|
-
endTime: null,
|
|
133
|
-
status: "running",
|
|
134
|
-
parentId,
|
|
135
|
-
children: [],
|
|
136
|
-
metadata: opts.metadata ? { ...opts.metadata } : {},
|
|
137
|
-
state: {}
|
|
138
|
-
};
|
|
139
|
-
nodes.set(id, node);
|
|
140
|
-
if (parentId !== null) {
|
|
141
|
-
const parent = nodes.get(parentId);
|
|
142
|
-
if (parent) {
|
|
143
|
-
parent.children.push(id);
|
|
144
|
-
}
|
|
145
|
-
edges.push({ from: parentId, to: id, type: "spawned" });
|
|
146
|
-
}
|
|
147
|
-
if (rootNodeId === null) {
|
|
148
|
-
rootNodeId = id;
|
|
149
|
-
}
|
|
150
|
-
recordEvent(id, "agent_start", { type: opts.type, name: opts.name });
|
|
151
|
-
return id;
|
|
152
|
-
},
|
|
153
|
-
endNode(nodeId, status = "completed") {
|
|
154
|
-
assertNotBuilt();
|
|
155
|
-
const node = getNode2(nodeId);
|
|
156
|
-
if (node.endTime !== null) {
|
|
157
|
-
throw new Error(
|
|
158
|
-
`GraphBuilder: node "${nodeId}" has already ended (status: ${node.status})`
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
node.endTime = Date.now();
|
|
162
|
-
node.status = status;
|
|
163
|
-
recordEvent(nodeId, "agent_end", { status });
|
|
164
|
-
},
|
|
165
|
-
failNode(nodeId, error) {
|
|
166
|
-
assertNotBuilt();
|
|
167
|
-
const node = getNode2(nodeId);
|
|
168
|
-
if (node.endTime !== null) {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`GraphBuilder: node "${nodeId}" has already ended (status: ${node.status})`
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
const errorMessage = error instanceof Error ? error.message : error;
|
|
174
|
-
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
175
|
-
node.endTime = Date.now();
|
|
176
|
-
node.status = "failed";
|
|
177
|
-
node.metadata.error = errorMessage;
|
|
178
|
-
if (errorStack) {
|
|
179
|
-
node.metadata.errorStack = errorStack;
|
|
180
|
-
}
|
|
181
|
-
recordEvent(nodeId, "tool_error", { error: errorMessage });
|
|
182
|
-
},
|
|
183
|
-
addEdge(from, to, type) {
|
|
184
|
-
assertNotBuilt();
|
|
185
|
-
getNode2(from);
|
|
186
|
-
getNode2(to);
|
|
187
|
-
edges.push({ from, to, type });
|
|
188
|
-
recordEvent(from, "custom", { to, type, action: "edge_add" });
|
|
189
|
-
},
|
|
190
|
-
pushEvent(event) {
|
|
191
|
-
assertNotBuilt();
|
|
192
|
-
getNode2(event.nodeId);
|
|
193
|
-
events.push({
|
|
194
|
-
...event,
|
|
195
|
-
timestamp: Date.now()
|
|
196
|
-
});
|
|
197
|
-
},
|
|
198
|
-
updateState(nodeId, state) {
|
|
199
|
-
assertNotBuilt();
|
|
200
|
-
const node = getNode2(nodeId);
|
|
201
|
-
Object.assign(node.state, state);
|
|
202
|
-
recordEvent(nodeId, "custom", { action: "state_update", ...state });
|
|
203
|
-
},
|
|
204
|
-
withParent(parentId, fn) {
|
|
205
|
-
assertNotBuilt();
|
|
206
|
-
getNode2(parentId);
|
|
207
|
-
parentStack.push(parentId);
|
|
208
|
-
try {
|
|
209
|
-
return fn();
|
|
210
|
-
} finally {
|
|
211
|
-
parentStack.pop();
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
getSnapshot() {
|
|
215
|
-
return buildGraph();
|
|
216
|
-
},
|
|
217
|
-
build() {
|
|
218
|
-
assertNotBuilt();
|
|
219
|
-
const graph = buildGraph();
|
|
220
|
-
built = true;
|
|
221
|
-
return graph;
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
return builder;
|
|
225
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
createGraphBuilder,
|
|
3
|
+
graphToJson,
|
|
4
|
+
loadGraph,
|
|
5
|
+
runTraced
|
|
6
|
+
} from "./chunk-TT5DLU73.js";
|
|
226
7
|
|
|
227
8
|
// src/graph-stitch.ts
|
|
228
9
|
function groupByTraceId(graphs) {
|
|
@@ -440,6 +221,9 @@ export {
|
|
|
440
221
|
getStats,
|
|
441
222
|
getSubtree,
|
|
442
223
|
getTraceTree,
|
|
224
|
+
graphToJson,
|
|
443
225
|
groupByTraceId,
|
|
226
|
+
loadGraph,
|
|
227
|
+
runTraced,
|
|
444
228
|
stitchTrace
|
|
445
229
|
};
|