agentglow 0.2.12 → 0.3.1

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.
@@ -46,6 +46,8 @@ export type WorldEvent = {
46
46
  id: string;
47
47
  tokens_in: number;
48
48
  tokens_out: number;
49
+ tokens_cached?: number;
50
+ tokens_cache_write?: number;
49
51
  latency_ms: number;
50
52
  ts: number;
51
53
  } | {
@@ -88,6 +90,49 @@ export type WorldEvent = {
88
90
  name: string;
89
91
  status: "start" | "end";
90
92
  ts: number;
93
+ } | {
94
+ type: "decision";
95
+ run_id: string;
96
+ id: string;
97
+ kind: DecisionKind;
98
+ question: string;
99
+ result: string;
100
+ p?: number;
101
+ options?: {
102
+ name: string;
103
+ p: number;
104
+ }[];
105
+ provider: string;
106
+ purpose?: string;
107
+ target?: string;
108
+ scope?: string;
109
+ ms: number;
110
+ ts: number;
111
+ hv?: boolean;
112
+ why?: string;
113
+ } | {
114
+ type: "decision_stats";
115
+ run_id: string;
116
+ id: string;
117
+ window_ms: number;
118
+ n: number;
119
+ by_purpose: DecisionStatsByPurpose;
120
+ p50_ms: number;
121
+ p95_ms: number;
122
+ providers: Record<string, number>;
123
+ ts: number;
124
+ } | {
125
+ type: "order";
126
+ run_id: string;
127
+ id: string;
128
+ side: string;
129
+ qty: number;
130
+ price?: number;
131
+ status: OrderStatus;
132
+ instrument: string;
133
+ dry_run: boolean;
134
+ reason?: string;
135
+ ts: number;
91
136
  } | {
92
137
  type: "mcp_register";
93
138
  run_id?: string;
@@ -109,6 +154,123 @@ export type WorldEvent = {
109
154
  resource?: string;
110
155
  resource_kind?: ResourceKind;
111
156
  };
157
+ export type DecisionKind = "choice" | "score" | "noul";
158
+ export type DecisionStatsByPurpose = {
159
+ route?: {
160
+ n: number;
161
+ results: Record<string, number>;
162
+ };
163
+ guard?: {
164
+ n: number;
165
+ allow: number;
166
+ deny: number;
167
+ };
168
+ check?: {
169
+ n: number;
170
+ yes: number;
171
+ no: number;
172
+ };
173
+ };
174
+ export type OrderStatus = "would_place" | "placed" | "filled" | "rejected" | "cancelled";
175
+ /** One order on one agent; `at` = performance.now() when it arrived (its chip plays from there). */
176
+ export type OrderUse = Omit<Extract<WorldEvent, {
177
+ type: "order";
178
+ }>, "type" | "run_id" | "id"> & {
179
+ at: number;
180
+ };
181
+ export declare const ORDER_LIFE_MS = 1500;
182
+ /**
183
+ * High-volume decisions, per agent (from `decision_stats`): smoothed (EMA) so the halo and its label never flicker.
184
+ * `seg` = smoothed share of each outcome category, in HALO_CATS order.
185
+ */
186
+ export type HvStats = {
187
+ at: number;
188
+ bump: number;
189
+ bumpDeny: boolean;
190
+ rate: number;
191
+ deny: number;
192
+ p50: number;
193
+ p95: number;
194
+ seg: number[];
195
+ provider: string;
196
+ n: number;
197
+ windows: number;
198
+ };
199
+ /** outcome categories of a decision halo arc: allow, deny, check yes, check no, route result slots 0..3, other */
200
+ export declare const HALO_CATS: readonly ["allow", "deny", "yes", "no", "r0", "r1", "r2", "r3", "other"];
201
+ export declare const HALO_COLORS: string[];
202
+ /** route result name -> accent slot 0..3 (first four names seen in this session; later ones are "other") */
203
+ export declare const routeSlots: Map<string, number>;
204
+ /** a halo fades out once its agent sent no stats for this long (ms), over HV_FADE_MS */
205
+ export declare const HV_QUIET_MS = 2500;
206
+ export declare const HV_FADE_MS = 1500;
207
+ /** 0..1 visibility of an agent's decision halo */
208
+ export declare function haloMix(h: HvStats | null, now?: number): number;
209
+ /** the agent is in high-volume mode now (recent decision_stats) */
210
+ export declare const hvActive: (i: Instance, now?: number) => boolean;
211
+ /** `jev 42/s · 3% deny · p50 38ms` */
212
+ export declare function haloText(h: HvStats): string;
213
+ /** One decision on one agent; `at` (performance.now()) = when its glyph starts (staggered so a burst reads one by one). */
214
+ export type DecisionUse = Omit<Extract<WorldEvent, {
215
+ type: "decision";
216
+ }>, "type" | "run_id" | "id"> & {
217
+ at: number;
218
+ /** high volume: not shown as a glyph (over the on-screen cap; it only flashed the halo) */
219
+ hidden?: boolean;
220
+ /** high volume: pre-empted by a more important glyph at this time (fades out fast) */
221
+ cut?: number;
222
+ };
223
+ /** Decision glyph timing (ms): snap in fast (much faster than LLM pulses / skill rings on purpose), then hold long
224
+ * enough to read on a video; a guard deny holds longer. */
225
+ export declare const DECISION_SNAP_MS = 150;
226
+ export declare const DECISION_HOLD_MS = 1800;
227
+ export declare const DECISION_LIFE_MS = 2400;
228
+ export declare const DENY_HOLD_MS = 2500;
229
+ export declare const DENY_LIFE_MS = 3100;
230
+ /** min gap between two glyph starts on one agent (a burst of decisions plays as a quick sequence) */
231
+ export declare const DECISION_STAGGER_MS = 220;
232
+ /** high-volume mode (`hv` decisions): short holds so glyphs don't pile up */
233
+ export declare const HV_HOLD_MS = 800;
234
+ export declare const HV_LIFE_MS = 1150;
235
+ /** how long a decision's glyph lives (ms) */
236
+ export declare const decisionLife: (d: {
237
+ kind: string;
238
+ result: string;
239
+ purpose?: string;
240
+ hv?: boolean;
241
+ }) => number;
242
+ /** 0..1 visibility of a decision glyph: snaps in (DECISION_SNAP_MS), holds, fades out by decisionLife(d). */
243
+ export declare function decisionMix(d: DecisionUse, now?: number): number;
244
+ /** a guardrail that said no (shown as a red X / shut gate) */
245
+ export declare const isDeny: (d: {
246
+ kind: string;
247
+ result: string;
248
+ purpose?: string;
249
+ }) => boolean;
250
+ export declare const pct: (p?: number) => string;
251
+ /** short text for a decision: `jev · route → haiku 92%`, `guard: deny rollback_deploy 97%` */
252
+ export declare function decisionText(d: {
253
+ kind: string;
254
+ question: string;
255
+ result: string;
256
+ p?: number;
257
+ provider: string;
258
+ purpose?: string;
259
+ target?: string;
260
+ }): string;
261
+ /** friendly decision-kind badge (display only; events and docs keep the raw kind) */
262
+ export declare const kindBadge: (kind: string) => "YES/NO" | "SCORE" | "PICK";
263
+ /** friendly high-volume reason tag (`why`) */
264
+ export declare const whyBadge: (why: string) => string;
265
+ /** provider badge: code guards read as a rule */
266
+ export declare const providerBadge: (p: string) => string;
267
+ /** `YES 3 @ 42c`, `SELL 10 @ 101.5` (prices below 1 read as cents) */
268
+ export declare function orderText(o: {
269
+ side: string;
270
+ qty: number;
271
+ price?: number;
272
+ status?: string;
273
+ }): string;
112
274
  /** What sits behind an MCP server (the server is a node; its backends are nodes too). */
113
275
  export type ResourceKind = "db" | "warehouse" | "spark" | "api" | "storage" | "queue";
114
276
  export declare const AGENT_TYPES: {
@@ -146,6 +308,7 @@ export type Instance = {
146
308
  pulse: number;
147
309
  pulseAt: number;
148
310
  tokens: number;
311
+ tokensCached: number;
149
312
  index: number;
150
313
  recent: WorldEvent[];
151
314
  llmCalls: number;
@@ -157,6 +320,14 @@ export type Instance = {
157
320
  /** newest started skill ("" = none yet) and when the last active one ended (0 while one is active) */
158
321
  skill: string;
159
322
  skillEndAt: number;
323
+ /** recent decisions (newest last, capped); glyphs play from `at` */
324
+ decisions: DecisionUse[];
325
+ /** high-volume decision stats (null until its first `decision_stats`) */
326
+ hv: HvStats | null;
327
+ /** recent orders (newest last, capped) */
328
+ orders: OrderUse[];
329
+ /** the MCP server this agent called last (a guard deny flashes the agent's line to it) */
330
+ lastMcp?: string;
160
331
  /** opt-in prompt capture: this agent's turns, oldest first (user prompt, then its reply), capped */
161
332
  chat: {
162
333
  role: "user" | "agent";
@@ -329,6 +500,8 @@ export declare const world: {
329
500
  mcpResolved: (McpPending & {
330
501
  resolvedAt: number;
331
502
  })[];
503
+ /** MCP tool name -> the server it was last called on (a guard deny on that tool flashes the line to it) */
504
+ mcpTools: Map<string, string>;
332
505
  ticker: WorldEvent[];
333
506
  stats: {
334
507
  runs: number;
@@ -339,6 +512,35 @@ export declare const world: {
339
512
  graphReads: number;
340
513
  graphWrites: number;
341
514
  mcpCalls: number;
515
+ decisions: number;
516
+ decisionMs: number;
517
+ };
518
+ /** decisions per provider (jev / laya / llm ...): count + summed latency (HUD chip tooltip) */
519
+ decisionProviders: Map<string, {
520
+ n: number;
521
+ ms: number;
522
+ }>;
523
+ /** session-wide decision rate (HUD): last full second's rate, deny share, p50/p95, 60 s sparkline (per second) */
524
+ rate: {
525
+ perS: number;
526
+ deny: number;
527
+ p50: number;
528
+ p95: number;
529
+ spark: number[];
530
+ rollAt: number;
531
+ acc: {
532
+ n: number;
533
+ deny: number;
534
+ msN: number;
535
+ p50: number;
536
+ p95: number;
537
+ };
538
+ };
539
+ /** orders seen: total, paper (dry_run), rejected/cancelled */
540
+ orders: {
541
+ n: number;
542
+ paper: number;
543
+ rejected: number;
342
544
  };
343
545
  lastFinal: string;
344
546
  simulated: boolean;
@@ -368,7 +570,28 @@ export declare const world: {
368
570
  selected: string | null;
369
571
  /** the server answered 401 for this scope/run/token (the HUD shows a notice; no simulator fallback) */
370
572
  unauthorized: boolean;
573
+ /** desk-wide halts (global-scope guard denies, see Halt), keyed by the owning agent */
574
+ halts: Map<string, Halt>;
371
575
  };
576
+ /**
577
+ * A desk-wide halt: a guard decision with `scope: "global"` (docs/SPEC.md "Decisions") said no, e.g. a kill switch.
578
+ * Owned by the topmost agent that reports global guards (a desk), drawn ONCE on it (red, `HALTED · <reason>`) instead
579
+ * of a red X on every agent below it; their own global denies only flash their halos. Ends when every global guard
580
+ * the owner said no to says yes again, or the owner / its run ends.
581
+ */
582
+ export type Halt = {
583
+ id: string;
584
+ run: string;
585
+ reason: string;
586
+ since: number;
587
+ end: number;
588
+ qs: Map<string, boolean>;
589
+ };
590
+ /** the halt shown on agent `id` right now (also while it fades out after the end), if any */
591
+ export declare function haltOn(id: string, now?: number): Halt | undefined;
592
+ export declare const HALT_FADE_MS = 900;
593
+ /** some agent is halted now (HUD chip) */
594
+ export declare function haltedNow(now?: number): Halt | undefined;
372
595
  /** Subscribe a React component to world changes (HUD / DOM). Scenes should read `world` in useFrame instead. */
373
596
  export declare function useWorld(): {
374
597
  runs: Map<string, Run>;
@@ -389,6 +612,8 @@ export declare function useWorld(): {
389
612
  mcpResolved: (McpPending & {
390
613
  resolvedAt: number;
391
614
  })[];
615
+ /** MCP tool name -> the server it was last called on (a guard deny on that tool flashes the line to it) */
616
+ mcpTools: Map<string, string>;
392
617
  ticker: WorldEvent[];
393
618
  stats: {
394
619
  runs: number;
@@ -399,6 +624,35 @@ export declare function useWorld(): {
399
624
  graphReads: number;
400
625
  graphWrites: number;
401
626
  mcpCalls: number;
627
+ decisions: number;
628
+ decisionMs: number;
629
+ };
630
+ /** decisions per provider (jev / laya / llm ...): count + summed latency (HUD chip tooltip) */
631
+ decisionProviders: Map<string, {
632
+ n: number;
633
+ ms: number;
634
+ }>;
635
+ /** session-wide decision rate (HUD): last full second's rate, deny share, p50/p95, 60 s sparkline (per second) */
636
+ rate: {
637
+ perS: number;
638
+ deny: number;
639
+ p50: number;
640
+ p95: number;
641
+ spark: number[];
642
+ rollAt: number;
643
+ acc: {
644
+ n: number;
645
+ deny: number;
646
+ msN: number;
647
+ p50: number;
648
+ p95: number;
649
+ };
650
+ };
651
+ /** orders seen: total, paper (dry_run), rejected/cancelled */
652
+ orders: {
653
+ n: number;
654
+ paper: number;
655
+ rejected: number;
402
656
  };
403
657
  lastFinal: string;
404
658
  simulated: boolean;
@@ -428,9 +682,13 @@ export declare function useWorld(): {
428
682
  selected: string | null;
429
683
  /** the server answered 401 for this scope/run/token (the HUD shows a notice; no simulator fallback) */
430
684
  unauthorized: boolean;
685
+ /** desk-wide halts (global-scope guard denies, see Halt), keyed by the owning agent */
686
+ halts: Map<string, Halt>;
431
687
  };
432
688
  /** Stable pseudo-random 0..1 from a string (same id → same value, different ids → different values). */
433
689
  export declare const hash01: (id: string, salt?: number) => number;
690
+ /** high volume: most individual decision glyphs on screen at once (all agents) */
691
+ export declare const HV_GLYPHS_MAX = 3;
434
692
  export declare function apply(ev: WorldEvent): void;
435
693
  export declare function tick(now?: number): void;
436
694
  /** 0..1 visibility for an instance: grows in on spawn, fades out after exit. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentglow",
3
- "version": "0.2.12",
3
+ "version": "0.3.1",
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",