@warlock.js/ai-panoptic 4.3.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +111 -0
  4. package/cjs/index.cjs +1370 -0
  5. package/cjs/index.cjs.map +1 -0
  6. package/esm/collector/collector.d.mts +19 -0
  7. package/esm/collector/collector.d.mts.map +1 -0
  8. package/esm/collector/collector.mjs +89 -0
  9. package/esm/collector/collector.mjs.map +1 -0
  10. package/esm/collector/extract-span-attributes.d.mts +29 -0
  11. package/esm/collector/extract-span-attributes.d.mts.map +1 -0
  12. package/esm/collector/extract-span-attributes.mjs +76 -0
  13. package/esm/collector/extract-span-attributes.mjs.map +1 -0
  14. package/esm/collector/index.d.mts +5 -0
  15. package/esm/collector/normalize-error.d.mts +27 -0
  16. package/esm/collector/normalize-error.d.mts.map +1 -0
  17. package/esm/collector/normalize-error.mjs +49 -0
  18. package/esm/collector/normalize-error.mjs.map +1 -0
  19. package/esm/collector/report-to-span.d.mts +31 -0
  20. package/esm/collector/report-to-span.d.mts.map +1 -0
  21. package/esm/collector/report-to-span.mjs +53 -0
  22. package/esm/collector/report-to-span.mjs.map +1 -0
  23. package/esm/collector/report-to-trace.d.mts +35 -0
  24. package/esm/collector/report-to-trace.d.mts.map +1 -0
  25. package/esm/collector/report-to-trace.mjs +53 -0
  26. package/esm/collector/report-to-trace.mjs.map +1 -0
  27. package/esm/contracts/collector.contract.d.mts +63 -0
  28. package/esm/contracts/collector.contract.d.mts.map +1 -0
  29. package/esm/contracts/exporter.contract.d.mts +76 -0
  30. package/esm/contracts/exporter.contract.d.mts.map +1 -0
  31. package/esm/contracts/index.d.mts +3 -0
  32. package/esm/contracts/trace.type.d.mts +122 -0
  33. package/esm/contracts/trace.type.d.mts.map +1 -0
  34. package/esm/exporters/console/console-exporter.d.mts +25 -0
  35. package/esm/exporters/console/console-exporter.d.mts.map +1 -0
  36. package/esm/exporters/console/console-exporter.mjs +79 -0
  37. package/esm/exporters/console/console-exporter.mjs.map +1 -0
  38. package/esm/exporters/console/console-exporter.type.d.mts +36 -0
  39. package/esm/exporters/console/console-exporter.type.d.mts.map +1 -0
  40. package/esm/exporters/console/format-span-line.d.mts +17 -0
  41. package/esm/exporters/console/format-span-line.d.mts.map +1 -0
  42. package/esm/exporters/console/format-span-line.mjs +41 -0
  43. package/esm/exporters/console/format-span-line.mjs.map +1 -0
  44. package/esm/exporters/console/index.d.mts +3 -0
  45. package/esm/exporters/file/file-exporter.d.mts +23 -0
  46. package/esm/exporters/file/file-exporter.d.mts.map +1 -0
  47. package/esm/exporters/file/file-exporter.mjs +93 -0
  48. package/esm/exporters/file/file-exporter.mjs.map +1 -0
  49. package/esm/exporters/file/file-exporter.type.d.mts +39 -0
  50. package/esm/exporters/file/file-exporter.type.d.mts.map +1 -0
  51. package/esm/exporters/file/index.d.mts +2 -0
  52. package/esm/exporters/index.d.mts +12 -0
  53. package/esm/exporters/index.mjs +12 -0
  54. package/esm/exporters/langfuse/index.d.mts +2 -0
  55. package/esm/exporters/langfuse/index.mjs +3 -0
  56. package/esm/exporters/langfuse/langfuse-exporter.d.mts +27 -0
  57. package/esm/exporters/langfuse/langfuse-exporter.d.mts.map +1 -0
  58. package/esm/exporters/langfuse/langfuse-exporter.mjs +158 -0
  59. package/esm/exporters/langfuse/langfuse-exporter.mjs.map +1 -0
  60. package/esm/exporters/langfuse/langfuse-exporter.type.d.mts +90 -0
  61. package/esm/exporters/langfuse/langfuse-exporter.type.d.mts.map +1 -0
  62. package/esm/exporters/otel/index.d.mts +2 -0
  63. package/esm/exporters/otel/index.mjs +3 -0
  64. package/esm/exporters/otel/otel-exporter.d.mts +29 -0
  65. package/esm/exporters/otel/otel-exporter.d.mts.map +1 -0
  66. package/esm/exporters/otel/otel-exporter.mjs +142 -0
  67. package/esm/exporters/otel/otel-exporter.mjs.map +1 -0
  68. package/esm/exporters/otel/otel-exporter.type.d.mts +39 -0
  69. package/esm/exporters/otel/otel-exporter.type.d.mts.map +1 -0
  70. package/esm/exporters/utils/gen-ai-attributes.d.mts +65 -0
  71. package/esm/exporters/utils/gen-ai-attributes.d.mts.map +1 -0
  72. package/esm/exporters/utils/gen-ai-attributes.mjs +88 -0
  73. package/esm/exporters/utils/gen-ai-attributes.mjs.map +1 -0
  74. package/esm/exporters/utils/index.d.mts +3 -0
  75. package/esm/exporters/utils/total-cost.d.mts +20 -0
  76. package/esm/exporters/utils/total-cost.d.mts.map +1 -0
  77. package/esm/exporters/utils/total-cost.mjs +23 -0
  78. package/esm/exporters/utils/total-cost.mjs.map +1 -0
  79. package/esm/exporters/utils/walk-spans.d.mts +18 -0
  80. package/esm/exporters/utils/walk-spans.d.mts.map +1 -0
  81. package/esm/exporters/utils/walk-spans.mjs +21 -0
  82. package/esm/exporters/utils/walk-spans.mjs.map +1 -0
  83. package/esm/index.d.mts +31 -0
  84. package/esm/index.mjs +22 -0
  85. package/esm/panoptic/index.d.mts +4 -0
  86. package/esm/panoptic/panoptic-middleware.d.mts +38 -0
  87. package/esm/panoptic/panoptic-middleware.d.mts.map +1 -0
  88. package/esm/panoptic/panoptic-middleware.mjs +68 -0
  89. package/esm/panoptic/panoptic-middleware.mjs.map +1 -0
  90. package/esm/panoptic/panoptic-target.type.d.mts +53 -0
  91. package/esm/panoptic/panoptic-target.type.d.mts.map +1 -0
  92. package/esm/panoptic/panoptic.d.mts +37 -0
  93. package/esm/panoptic/panoptic.d.mts.map +1 -0
  94. package/esm/panoptic/panoptic.mjs +134 -0
  95. package/esm/panoptic/panoptic.mjs.map +1 -0
  96. package/esm/panoptic/panoptic.type.d.mts +100 -0
  97. package/esm/panoptic/panoptic.type.d.mts.map +1 -0
  98. package/esm/store/in-memory-trace-store.d.mts +32 -0
  99. package/esm/store/in-memory-trace-store.d.mts.map +1 -0
  100. package/esm/store/in-memory-trace-store.mjs +134 -0
  101. package/esm/store/in-memory-trace-store.mjs.map +1 -0
  102. package/esm/store/index.d.mts +6 -0
  103. package/esm/store/index.mjs +5 -0
  104. package/esm/store/match-trace.d.mts +21 -0
  105. package/esm/store/match-trace.d.mts.map +1 -0
  106. package/esm/store/match-trace.mjs +44 -0
  107. package/esm/store/match-trace.mjs.map +1 -0
  108. package/esm/store/sum-usage.d.mts +34 -0
  109. package/esm/store/sum-usage.d.mts.map +1 -0
  110. package/esm/store/sum-usage.mjs +65 -0
  111. package/esm/store/sum-usage.mjs.map +1 -0
  112. package/esm/store/trace-aggregate.type.d.mts +51 -0
  113. package/esm/store/trace-aggregate.type.d.mts.map +1 -0
  114. package/esm/store/trace-query.type.d.mts +45 -0
  115. package/esm/store/trace-query.type.d.mts.map +1 -0
  116. package/esm/store/trace-store.contract.d.mts +55 -0
  117. package/esm/store/trace-store.contract.d.mts.map +1 -0
  118. package/llms-full.txt +450 -0
  119. package/llms.txt +11 -0
  120. package/package.json +40 -0
  121. package/skills/README.md +17 -0
  122. package/skills/export-traces/SKILL.md +152 -0
  123. package/skills/observe-with-panoptic/SKILL.md +128 -0
  124. package/skills/query-traces/SKILL.md +152 -0
