agentglow 0.3.1 → 0.4.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.
@@ -1,3 +1,5 @@
1
+ import { type Backlog, type PrimEdge, type PrimState, type PrimWorldEvent, type ResStat } from "./prims";
2
+ export type { PrimState, PrimWorldEvent } from "./prims";
1
3
  export type AgentType = "planner" | "researcher" | "graph_scout" | "records_scout" | "data_scout" | "writer";
2
4
  /** Any workflow-defined step name (the backend scrubs and caps it). */
3
5
  export type StepName = string;
@@ -10,7 +12,14 @@ export type WorldEvent = {
10
12
  topic: string;
11
13
  workflow: string;
12
14
  ts: number;
15
+ reason?: string;
13
16
  } | {
17
+ type: "run";
18
+ run_id: string;
19
+ status: "idle" | "active";
20
+ since?: number;
21
+ ts: number;
22
+ } | ({
14
23
  type: "step";
15
24
  run_id: string;
16
25
  step: StepName;
@@ -18,13 +27,15 @@ export type WorldEvent = {
18
27
  reason?: string;
19
28
  until?: number;
20
29
  ts: number;
21
- } | {
30
+ } & WaitExtra) | {
22
31
  type: "spawn";
23
32
  run_id: string;
24
33
  id: string;
25
34
  agent: string;
26
35
  parent_id: string | null;
27
36
  subagent?: boolean;
37
+ job?: boolean;
38
+ since?: number;
28
39
  ts: number;
29
40
  } | {
30
41
  type: "exit";
@@ -32,7 +43,7 @@ export type WorldEvent = {
32
43
  id: string;
33
44
  status: "done" | "failed";
34
45
  ts: number;
35
- } | {
46
+ } | ({
36
47
  type: "agent";
37
48
  run_id: string;
38
49
  id: string;
@@ -40,7 +51,7 @@ export type WorldEvent = {
40
51
  reason?: string;
41
52
  until?: number;
42
53
  ts: number;
43
- } | {
54
+ } & WaitExtra) | {
44
55
  type: "llm";
45
56
  run_id: string;
46
57
  id: string;
@@ -57,6 +68,7 @@ export type WorldEvent = {
57
68
  to_id: string;
58
69
  text: string;
59
70
  ts: number;
71
+ failed?: boolean;
60
72
  } | {
61
73
  type: "tool";
62
74
  run_id: string;
@@ -106,6 +118,7 @@ export type WorldEvent = {
106
118
  purpose?: string;
107
119
  target?: string;
108
120
  scope?: string;
121
+ threshold?: number;
109
122
  ms: number;
110
123
  ts: number;
111
124
  hv?: boolean;
@@ -121,6 +134,40 @@ export type WorldEvent = {
121
134
  p95_ms: number;
122
135
  providers: Record<string, number>;
123
136
  ts: number;
137
+ } | {
138
+ type: "request";
139
+ run_id: string;
140
+ id: string;
141
+ service: string;
142
+ name: string;
143
+ kind: "http" | "rpc" | "message" | "event";
144
+ status?: number;
145
+ error: boolean;
146
+ rejected?: boolean;
147
+ ms: number;
148
+ ts: number;
149
+ hv?: boolean;
150
+ } | {
151
+ type: "service_stats";
152
+ run_id: string;
153
+ id: string;
154
+ service: string;
155
+ window_ms: number;
156
+ n: number;
157
+ errors: number;
158
+ codes: Record<string, number>;
159
+ p50_ms: number;
160
+ p95_ms: number;
161
+ routes: Record<string, number>;
162
+ ts: number;
163
+ instances?: number;
164
+ inflight?: number;
165
+ } | {
166
+ type: "drives";
167
+ run_id: string;
168
+ id: string;
169
+ target_run: string;
170
+ ts: number;
124
171
  } | {
125
172
  type: "order";
126
173
  run_id: string;
@@ -153,8 +200,33 @@ export type WorldEvent = {
153
200
  ts: number;
154
201
  resource?: string;
155
202
  resource_kind?: ResourceKind;
156
- };
203
+ units?: number;
204
+ unit?: string;
205
+ device?: string;
206
+ } | PrimWorldEvent;
157
207
  export type DecisionKind = "choice" | "score" | "noul";
208
+ /** The decision that triggered a wait (docs/SPEC.md "Human approval"): a summary of its `decision` event. */
209
+ export type WaitBecause = {
210
+ id?: string;
211
+ kind: string;
212
+ question: string;
213
+ result: string;
214
+ p?: number;
215
+ provider?: string;
216
+ purpose?: string;
217
+ target?: string;
218
+ threshold?: number;
219
+ ms?: number;
220
+ ts?: number;
221
+ };
222
+ /** A declared wait's drawer fields (`agentglow.wait.*`): `kind` "approval" = a human approval ("Needs you"). */
223
+ export type WaitExtra = {
224
+ kind?: string;
225
+ title?: string;
226
+ details?: Record<string, string | number | boolean>;
227
+ url?: string;
228
+ because?: WaitBecause;
229
+ };
158
230
  export type DecisionStatsByPurpose = {
159
231
  route?: {
160
232
  n: number;
@@ -195,6 +267,9 @@ export type HvStats = {
195
267
  provider: string;
196
268
  n: number;
197
269
  windows: number;
270
+ unit?: "req" | "msg";
271
+ instances?: number;
272
+ inflight?: number;
198
273
  };
199
274
  /** outcome categories of a decision halo arc: allow, deny, check yes, check no, route result slots 0..3, other */
200
275
  export declare const HALO_CATS: readonly ["allow", "deny", "yes", "no", "r0", "r1", "r2", "r3", "other"];
@@ -206,10 +281,27 @@ export declare const HV_QUIET_MS = 2500;
206
281
  export declare const HV_FADE_MS = 1500;
207
282
  /** 0..1 visibility of an agent's decision halo */
208
283
  export declare function haloMix(h: HvStats | null, now?: number): number;
284
+ /** a backend service with no traffic for this long (ms) reads as idle: dimmed, label `feed · idle` */
285
+ export declare const SVC_IDLE_MS = 10000;
286
+ /** a top-level backend service agent (`svc:` id) */
287
+ export declare const isSvc: (i: Instance) => boolean;
288
+ /** a backend service with no requests / messages in the last SVC_IDLE_MS (or none since it appeared) */
289
+ export declare const svcIdle: (i: Instance, now?: number) => boolean;
209
290
  /** the agent is in high-volume mode now (recent decision_stats) */
210
291
  export declare const hvActive: (i: Instance, now?: number) => boolean;
211
- /** `jev 42/s · 3% deny · p50 38ms` */
292
+ /** The halo label: rate + error / deny share (only when > 0): `jev 42/s · 3% deny`; a service: `42 req/s · 2% errors`
293
+ * (`msg/s` for a consumer). Latency lives in the Selected panel and the label's hover tooltip (`haloLatency`). */
212
294
  export declare function haloText(h: HvStats): string;
295
+ /** `3% deny` / `2% errors` / "" when none */
296
+ export declare function haloShare(h: HvStats): string;
297
+ /** `p50 38ms · p95 120ms` (+ `· 3 in flight` for a service) */
298
+ export declare function haloLatency(h: HvStats): string;
299
+ /** `2m14s` / `38s` / `1h05m` */
300
+ export declare function elapsedText(ms: number): string;
301
+ /** a job's name label: `mkt:tick · 2m14s` (frozen at its end) */
302
+ export declare const jobText: (i: Instance) => string;
303
+ /** a job's end flash 0..1 (green done / red failed), ~1.2 s */
304
+ export declare const flashMix: (i: Instance, now?: number) => number;
213
305
  /** One decision on one agent; `at` (performance.now()) = when its glyph starts (staggered so a burst reads one by one). */
214
306
  export type DecisionUse = Omit<Extract<WorldEvent, {
215
307
  type: "decision";
@@ -272,7 +364,10 @@ export declare function orderText(o: {
272
364
  status?: string;
273
365
  }): string;
274
366
  /** What sits behind an MCP server (the server is a node; its backends are nodes too). */
275
- export type ResourceKind = "db" | "warehouse" | "spark" | "api" | "storage" | "queue";
367
+ export type ResourceKind = "db" | "warehouse" | "spark" | "api" | "storage" | "queue" | "model" | "gpu" | "worker" | "cache";
368
+ /** the six shapes every theme draws: newer kinds map onto the closest one (model / gpu -> spark, worker -> storage, cache -> db) */
369
+ export type ShapeKind = "db" | "warehouse" | "spark" | "api" | "storage" | "queue";
370
+ export declare const shapeKind: (k: string | undefined) => ShapeKind;
276
371
  export declare const AGENT_TYPES: {
277
372
  type: AgentType;
278
373
  label: string;
@@ -335,12 +430,34 @@ export type Instance = {
335
430
  }[];
336
431
  /** what it is waiting on (status "waiting" with a reason), else null */
337
432
  wait: Wait | null;
433
+ /** backend service agent: requests / errors handled (from `service_stats`) */
434
+ svcN?: number;
435
+ svcErr?: number;
436
+ /** backend service agent: performance.now() of its last traffic (requests / messages in or out); see svcIdle */
437
+ svcAt?: number;
438
+ /** a long-running request of a service shown as its subagent: started / ended (epoch ms, end 0 while open) */
439
+ job?: {
440
+ since: number;
441
+ end: number;
442
+ };
443
+ /** a job just ended: its halo flashes green (ok) / red (performance.now()) */
444
+ flash?: {
445
+ at: number;
446
+ ok: boolean;
447
+ };
448
+ /** generic primitives (prims.ts): created on the instance's first primitive event */
449
+ prim?: PrimState;
338
450
  };
339
451
  /** A declared wait: what it waits on ("approval", "sleep", an event key) and its deadline / wake-up (epoch ms, 0 = none). */
340
452
  export type Wait = {
341
453
  reason: string;
342
454
  until: number;
343
- };
455
+ } & WaitExtra;
456
+ /** reason / until + the drawer fields of a waiting `step` / `agent` event */
457
+ export declare function waitOf(ev: {
458
+ reason?: string;
459
+ until?: number;
460
+ } & WaitExtra): Wait;
344
461
  /** one skill on one agent; startAt / endAt (performance.now()) drive its sigil ring (endAt 0 while active) */
345
462
  export type SkillUse = {
346
463
  active: boolean;
@@ -387,6 +504,10 @@ export type Run = {
387
504
  /** the step that finished most recently (handoff source) */
388
505
  lastDone: StepName;
389
506
  final: string;
507
+ /** performance.now() of the run's latest event (stale detection) */
508
+ lastEventAt: number;
509
+ /** server-declared idle since (epoch ms of its last event), 0 = active (isIdle / idleText) */
510
+ idleSince: number;
390
511
  };
391
512
  /** Which of the 3 scene slots a step is drawn in (see STEP_SLOTS). */
392
513
  export declare function stepSlot(r: Run, step: StepName): number;
@@ -400,6 +521,23 @@ export declare function runWait(r: Run): (Wait & {
400
521
  step: StepName;
401
522
  }) | null;
402
523
  /** "sleeping until 14:05" / "waiting on approval" / "waiting on approval (until Oct 3, 09:00)" */
524
+ /** A live run is stale when nothing arrived from it for STALE_QUIET_MS while a wait of it is past its deadline by
525
+ * STALE_GRACE_MS (e.g. its worker restarted mid-run: nobody will resolve the wait or end the run). */
526
+ export declare const STALE_QUIET_MS = 20000;
527
+ export declare const STALE_GRACE_MS = 15000;
528
+ /** Idle run (server `run` status "idle"): its agents are dimmed (IDLE_DIM) and its label says "idle · 4m". */
529
+ export declare const IDLE_DIM = 0.55;
530
+ export declare const isIdle: (r: Run | undefined) => boolean;
531
+ export declare function idleText(r: Run, wall?: number): string;
532
+ export declare const isDismissed: (runId: string | undefined) => boolean;
533
+ /** Hide a run (and its agents) for this viewer. */
534
+ export declare function dismissRun(runId: string): void;
535
+ /** Show the given runs again (all dismissed ones when omitted). */
536
+ export declare function undismissRuns(ids?: string[]): void;
537
+ /** Dismissed runs that are still in the world (the HUD's "N hidden" chip). */
538
+ export declare const dismissedRuns: () => string[];
539
+ export declare function isStale(r: Run | undefined, now?: number, wall?: number): boolean;
540
+ export declare const STALE_TEXT = "stale: no worker activity";
403
541
  export declare function waitLabel(w: Wait, now?: number): string;
404
542
  export declare function stepChips(r: Run, max?: number): {
405
543
  shown: StepName[];
@@ -413,11 +551,21 @@ export type Comet = {
413
551
  start: number;
414
552
  dur: number;
415
553
  text: string;
554
+ failed?: boolean;
416
555
  };
556
+ /** a failed comet (a publish that raised) sputters out half way: it stalls here and is gone at FIZZLE_END of its life */
557
+ export declare const FIZZLE_STALL = 0.55;
558
+ export declare const FIZZLE_END = 0.8;
559
+ /** path parameter (0..1, before a theme's easing) of a comet: a failed one decelerates to the midpoint and stops */
560
+ export declare function cometPos(c: Comet, now?: number): number;
561
+ /** the comet head is still drawn (a failed one never arrives: no arrival flash) */
562
+ export declare const cometOn: (c: Comet, now?: number) => boolean;
417
563
  /** External MCP servers agents call (persistent "satellites"; registered on first use). */
564
+ /** `kind` = the shape themes draw (shapeKind), `sub` = the reported kind (model, gpu, worker, cache, ...) */
418
565
  export type McpResource = {
419
566
  name: string;
420
- kind: ResourceKind;
567
+ kind: ShapeKind;
568
+ sub: ResourceKind;
421
569
  activeAt: number;
422
570
  inflight: number;
423
571
  calls: number;
@@ -514,6 +662,9 @@ export declare const world: {
514
662
  mcpCalls: number;
515
663
  decisions: number;
516
664
  decisionMs: number;
665
+ requests: number;
666
+ errors: number;
667
+ rejected: number;
517
668
  };
518
669
  /** decisions per provider (jev / laya / llm ...): count + summed latency (HUD chip tooltip) */
519
670
  decisionProviders: Map<string, {
@@ -572,7 +723,32 @@ export declare const world: {
572
723
  unauthorized: boolean;
573
724
  /** desk-wide halts (global-scope guard denies, see Halt), keyed by the owning agent */
574
725
  halts: Map<string, Halt>;
726
+ /** transient dashed edges: fallbacks and completed deferred callbacks (prims.ts) */
727
+ primEdges: PrimEdge[];
728
+ /** broker backlog per topic (prims.ts) */
729
+ backlogs: Map<string, Backlog>;
730
+ /** resource stats (pools, caches, models) keyed `server|resource` */
731
+ resStats: Map<string, ResStat>;
732
+ /** message topics between two services (`mkt:tick` feed -> worker), keyed `from|to|topic`: a persistent edge */
733
+ topics: Map<string, SvcTopic>;
734
+ /** service -> agent run it drives (`drives`), keyed `svc|run` */
735
+ drives: Map<string, {
736
+ svc: string;
737
+ run: string;
738
+ }>;
739
+ };
740
+ /** A topic between two services: first / last message (performance.now()), messages seen, the last one failed. */
741
+ export type SvcTopic = {
742
+ from: string;
743
+ to: string;
744
+ topic: string;
745
+ first: number;
746
+ at: number;
747
+ n: number;
748
+ failed: boolean;
575
749
  };
750
+ /** a topic edge stays drawn this long after its last message */
751
+ export declare const TOPIC_STALE_MS = 15000;
576
752
  /**
577
753
  * A desk-wide halt: a guard decision with `scope: "global"` (docs/SPEC.md "Decisions") said no, e.g. a kill switch.
578
754
  * Owned by the topmost agent that reports global guards (a desk), drawn ONCE on it (red, `HALTED · <reason>`) instead
@@ -626,6 +802,9 @@ export declare function useWorld(): {
626
802
  mcpCalls: number;
627
803
  decisions: number;
628
804
  decisionMs: number;
805
+ requests: number;
806
+ errors: number;
807
+ rejected: number;
629
808
  };
630
809
  /** decisions per provider (jev / laya / llm ...): count + summed latency (HUD chip tooltip) */
631
810
  decisionProviders: Map<string, {
@@ -684,6 +863,19 @@ export declare function useWorld(): {
684
863
  unauthorized: boolean;
685
864
  /** desk-wide halts (global-scope guard denies, see Halt), keyed by the owning agent */
686
865
  halts: Map<string, Halt>;
866
+ /** transient dashed edges: fallbacks and completed deferred callbacks (prims.ts) */
867
+ primEdges: PrimEdge[];
868
+ /** broker backlog per topic (prims.ts) */
869
+ backlogs: Map<string, Backlog>;
870
+ /** resource stats (pools, caches, models) keyed `server|resource` */
871
+ resStats: Map<string, ResStat>;
872
+ /** message topics between two services (`mkt:tick` feed -> worker), keyed `from|to|topic`: a persistent edge */
873
+ topics: Map<string, SvcTopic>;
874
+ /** service -> agent run it drives (`drives`), keyed `svc|run` */
875
+ drives: Map<string, {
876
+ svc: string;
877
+ run: string;
878
+ }>;
687
879
  };
688
880
  /** Stable pseudo-random 0..1 from a string (same id → same value, different ids → different values). */
689
881
  export declare const hash01: (id: string, salt?: number) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentglow",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Live 3D views of agent systems (LangGraph, deepagents, any OpenTelemetry-instrumented agents) as a React component.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -12,7 +12,8 @@
12
12
  "deepagents",
13
13
  "react",
14
14
  "observability",
15
- "react-three-fiber"
15
+ "react-three-fiber",
16
+ "nodejs"
16
17
  ],
17
18
  "license": "MIT",
18
19
  "repository": {
@@ -34,6 +35,17 @@
34
35
  "import": "./dist/index.js",
35
36
  "default": "./dist/index.js"
36
37
  },
38
+ "./pulse": {
39
+ "types": "./dist/types/pulse.d.ts",
40
+ "import": "./dist/pulse.js",
41
+ "default": "./dist/pulse.js"
42
+ },
43
+ "./node": {
44
+ "types": "./dist/types/node.d.ts",
45
+ "node": "./dist/node.js",
46
+ "import": "./dist/node.js",
47
+ "default": "./dist/node.js"
48
+ },
37
49
  "./style.css": "./dist/style.css",
38
50
  "./package.json": "./package.json"
39
51
  },
@@ -64,7 +76,37 @@
64
76
  },
65
77
  "peerDependencies": {
66
78
  "react": "^19.0.0",
67
- "react-dom": "^19.0.0"
79
+ "react-dom": "^19.0.0",
80
+ "@opentelemetry/api": "^1.9.0",
81
+ "@opentelemetry/exporter-trace-otlp-http": ">=0.200.0 <1",
82
+ "@opentelemetry/instrumentation": ">=0.200.0 <1",
83
+ "@opentelemetry/instrumentation-http": ">=0.200.0 <1",
84
+ "@opentelemetry/instrumentation-undici": ">=0.10.0 <1",
85
+ "@opentelemetry/resources": "^2.0.0",
86
+ "@opentelemetry/sdk-trace-node": "^2.0.0"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "@opentelemetry/api": {
90
+ "optional": true
91
+ },
92
+ "@opentelemetry/exporter-trace-otlp-http": {
93
+ "optional": true
94
+ },
95
+ "@opentelemetry/instrumentation": {
96
+ "optional": true
97
+ },
98
+ "@opentelemetry/instrumentation-http": {
99
+ "optional": true
100
+ },
101
+ "@opentelemetry/instrumentation-undici": {
102
+ "optional": true
103
+ },
104
+ "@opentelemetry/resources": {
105
+ "optional": true
106
+ },
107
+ "@opentelemetry/sdk-trace-node": {
108
+ "optional": true
109
+ }
68
110
  },
69
111
  "dependencies": {
70
112
  "@react-three/drei": "^10.7.9",
@@ -74,13 +116,20 @@
74
116
  "three": "^0.186.1"
75
117
  },
76
118
  "devDependencies": {
119
+ "@opentelemetry/api": "^1.9.1",
120
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
121
+ "@opentelemetry/instrumentation": "^0.222.0",
122
+ "@opentelemetry/instrumentation-http": "^0.222.0",
123
+ "@opentelemetry/instrumentation-undici": "^0.32.0",
124
+ "@opentelemetry/resources": "^2.11.0",
125
+ "@opentelemetry/sdk-trace-node": "^2.11.0",
77
126
  "@types/node": "^22.20.4",
78
127
  "@types/react": "^19.0.0",
79
128
  "@types/react-dom": "^19.0.0",
80
129
  "@vitejs/plugin-react": "^4.0.0",
81
- "typescript": "^5.6.0",
82
- "vite": "^6.0.0",
83
130
  "react": "^19.0.0",
84
- "react-dom": "^19.0.0"
131
+ "react-dom": "^19.0.0",
132
+ "typescript": "^5.6.0",
133
+ "vite": "^6.0.0"
85
134
  }
86
135
  }