@warmdrift/kgauto-compiler 2.0.0-alpha.7 → 2.0.0-alpha.70

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.
Files changed (55) hide show
  1. package/README.md +176 -46
  2. package/dist/brain-proxy.d.mts +113 -0
  3. package/dist/brain-proxy.d.ts +113 -0
  4. package/dist/brain-proxy.js +193 -0
  5. package/dist/brain-proxy.mjs +6 -0
  6. package/dist/chunk-4UO4CCSP.mjs +1620 -0
  7. package/dist/chunk-65ZMX5OT.mjs +169 -0
  8. package/dist/{chunk-5TI6PNSK.mjs → chunk-BVEXV5KC.mjs} +11 -0
  9. package/dist/chunk-FR4DNGLW.mjs +203 -0
  10. package/dist/chunk-NBO4R5PC.mjs +313 -0
  11. package/dist/chunk-P3TOAEG4.mjs +56 -0
  12. package/dist/chunk-RO22VFIF.mjs +29 -0
  13. package/dist/chunk-SBFSYCQG.mjs +719 -0
  14. package/dist/dialect.d.mts +41 -3
  15. package/dist/dialect.d.ts +41 -3
  16. package/dist/dialect.js +14 -2
  17. package/dist/dialect.mjs +5 -3
  18. package/dist/glassbox/index.d.mts +59 -0
  19. package/dist/glassbox/index.d.ts +59 -0
  20. package/dist/glassbox/index.js +312 -0
  21. package/dist/glassbox/index.mjs +12 -0
  22. package/dist/glassbox-routes/format.d.mts +24 -0
  23. package/dist/glassbox-routes/format.d.ts +24 -0
  24. package/dist/glassbox-routes/format.js +86 -0
  25. package/dist/glassbox-routes/format.mjs +18 -0
  26. package/dist/glassbox-routes/index.d.mts +191 -0
  27. package/dist/glassbox-routes/index.d.ts +191 -0
  28. package/dist/glassbox-routes/index.js +2888 -0
  29. package/dist/glassbox-routes/index.mjs +667 -0
  30. package/dist/glassbox-routes/react/index.d.mts +74 -0
  31. package/dist/glassbox-routes/react/index.d.ts +74 -0
  32. package/dist/glassbox-routes/react/index.js +819 -0
  33. package/dist/glassbox-routes/react/index.mjs +754 -0
  34. package/dist/index.d.mts +2739 -17
  35. package/dist/index.d.ts +2739 -17
  36. package/dist/index.js +8989 -1659
  37. package/dist/index.mjs +5078 -367
  38. package/dist/ir-Bqn1RVdV.d.mts +1583 -0
  39. package/dist/ir-Bvlkw5ja.d.ts +1583 -0
  40. package/dist/key-health.d.mts +131 -0
  41. package/dist/key-health.d.ts +131 -0
  42. package/dist/key-health.js +228 -0
  43. package/dist/key-health.mjs +6 -0
  44. package/dist/profiles.d.mts +292 -2
  45. package/dist/profiles.d.ts +292 -2
  46. package/dist/profiles.js +1231 -16
  47. package/dist/profiles.mjs +9 -1
  48. package/dist/types-Bon96eyc.d.ts +131 -0
  49. package/dist/types-CwGhacGT.d.mts +149 -0
  50. package/dist/types-DIxRZ3Dj.d.ts +149 -0
  51. package/dist/types-DsEq35WY.d.mts +131 -0
  52. package/package.json +54 -8
  53. package/dist/chunk-MBEI5UOM.mjs +0 -409
  54. package/dist/profiles-B3eNQ2py.d.ts +0 -619
  55. package/dist/profiles-Py8c7zjJ.d.mts +0 -619
@@ -12,8 +12,15 @@
12
12
  * The brain learns by `(archetype, model, shape)` tuples. Apps that declare
13
13
  * the same tuple inherit each other's mutations.
14
14
  *
15
- * v1 is intentionally small (9 archetypes, 5 shape dimensions). New entries
16
- * arrive in v2/v3 from real usage signals — never speculatively.
15
+ * v1 is intentionally small (10 archetypes, 5 shape dimensions). New entries
16
+ * arrive from real usage signals — never speculatively.
17
+ *
18
+ * Versioning note (alpha.62): `judge` was added as an ADDITIVE 10th entry
19
+ * under dialect-v1 rather than bumping to v2. Bumping the version string
20
+ * would fragment every existing learning key (`v1::…` → `v2::…`) for a
21
+ * purely additive vocabulary change; existing keys are untouched and judge
22
+ * keys are new, so no collision is possible. A v2 bump is reserved for
23
+ * changes that alter the MEANING of existing entries or the shape hash.
17
24
  */