@@ -0,0 +1,68 @@
1
+ //#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic-middleware.ts
2
+ /**
3
+ * Build an {@link AgentMiddleware} that feeds a collector from the
4
+ * `execute`- and `supervisor`-level hooks. An alternative wiring to event
5
+ * subscription for apps that already compose cross-cutting concerns
6
+ * through the agent middleware pipeline (`[cache, budget, guardrail,
7
+ * observability]`). Declaring both hook maps lets a single middleware
8
+ * object work uniformly on agents (which fire the `execute` map) and
9
+ * supervisors (which fire the `supervisor` map) — registering it on a
10
+ * supervisor would otherwise install and collect nothing silently.
11
+ *
12
+ * Both terminal paths are covered on each surface:
13
+ * - `after` — fires on a run that produced a result. A run can complete
14
+ * with `result.error` populated (the engine still calls `after`), so
15
+ * the report AND the envelope error are collected; the error type and
16
+ * message land on the root span.
17
+ * - `onError` — fires when the run threw before assembling a result. The
18
+ * error carries the partial result's report on its envelope; when
19
+ * present it is collected, with the error itself threaded onto the
20
+ * root span so failed runs still produce a trace.
21
+ *
22
+ * The hooks never return a value, so they never mutate the agent's /
23
+ * supervisor's result. The `collect` call is fire-and-forget relative to
24
+ * the run — the collector isolates exporter failures internally, and we
25
+ * additionally swallow any rejection here so an observability fault can
26
+ * never surface on the run's hot path.
27
+ *
28
+ * @param collector - the collector traces are fed into.
29
+ * @param name - stable middleware name (kebab-case). Defaults to
30
+ * `"panoptic"`.
31
+ */
32
+ function createPanopticMiddleware(collector, name = "panoptic") {
33
+ const collectReport = (report, rootError) => {
34
+ if (!isReport(report)) return;
35
+ collector.collect(report, rootError).catch(() => {});
36
+ };
37
+ const onResult = (result) => {
38
+ collectReport(result.report, result.error);
39
+ };
40
+ const onError = (error) => {
41
+ collectReport(error.report, error);
42
+ };
43
+ const terminalHooks = {
44
+ after(_ctx, result) {
45
+ onResult(result);
46
+ },
47
+ onError(_ctx, error) {
48
+ onError(error);
49
+ }
50
+ };
51
+ return {
52
+ name,
53
+ execute: terminalHooks,
54
+ supervisor: terminalHooks
55
+ };
56
+ }
57
+ /**
58
+ * Narrow an unknown value to a `BaseReport`-shaped object. Structural
59
+ * (checks the lineage fields the collector reads) so it accepts any
60
+ * primitive's report subtype without importing each concrete type.
61
+ */
62
+ function isReport(value) {
63
+ return typeof value === "object" && value !== null && typeof value.runId === "string" && typeof value.rootRunId === "string";
64
+ }
65
+
66
+ //#endregion
67
+ export { createPanopticMiddleware };
68
+ //# sourceMappingURL=panoptic-middleware.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panoptic-middleware.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic-middleware.ts"],"sourcesContent":["import type { AgentMiddleware } from \"@warlock.js/ai\";\nimport type { CollectorContract } from \"../contracts/collector.contract\";\n\n/**\n * Build an {@link AgentMiddleware} that feeds a collector from the\n * `execute`- and `supervisor`-level hooks. An alternative wiring to event\n * subscription for apps that already compose cross-cutting concerns\n * through the agent middleware pipeline (`[cache, budget, guardrail,\n * observability]`). Declaring both hook maps lets a single middleware\n * object work uniformly on agents (which fire the `execute` map) and\n * supervisors (which fire the `supervisor` map) — registering it on a\n * supervisor would otherwise install and collect nothing silently.\n *\n * Both terminal paths are covered on each surface:\n * - `after` — fires on a run that produced a result. A run can complete\n * with `result.error` populated (the engine still calls `after`), so\n * the report AND the envelope error are collected; the error type and\n * message land on the root span.\n * - `onError` — fires when the run threw before assembling a result. The\n * error carries the partial result's report on its envelope; when\n * present it is collected, with the error itself threaded onto the\n * root span so failed runs still produce a trace.\n *\n * The hooks never return a value, so they never mutate the agent's /\n * supervisor's result. The `collect` call is fire-and-forget relative to\n * the run — the collector isolates exporter failures internally, and we\n * additionally swallow any rejection here so an observability fault can\n * never surface on the run's hot path.\n *\n * @param collector - the collector traces are fed into.\n * @param name - stable middleware name (kebab-case). Defaults to\n * `\"panoptic\"`.\n */\nexport function createPanopticMiddleware(\n collector: CollectorContract,\n name = \"panoptic\",\n): AgentMiddleware {\n const collectReport = (report: unknown, rootError?: unknown): void => {\n if (!isReport(report)) {\n return;\n }\n\n void collector.collect(report, rootError).catch(() => {\n // Swallow — the collector already isolates exporter failures; this\n // guard keeps an observability fault off the run's hot path.\n });\n };\n\n const onResult = (result: unknown): void => {\n // A run can complete with `result.error` populated (`after` still\n // fires); thread that envelope error onto the root span.\n collectReport(\n (result as { report?: unknown }).report,\n (result as { error?: unknown }).error,\n );\n };\n\n const onError = (error: unknown): void => {\n // A failed run's report rides on the error envelope when the engine\n // built one before throwing; collect it so failures trace, threading\n // the error itself onto the root span.\n collectReport((error as { report?: unknown }).report, error);\n };\n\n const terminalHooks = {\n after(_ctx: unknown, result: unknown) {\n onResult(result);\n },\n onError(_ctx: unknown, error: unknown) {\n onError(error);\n },\n };\n\n return {\n name,\n execute: terminalHooks as AgentMiddleware[\"execute\"],\n supervisor: terminalHooks as AgentMiddleware[\"supervisor\"],\n };\n}\n\n/**\n * Narrow an unknown value to a `BaseReport`-shaped object. Structural\n * (checks the lineage fields the collector reads) so it accepts any\n * primitive's report subtype without importing each concrete type.\n */\nfunction isReport(value: unknown): value is import(\"@warlock.js/ai\").BaseReport {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { runId?: unknown }).runId === \"string\" &&\n typeof (value as { rootRunId?: unknown }).rootRunId === \"string\"\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,yBACd,WACA,OAAO,YACU;CACjB,MAAM,iBAAiB,QAAiB,cAA8B;EACpE,IAAI,CAAC,SAAS,MAAM,GAClB;EAGF,AAAK,UAAU,QAAQ,QAAQ,SAAS,CAAC,CAAC,YAAY,CAGtD,CAAC;CACH;CAEA,MAAM,YAAY,WAA0B;EAG1C,cACG,OAAgC,QAChC,OAA+B,KAClC;CACF;CAEA,MAAM,WAAW,UAAyB;EAIxC,cAAe,MAA+B,QAAQ,KAAK;CAC7D;CAEA,MAAM,gBAAgB;EACpB,MAAM,MAAe,QAAiB;GACpC,SAAS,MAAM;EACjB;EACA,QAAQ,MAAe,OAAgB;GACrC,QAAQ,KAAK;EACf;CACF;CAEA,OAAO;EACL;EACA,SAAS;EACT,YAAY;CACd;AACF;;;;;;AAOA,SAAS,SAAS,OAA8D;CAC9E,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA8B,UAAU,YAChD,OAAQ,MAAkC,cAAc;AAE5D"}
@@ -0,0 +1,53 @@
1
+ import { BaseReport, BaseResult } from "@warlock.js/ai";
2
+
3
+ //#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic-target.type.d.ts
4
+ /**
5
+ * The minimal completed-event payload Panoptic reads off any primitive's
6
+ * terminal `*.completed` event. Every core `@warlock.js/ai` primitive
7
+ * (agent, workflow, supervisor) emits a `*.completed` event carrying the
8
+ * full `result` — and the result always carries a `report` tree — even
9
+ * when the run failed (`agent.error` / `workflow.error` fire first, then
10
+ * `*.completed` still fires). Reading the report off this single event is
11
+ * therefore enough to capture every run regardless of outcome.
12
+ */
13
+ type CompletedEventPayload = {
14
+ /** The finalized result of the run; carries the full `report` tree. */result: BaseResult & {
15
+ report: BaseReport;
16
+ };
17
+ };
18
+ /**
19
+ * Structural shape of anything Panoptic can subscribe to — the
20
+ * `on(event, handler)` surface shared verbatim by every core
21
+ * `@warlock.js/ai` primitive (`AgentContract`, `WorkflowInstance`,
22
+ * `SupervisorContract`, `OrchestratorContract`).
23
+ *
24
+ * Kept intentionally structural (not an import of the concrete
25
+ * contracts) so {@link panoptic} attaches to any of the four primitives
26
+ * — and to user-defined executables that mirror the same surface —
27
+ * without Panoptic taking a hard dependency on each primitive's exact
28
+ * event-map generics. `on` returns an unsubscribe function in the core,
29
+ * so the subscriber can detach cleanly.
30
+ *
31
+ * @example
32
+ * // Every primitive satisfies this:
33
+ * const agent = ai.agent({ model });
34
+ * const sub = panoptic({ exporters: [consoleExporter()] });
35
+ * const detach = sub.attach(agent); // agent is a PanopticTarget
36
+ */
37
+ type PanopticTarget = {
38
+ /**
39
+ * Stable identifier of the primitive. Used only for diagnostics
40
+ * (Panoptic never branches behavior on it). Present on every core
41
+ * primitive instance.
42
+ */
43
+ readonly name?: string;
44
+ /**
45
+ * Subscribe a handler to a single event name for the lifetime of the
46
+ * instance, returning an unsubscribe function. Mirrors the core 3-tier
47
+ * model's instance tier (`agent.on(...)`, `workflow.on(...)`, …).
48
+ */
49
+ on(event: string, handler: (payload: unknown) => void): () => void;
50
+ };
51
+ //#endregion
52
+ export { CompletedEventPayload, PanopticTarget };
53
+ //# sourceMappingURL=panoptic-target.type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panoptic-target.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic-target.type.ts"],"mappings":";;;;;AAWA;;;;;;;KAAY,qBAAA;EAE+B,uEAAzC,MAAA,EAAQ,UAAA;IAAe,MAAA,EAAQ,UAAU;EAAA;AAAA;;;;;;;;AAkCY;;;;;;;;;;;;KAZ3C,cAAA;;;;;;WAMD,IAAA;;;;;;EAMT,EAAA,CAAG,KAAA,UAAe,OAAA,GAAU,OAAA;AAAA"}
@@ -0,0 +1,37 @@
1
+ import { Panoptic, PanopticOptions } from "./panoptic.type.mjs";
2
+
3
+ //#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic.d.ts
4
+ /**
5
+ * Create a Panoptic subscriber — the one-call entry point that wires the
6
+ * observability pipeline. Pass the exporters you want and Panoptic
7
+ * builds a collector, registers them, and hands back a subscriber you can
8
+ * `attach()` to any agent/workflow/supervisor, install as agent
9
+ * `middleware()`, or feed reports to directly with `collect()`.
10
+ *
11
+ * @example
12
+ * // Attach to a primitive's event stream (captures every run):
13
+ * const observe = panoptic({
14
+ * exporters: [consoleExporter(), otelExporter({ tracerName: "app" })],
15
+ * });
16
+ *
17
+ * const agent = ai.agent({ model });
18
+ * const detach = observe.attach(agent);
19
+ *
20
+ * await agent.execute("Summarize this");
21
+ * // ...later, on shutdown:
22
+ * await observe.shutdown();
23
+ *
24
+ * @example
25
+ * // Or wire it through the agent middleware pipeline:
26
+ * const observe = panoptic({ exporters: [langfuseExporter({ ... })] });
27
+ * const agent = ai.agent({ model, middleware: [observe.middleware()] });
28
+ *
29
+ * @example
30
+ * // Orchestrator turns carry no result-bearing event — collect directly:
31
+ * const result = await orchestrator.execute(input, { sessionId });
32
+ * await observe.collect(result.report);
33
+ */
34
+ declare function panoptic(options?: PanopticOptions): Panoptic;
35
+ //#endregion
36
+ export { panoptic };
37
+ //# sourceMappingURL=panoptic.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panoptic.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic.ts"],"mappings":";;;;;AAwLA;;;;;;;;AAAiE;;;;;;;;;;;;;;;;;;;;iBAAjD,QAAA,CAAS,OAAA,GAAS,eAAA,GAAuB,QAAQ"}
@@ -0,0 +1,134 @@
1
+ import { createCollector } from "../collector/collector.mjs";
2
+ import { createPanopticMiddleware } from "./panoptic-middleware.mjs";
3
+
4
+ //#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic.ts
5
+ /**
6
+ * Terminal `*.completed` events of every core primitive that carries the
7
+ * finalized `result` (and therefore the `report` tree). These fire once
8
+ * per run regardless of outcome — the matching `*.error` event fires
9
+ * first on failure, then `*.completed` still fires — so subscribing here
10
+ * captures completed, failed, and cancelled runs alike.
11
+ *
12
+ * The orchestrator is intentionally absent: its `orchestrator.turn.*`
13
+ * events carry only session identity, not a result. Collect an
14
+ * orchestrator turn via {@link Panoptic.collect} with
15
+ * `result.report` instead.
16
+ */
17
+ const DEFAULT_COMPLETED_EVENTS = [
18
+ "agent.completed",
19
+ "workflow.completed",
20
+ "supervisor.completed"
21
+ ];
22
+ /**
23
+ * The Panoptic subscriber — binds a collector + its exporters to the
24
+ * three feed paths (events, middleware, direct). Instantiated via
25
+ * {@link panoptic}; callers never see `new`.
26
+ */
27
+ var PanopticSubscriber = class {
28
+ constructor(options = {}) {
29
+ this.collector = options.collector ?? createCollector();
30
+ for (const exporter of options.exporters ?? []) this.collector.use(exporter);
31
+ this.completedEvents = options.completedEvents ?? [...DEFAULT_COMPLETED_EVENTS];
32
+ this.middlewareName = options.middlewareName ?? "panoptic";
33
+ }
34
+ use(exporter) {
35
+ this.collector.use(exporter);
36
+ return this;
37
+ }
38
+ attach(target) {
39
+ const unsubscribes = [];
40
+ for (const event of this.completedEvents) {
41
+ const unsubscribe = target.on(event, (payload) => {
42
+ this.handleCompleted(payload);
43
+ });
44
+ unsubscribes.push(unsubscribe);
45
+ }
46
+ return () => {
47
+ for (const unsubscribe of unsubscribes) unsubscribe();
48
+ };
49
+ }
50
+ middleware() {
51
+ return createPanopticMiddleware(this.collector, this.middlewareName);
52
+ }
53
+ async collect(report) {
54
+ await this.collector.collect(report);
55
+ }
56
+ toTrace(report) {
57
+ return this.collector.toTrace(report);
58
+ }
59
+ async flush() {
60
+ await this.collector.flush();
61
+ }
62
+ async shutdown() {
63
+ await this.collector.shutdown();
64
+ }
65
+ /**
66
+ * Project one terminal `*.completed` payload's report into the
67
+ * collector. The fan-out is fire-and-forget relative to the emitting
68
+ * run: the core swallows handler errors, the collector isolates
69
+ * exporter failures, and we additionally guard the rejection here so an
70
+ * observability fault never escapes the event handler.
71
+ */
72
+ handleCompleted(payload) {
73
+ const report = readReport(payload);
74
+ if (!report) return;
75
+ const rootError = readResultError(payload);
76
+ this.collector.collect(report, rootError).catch(() => {});
77
+ }
78
+ };
79
+ /**
80
+ * Read the envelope error off a primitive's completed-event payload
81
+ * (`{ result: { error } }`). The error rides on the result envelope, not
82
+ * the report tree, so the collector needs it separately to populate a
83
+ * failed root span. Returns `undefined` when the run succeeded.
84
+ */
85
+ function readResultError(payload) {
86
+ return (payload?.result)?.error;
87
+ }
88
+ /**
89
+ * Read the `report` tree off a primitive's completed-event payload.
90
+ * Structural (no concrete-type import) so it accepts every primitive's
91
+ * result subtype; returns `undefined` when the payload isn't the
92
+ * expected `{ result: { report } }` shape.
93
+ */
94
+ function readReport(payload) {
95
+ const report = (payload?.result)?.report;
96
+ if (typeof report === "object" && report !== null && typeof report.runId === "string" && typeof report.rootRunId === "string") return report;
97
+ }
98
+ /**
99
+ * Create a Panoptic subscriber — the one-call entry point that wires the
100
+ * observability pipeline. Pass the exporters you want and Panoptic
101
+ * builds a collector, registers them, and hands back a subscriber you can
102
+ * `attach()` to any agent/workflow/supervisor, install as agent
103
+ * `middleware()`, or feed reports to directly with `collect()`.
104
+ *
105
+ * @example
106
+ * // Attach to a primitive's event stream (captures every run):
107
+ * const observe = panoptic({
108
+ * exporters: [consoleExporter(), otelExporter({ tracerName: "app" })],
109
+ * });
110
+ *
111
+ * const agent = ai.agent({ model });
112
+ * const detach = observe.attach(agent);
113
+ *
114
+ * await agent.execute("Summarize this");
115
+ * // ...later, on shutdown:
116
+ * await observe.shutdown();
117
+ *
118
+ * @example
119
+ * // Or wire it through the agent middleware pipeline:
120
+ * const observe = panoptic({ exporters: [langfuseExporter({ ... })] });
121
+ * const agent = ai.agent({ model, middleware: [observe.middleware()] });
122
+ *
123
+ * @example
124
+ * // Orchestrator turns carry no result-bearing event — collect directly:
125
+ * const result = await orchestrator.execute(input, { sessionId });
126
+ * await observe.collect(result.report);
127
+ */
128
+ function panoptic(options = {}) {
129
+ return new PanopticSubscriber(options);
130
+ }
131
+
132
+ //#endregion
133
+ export { panoptic };
134
+ //# sourceMappingURL=panoptic.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panoptic.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic.ts"],"sourcesContent":["import type { AgentMiddleware, BaseReport } from \"@warlock.js/ai\";\nimport { createCollector } from \"../collector/collector\";\nimport type { CollectorContract } from \"../contracts/collector.contract\";\nimport type { ExporterContract } from \"../contracts/exporter.contract\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport { createPanopticMiddleware } from \"./panoptic-middleware\";\nimport type { CompletedEventPayload, PanopticTarget } from \"./panoptic-target.type\";\nimport type { Panoptic, PanopticOptions } from \"./panoptic.type\";\n\n/**\n * Terminal `*.completed` events of every core primitive that carries the\n * finalized `result` (and therefore the `report` tree). These fire once\n * per run regardless of outcome — the matching `*.error` event fires\n * first on failure, then `*.completed` still fires — so subscribing here\n * captures completed, failed, and cancelled runs alike.\n *\n * The orchestrator is intentionally absent: its `orchestrator.turn.*`\n * events carry only session identity, not a result. Collect an\n * orchestrator turn via {@link Panoptic.collect} with\n * `result.report` instead.\n */\nconst DEFAULT_COMPLETED_EVENTS = [\n \"agent.completed\",\n \"workflow.completed\",\n \"supervisor.completed\",\n] as const;\n\n/**\n * The Panoptic subscriber — binds a collector + its exporters to the\n * three feed paths (events, middleware, direct). Instantiated via\n * {@link panoptic}; callers never see `new`.\n */\nclass PanopticSubscriber implements Panoptic {\n public readonly collector: CollectorContract;\n\n private readonly completedEvents: string[];\n\n private readonly middlewareName: string;\n\n public constructor(options: PanopticOptions = {}) {\n this.collector = options.collector ?? createCollector();\n\n for (const exporter of options.exporters ?? []) {\n this.collector.use(exporter);\n }\n\n this.completedEvents =\n options.completedEvents ?? [...DEFAULT_COMPLETED_EVENTS];\n this.middlewareName = options.middlewareName ?? \"panoptic\";\n }\n\n public use(exporter: ExporterContract): Panoptic {\n this.collector.use(exporter);\n\n return this;\n }\n\n public attach(target: PanopticTarget): () => void {\n const unsubscribes: Array<() => void> = [];\n\n for (const event of this.completedEvents) {\n const unsubscribe = target.on(event, (payload) => {\n this.handleCompleted(payload);\n });\n\n unsubscribes.push(unsubscribe);\n }\n\n return () => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe();\n }\n };\n }\n\n public middleware(): AgentMiddleware {\n return createPanopticMiddleware(this.collector, this.middlewareName);\n }\n\n public async collect(report: BaseReport): Promise<void> {\n await this.collector.collect(report);\n }\n\n public toTrace(report: BaseReport): Trace {\n return this.collector.toTrace(report);\n }\n\n public async flush(): Promise<void> {\n await this.collector.flush();\n }\n\n public async shutdown(): Promise<void> {\n await this.collector.shutdown();\n }\n\n /**\n * Project one terminal `*.completed` payload's report into the\n * collector. The fan-out is fire-and-forget relative to the emitting\n * run: the core swallows handler errors, the collector isolates\n * exporter failures, and we additionally guard the rejection here so an\n * observability fault never escapes the event handler.\n */\n private handleCompleted(payload: unknown): void {\n const report = readReport(payload);\n\n if (!report) {\n return;\n }\n\n // The failing run's typed error lives on the result envelope\n // (`BaseResult.error`), never on the report tree — thread it so a\n // failed root span carries its error type/message.\n const rootError = readResultError(payload);\n\n void this.collector.collect(report, rootError).catch(() => {\n // Swallow — see the JSDoc above. Never surface on the run.\n });\n }\n}\n\n/**\n * Read the envelope error off a primitive's completed-event payload\n * (`{ result: { error } }`). The error rides on the result envelope, not\n * the report tree, so the collector needs it separately to populate a\n * failed root span. Returns `undefined` when the run succeeded.\n */\nfunction readResultError(payload: unknown): unknown {\n const result = (payload as Partial<CompletedEventPayload>)?.result;\n\n return (result as { error?: unknown })?.error;\n}\n\n/**\n * Read the `report` tree off a primitive's completed-event payload.\n * Structural (no concrete-type import) so it accepts every primitive's\n * result subtype; returns `undefined` when the payload isn't the\n * expected `{ result: { report } }` shape.\n */\nfunction readReport(payload: unknown): BaseReport | undefined {\n const result = (payload as Partial<CompletedEventPayload>)?.result;\n const report = (result as { report?: unknown })?.report;\n\n if (\n typeof report === \"object\" &&\n report !== null &&\n typeof (report as { runId?: unknown }).runId === \"string\" &&\n typeof (report as { rootRunId?: unknown }).rootRunId === \"string\"\n ) {\n return report as BaseReport;\n }\n\n return undefined;\n}\n\n/**\n * Create a Panoptic subscriber — the one-call entry point that wires the\n * observability pipeline. Pass the exporters you want and Panoptic\n * builds a collector, registers them, and hands back a subscriber you can\n * `attach()` to any agent/workflow/supervisor, install as agent\n * `middleware()`, or feed reports to directly with `collect()`.\n *\n * @example\n * // Attach to a primitive's event stream (captures every run):\n * const observe = panoptic({\n * exporters: [consoleExporter(), otelExporter({ tracerName: \"app\" })],\n * });\n *\n * const agent = ai.agent({ model });\n * const detach = observe.attach(agent);\n *\n * await agent.execute(\"Summarize this\");\n * // ...later, on shutdown:\n * await observe.shutdown();\n *\n * @example\n * // Or wire it through the agent middleware pipeline:\n * const observe = panoptic({ exporters: [langfuseExporter({ ... })] });\n * const agent = ai.agent({ model, middleware: [observe.middleware()] });\n *\n * @example\n * // Orchestrator turns carry no result-bearing event — collect directly:\n * const result = await orchestrator.execute(input, { sessionId });\n * await observe.collect(result.report);\n */\nexport function panoptic(options: PanopticOptions = {}): Panoptic {\n return new PanopticSubscriber(options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,MAAM,2BAA2B;CAC/B;CACA;CACA;AACF;;;;;;AAOA,IAAM,qBAAN,MAA6C;CAO3C,AAAO,YAAY,UAA2B,CAAC,GAAG;EAChD,KAAK,YAAY,QAAQ,aAAa,gBAAgB;EAEtD,KAAK,MAAM,YAAY,QAAQ,aAAa,CAAC,GAC3C,KAAK,UAAU,IAAI,QAAQ;EAG7B,KAAK,kBACH,QAAQ,mBAAmB,CAAC,GAAG,wBAAwB;EACzD,KAAK,iBAAiB,QAAQ,kBAAkB;CAClD;CAEA,AAAO,IAAI,UAAsC;EAC/C,KAAK,UAAU,IAAI,QAAQ;EAE3B,OAAO;CACT;CAEA,AAAO,OAAO,QAAoC;EAChD,MAAM,eAAkC,CAAC;EAEzC,KAAK,MAAM,SAAS,KAAK,iBAAiB;GACxC,MAAM,cAAc,OAAO,GAAG,QAAQ,YAAY;IAChD,KAAK,gBAAgB,OAAO;GAC9B,CAAC;GAED,aAAa,KAAK,WAAW;EAC/B;EAEA,aAAa;GACX,KAAK,MAAM,eAAe,cACxB,YAAY;EAEhB;CACF;CAEA,AAAO,aAA8B;EACnC,OAAO,yBAAyB,KAAK,WAAW,KAAK,cAAc;CACrE;CAEA,MAAa,QAAQ,QAAmC;EACtD,MAAM,KAAK,UAAU,QAAQ,MAAM;CACrC;CAEA,AAAO,QAAQ,QAA2B;EACxC,OAAO,KAAK,UAAU,QAAQ,MAAM;CACtC;CAEA,MAAa,QAAuB;EAClC,MAAM,KAAK,UAAU,MAAM;CAC7B;CAEA,MAAa,WAA0B;EACrC,MAAM,KAAK,UAAU,SAAS;CAChC;;;;;;;;CASA,AAAQ,gBAAgB,SAAwB;EAC9C,MAAM,SAAS,WAAW,OAAO;EAEjC,IAAI,CAAC,QACH;EAMF,MAAM,YAAY,gBAAgB,OAAO;EAEzC,AAAK,KAAK,UAAU,QAAQ,QAAQ,SAAS,CAAC,CAAC,YAAY,CAE3D,CAAC;CACH;AACF;;;;;;;AAQA,SAAS,gBAAgB,SAA2B;CAGlD,QAFgB,SAA4C,OAE9C,EAA0B;AAC1C;;;;;;;AAQA,SAAS,WAAW,SAA0C;CAE5D,MAAM,UADU,SAA4C,OACtC,EAA2B;CAEjD,IACE,OAAO,WAAW,YAClB,WAAW,QACX,OAAQ,OAA+B,UAAU,YACjD,OAAQ,OAAmC,cAAc,UAEzD,OAAO;AAIX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,SAAS,UAA2B,CAAC,GAAa;CAChE,OAAO,IAAI,mBAAmB,OAAO;AACvC"}
@@ -0,0 +1,100 @@
1
+ import { Trace } from "../contracts/trace.type.mjs";
2
+ import { ExporterContract } from "../contracts/exporter.contract.mjs";
3
+ import { CollectorContract } from "../contracts/collector.contract.mjs";
4
+ import { PanopticTarget } from "./panoptic-target.type.mjs";
5
+ import { AgentMiddleware, BaseReport } from "@warlock.js/ai";
6
+
7
+ //#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic.type.d.ts
8
+ /**
9
+ * Options for the {@link panoptic} subscriber factory.
10
+ *
11
+ * The common case is `{ exporters: [...] }` — Panoptic creates its own
12
+ * collector and registers the exporters on it. Pass `collector` to reuse
13
+ * a pre-built collector (e.g. one that already has a store registered);
14
+ * any `exporters` supplied are registered onto it as well.
15
+ */
16
+ type PanopticOptions = {
17
+ /**
18
+ * Exporters to register on the collector. Each is deduped by
19
+ * `ExporterContract.name` (registering the same exporter twice is a
20
+ * no-op). Optional — you can also `use()` exporters after construction.
21
+ */
22
+ exporters?: ExporterContract[];
23
+ /**
24
+ * Bring your own collector instead of letting Panoptic create one.
25
+ * When supplied, `exporters` are registered onto it; when omitted,
26
+ * Panoptic calls `createCollector()` internally. Inject this to share
27
+ * one collector across several subscribers or to pre-wire a store.
28
+ */
29
+ collector?: CollectorContract;
30
+ /**
31
+ * Names of the terminal `*.completed` events to subscribe to when
32
+ * {@link Panoptic.attach} is called. Defaults to the completed event of
33
+ * every core primitive — `agent.completed`, `workflow.completed`,
34
+ * `supervisor.completed`. Override to widen/narrow the surface (e.g.
35
+ * attach only to agents). Panoptic subscribes to each name and silently
36
+ * ignores the ones the target doesn't emit.
37
+ */
38
+ completedEvents?: string[];
39
+ /**
40
+ * Stable name reported by the middleware returned from
41
+ * {@link Panoptic.middleware}. Defaults to `"panoptic"`. Override when
42
+ * registering more than one Panoptic middleware on the same agent.
43
+ */
44
+ middlewareName?: string;
45
+ };
46
+ /**
47
+ * The subscriber returned by {@link panoptic}. Wraps a collector +
48
+ * exporters and offers three ways to feed it:
49
+ *
50
+ * - **{@link Panoptic.attach}** — subscribe to a primitive's terminal
51
+ * `*.completed` events (the 3-tier model's instance tier) so every run
52
+ * is captured automatically. Returns a detach function.
53
+ * - **{@link Panoptic.middleware}** — an `AgentMiddleware` that collects
54
+ * each `agent.execute()` result from the `execute.after` / `onError`
55
+ * hooks, for apps that prefer the middleware pipeline over events.
56
+ * - **{@link Panoptic.collect}** — feed a `BaseReport` directly (e.g. an
57
+ * orchestrator turn's `result.report`, which has no result-bearing
58
+ * completed event).
59
+ *
60
+ * All three converge on the same collector, so a trace flows to every
61
+ * registered exporter exactly once per run.
62
+ */
63
+ type Panoptic = {
64
+ /** The underlying collector — exposed for `use()`, `toTrace()`, etc. */readonly collector: CollectorContract;
65
+ /**
66
+ * Register another exporter on the collector. Returns `this` for
67
+ * chaining; mirrors `CollectorContract.use`.
68
+ */
69
+ use(exporter: ExporterContract): Panoptic;
70
+ /**
71
+ * Subscribe to a primitive's terminal completed events so every run it
72
+ * performs is collected automatically. Safe to call on agents,
73
+ * workflows, and supervisors. Returns a detach function that removes
74
+ * every listener this call added.
75
+ */
76
+ attach(target: PanopticTarget): () => void;
77
+ /**
78
+ * An `AgentMiddleware` that feeds the collector from the
79
+ * `execute`-level `after` / `onError` hooks — an alternative to
80
+ * {@link Panoptic.attach} for apps wiring observability through the
81
+ * middleware pipeline. The hooks never mutate the result.
82
+ */
83
+ middleware(): AgentMiddleware;
84
+ /**
85
+ * Feed a finalized root report straight into the collector. Use for
86
+ * primitives whose completed event carries no result (the
87
+ * orchestrator) or to collect a report captured out of band.
88
+ */
89
+ collect(report: BaseReport): Promise<void>;
90
+ /**
91
+ * Pure projection of a report into a {@link Trace} without dispatching
92
+ * — passthrough to `CollectorContract.toTrace`.
93
+ */
94
+ toTrace(report: BaseReport): Trace; /** Flush every registered exporter that supports it. */
95
+ flush(): Promise<void>; /** Flush and shut down every registered exporter. */
96
+ shutdown(): Promise<void>;
97
+ };
98
+ //#endregion
99
+ export { Panoptic, PanopticOptions };
100
+ //# sourceMappingURL=panoptic.type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panoptic.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic.type.ts"],"mappings":";;;;;;;;;AAcA;;;;;;KAAY,eAAA;EAaE;;;;AAeE;EAtBd,SAAA,GAAY,gBAAA;EA0CM;;;;;;EAnClB,SAAA,GAAY,iBAAiB;EA8Db;;;;;;;;EArDhB,eAAA;EA4BoB;;;;;EAtBpB,cAAA;AAAA;;;;;;;;;;;;;;;;;AAwDmB;KApCT,QAAA;mFAED,SAAA,EAAW,iBAAA;;;;;EAKpB,GAAA,CAAI,QAAA,EAAU,gBAAA,GAAmB,QAAA;;;;;;;EAOjC,MAAA,CAAO,MAAA,EAAQ,cAAA;;;;;;;EAOf,UAAA,IAAc,eAAA;;;;;;EAMd,OAAA,CAAQ,MAAA,EAAQ,UAAA,GAAa,OAAA;;;;;EAK7B,OAAA,CAAQ,MAAA,EAAQ,UAAA,GAAa,KAAA;EAE7B,KAAA,IAAS,OAAA;EAET,QAAA,IAAY,OAAA;AAAA"}
@@ -0,0 +1,32 @@
1
+ import { ExporterContract } from "../contracts/exporter.contract.mjs";
2
+ import { TraceStoreContract } from "./trace-store.contract.mjs";
3
+
4
+ //#region ../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.d.ts
5
+ /**
6
+ * Options for {@link createInMemoryTraceStore}.
7
+ */
8
+ type InMemoryTraceStoreOptions = {
9
+ /**
10
+ * Maximum number of traces to retain. When set and exceeded, the
11
+ * oldest-ingested trace is evicted (insertion-order FIFO) so the
12
+ * store stays bounded for long-lived processes. Absent / `0` =
13
+ * unbounded (keep everything until `clear`).
14
+ */
15
+ capacity?: number;
16
+ };
17
+ /**
18
+ * Create an in-memory trace store. Optionally bound it with `capacity`
19
+ * for long-lived processes; leave it unset for dev/test where you want
20
+ * every trace retained.
21
+ *
22
+ * @example
23
+ * const store = createInMemoryTraceStore({ capacity: 1000 });
24
+ * collector.use(store);
25
+ * // later:
26
+ * const recentFailures = store.query({ status: "failed" });
27
+ * const sessionSpend = store.aggregate({ sessionId });
28
+ */
29
+ declare function createInMemoryTraceStore(options?: InMemoryTraceStoreOptions): TraceStoreContract & ExporterContract;
30
+ //#endregion
31
+ export { InMemoryTraceStoreOptions, createInMemoryTraceStore };
32
+ //# sourceMappingURL=in-memory-trace-store.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-trace-store.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.ts"],"mappings":";;;;;;AAWA;KAAY,yBAAA;;;AAOF;AAoLV;;;EApLE,QAAQ;AAAA;;;;;;;;;AAoL0G;;;;iBAApG,wBAAA,CAAyB,OAAA,GAAU,yBAAA,GAA4B,kBAAA,GAAqB,gBAAA"}
@@ -0,0 +1,134 @@
1
+ import { matchTrace } from "./match-trace.mjs";
2
+ import { emptyUsage, sumUsage } from "./sum-usage.mjs";
3
+
4
+ //#region ../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.ts
5
+ /**
6
+ * In-memory {@link TraceStoreContract} that doubles as an
7
+ * {@link ExporterContract} — register it on a collector
8
+ * (`collector.use(store)`) and it fills as traces complete, then query
9
+ * or aggregate it after the fact.
10
+ *
11
+ * Backed by an insertion-ordered `Map` keyed by `traceId`, giving O(1)
12
+ * `get` / `add` / overwrite and O(n) scans for `query` / `aggregate`
13
+ * (the price of an in-memory store with no secondary indexes — fine for
14
+ * the dev/test and modest-volume runtime use this targets). When a
15
+ * `capacity` is configured, ingesting past the cap evicts the oldest
16
+ * trace.
17
+ *
18
+ * Instantiated fresh per store via {@link createInMemoryTraceStore};
19
+ * callers never see `new`.
20
+ */
21
+ var InMemoryTraceStore = class {
22
+ constructor(options) {
23
+ this.name = "in-memory-trace-store";
24
+ this.traces = /* @__PURE__ */ new Map();
25
+ this.capacity = options?.capacity ?? 0;
26
+ }
27
+ get size() {
28
+ return this.traces.size;
29
+ }
30
+ add(trace) {
31
+ this.traces.delete(trace.traceId);
32
+ this.traces.set(trace.traceId, trace);
33
+ this.evictOverflow();
34
+ }
35
+ /**
36
+ * `ExporterContract.export` — a collector dispatches a completed
37
+ * trace here, which is exactly an `add`. Lets the store be wired into
38
+ * a collector as a sink without an adapter.
39
+ */
40
+ export(trace) {
41
+ this.add(trace);
42
+ }
43
+ get(traceId) {
44
+ return this.traces.get(traceId);
45
+ }
46
+ query(filter) {
47
+ const matched = [];
48
+ for (const trace of this.traces.values()) if (matchTrace(trace, filter)) matched.push(trace);
49
+ return this.sortNewestFirst(matched);
50
+ }
51
+ aggregate(filter) {
52
+ const aggregate = {
53
+ traces: 0,
54
+ completed: 0,
55
+ failed: 0,
56
+ cancelled: 0,
57
+ usage: emptyUsage(),
58
+ totalDuration: 0
59
+ };
60
+ for (const trace of this.traces.values()) {
61
+ if (!matchTrace(trace, filter)) continue;
62
+ aggregate.traces += 1;
63
+ aggregate.totalDuration += trace.duration;
64
+ aggregate.usage = sumUsage(aggregate.usage, trace.usage);
65
+ this.countStatus(aggregate, trace);
66
+ }
67
+ if (aggregate.usage.cost !== void 0) aggregate.cost = aggregate.usage.cost;
68
+ return aggregate;
69
+ }
70
+ clear() {
71
+ this.traces.clear();
72
+ }
73
+ /**
74
+ * Increment the matching terminal-status counter for one trace.
75
+ * Non-terminal statuses (`awaiting-input`, `max-iterations`) are
76
+ * counted in `traces` but tracked by none of the three headline
77
+ * counters — intentional, those three answer the common
78
+ * "succeeded / errored / aborted" question.
79
+ */
80
+ countStatus(aggregate, trace) {
81
+ switch (trace.root.status) {
82
+ case "completed":
83
+ aggregate.completed += 1;
84
+ break;
85
+ case "failed":
86
+ aggregate.failed += 1;
87
+ break;
88
+ case "cancelled":
89
+ aggregate.cancelled += 1;
90
+ break;
91
+ default: break;
92
+ }
93
+ }
94
+ /**
95
+ * Sort matched traces newest-started first. A copy is sorted so the
96
+ * underlying insertion order (which eviction depends on) is never
97
+ * disturbed.
98
+ */
99
+ sortNewestFirst(traces) {
100
+ return traces.sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt));
101
+ }
102
+ /**
103
+ * Evict oldest-inserted traces until the store is within `capacity`.
104
+ * No-op when unbounded. The `Map` iterator yields keys in insertion
105
+ * order, so the first key is always the oldest.
106
+ */
107
+ evictOverflow() {
108
+ if (this.capacity <= 0) return;
109
+ while (this.traces.size > this.capacity) {
110
+ const oldest = this.traces.keys().next().value;
111
+ if (oldest === void 0) return;
112
+ this.traces.delete(oldest);
113
+ }
114
+ }
115
+ };
116
+ /**
117
+ * Create an in-memory trace store. Optionally bound it with `capacity`
118
+ * for long-lived processes; leave it unset for dev/test where you want
119
+ * every trace retained.
120
+ *
121
+ * @example
122
+ * const store = createInMemoryTraceStore({ capacity: 1000 });
123
+ * collector.use(store);
124
+ * // later:
125
+ * const recentFailures = store.query({ status: "failed" });
126
+ * const sessionSpend = store.aggregate({ sessionId });
127
+ */
128
+ function createInMemoryTraceStore(options) {
129
+ return new InMemoryTraceStore(options);
130
+ }
131
+
132
+ //#endregion
133
+ export { createInMemoryTraceStore };
134
+ //# sourceMappingURL=in-memory-trace-store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-trace-store.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.ts"],"sourcesContent":["import type { ExporterContract } from \"../contracts/exporter.contract\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport { matchTrace } from \"./match-trace\";\nimport { emptyUsage, sumUsage } from \"./sum-usage\";\nimport type { TraceAggregate } from \"./trace-aggregate.type\";\nimport type { TraceQuery } from \"./trace-query.type\";\nimport type { TraceStoreContract } from \"./trace-store.contract\";\n\n/**\n * Options for {@link createInMemoryTraceStore}.\n */\nexport type InMemoryTraceStoreOptions = {\n /**\n * Maximum number of traces to retain. When set and exceeded, the\n * oldest-ingested trace is evicted (insertion-order FIFO) so the\n * store stays bounded for long-lived processes. Absent / `0` =\n * unbounded (keep everything until `clear`).\n */\n capacity?: number;\n};\n\n/**\n * In-memory {@link TraceStoreContract} that doubles as an\n * {@link ExporterContract} — register it on a collector\n * (`collector.use(store)`) and it fills as traces complete, then query\n * or aggregate it after the fact.\n *\n * Backed by an insertion-ordered `Map` keyed by `traceId`, giving O(1)\n * `get` / `add` / overwrite and O(n) scans for `query` / `aggregate`\n * (the price of an in-memory store with no secondary indexes — fine for\n * the dev/test and modest-volume runtime use this targets). When a\n * `capacity` is configured, ingesting past the cap evicts the oldest\n * trace.\n *\n * Instantiated fresh per store via {@link createInMemoryTraceStore};\n * callers never see `new`.\n */\nclass InMemoryTraceStore implements TraceStoreContract, ExporterContract {\n /** Stable exporter id so a collector can dedupe / log this sink. */\n public readonly name = \"in-memory-trace-store\";\n\n /**\n * Retained traces keyed by `traceId`. A `Map` preserves insertion\n * order, which is what FIFO eviction and newest-first `query` ordering\n * both rely on.\n */\n private readonly traces = new Map<string, Trace>();\n\n private readonly capacity: number;\n\n public constructor(options?: InMemoryTraceStoreOptions) {\n this.capacity = options?.capacity ?? 0;\n }\n\n public get size(): number {\n return this.traces.size;\n }\n\n public add(trace: Trace): void {\n // Re-insert so an overwrite also refreshes insertion position —\n // keeps \"oldest\" honest for FIFO eviction.\n this.traces.delete(trace.traceId);\n this.traces.set(trace.traceId, trace);\n\n this.evictOverflow();\n }\n\n /**\n * `ExporterContract.export` — a collector dispatches a completed\n * trace here, which is exactly an `add`. Lets the store be wired into\n * a collector as a sink without an adapter.\n */\n public export(trace: Trace): void {\n this.add(trace);\n }\n\n public get(traceId: string): Trace | undefined {\n return this.traces.get(traceId);\n }\n\n public query(filter?: TraceQuery): Trace[] {\n const matched: Trace[] = [];\n\n for (const trace of this.traces.values()) {\n if (matchTrace(trace, filter)) {\n matched.push(trace);\n }\n }\n\n return this.sortNewestFirst(matched);\n }\n\n public aggregate(filter?: TraceQuery): TraceAggregate {\n const aggregate: TraceAggregate = {\n traces: 0,\n completed: 0,\n failed: 0,\n cancelled: 0,\n usage: emptyUsage(),\n totalDuration: 0,\n };\n\n for (const trace of this.traces.values()) {\n if (!matchTrace(trace, filter)) {\n continue;\n }\n\n aggregate.traces += 1;\n aggregate.totalDuration += trace.duration;\n aggregate.usage = sumUsage(aggregate.usage, trace.usage);\n\n this.countStatus(aggregate, trace);\n }\n\n if (aggregate.usage.cost !== undefined) {\n aggregate.cost = aggregate.usage.cost;\n }\n\n return aggregate;\n }\n\n public clear(): void {\n this.traces.clear();\n }\n\n /**\n * Increment the matching terminal-status counter for one trace.\n * Non-terminal statuses (`awaiting-input`, `max-iterations`) are\n * counted in `traces` but tracked by none of the three headline\n * counters — intentional, those three answer the common\n * \"succeeded / errored / aborted\" question.\n */\n private countStatus(aggregate: TraceAggregate, trace: Trace): void {\n switch (trace.root.status) {\n case \"completed\": {\n aggregate.completed += 1;\n break;\n }\n\n case \"failed\": {\n aggregate.failed += 1;\n break;\n }\n\n case \"cancelled\": {\n aggregate.cancelled += 1;\n break;\n }\n\n default: {\n break;\n }\n }\n }\n\n /**\n * Sort matched traces newest-started first. A copy is sorted so the\n * underlying insertion order (which eviction depends on) is never\n * disturbed.\n */\n private sortNewestFirst(traces: Trace[]): Trace[] {\n return traces.sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt));\n }\n\n /**\n * Evict oldest-inserted traces until the store is within `capacity`.\n * No-op when unbounded. The `Map` iterator yields keys in insertion\n * order, so the first key is always the oldest.\n */\n private evictOverflow(): void {\n if (this.capacity <= 0) {\n return;\n }\n\n while (this.traces.size > this.capacity) {\n const oldest = this.traces.keys().next().value;\n\n if (oldest === undefined) {\n return;\n }\n\n this.traces.delete(oldest);\n }\n }\n}\n\n/**\n * Create an in-memory trace store. Optionally bound it with `capacity`\n * for long-lived processes; leave it unset for dev/test where you want\n * every trace retained.\n *\n * @example\n * const store = createInMemoryTraceStore({ capacity: 1000 });\n * collector.use(store);\n * // later:\n * const recentFailures = store.query({ status: \"failed\" });\n * const sessionSpend = store.aggregate({ sessionId });\n */\nexport function createInMemoryTraceStore(options?: InMemoryTraceStoreOptions): TraceStoreContract & ExporterContract {\n return new InMemoryTraceStore(options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqCA,IAAM,qBAAN,MAAyE;CAavE,AAAO,YAAY,SAAqC;cAXjC;gCAOG,IAAI,IAAmB;EAK/C,KAAK,WAAW,SAAS,YAAY;CACvC;CAEA,IAAW,OAAe;EACxB,OAAO,KAAK,OAAO;CACrB;CAEA,AAAO,IAAI,OAAoB;EAG7B,KAAK,OAAO,OAAO,MAAM,OAAO;EAChC,KAAK,OAAO,IAAI,MAAM,SAAS,KAAK;EAEpC,KAAK,cAAc;CACrB;;;;;;CAOA,AAAO,OAAO,OAAoB;EAChC,KAAK,IAAI,KAAK;CAChB;CAEA,AAAO,IAAI,SAAoC;EAC7C,OAAO,KAAK,OAAO,IAAI,OAAO;CAChC;CAEA,AAAO,MAAM,QAA8B;EACzC,MAAM,UAAmB,CAAC;EAE1B,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,GACrC,IAAI,WAAW,OAAO,MAAM,GAC1B,QAAQ,KAAK,KAAK;EAItB,OAAO,KAAK,gBAAgB,OAAO;CACrC;CAEA,AAAO,UAAU,QAAqC;EACpD,MAAM,YAA4B;GAChC,QAAQ;GACR,WAAW;GACX,QAAQ;GACR,WAAW;GACX,OAAO,WAAW;GAClB,eAAe;EACjB;EAEA,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,GAAG;GACxC,IAAI,CAAC,WAAW,OAAO,MAAM,GAC3B;GAGF,UAAU,UAAU;GACpB,UAAU,iBAAiB,MAAM;GACjC,UAAU,QAAQ,SAAS,UAAU,OAAO,MAAM,KAAK;GAEvD,KAAK,YAAY,WAAW,KAAK;EACnC;EAEA,IAAI,UAAU,MAAM,SAAS,QAC3B,UAAU,OAAO,UAAU,MAAM;EAGnC,OAAO;CACT;CAEA,AAAO,QAAc;EACnB,KAAK,OAAO,MAAM;CACpB;;;;;;;;CASA,AAAQ,YAAY,WAA2B,OAAoB;EACjE,QAAQ,MAAM,KAAK,QAAnB;GACE,KAAK;IACH,UAAU,aAAa;IACvB;GAGF,KAAK;IACH,UAAU,UAAU;IACpB;GAGF,KAAK;IACH,UAAU,aAAa;IACvB;GAGF,SACE;EAEJ;CACF;;;;;;CAOA,AAAQ,gBAAgB,QAA0B;EAChD,OAAO,OAAO,MAAM,MAAM,UAAU,KAAK,MAAM,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC;CAC9F;;;;;;CAOA,AAAQ,gBAAsB;EAC5B,IAAI,KAAK,YAAY,GACnB;EAGF,OAAO,KAAK,OAAO,OAAO,KAAK,UAAU;GACvC,MAAM,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;GAEzC,IAAI,WAAW,QACb;GAGF,KAAK,OAAO,OAAO,MAAM;EAC3B;CACF;AACF;;;;;;;;;;;;;AAcA,SAAgB,yBAAyB,SAA4E;CACnH,OAAO,IAAI,mBAAmB,OAAO;AACvC"}
@@ -0,0 +1,6 @@
1
+ import { TraceAggregate } from "./trace-aggregate.type.mjs";
2
+ import { TraceQuery } from "./trace-query.type.mjs";
3
+ import { TraceStoreContract } from "./trace-store.contract.mjs";
4
+ import { InMemoryTraceStoreOptions, createInMemoryTraceStore } from "./in-memory-trace-store.mjs";
5
+ import { matchTrace } from "./match-trace.mjs";
6
+ import { emptyUsage, sumUsage } from "./sum-usage.mjs";
@@ -0,0 +1,5 @@
1
+ import { matchTrace } from "./match-trace.mjs";
2
+ import { emptyUsage, sumUsage } from "./sum-usage.mjs";
3
+ import { createInMemoryTraceStore } from "./in-memory-trace-store.mjs";
4
+
5
+ export { };