18
25
  declare const DIALECT_VERSION: "v1";
19
26
  declare const INTENT_ARCHETYPES: {
@@ -62,6 +69,11 @@ declare const INTENT_ARCHETYPES: {
62
69
  readonly description: "Change format or style while preserving content";
63
70
  readonly examples: ["markdown → html", "formal → casual", "translate"];
64
71
  };
72
+ readonly judge: {
73
+ readonly name: "judge";
74
+ readonly description: "Pairwise comparison of two candidate outputs for the same input";
75
+ readonly examples: ["golden-set eval A/B verdict", "model-swap non-inferiority check", "response bake-off"];
76
+ };
65
77
  };
66
78
  type IntentArchetypeName = keyof typeof INTENT_ARCHETYPES;
67
79
  declare const ALL_ARCHETYPES: IntentArchetypeName[];
@@ -82,6 +94,32 @@ interface ShapeSignature {
82
94
  /** Whether the prompt includes few-shot examples. */
83
95
  hasExamples: boolean;
84
96
  }
97
+ /**
98
+ * SINGLE source of truth for a call's output shape.
99
+ *
100
+ * Two independent expressions of this concept used to exist — `computeShape`
101
+ * (three-valued, feeding `compile_outcomes.shape_key`) and compile()'s Factor C
102
+ * screen (two-valued). They disagreed on exactly one case: tools present with
103
+ * no `structuredOutput`, which the shape signature calls `tool_call` and the
104
+ * screen called `text`. That gap let alpha.68's shape-altering discipline gates
105
+ * fire on tool-call surfaces — the surface class the design contract names as
106
+ * forbidden, because labelling claims inside a tool-call envelope manufactures
107
+ * the structured-output violations alpha.66 ships a retry for.
108
+ *
109
+ * A consumer `declared` value WINS over inference. The inference cannot see
110
+ * that an agentic surface carrying tools ultimately emits prose, so it is
111
+ * deliberately conservative — wrongly firing a shape-altering rule breaks a
112
+ * parser, wrongly withholding one only forgoes a lift. `constraints.outputMode`
113
+ * is the escape hatch for surfaces that know better.
114
+ *
115
+ * Takes the facts rather than a `PromptIR` so `dialect.ts` stays
116
+ * dependency-free (`ir.ts` imports THIS module, not the reverse).
117
+ */
118
+ declare function resolveOutputMode(args: {
119
+ declared?: OutputMode;
120
+ structuredOutput?: boolean;
121
+ toolCount: number;
122
+ }): OutputMode;
85
123
  declare function bucketContext(tokens: number): ContextBucket;
86
124
  declare function bucketToolCount(count: number): ToolCountBucket;
87
125
  declare function bucketHistory(turnCount: number): HistoryDepth;
@@ -96,4 +134,4 @@ declare function hashShape(s: ShapeSignature): string;
96
134
  */
97
135
  declare function learningKey(archetype: IntentArchetypeName, model: string, shape: ShapeSignature): string;
98
136
 
99
- export { ALL_ARCHETYPES, type ContextBucket, DIALECT_VERSION, type HistoryDepth, INTENT_ARCHETYPES, type IntentArchetypeName, type OutputMode, type ShapeSignature, type ToolCountBucket, bucketContext, bucketHistory, bucketToolCount, hashShape, isArchetype, learningKey };
137
+ export { ALL_ARCHETYPES, type ContextBucket, DIALECT_VERSION, type HistoryDepth, INTENT_ARCHETYPES, type IntentArchetypeName, type OutputMode, type ShapeSignature, type ToolCountBucket, bucketContext, bucketHistory, bucketToolCount, hashShape, isArchetype, learningKey, resolveOutputMode };
package/dist/dialect.d.ts CHANGED
@@ -12,8 +12,15 @@
12
12
  * The brain learns by `(archetype, model, shape)` tuples. Apps that declare
13
13
  * the same tuple inherit each other's mutations.
14
14
  *
15
- * v1 is intentionally small (9 archetypes, 5 shape dimensions). New entries
16
- * arrive in v2/v3 from real usage signals — never speculatively.
15
+ * v1 is intentionally small (10 archetypes, 5 shape dimensions). New entries
16
+ * arrive from real usage signals — never speculatively.
17
+ *
18
+ * Versioning note (alpha.62): `judge` was added as an ADDITIVE 10th entry
19
+ * under dialect-v1 rather than bumping to v2. Bumping the version string
20
+ * would fragment every existing learning key (`v1::…` → `v2::…`) for a
21
+ * purely additive vocabulary change; existing keys are untouched and judge
22
+ * keys are new, so no collision is possible. A v2 bump is reserved for
23
+ * changes that alter the MEANING of existing entries or the shape hash.
17
24
  */
18
25
  declare const DIALECT_VERSION: "v1";
19
26
  declare const INTENT_ARCHETYPES: {
@@ -62,6 +69,11 @@ declare const INTENT_ARCHETYPES: {
62
69
  readonly description: "Change format or style while preserving content";
63
70
  readonly examples: ["markdown → html", "formal → casual", "translate"];
64
71
  };
72
+ readonly judge: {
73
+ readonly name: "judge";
74
+ readonly description: "Pairwise comparison of two candidate outputs for the same input";
75
+ readonly examples: ["golden-set eval A/B verdict", "model-swap non-inferiority check", "response bake-off"];
76
+ };
65
77
  };
66
78
  type IntentArchetypeName = keyof typeof INTENT_ARCHETYPES;
67
79
  declare const ALL_ARCHETYPES: IntentArchetypeName[];
@@ -82,6 +94,32 @@ interface ShapeSignature {
82
94
  /** Whether the prompt includes few-shot examples. */
83
95
  hasExamples: boolean;
84
96
  }
97
+ /**
98
+ * SINGLE source of truth for a call's output shape.
99
+ *
100
+ * Two independent expressions of this concept used to exist — `computeShape`
101
+ * (three-valued, feeding `compile_outcomes.shape_key`) and compile()'s Factor C
102
+ * screen (two-valued). They disagreed on exactly one case: tools present with
103
+ * no `structuredOutput`, which the shape signature calls `tool_call` and the
104
+ * screen called `text`. That gap let alpha.68's shape-altering discipline gates
105
+ * fire on tool-call surfaces — the surface class the design contract names as
106
+ * forbidden, because labelling claims inside a tool-call envelope manufactures
107
+ * the structured-output violations alpha.66 ships a retry for.
108
+ *
109
+ * A consumer `declared` value WINS over inference. The inference cannot see
110
+ * that an agentic surface carrying tools ultimately emits prose, so it is
111
+ * deliberately conservative — wrongly firing a shape-altering rule breaks a
112
+ * parser, wrongly withholding one only forgoes a lift. `constraints.outputMode`
113
+ * is the escape hatch for surfaces that know better.
114
+ *
115
+ * Takes the facts rather than a `PromptIR` so `dialect.ts` stays
116
+ * dependency-free (`ir.ts` imports THIS module, not the reverse).
117
+ */
118
+ declare function resolveOutputMode(args: {
119
+ declared?: OutputMode;
120
+ structuredOutput?: boolean;
121
+ toolCount: number;
122
+ }): OutputMode;
85
123
  declare function bucketContext(tokens: number): ContextBucket;
86
124
  declare function bucketToolCount(count: number): ToolCountBucket;
87
125
  declare function bucketHistory(turnCount: number): HistoryDepth;
@@ -96,4 +134,4 @@ declare function hashShape(s: ShapeSignature): string;
96
134
  */
97
135
  declare function learningKey(archetype: IntentArchetypeName, model: string, shape: ShapeSignature): string;
98
136
 
99
- export { ALL_ARCHETYPES, type ContextBucket, DIALECT_VERSION, type HistoryDepth, INTENT_ARCHETYPES, type IntentArchetypeName, type OutputMode, type ShapeSignature, type ToolCountBucket, bucketContext, bucketHistory, bucketToolCount, hashShape, isArchetype, learningKey };
137
+ export { ALL_ARCHETYPES, type ContextBucket, DIALECT_VERSION, type HistoryDepth, INTENT_ARCHETYPES, type IntentArchetypeName, type OutputMode, type ShapeSignature, type ToolCountBucket, bucketContext, bucketHistory, bucketToolCount, hashShape, isArchetype, learningKey, resolveOutputMode };
package/dist/dialect.js CHANGED
@@ -28,7 +28,8 @@ __export(dialect_exports, {
28
28
  bucketToolCount: () => bucketToolCount,
29
29
  hashShape: () => hashShape,
30
30
  isArchetype: () => isArchetype,
31
- learningKey: () => learningKey
31
+ learningKey: () => learningKey,
32
+ resolveOutputMode: () => resolveOutputMode
32
33
  });
33
34
  module.exports = __toCommonJS(dialect_exports);
34
35
  var DIALECT_VERSION = "v1";
@@ -77,12 +78,22 @@ var INTENT_ARCHETYPES = {
77
78
  name: "transform",
78
79
  description: "Change format or style while preserving content",
79
80
  examples: ["markdown \u2192 html", "formal \u2192 casual", "translate"]
81
+ },
82
+ judge: {
83
+ name: "judge",
84
+ description: "Pairwise comparison of two candidate outputs for the same input",
85
+ examples: ["golden-set eval A/B verdict", "model-swap non-inferiority check", "response bake-off"]
80
86
  }
81
87
  };
82
88
  var ALL_ARCHETYPES = Object.keys(INTENT_ARCHETYPES);
83
89
  function isArchetype(name) {
84
90
  return name in INTENT_ARCHETYPES;
85
91
  }
92
+ function resolveOutputMode(args) {
93
+ if (args.declared) return args.declared;
94
+ if (args.structuredOutput === true) return "json";
95
+ return args.toolCount > 0 ? "tool_call" : "text";
96
+ }
86
97
  function bucketContext(tokens) {
87
98
  if (tokens < 1e3) return "tiny";
88
99
  if (tokens < 4e3) return "small";
@@ -123,5 +134,6 @@ function learningKey(archetype, model, shape) {
123
134
  bucketToolCount,
124
135
  hashShape,
125
136
  isArchetype,
126
- learningKey
137
+ learningKey,
138
+ resolveOutputMode
127
139
  });
package/dist/dialect.mjs CHANGED
@@ -7,8 +7,9 @@ import {
7
7
  bucketToolCount,
8
8
  hashShape,
9
9
  isArchetype,
10
- learningKey
11
- } from "./chunk-5TI6PNSK.mjs";
10
+ learningKey,
11
+ resolveOutputMode
12
+ } from "./chunk-BVEXV5KC.mjs";
12
13
  export {
13
14
  ALL_ARCHETYPES,
14
15
  DIALECT_VERSION,
@@ -18,5 +19,6 @@ export {
18
19
  bucketToolCount,
19
20
  hashShape,
20
21
  isArchetype,
21
- learningKey
22
+ learningKey,
23
+ resolveOutputMode
22
24
  };
@@ -0,0 +1,59 @@
1
+ import { G as GlassboxEvent } from '../types-DsEq35WY.mjs';
2
+ export { A as AdvisoryFiredData, C as CompileDoneData, a as CompileStartData, E as ExecuteAttemptData, b as ExecuteSuccessData, F as FallbackWalkedData, c as GLASSBOX_STREAM_TTL_MS, d as GlassboxEventKind, e as GlassboxPubSub } from '../types-DsEq35WY.mjs';
3
+ import '../ir-Bqn1RVdV.mjs';
4
+ import '../dialect.mjs';
5
+
6
+ /**
7
+ * subscribe(traceId) — public Glass-Box subscription export.
8
+ *
9
+ * Consumer route handlers (e.g. Vercel Edge `/api/glassbox/stream`) import
10
+ * this and bridge the ReadableStream to SSE. The browser-side panel then
11
+ * renders events in real time:
12
+ *
13
+ * import { subscribe } from '@warmdrift/kgauto-compiler/glassbox';
14
+ *
15
+ * export async function GET(req: Request) {
16
+ * const { searchParams } = new URL(req.url);
17
+ * const traceId = searchParams.get('traceId');
18
+ * if (!traceId) return new Response('missing traceId', { status: 400 });
19
+ * const stream = subscribe(traceId);
20
+ * // ... bridge ReadableStream<GlassboxEvent> → SSE here ...
21
+ * }
22
+ *
23
+ * Stream behavior:
24
+ * - Emits events for `traceId` as they're published.
25
+ * - Closes cleanly 60s after the last event (rolling TTL).
26
+ * - If no events arrive within 60s of subscription, closes empty.
27
+ * - Multiple subscribers on the same traceId all fan out.
28
+ *
29
+ * No replay: subscribe() picks up only events published AFTER subscription.
30
+ * Replay is the brain-poll surface's job (see design doc).
31
+ */
32
+
33
+ /**
34
+ * Subscribe to Glass-Box events for a traceId. Returns a ReadableStream
35
+ * that yields GlassboxEvent objects until the per-trace TTL elapses.
36
+ *
37
+ * Cancelling the stream (consumer disconnect, AbortController, etc.) tears
38
+ * down the subscription cleanly via the underlying adapter.
39
+ */
40
+ declare function subscribe(traceId: string): ReadableStream<GlassboxEvent>;
41
+ /**
42
+ * Subscribe to Glass-Box events for ALL traces under an appId — the
43
+ * "tail-all" surface (alpha.26). Backs the extension's default Live mode:
44
+ * rather than needing a specific traceId ahead of time, the panel sees
45
+ * every event for the configured consumer as calls happen.
46
+ *
47
+ * Per-app routing fires only when emit call-sites pass `appId` (the typed
48
+ * builders thread `ir.appId` automatically). Per-trace channel continues to
49
+ * fire for `subscribe(traceId)` callers — both arms are independent.
50
+ *
51
+ * Cross-tenant isolation: the consumer's `/api/glassbox/stream` uses the
52
+ * factory's configured appId — no caller-supplied `?appId=` URL parameter,
53
+ * so a consumer cannot subscribe to another consumer's stream.
54
+ */
55
+ declare function subscribeApp({ appId, }: {
56
+ appId: string;
57
+ }): ReadableStream<GlassboxEvent>;
58
+
59
+ export { GlassboxEvent, subscribe, subscribeApp };
@@ -0,0 +1,59 @@
1
+ import { G as GlassboxEvent } from '../types-Bon96eyc.js';
2
+ export { A as AdvisoryFiredData, C as CompileDoneData, a as CompileStartData, E as ExecuteAttemptData, b as ExecuteSuccessData, F as FallbackWalkedData, c as GLASSBOX_STREAM_TTL_MS, d as GlassboxEventKind, e as GlassboxPubSub } from '../types-Bon96eyc.js';
3
+ import '../ir-Bvlkw5ja.js';
4
+ import '../dialect.js';
5
+
6
+ /**
7
+ * subscribe(traceId) — public Glass-Box subscription export.
8
+ *
9
+ * Consumer route handlers (e.g. Vercel Edge `/api/glassbox/stream`) import
10
+ * this and bridge the ReadableStream to SSE. The browser-side panel then
11
+ * renders events in real time:
12
+ *
13
+ * import { subscribe } from '@warmdrift/kgauto-compiler/glassbox';
14
+ *
15
+ * export async function GET(req: Request) {
16
+ * const { searchParams } = new URL(req.url);
17
+ * const traceId = searchParams.get('traceId');
18
+ * if (!traceId) return new Response('missing traceId', { status: 400 });
19
+ * const stream = subscribe(traceId);
20
+ * // ... bridge ReadableStream<GlassboxEvent> → SSE here ...
21
+ * }
22
+ *
23
+ * Stream behavior:
24
+ * - Emits events for `traceId` as they're published.
25
+ * - Closes cleanly 60s after the last event (rolling TTL).
26
+ * - If no events arrive within 60s of subscription, closes empty.
27
+ * - Multiple subscribers on the same traceId all fan out.
28
+ *
29
+ * No replay: subscribe() picks up only events published AFTER subscription.
30
+ * Replay is the brain-poll surface's job (see design doc).
31
+ */
32
+
33
+ /**
34
+ * Subscribe to Glass-Box events for a traceId. Returns a ReadableStream
35
+ * that yields GlassboxEvent objects until the per-trace TTL elapses.
36
+ *
37
+ * Cancelling the stream (consumer disconnect, AbortController, etc.) tears
38
+ * down the subscription cleanly via the underlying adapter.
39
+ */
40
+ declare function subscribe(traceId: string): ReadableStream<GlassboxEvent>;
41
+ /**
42
+ * Subscribe to Glass-Box events for ALL traces under an appId — the
43
+ * "tail-all" surface (alpha.26). Backs the extension's default Live mode:
44
+ * rather than needing a specific traceId ahead of time, the panel sees
45
+ * every event for the configured consumer as calls happen.
46
+ *
47
+ * Per-app routing fires only when emit call-sites pass `appId` (the typed
48
+ * builders thread `ir.appId` automatically). Per-trace channel continues to
49
+ * fire for `subscribe(traceId)` callers — both arms are independent.
50
+ *
51
+ * Cross-tenant isolation: the consumer's `/api/glassbox/stream` uses the
52
+ * factory's configured appId — no caller-supplied `?appId=` URL parameter,
53
+ * so a consumer cannot subscribe to another consumer's stream.
54
+ */
55
+ declare function subscribeApp({ appId, }: {
56
+ appId: string;
57
+ }): ReadableStream<GlassboxEvent>;
58
+
59
+ export { GlassboxEvent, subscribe, subscribeApp };
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/glassbox/index.ts
21
+ var glassbox_exports = {};
22
+ __export(glassbox_exports, {
23
+ GLASSBOX_STREAM_TTL_MS: () => GLASSBOX_STREAM_TTL_MS,
24
+ subscribe: () => subscribe,
25
+ subscribeApp: () => subscribeApp
26
+ });
27
+ module.exports = __toCommonJS(glassbox_exports);
28
+
29
+ // src/glassbox/types.ts
30
+ var GLASSBOX_STREAM_TTL_MS = 6e4;
31
+
32
+ // src/glassbox/pubsub-memory.ts
33
+ var MemoryPubSub = class {
34
+ subscribers = /* @__PURE__ */ new Map();
35
+ async publish(channelKey, event) {
36
+ const subs = this.subscribers.get(channelKey);
37
+ if (!subs || subs.size === 0) return;
38
+ for (const sub of subs) {
39
+ if (sub.closed) continue;
40
+ try {
41
+ sub.controller.enqueue(event);
42
+ } catch {
43
+ sub.closed = true;
44
+ continue;
45
+ }
46
+ this.refreshTtl(channelKey, sub);
47
+ }
48
+ }
49
+ subscribe(channelKey) {
50
+ const self = this;
51
+ let sub;
52
+ return new ReadableStream({
53
+ start(controller) {
54
+ sub = {
55
+ controller,
56
+ ttlTimer: setTimeout(() => {
57
+ self.closeSubscriber(channelKey, sub);
58
+ }, GLASSBOX_STREAM_TTL_MS),
59
+ closed: false
60
+ };
61
+ let set = self.subscribers.get(channelKey);
62
+ if (!set) {
63
+ set = /* @__PURE__ */ new Set();
64
+ self.subscribers.set(channelKey, set);
65
+ }
66
+ set.add(sub);
67
+ },
68
+ cancel() {
69
+ if (sub) self.removeSubscriber(channelKey, sub);
70
+ }
71
+ });
72
+ }
73
+ /**
74
+ * Refresh the rolling TTL for a subscriber after an event lands. Replaces
75
+ * the existing timer with a fresh 60s one.
76
+ */
77
+ refreshTtl(channelKey, sub) {
78
+ clearTimeout(sub.ttlTimer);
79
+ sub.ttlTimer = setTimeout(() => {
80
+ this.closeSubscriber(channelKey, sub);
81
+ }, GLASSBOX_STREAM_TTL_MS);
82
+ }
83
+ /**
84
+ * Close the subscriber's stream cleanly and remove from the fan-out set.
85
+ * Idempotent — safe to call multiple times.
86
+ */
87
+ closeSubscriber(channelKey, sub) {
88
+ if (sub.closed) return;
89
+ sub.closed = true;
90
+ clearTimeout(sub.ttlTimer);
91
+ try {
92
+ sub.controller.close();
93
+ } catch {
94
+ }
95
+ this.removeSubscriber(channelKey, sub);
96
+ }
97
+ removeSubscriber(channelKey, sub) {
98
+ clearTimeout(sub.ttlTimer);
99
+ const set = this.subscribers.get(channelKey);
100
+ if (!set) return;
101
+ set.delete(sub);
102
+ if (set.size === 0) this.subscribers.delete(channelKey);
103
+ }
104
+ /**
105
+ * Test-only reset. Tears down all subscribers, clears all state. Calling
106
+ * outside of tests is harmless but cancels every active stream.
107
+ */
108
+ _reset() {
109
+ for (const [, set] of this.subscribers) {
110
+ for (const sub of set) {
111
+ this.closeSubscriber("", sub);
112
+ }
113
+ }
114
+ this.subscribers.clear();
115
+ }
116
+ };
117
+
118
+ // src/glassbox/pubsub-upstash.ts
119
+ var UpstashPubSub = class {
120
+ url;
121
+ token;
122
+ fetchImpl;
123
+ blockMs;
124
+ maxLen;
125
+ constructor(cfg) {
126
+ this.url = cfg.url.replace(/\/$/, "");
127
+ this.token = cfg.token;
128
+ this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
129
+ this.blockMs = cfg.blockMs ?? 100;
130
+ this.maxLen = cfg.maxLen ?? 100;
131
+ }
132
+ async publish(channelKey, event) {
133
+ const key = channelKey;
134
+ const payload = JSON.stringify(event);
135
+ await this.cmd([
136
+ "XADD",
137
+ key,
138
+ "MAXLEN",
139
+ "~",
140
+ String(this.maxLen),
141
+ "*",
142
+ "event",
143
+ payload
144
+ ]);
145
+ await this.cmd(["EXPIRE", key, String(Math.ceil(GLASSBOX_STREAM_TTL_MS / 1e3))]);
146
+ }
147
+ subscribe(channelKey) {
148
+ const key = channelKey;
149
+ const self = this;
150
+ let cursor = "$";
151
+ let cancelled = false;
152
+ let ttlDeadline = Date.now() + GLASSBOX_STREAM_TTL_MS;
153
+ return new ReadableStream({
154
+ async start(controller) {
155
+ try {
156
+ while (!cancelled && Date.now() < ttlDeadline) {
157
+ const resp = await self.cmd([
158
+ "XREAD",
159
+ "BLOCK",
160
+ String(self.blockMs),
161
+ "STREAMS",
162
+ key,
163
+ cursor
164
+ ]);
165
+ if (cancelled) break;
166
+ const parsed = parseXReadResult(resp.result);
167
+ if (parsed.entries.length === 0) {
168
+ continue;
169
+ }
170
+ for (const entry of parsed.entries) {
171
+ const evt = decodeEvent(entry.fields);
172
+ if (evt) {
173
+ try {
174
+ controller.enqueue(evt);
175
+ } catch {
176
+ cancelled = true;
177
+ break;
178
+ }
179
+ }
180
+ cursor = entry.id;
181
+ }
182
+ ttlDeadline = Date.now() + GLASSBOX_STREAM_TTL_MS;
183
+ }
184
+ } catch (err) {
185
+ if (!cancelled) {
186
+ try {
187
+ controller.error(err);
188
+ } catch {
189
+ }
190
+ return;
191
+ }
192
+ }
193
+ try {
194
+ controller.close();
195
+ } catch {
196
+ }
197
+ },
198
+ cancel() {
199
+ cancelled = true;
200
+ }
201
+ });
202
+ }
203
+ async cmd(args) {
204
+ const res = await this.fetchImpl(this.url, {
205
+ method: "POST",
206
+ headers: {
207
+ Authorization: `Bearer ${this.token}`,
208
+ "Content-Type": "application/json"
209
+ },
210
+ body: JSON.stringify(args)
211
+ });
212
+ if (!res.ok) {
213
+ throw new Error(`Upstash ${args[0]} failed: HTTP ${res.status}`);
214
+ }
215
+ const json = await res.json();
216
+ if (json.error) {
217
+ throw new Error(`Upstash ${args[0]} failed: ${json.error}`);
218
+ }
219
+ return json;
220
+ }
221
+ };
222
+ function traceChannel(traceId) {
223
+ return `glassbox:trace:${traceId}`;
224
+ }
225
+ function appChannel(appId) {
226
+ return `glassbox:app:${appId}`;
227
+ }
228
+ function decodeEvent(fields) {
229
+ const raw = fields["event"];
230
+ if (!raw) return void 0;
231
+ try {
232
+ const parsed = JSON.parse(raw);
233
+ if (typeof parsed.kind === "string" && typeof parsed.at === "number") {
234
+ return parsed;
235
+ }
236
+ return void 0;
237
+ } catch {
238
+ return void 0;
239
+ }
240
+ }
241
+ function parseXReadResult(raw) {
242
+ if (!Array.isArray(raw)) return { entries: [] };
243
+ const entries = [];
244
+ for (const stream of raw) {
245
+ if (!Array.isArray(stream) || stream.length < 2) continue;
246
+ const streamEntries = stream[1];
247
+ if (!Array.isArray(streamEntries)) continue;
248
+ for (const entry of streamEntries) {
249
+ if (!Array.isArray(entry) || entry.length < 2) continue;
250
+ const id = String(entry[0]);
251
+ const flat = entry[1];
252
+ if (!Array.isArray(flat)) continue;
253
+ const fields = {};
254
+ for (let i = 0; i < flat.length; i += 2) {
255
+ const k = flat[i];
256
+ const v = flat[i + 1];
257
+ if (typeof k === "string") fields[k] = String(v ?? "");
258
+ }
259
+ entries.push({ id, fields });
260
+ }
261
+ }
262
+ return { entries };
263
+ }
264
+
265
+ // src/glassbox/emit.ts
266
+ var activePubSub;
267
+ function getPubSub() {
268
+ if (activePubSub) return activePubSub;
269
+ const url = readEnv("UPSTASH_REDIS_URL");
270
+ const token = readEnv("UPSTASH_REDIS_TOKEN");
271
+ if (url && token) {
272
+ activePubSub = new UpstashPubSub({ url, token });
273
+ } else {
274
+ activePubSub = new MemoryPubSub();
275
+ }
276
+ return activePubSub;
277
+ }
278
+ function readEnv(key) {
279
+ try {
280
+ if (typeof process !== "undefined" && process.env) {
281
+ const v = process.env[key];
282
+ return v && v.trim() !== "" ? v : void 0;
283
+ }
284
+ } catch {
285
+ }
286
+ return void 0;
287
+ }
288
+
289
+ // src/glassbox/subscribe.ts
290
+ function emptyStream() {
291
+ return new ReadableStream({
292
+ start(controller) {
293
+ controller.close();
294
+ }
295
+ });
296
+ }
297
+ function subscribe(traceId) {
298
+ if (!traceId) return emptyStream();
299
+ return getPubSub().subscribe(traceChannel(traceId));
300
+ }
301
+ function subscribeApp({
302
+ appId
303
+ }) {
304
+ if (!appId) return emptyStream();
305
+ return getPubSub().subscribe(appChannel(appId));
306
+ }
307
+ // Annotate the CommonJS export names for ESM import in node:
308
+ 0 && (module.exports = {
309
+ GLASSBOX_STREAM_TTL_MS,
310
+ subscribe,
311
+ subscribeApp
312
+ });
@@ -0,0 +1,12 @@
1
+ import {
2
+ subscribe,
3
+ subscribeApp
4
+ } from "../chunk-RO22VFIF.mjs";
5
+ import {
6
+ GLASSBOX_STREAM_TTL_MS
7
+ } from "../chunk-NBO4R5PC.mjs";
8
+ export {
9
+ GLASSBOX_STREAM_TTL_MS,
10
+ subscribe,
11
+ subscribeApp
12
+ };
@@ -0,0 +1,24 @@
1
+ import { T as TraceHealth } from '../types-CwGhacGT.mjs';
2
+ import '../ir-Bqn1RVdV.mjs';
3
+ import '../dialect.mjs';
4
+
5
+ /**
6
+ * Pure formatting helpers for Glass-Box trace display.
7
+ *
8
+ * Zero deps, no React, no design tokens. Safe to import from any consumer
9
+ * (React component, server route, CLI, Edge worker).
10
+ *
11
+ * Extracted from the alpha.32 tt-intelligence reference component port in
12
+ * alpha.35 so multiple consumer dashboards can render trace data with
13
+ * matching formatting without re-implementing each helper.
14
+ */
15
+
16
+ declare function formatAgo(iso: string, now?: number): string;
17
+ declare function shortTraceId(traceId: string): string;
18
+ declare function formatCost(usd: number | undefined | null): string;
19
+ declare function formatMs(ms: number | undefined | null): string;
20
+ declare function formatOrDash(v: number | string | undefined | null): string;
21
+ declare function inputRatioLabel(status: TraceHealth['inputRatioStatus']): string;
22
+ declare function formatMutation(mutation: string): string;
23
+
24
+ export { formatAgo, formatCost, formatMs, formatMutation, formatOrDash, inputRatioLabel, shortTraceId };