@warlock.js/ai-panoptic 4.3.0 → 4.5.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 (114) hide show
  1. package/CHANGELOG.md +46 -30
  2. package/cjs/index.cjs +2636 -67
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/collector/collector.d.mts +20 -2
  5. package/esm/collector/collector.d.mts.map +1 -1
  6. package/esm/collector/collector.mjs +47 -7
  7. package/esm/collector/collector.mjs.map +1 -1
  8. package/esm/collector/content-capture.type.d.mts +52 -0
  9. package/esm/collector/content-capture.type.d.mts.map +1 -0
  10. package/esm/collector/extract-span-attributes.d.mts.map +1 -1
  11. package/esm/collector/extract-span-attributes.mjs +3 -0
  12. package/esm/collector/extract-span-attributes.mjs.map +1 -1
  13. package/esm/collector/index.d.mts +2 -1
  14. package/esm/collector/index.mjs +7 -0
  15. package/esm/collector/normalize-error.d.mts.map +1 -1
  16. package/esm/collector/normalize-error.mjs +6 -3
  17. package/esm/collector/normalize-error.mjs.map +1 -1
  18. package/esm/collector/report-to-span.d.mts +8 -1
  19. package/esm/collector/report-to-span.d.mts.map +1 -1
  20. package/esm/collector/report-to-span.mjs +87 -2
  21. package/esm/collector/report-to-span.mjs.map +1 -1
  22. package/esm/collector/report-to-trace.d.mts +10 -6
  23. package/esm/collector/report-to-trace.d.mts.map +1 -1
  24. package/esm/collector/report-to-trace.mjs +10 -7
  25. package/esm/collector/report-to-trace.mjs.map +1 -1
  26. package/esm/config/apply-panoptic-config.d.mts +27 -0
  27. package/esm/config/apply-panoptic-config.d.mts.map +1 -0
  28. package/esm/config/apply-panoptic-config.mjs +89 -0
  29. package/esm/config/apply-panoptic-config.mjs.map +1 -0
  30. package/esm/config/index.d.mts +2 -0
  31. package/esm/config/index.mjs +3 -0
  32. package/esm/config/panoptic-config.type.d.mts +84 -0
  33. package/esm/config/panoptic-config.type.d.mts.map +1 -0
  34. package/esm/contracts/trace.type.d.mts +31 -5
  35. package/esm/contracts/trace.type.d.mts.map +1 -1
  36. package/esm/dashboard/dashboard.d.mts +32 -0
  37. package/esm/dashboard/dashboard.d.mts.map +1 -0
  38. package/esm/dashboard/dashboard.mjs +132 -0
  39. package/esm/dashboard/dashboard.mjs.map +1 -0
  40. package/esm/dashboard/dashboard.type.d.mts +63 -0
  41. package/esm/dashboard/dashboard.type.d.mts.map +1 -0
  42. package/esm/dashboard/index.d.mts +3 -0
  43. package/esm/dashboard/parse-query.d.mts +1 -0
  44. package/esm/dashboard/parse-query.mjs +54 -0
  45. package/esm/dashboard/parse-query.mjs.map +1 -0
  46. package/esm/dashboard/serve.d.mts +1 -0
  47. package/esm/dashboard/serve.mjs +111 -0
  48. package/esm/dashboard/serve.mjs.map +1 -0
  49. package/esm/dashboard/trace-filter.d.mts +187 -0
  50. package/esm/dashboard/trace-filter.d.mts.map +1 -0
  51. package/esm/dashboard/trace-filter.mjs +270 -0
  52. package/esm/dashboard/trace-filter.mjs.map +1 -0
  53. package/esm/dashboard/ui.html.mjs +1330 -0
  54. package/esm/dashboard/ui.html.mjs.map +1 -0
  55. package/esm/dashboard/warlock-logo.mjs +13 -0
  56. package/esm/dashboard/warlock-logo.mjs.map +1 -0
  57. package/esm/exporters/console/console-exporter.d.mts +11 -3
  58. package/esm/exporters/console/console-exporter.d.mts.map +1 -1
  59. package/esm/exporters/console/console-exporter.mjs +31 -13
  60. package/esm/exporters/console/console-exporter.mjs.map +1 -1
  61. package/esm/exporters/console/console-exporter.type.d.mts +15 -0
  62. package/esm/exporters/console/console-exporter.type.d.mts.map +1 -1
  63. package/esm/exporters/console/format-span-io.d.mts +27 -0
  64. package/esm/exporters/console/format-span-io.d.mts.map +1 -0
  65. package/esm/exporters/console/format-span-io.mjs +52 -0
  66. package/esm/exporters/console/format-span-io.mjs.map +1 -0
  67. package/esm/exporters/console/index.d.mts +2 -1
  68. package/esm/exporters/file/file-exporter.mjs +5 -2
  69. package/esm/exporters/file/file-exporter.mjs.map +1 -1
  70. package/esm/exporters/index.d.mts +1 -0
  71. package/esm/exporters/index.mjs +1 -0
  72. package/esm/exporters/langfuse/langfuse-exporter.d.mts +10 -6
  73. package/esm/exporters/langfuse/langfuse-exporter.d.mts.map +1 -1
  74. package/esm/exporters/langfuse/langfuse-exporter.mjs +80 -30
  75. package/esm/exporters/langfuse/langfuse-exporter.mjs.map +1 -1
  76. package/esm/exporters/langfuse/langfuse-exporter.type.d.mts +6 -2
  77. package/esm/exporters/langfuse/langfuse-exporter.type.d.mts.map +1 -1
  78. package/esm/exporters/otel/otel-api.shim.type.d.mts +60 -0
  79. package/esm/exporters/otel/otel-api.shim.type.d.mts.map +1 -0
  80. package/esm/exporters/otel/otel-exporter.d.mts.map +1 -1
  81. package/esm/exporters/otel/otel-exporter.mjs +15 -0
  82. package/esm/exporters/otel/otel-exporter.mjs.map +1 -1
  83. package/esm/exporters/otel/otel-exporter.type.d.mts +7 -3
  84. package/esm/exporters/otel/otel-exporter.type.d.mts.map +1 -1
  85. package/esm/exporters/utils/gen-ai-attributes.d.mts +3 -1
  86. package/esm/exporters/utils/gen-ai-attributes.d.mts.map +1 -1
  87. package/esm/exporters/utils/gen-ai-attributes.mjs +5 -1
  88. package/esm/exporters/utils/gen-ai-attributes.mjs.map +1 -1
  89. package/esm/index.d.mts +11 -2
  90. package/esm/index.mjs +10 -1
  91. package/esm/panoptic/index.mjs +4 -0
  92. package/esm/panoptic/panoptic.d.mts.map +1 -1
  93. package/esm/panoptic/panoptic.mjs +9 -1
  94. package/esm/panoptic/panoptic.mjs.map +1 -1
  95. package/esm/panoptic/panoptic.type.d.mts +32 -0
  96. package/esm/panoptic/panoptic.type.d.mts.map +1 -1
  97. package/esm/register.mjs +12 -0
  98. package/esm/register.mjs.map +1 -0
  99. package/esm/store/cache-trace-store.d.mts +81 -0
  100. package/esm/store/cache-trace-store.d.mts.map +1 -0
  101. package/esm/store/cache-trace-store.mjs +290 -0
  102. package/esm/store/cache-trace-store.mjs.map +1 -0
  103. package/esm/store/in-memory-trace-store.mjs +9 -2
  104. package/esm/store/in-memory-trace-store.mjs.map +1 -1
  105. package/esm/store/index.d.mts +1 -0
  106. package/esm/store/index.mjs +1 -0
  107. package/llms-full.txt +351 -4
  108. package/llms.txt +4 -3
  109. package/package.json +6 -2
  110. package/skills/README.md +7 -3
  111. package/skills/export-traces/SKILL.md +33 -1
  112. package/skills/observe-with-panoptic/SKILL.md +49 -2
  113. package/skills/query-traces/SKILL.md +52 -1
  114. package/skills/use-local-dashboard/SKILL.md +213 -0
@@ -0,0 +1,27 @@
1
+ import { PanopticConfig } from "./panoptic-config.type.mjs";
2
+
3
+ //#region ../@warlock.js/ai-panoptic/src/config/apply-panoptic-config.d.ts
4
+ /**
5
+ * Read a {@link PanopticConfig} and wire panoptic onto the core observe
6
+ * seam — the bridge `ai.config({ panoptic })` resolves through. Builds a
7
+ * collector via the EXISTING {@link panoptic} factory (reusing its
8
+ * collection pipeline, not reinventing it), registers it once via core's
9
+ * `registerObserver` (the subscriber's `collect(report)` structurally IS
10
+ * an {@link Observer}, threaded through a thin wrapper), sets
11
+ * `observeAll`, and — when `config.dashboard` is set — starts the
12
+ * dashboard over the config's store (a store-shaped exporter, or a fresh
13
+ * in-memory store panoptic registers when none was supplied).
14
+ *
15
+ * **Idempotent.** Safe to call on every `onConfigApplied` notification:
16
+ * the observer is registered once, the dashboard started once. Repeat
17
+ * calls update `observeAll` to the latest value but never double-register
18
+ * or double-start. `undefined` config is a no-op (nothing wired).
19
+ *
20
+ * @returns the resolved store-shaped exporter when the dashboard needs
21
+ * one and a fresh store was created — primarily for tests; callers can
22
+ * ignore it.
23
+ */
24
+ declare function applyPanopticConfig(config?: PanopticConfig): void;
25
+ //#endregion
26
+ export { applyPanopticConfig };
27
+ //# sourceMappingURL=apply-panoptic-config.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-panoptic-config.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/config/apply-panoptic-config.ts"],"mappings":";;;;AAgDA;;;;AAA2D;;;;;;;;;;;;;;;iBAA3C,mBAAA,CAAoB,MAAuB,GAAd,cAAc"}
@@ -0,0 +1,89 @@
1
+ import { createCacheTraceStore } from "../store/cache-trace-store.mjs";
2
+ import { createInMemoryTraceStore } from "../store/in-memory-trace-store.mjs";
3
+ import { panoptic } from "../panoptic/panoptic.mjs";
4
+ import { dashboard } from "../dashboard/dashboard.mjs";
5
+ import { registerObserver, setObserveAll } from "@warlock.js/ai";
6
+
7
+ //#region ../@warlock.js/ai-panoptic/src/config/apply-panoptic-config.ts
8
+ let applied;
9
+ /**
10
+ * Read a {@link PanopticConfig} and wire panoptic onto the core observe
11
+ * seam — the bridge `ai.config({ panoptic })` resolves through. Builds a
12
+ * collector via the EXISTING {@link panoptic} factory (reusing its
13
+ * collection pipeline, not reinventing it), registers it once via core's
14
+ * `registerObserver` (the subscriber's `collect(report)` structurally IS
15
+ * an {@link Observer}, threaded through a thin wrapper), sets
16
+ * `observeAll`, and — when `config.dashboard` is set — starts the
17
+ * dashboard over the config's store (a store-shaped exporter, or a fresh
18
+ * in-memory store panoptic registers when none was supplied).
19
+ *
20
+ * **Idempotent.** Safe to call on every `onConfigApplied` notification:
21
+ * the observer is registered once, the dashboard started once. Repeat
22
+ * calls update `observeAll` to the latest value but never double-register
23
+ * or double-start. `undefined` config is a no-op (nothing wired).
24
+ *
25
+ * @returns the resolved store-shaped exporter when the dashboard needs
26
+ * one and a fresh store was created — primarily for tests; callers can
27
+ * ignore it.
28
+ */
29
+ function applyPanopticConfig(config) {
30
+ if (config === void 0) return;
31
+ setObserveAll(Boolean(config.observeAll));
32
+ if (applied === void 0) {
33
+ const exporters = [...config.exporters ?? []];
34
+ let store = findStore(exporters);
35
+ let cacheStore;
36
+ if (store === void 0 && config.dashboard) {
37
+ if (config.cache !== void 0) {
38
+ cacheStore = createCacheTraceStore(config.cache);
39
+ store = cacheStore;
40
+ } else store = createInMemoryTraceStore();
41
+ exporters.push(store);
42
+ }
43
+ const panopticInstance = panoptic({
44
+ exporters,
45
+ captureContent: config.captureContent,
46
+ fullHistory: config.fullHistory
47
+ });
48
+ registerObserver(toObserver(panopticInstance));
49
+ applied = { panopticInstance };
50
+ if (config.dashboard && store !== void 0) {
51
+ const resolvedStore = store;
52
+ (cacheStore !== void 0 ? cacheStore.ready() : Promise.resolve()).catch(() => {}).then(() => startDashboard(resolvedStore, config.dashboard ?? {})).then((handle) => {
53
+ if (applied !== void 0) applied.dashboardHandle = handle;
54
+ });
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Wrap a {@link Panoptic} subscriber as a core {@link Observer}. The
60
+ * subscriber already exposes `collect(report)` with a matching signature,
61
+ * but the thin wrapper makes the structural adaptation explicit and keeps
62
+ * the registered object a minimal `Observer` rather than the whole
63
+ * subscriber surface.
64
+ */
65
+ function toObserver(instance) {
66
+ return { collect(report) {
67
+ return instance.collect(report);
68
+ } };
69
+ }
70
+ /**
71
+ * Find the first exporter that satisfies the {@link TraceStoreContract}
72
+ * read surface (`query` / `get` / `aggregate`) — the in-memory store
73
+ * doubles as an exporter, so a store passed via `exporters` is reused for
74
+ * the dashboard rather than creating a second one.
75
+ */
76
+ function findStore(exporters) {
77
+ for (const exporter of exporters) {
78
+ const candidate = exporter;
79
+ if (typeof candidate.query === "function" && typeof candidate.get === "function" && typeof candidate.aggregate === "function") return candidate;
80
+ }
81
+ }
82
+ /** Start the dashboard, normalizing the `true | DashboardOptions` switch. */
83
+ function startDashboard(store, dashboardConfig) {
84
+ return dashboard(store, typeof dashboardConfig === "object" ? dashboardConfig : {});
85
+ }
86
+
87
+ //#endregion
88
+ export { applyPanopticConfig };
89
+ //# sourceMappingURL=apply-panoptic-config.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-panoptic-config.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/config/apply-panoptic-config.ts"],"sourcesContent":["import type { ExecutionReport, Observer } from \"@warlock.js/ai\";\nimport { registerObserver, setObserveAll } from \"@warlock.js/ai\";\nimport type { ExporterContract } from \"../contracts/exporter.contract\";\nimport { dashboard } from \"../dashboard/dashboard\";\nimport type { DashboardHandle, DashboardOptions } from \"../dashboard/dashboard.type\";\nimport { panoptic } from \"../panoptic/panoptic\";\nimport type { Panoptic } from \"../panoptic/panoptic.type\";\nimport { createCacheTraceStore } from \"../store/cache-trace-store\";\nimport type { CacheTraceStoreHandle } from \"../store/cache-trace-store\";\nimport { createInMemoryTraceStore } from \"../store/in-memory-trace-store\";\nimport type { TraceStoreContract } from \"../store/trace-store.contract\";\nimport type { PanopticConfig } from \"./panoptic-config.type\";\n\n/**\n * Module-level applied state. Applying panoptic config is idempotent:\n * the collector is registered as a core `Observer` exactly once, the\n * dashboard is started at most once, and repeat calls only refresh the\n * `observeAll` flag (the latest config wins) without double-registering.\n */\ntype AppliedState = {\n /** The single collector registered as a core `Observer`. */\n panopticInstance: Panoptic;\n /** The dashboard handle, once started. Started at most once. */\n dashboardHandle?: DashboardHandle;\n};\n\nlet applied: AppliedState | undefined;\n\n/**\n * Read a {@link PanopticConfig} and wire panoptic onto the core observe\n * seam — the bridge `ai.config({ panoptic })` resolves through. Builds a\n * collector via the EXISTING {@link panoptic} factory (reusing its\n * collection pipeline, not reinventing it), registers it once via core's\n * `registerObserver` (the subscriber's `collect(report)` structurally IS\n * an {@link Observer}, threaded through a thin wrapper), sets\n * `observeAll`, and — when `config.dashboard` is set — starts the\n * dashboard over the config's store (a store-shaped exporter, or a fresh\n * in-memory store panoptic registers when none was supplied).\n *\n * **Idempotent.** Safe to call on every `onConfigApplied` notification:\n * the observer is registered once, the dashboard started once. Repeat\n * calls update `observeAll` to the latest value but never double-register\n * or double-start. `undefined` config is a no-op (nothing wired).\n *\n * @returns the resolved store-shaped exporter when the dashboard needs\n * one and a fresh store was created — primarily for tests; callers can\n * ignore it.\n */\nexport function applyPanopticConfig(config?: PanopticConfig): void {\n if (config === undefined) {\n return;\n }\n\n // observeAll always tracks the latest config, even on a repeat call.\n setObserveAll(Boolean(config.observeAll));\n\n if (applied === undefined) {\n const exporters = [...(config.exporters ?? [])];\n let store = findStore(exporters);\n\n // Track a cache store separately so we can await its hydration before\n // the dashboard reads it — the in-memory store needs no warm-up.\n let cacheStore: CacheTraceStoreHandle | undefined;\n\n // The dashboard needs a queryable store. If none was supplied via\n // exporters but a dashboard is requested, create one and register it\n // as an exporter so the collector fills it. A configured `cache` picks\n // the durable cache-backed store; otherwise fall back to in-memory so\n // the dashboard works with no cache configured.\n if (store === undefined && config.dashboard) {\n if (config.cache !== undefined) {\n cacheStore = createCacheTraceStore(config.cache);\n store = cacheStore;\n } else {\n store = createInMemoryTraceStore();\n }\n\n exporters.push(store as unknown as ExporterContract);\n }\n\n const panopticInstance = panoptic({\n exporters,\n captureContent: config.captureContent,\n fullHistory: config.fullHistory,\n });\n registerObserver(toObserver(panopticInstance));\n\n applied = { panopticInstance };\n\n if (config.dashboard && store !== undefined) {\n const resolvedStore = store;\n\n // Hydrate the cache mirror (if any) before serving, so a restart\n // surfaces previously-persisted traces immediately. A bare in-memory\n // store resolves the readyGate instantly.\n const readyGate =\n cacheStore !== undefined ? cacheStore.ready() : Promise.resolve();\n\n void readyGate\n .catch(() => {\n // Hydration is best-effort — never block the dashboard on it.\n })\n .then(() => startDashboard(resolvedStore, config.dashboard ?? {}))\n .then((handle) => {\n if (applied !== undefined) {\n applied.dashboardHandle = handle;\n }\n });\n }\n }\n}\n\n/**\n * Reset the applied state — internal, test-only. Closes any running\n * dashboard and forgets the registered collector so a fresh\n * `applyPanopticConfig` starts clean. NOT part of the public surface and\n * does NOT unregister from the core observer registry (use core's\n * `clearObservers` for that in tests).\n */\nexport async function resetAppliedPanopticConfig(): Promise<void> {\n const handle = applied?.dashboardHandle;\n applied = undefined;\n\n if (handle !== undefined) {\n await handle.close();\n }\n}\n\n/**\n * The currently-running dashboard handle, or `undefined` when no\n * dashboard is active. Internal, test-only — lets a spec await the\n * asynchronously-started dashboard. NOT part of the public surface.\n */\nexport function getActiveDashboardHandle(): DashboardHandle | undefined {\n return applied?.dashboardHandle;\n}\n\n/**\n * Wrap a {@link Panoptic} subscriber as a core {@link Observer}. The\n * subscriber already exposes `collect(report)` with a matching signature,\n * but the thin wrapper makes the structural adaptation explicit and keeps\n * the registered object a minimal `Observer` rather than the whole\n * subscriber surface.\n */\nfunction toObserver(instance: Panoptic): Observer {\n return {\n collect(report: ExecutionReport): Promise<void> {\n return instance.collect(report);\n },\n };\n}\n\n/**\n * Find the first exporter that satisfies the {@link TraceStoreContract}\n * read surface (`query` / `get` / `aggregate`) — the in-memory store\n * doubles as an exporter, so a store passed via `exporters` is reused for\n * the dashboard rather than creating a second one.\n */\nfunction findStore(exporters: ExporterContract[]): TraceStoreContract | undefined {\n for (const exporter of exporters) {\n const candidate = exporter as unknown as Partial<TraceStoreContract>;\n\n if (\n typeof candidate.query === \"function\" &&\n typeof candidate.get === \"function\" &&\n typeof candidate.aggregate === \"function\"\n ) {\n return candidate as TraceStoreContract;\n }\n }\n\n return undefined;\n}\n\n/** Start the dashboard, normalizing the `true | DashboardOptions` switch. */\nfunction startDashboard(\n store: TraceStoreContract,\n dashboardConfig: boolean | DashboardOptions,\n): Promise<DashboardHandle> {\n const options: DashboardOptions =\n typeof dashboardConfig === \"object\" ? dashboardConfig : {};\n\n return dashboard(store, options);\n}\n"],"mappings":";;;;;;;AA0BA,IAAI;;;;;;;;;;;;;;;;;;;;;AAsBJ,SAAgB,oBAAoB,QAA+B;CACjE,IAAI,WAAW,QACb;CAIF,cAAc,QAAQ,OAAO,UAAU,CAAC;CAExC,IAAI,YAAY,QAAW;EACzB,MAAM,YAAY,CAAC,GAAI,OAAO,aAAa,CAAC,CAAE;EAC9C,IAAI,QAAQ,UAAU,SAAS;EAI/B,IAAI;EAOJ,IAAI,UAAU,UAAa,OAAO,WAAW;GAC3C,IAAI,OAAO,UAAU,QAAW;IAC9B,aAAa,sBAAsB,OAAO,KAAK;IAC/C,QAAQ;GACV,OACE,QAAQ,yBAAyB;GAGnC,UAAU,KAAK,KAAoC;EACrD;EAEA,MAAM,mBAAmB,SAAS;GAChC;GACA,gBAAgB,OAAO;GACvB,aAAa,OAAO;EACtB,CAAC;EACD,iBAAiB,WAAW,gBAAgB,CAAC;EAE7C,UAAU,EAAE,iBAAiB;EAE7B,IAAI,OAAO,aAAa,UAAU,QAAW;GAC3C,MAAM,gBAAgB;GAQtB,CAFE,eAAe,SAAY,WAAW,MAAM,IAAI,QAAQ,QAAQ,EAEpD,CACX,YAAY,CAEb,CAAC,CAAC,CACD,WAAW,eAAe,eAAe,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,CACjE,MAAM,WAAW;IAChB,IAAI,YAAY,QACd,QAAQ,kBAAkB;GAE9B,CAAC;EACL;CACF;AACF;;;;;;;;AAkCA,SAAS,WAAW,UAA8B;CAChD,OAAO,EACL,QAAQ,QAAwC;EAC9C,OAAO,SAAS,QAAQ,MAAM;CAChC,EACF;AACF;;;;;;;AAQA,SAAS,UAAU,WAA+D;CAChF,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,YAAY;EAElB,IACE,OAAO,UAAU,UAAU,cAC3B,OAAO,UAAU,QAAQ,cACzB,OAAO,UAAU,cAAc,YAE/B,OAAO;CAEX;AAGF;;AAGA,SAAS,eACP,OACA,iBAC0B;CAI1B,OAAO,UAAU,OAFf,OAAO,oBAAoB,WAAW,kBAAkB,CAAC,CAE5B;AACjC"}
@@ -0,0 +1,2 @@
1
+ import { PanopticConfig } from "./panoptic-config.type.mjs";
2
+ import { applyPanopticConfig } from "./apply-panoptic-config.mjs";
@@ -0,0 +1,3 @@
1
+ import { applyPanopticConfig } from "./apply-panoptic-config.mjs";
2
+
3
+ export { };
@@ -0,0 +1,84 @@
1
+ import { ExporterContract } from "../contracts/exporter.contract.mjs";
2
+ import { CacheDriverInput } from "../store/cache-trace-store.mjs";
3
+ import { DashboardOptions } from "../dashboard/dashboard.type.mjs";
4
+
5
+ //#region ../@warlock.js/ai-panoptic/src/config/panoptic-config.type.d.ts
6
+ /**
7
+ * Declarative panoptic configuration, set once through the global
8
+ * `ai.config({ panoptic: { … } })`. Sugar layered over the existing
9
+ * {@link import("../panoptic/panoptic").panoptic} factory: reading this
10
+ * slot (on the side-effect import of `@warlock.js/ai-panoptic`) builds
11
+ * the same collector, registers it as a core `Observer`, and optionally
12
+ * starts the local dashboard — so app code never hand-wires a store and
13
+ * a server. The explicit `panoptic({ exporters })` / `.attach()` /
14
+ * `.collect()` API stays fully intact as the underlying primitive.
15
+ *
16
+ * @example
17
+ * import { ai } from "@warlock.js/ai";
18
+ * import "@warlock.js/ai-panoptic"; // side-effect wiring
19
+ *
20
+ * ai.config({
21
+ * panoptic: {
22
+ * exporters: [createInMemoryTraceStore({ capacity: 5000 })],
23
+ * dashboard: { port: 4319, open: true },
24
+ * observeAll: true,
25
+ * },
26
+ * });
27
+ */
28
+ type PanopticConfig = {
29
+ /**
30
+ * Exporters the collector feeds (a store, OTel, Langfuse, …). A
31
+ * {@link import("../store/in-memory-trace-store").createInMemoryTraceStore}
32
+ * store doubles as an `ExporterContract`, so passing one here both
33
+ * captures traces and gives the dashboard something to read.
34
+ */
35
+ exporters?: ExporterContract[];
36
+ /**
37
+ * Persist traces through a {@link import("@warlock.js/cache").CacheDriver}
38
+ * (Redis, Postgres, file, …) so they survive a process restart. Accepts a
39
+ * driver instance or a (possibly async) factory resolved lazily on first
40
+ * use — the factory form lets production defer the connect.
41
+ *
42
+ * When set (and no store-shaped exporter was supplied), the dashboard
43
+ * reads a {@link import("../store/cache-trace-store").createCacheTraceStore}
44
+ * store instead of a fresh in-memory one. When omitted, panoptic falls
45
+ * back to {@link import("../store/in-memory-trace-store").createInMemoryTraceStore}
46
+ * — so the dashboard works with no cache configured.
47
+ */
48
+ cache?: CacheDriverInput;
49
+ /**
50
+ * Serve the local dashboard. `true` uses the defaults
51
+ * ({@link DashboardOptions}); an object overrides them. When set, the
52
+ * dashboard reads the store supplied via `exporters` (the first
53
+ * store-shaped exporter), or a fresh in-memory store panoptic creates
54
+ * and registers when none was supplied.
55
+ */
56
+ dashboard?: boolean | DashboardOptions;
57
+ /**
58
+ * Observe every flow (`ai.agent` / `ai.workflow` / `ai.supervisor` /
59
+ * `ai.team`) by default. Individual flows opt out with `observe: false`.
60
+ * Default `false` — opt-in observability.
61
+ */
62
+ observeAll?: boolean;
63
+ /**
64
+ * Capture raw prompt / response / tool I/O onto each span so the dashboard
65
+ * + console exporter can surface them. Off by default (payloads are large
66
+ * and can be sensitive); set `true` for a content-rich local dashboard.
67
+ */
68
+ captureContent?: boolean;
69
+ /**
70
+ * Emit each agent's full message history (every trip, every role) onto
71
+ * `span.input` instead of just `[system, user]`. Requires the agent run to
72
+ * set `captureMessages`. Default `false`.
73
+ */
74
+ fullHistory?: boolean;
75
+ };
76
+ declare module "@warlock.js/ai" {
77
+ interface AIConfig {
78
+ /** Declarative panoptic observability config. See {@link PanopticConfig}. */
79
+ panoptic?: PanopticConfig;
80
+ }
81
+ } //# sourceMappingURL=panoptic-config.type.d.ts.map
82
+ //#endregion
83
+ export { PanopticConfig };
84
+ //# sourceMappingURL=panoptic-config.type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panoptic-config.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/config/panoptic-config.type.ts"],"mappings":";;;;;;;AA0BA;;;;;;;;;;;;;;;;;;AA8Ca;AACX;KA/CU,cAAA;;;;;;;EAOV,SAAA,GAAY,gBAAA;EAiDe;;;;;;;;;;;;EApC3B,KAAA,GAAQ,gBAAA;;;;;;;;EAQR,SAAA,aAAsB,gBAAA;;;;;;EAMtB,UAAA;;;;;;EAMA,cAAA;;;;;;EAMA,WAAA;AAAA;AAAA;EAAA,UAQU,QAAA;;IAER,QAAA,GAAW,cAAc;EAAA;AAAA"}
@@ -13,7 +13,9 @@ import { ReportStatus, ReportType, Usage } from "@warlock.js/ai";
13
13
  * about — identity (`spanId` / `parentSpanId` / `traceId`), timing
14
14
  * (`startedAt` / `endedAt` / `duration`), outcome (`status` / `error`),
15
15
  * and cost (`usage`) — so a collector can flatten a `BaseReport` tree
16
- * into spans without consulting any other source.
16
+ * into spans without consulting any other source. Raw content — prompts,
17
+ * responses, and tool payloads — is captured onto `input` / `output`
18
+ * only when the collector opts in via `captureContent` (off by default).
17
19
  *
18
20
  * @example
19
21
  * const root: TraceSpan = {
@@ -75,10 +77,27 @@ type TraceSpan = {
75
77
  /**
76
78
  * Free-form attribute bag forwarded verbatim onto the backend span
77
79
  * (OTel attributes, Langfuse metadata). Collectors populate this with
78
- * primitive-specific detail (trip index, step name, tool args digest)
79
- * that has no first-class field here. Absent when empty.
80
+ * primitive-specific metadata (trip / step / iteration counts, model
81
+ * identity, the tool's originating trip index) that has no first-class
82
+ * field here. Raw prompts / completions / tool payloads do NOT live
83
+ * here — they go on {@link TraceSpan.input} / {@link TraceSpan.output}
84
+ * under content capture. Absent when empty.
80
85
  */
81
86
  attributes?: Record<string, unknown>;
87
+ /**
88
+ * Captured input for this node — an agent's first-trip prompt or a
89
+ * tool's call arguments — present ONLY when the collector ran with
90
+ * content capture enabled (`captureContent`), off by default because
91
+ * payloads are large and often sensitive. Typed `unknown`: agent
92
+ * prompts are strings, tool args are the validated argument value.
93
+ */
94
+ input?: unknown;
95
+ /**
96
+ * Captured output for this node — an agent's final response text or a
97
+ * tool's return value — present only under content capture (see
98
+ * {@link TraceSpan.input}).
99
+ */
100
+ output?: unknown;
82
101
  /**
83
102
  * Child spans, mirroring `BaseReport.children` in invocation order.
84
103
  * Empty for leaf spans (pure tools). Exporters that emit a flat
@@ -92,9 +111,16 @@ type TraceSpan = {
92
111
  * serialization to a backend collector unchanged.
93
112
  */
94
113
  type TraceSpanError = {
95
- /** Stable error identifier (typed framework error name when available). */type: string; /** Human-readable message. */
96
- message: string; /** Optional captured stack trace, when the source error carried one. */
114
+ /** Stable error identifier (typed framework error name when available). */type: string; /** Human-readable message — secret-scrubbed before storage (S4). */
115
+ message: string; /** Optional captured stack trace, secret-scrubbed when present (S4). */
97
116
  stack?: string;
117
+ /**
118
+ * Optional redacted view of the error's `cause` (S4) — a raw provider
119
+ * SDK error can carry `Authorization` / `x-api-key` / `cookie` on its
120
+ * headers, so the cause is deep-redacted (sensitive keys stripped)
121
+ * before it is attached.
122
+ */
123
+ cause?: unknown;
98
124
  };
99
125
  /**
100
126
  * The root of a Panoptic trace — a top-level {@link TraceSpan} plus the
@@ -1 +1 @@
1
- {"version":3,"file":"trace.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/contracts/trace.type.ts"],"mappings":";;;;;AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;KAAY,SAAA;EAiDF;;;;EA5CR,MAAA;EAyDmB;AAAA;AAQrB;;EA5DE,YAAA;EA4DwB;;;;;AAMnB;EA3DL,OAAA;EAoEe;;;;;EA9Df,SAAA,WAoEM;EAlEN,IAAA;EAsEA;;;;;EAhEA,OAAA,WA0EmB;EAxEnB,IAAA,EAAM,UAAA;EAEN,MAAA,EAAQ,YAAA;EAER,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAA;;;;;;EAMP,KAAA,GAAQ,cAAA;;;;;;;EAOR,UAAA,GAAa,MAAA;;;;;;EAMb,QAAA,EAAU,SAAA;AAAA;;;;;;KAQA,cAAA;6EAEV,IAAA;EAEA,OAAA;EAEA,KAAA;AAAA;;;;;;;KASU,KAAA;mEAEV,OAAA;EAEA,SAAA;EAEA,IAAA,EAAM,SAAA;EAEN,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAK;;;;;;EAMZ,mBAAA;AAAA"}
1
+ {"version":3,"file":"trace.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/contracts/trace.type.ts"],"mappings":";;;;;AAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAAY,SAAA;EA2DG;;;;EAtDb,MAAA;EA0EmB;AAAA;AAQrB;;EA7EE,YAAA;EA6EwB;;;;;;EAtExB,OAAA;EA4FU;;;;;EAtFV,SAAA,WA4FA;EA1FA,IAAA;EA4FA;;;;;EAtFA,OAAA,WAkGmB;EAhGnB,IAAA,EAAM,UAAA;EAEN,MAAA,EAAQ,YAAA;EAER,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAA;;;;;;EAMP,KAAA,GAAQ,cAAA;;;;;;;;;;EAUR,UAAA,GAAa,MAAA;;;;;;;;EAQb,KAAA;;;;;;EAMA,MAAA;;;;;;EAMA,QAAA,EAAU,SAAA;AAAA;;;;;;KAQA,cAAA;6EAEV,IAAA;EAEA,OAAA;EAEA,KAAA;;;;;;;EAOA,KAAA;AAAA;;;;;;;KASU,KAAA;mEAEV,OAAA;EAEA,SAAA;EAEA,IAAA,EAAM,SAAA;EAEN,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAK;;;;;;EAMZ,mBAAA;AAAA"}
@@ -0,0 +1,32 @@
1
+ import { TraceStoreContract } from "../store/trace-store.contract.mjs";
2
+ import { DashboardHandle, DashboardOptions } from "./dashboard.type.mjs";
3
+
4
+ //#region ../@warlock.js/ai-panoptic/src/dashboard/dashboard.d.ts
5
+ /**
6
+ * Start the local Panoptic dashboard over a {@link TraceStoreContract} —
7
+ * a zero-dependency `node:http` server that serves a read-only JSON API
8
+ * and one self-contained HTML page polling it. The store is the live
9
+ * object the collector fills, so each poll reflects the latest completed
10
+ * traces with no extra wiring.
11
+ *
12
+ * This is a low-level building block. The documented path is
13
+ * `ai.config({ panoptic: { dashboard } })`, which constructs (or reuses)
14
+ * the store and calls this for you. Use it directly only when you manage
15
+ * the store yourself.
16
+ *
17
+ * Binds loopback-only by default (`127.0.0.1`) so prompt content is never
18
+ * exposed to the LAN. Pass `port: 0` for an ephemeral port — the resolved
19
+ * port comes back on the handle. A port already in use rejects with a
20
+ * clear, actionable `Error` rather than the raw `EADDRINUSE`.
21
+ *
22
+ * @example
23
+ * const store = createInMemoryTraceStore();
24
+ * const handle = await dashboard(store, { port: 4319, open: true });
25
+ * console.log(handle.url); // http://127.0.0.1:4319/
26
+ * // ...later:
27
+ * await handle.close();
28
+ */
29
+ declare function dashboard(store: TraceStoreContract, options?: DashboardOptions): Promise<DashboardHandle>;
30
+ //#endregion
31
+ export { dashboard };
32
+ //# sourceMappingURL=dashboard.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/dashboard/dashboard.ts"],"mappings":";;;;;;AAiCA;;;;;;;;;;;;;;;;AAG0B;;;;;;iBAHV,SAAA,CACd,KAAA,EAAO,kBAAA,EACP,OAAA,GAAS,gBAAA,GACR,OAAA,CAAQ,eAAA"}
@@ -0,0 +1,132 @@
1
+ import { createRequestHandler } from "./serve.mjs";
2
+ import { createServer } from "node:http";
3
+
4
+ //#region ../@warlock.js/ai-panoptic/src/dashboard/dashboard.ts
5
+ const DEFAULT_PORT = 4319;
6
+ const DEFAULT_HOST = "127.0.0.1";
7
+ const DEFAULT_TITLE = "Panoptic";
8
+ /**
9
+ * Start the local Panoptic dashboard over a {@link TraceStoreContract} —
10
+ * a zero-dependency `node:http` server that serves a read-only JSON API
11
+ * and one self-contained HTML page polling it. The store is the live
12
+ * object the collector fills, so each poll reflects the latest completed
13
+ * traces with no extra wiring.
14
+ *
15
+ * This is a low-level building block. The documented path is
16
+ * `ai.config({ panoptic: { dashboard } })`, which constructs (or reuses)
17
+ * the store and calls this for you. Use it directly only when you manage
18
+ * the store yourself.
19
+ *
20
+ * Binds loopback-only by default (`127.0.0.1`) so prompt content is never
21
+ * exposed to the LAN. Pass `port: 0` for an ephemeral port — the resolved
22
+ * port comes back on the handle. A port already in use rejects with a
23
+ * clear, actionable `Error` rather than the raw `EADDRINUSE`.
24
+ *
25
+ * @example
26
+ * const store = createInMemoryTraceStore();
27
+ * const handle = await dashboard(store, { port: 4319, open: true });
28
+ * console.log(handle.url); // http://127.0.0.1:4319/
29
+ * // ...later:
30
+ * await handle.close();
31
+ */
32
+ function dashboard(store, options = {}) {
33
+ const port = options.port ?? DEFAULT_PORT;
34
+ const host = options.host ?? DEFAULT_HOST;
35
+ const title = options.title ?? DEFAULT_TITLE;
36
+ const basePath = normalizeBasePath(options.basePath);
37
+ if (!isLoopbackHost(host) && !options.authToken) return Promise.reject(/* @__PURE__ */ new Error(`Panoptic dashboard: binding to a non-loopback host ("${host}") requires an \`authToken\` (the dashboard exposes raw prompt content). Pass \`authToken\`, or bind to 127.0.0.1.`));
38
+ const allowedHosts = (options.allowedHosts ?? defaultAllowedHosts(host)).map((h) => h.toLowerCase());
39
+ const server = createServer(createRequestHandler(store, {
40
+ basePath,
41
+ title,
42
+ authToken: options.authToken,
43
+ allowedHosts
44
+ }));
45
+ return new Promise((resolve, reject) => {
46
+ const onError = (error) => {
47
+ server.off("error", onError);
48
+ if (error.code === "EADDRINUSE") {
49
+ reject(/* @__PURE__ */ new Error(`Panoptic dashboard: port ${port} in use; pass { port: 0 } for an ephemeral port`));
50
+ return;
51
+ }
52
+ reject(error);
53
+ };
54
+ server.on("error", onError);
55
+ server.listen(port, host, () => {
56
+ server.off("error", onError);
57
+ const address = server.address();
58
+ const resolvedPort = typeof address === "object" && address !== null ? address.port : port;
59
+ const url = `http://${host}:${resolvedPort}${basePath}`;
60
+ if (options.open) openBrowser(url);
61
+ resolve({
62
+ url,
63
+ port: resolvedPort,
64
+ close() {
65
+ return new Promise((closeResolve, closeReject) => {
66
+ server.close((closeError) => {
67
+ if (closeError) {
68
+ closeReject(closeError);
69
+ return;
70
+ }
71
+ closeResolve();
72
+ });
73
+ });
74
+ }
75
+ });
76
+ });
77
+ });
78
+ }
79
+ /** Loopback hosts the dashboard may bind without an auth token. */
80
+ function isLoopbackHost(host) {
81
+ const h = host.toLowerCase();
82
+ return h === "127.0.0.1" || h === "::1" || h === "[::1]" || h === "localhost";
83
+ }
84
+ /**
85
+ * Default `Host` allowlist for a given bind host. A loopback bind accepts
86
+ * the loopback names a browser would send; a non-loopback bind defaults to
87
+ * just the bound host (callers can widen via `allowedHosts`).
88
+ */
89
+ function defaultAllowedHosts(host) {
90
+ if (isLoopbackHost(host)) return [
91
+ "localhost",
92
+ "127.0.0.1",
93
+ "[::1]",
94
+ "::1"
95
+ ];
96
+ return [host];
97
+ }
98
+ /**
99
+ * Normalize a caller `basePath` to a leading-and-trailing-slash form the
100
+ * router can prefix routes with. `undefined` / `""` → `"/"`.
101
+ */
102
+ function normalizeBasePath(basePath) {
103
+ if (basePath === void 0 || basePath.length === 0 || basePath === "/") return "/";
104
+ const withLeading = basePath.startsWith("/") ? basePath : `/${basePath}`;
105
+ return withLeading.endsWith("/") ? withLeading : `${withLeading}/`;
106
+ }
107
+ /**
108
+ * Best-effort open of the default browser at `url`. Fire-and-forget and
109
+ * fully swallowed — failing to open a browser must never reject the
110
+ * dashboard start. Uses the platform's native opener via a lazy
111
+ * `node:child_process` import so the dependency is paid only when
112
+ * `open: true`.
113
+ */
114
+ function openBrowser(url) {
115
+ import("node:child_process").then(({ spawn }) => {
116
+ const child = spawn(process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open", process.platform === "win32" ? [
117
+ "/c",
118
+ "start",
119
+ "",
120
+ url
121
+ ] : [url], {
122
+ stdio: "ignore",
123
+ detached: true
124
+ });
125
+ child.on("error", () => {});
126
+ child.unref();
127
+ }).catch(() => {});
128
+ }
129
+
130
+ //#endregion
131
+ export { dashboard };
132
+ //# sourceMappingURL=dashboard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/dashboard/dashboard.ts"],"sourcesContent":["import { createServer } from \"node:http\";\nimport type { TraceStoreContract } from \"../store/trace-store.contract\";\nimport type { DashboardHandle, DashboardOptions } from \"./dashboard.type\";\nimport { createRequestHandler } from \"./serve\";\n\nconst DEFAULT_PORT = 4319;\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_TITLE = \"Panoptic\";\n\n/**\n * Start the local Panoptic dashboard over a {@link TraceStoreContract} —\n * a zero-dependency `node:http` server that serves a read-only JSON API\n * and one self-contained HTML page polling it. The store is the live\n * object the collector fills, so each poll reflects the latest completed\n * traces with no extra wiring.\n *\n * This is a low-level building block. The documented path is\n * `ai.config({ panoptic: { dashboard } })`, which constructs (or reuses)\n * the store and calls this for you. Use it directly only when you manage\n * the store yourself.\n *\n * Binds loopback-only by default (`127.0.0.1`) so prompt content is never\n * exposed to the LAN. Pass `port: 0` for an ephemeral port — the resolved\n * port comes back on the handle. A port already in use rejects with a\n * clear, actionable `Error` rather than the raw `EADDRINUSE`.\n *\n * @example\n * const store = createInMemoryTraceStore();\n * const handle = await dashboard(store, { port: 4319, open: true });\n * console.log(handle.url); // http://127.0.0.1:4319/\n * // ...later:\n * await handle.close();\n */\nexport function dashboard(\n store: TraceStoreContract,\n options: DashboardOptions = {},\n): Promise<DashboardHandle> {\n const port = options.port ?? DEFAULT_PORT;\n const host = options.host ?? DEFAULT_HOST;\n const title = options.title ?? DEFAULT_TITLE;\n const basePath = normalizeBasePath(options.basePath);\n\n // Secure-by-default off loopback (S4): a non-loopback bind without an\n // auth token would expose raw prompt content to the network, so refuse\n // to start rather than silently exposing it.\n if (!isLoopbackHost(host) && !options.authToken) {\n return Promise.reject(\n new Error(\n `Panoptic dashboard: binding to a non-loopback host (\"${host}\") requires an \\`authToken\\` ` +\n \"(the dashboard exposes raw prompt content). Pass `authToken`, or bind to 127.0.0.1.\",\n ),\n );\n }\n\n const allowedHosts = (options.allowedHosts ?? defaultAllowedHosts(host)).map(h =>\n h.toLowerCase(),\n );\n\n const handler = createRequestHandler(store, {\n basePath,\n title,\n authToken: options.authToken,\n allowedHosts,\n });\n const server = createServer(handler);\n\n return new Promise<DashboardHandle>((resolve, reject) => {\n const onError = (error: NodeJS.ErrnoException): void => {\n server.off(\"error\", onError);\n\n if (error.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Panoptic dashboard: port ${port} in use; pass { port: 0 } for an ephemeral port`,\n ),\n );\n\n return;\n }\n\n reject(error);\n };\n\n server.on(\"error\", onError);\n\n server.listen(port, host, () => {\n server.off(\"error\", onError);\n\n const address = server.address();\n const resolvedPort = typeof address === \"object\" && address !== null ? address.port : port;\n const url = `http://${host}:${resolvedPort}${basePath}`;\n\n if (options.open) {\n openBrowser(url);\n }\n\n resolve({\n url,\n port: resolvedPort,\n close(): Promise<void> {\n return new Promise<void>((closeResolve, closeReject) => {\n server.close((closeError) => {\n if (closeError) {\n closeReject(closeError);\n\n return;\n }\n\n closeResolve();\n });\n });\n },\n });\n });\n });\n}\n\n/** Loopback hosts the dashboard may bind without an auth token. */\nfunction isLoopbackHost(host: string): boolean {\n const h = host.toLowerCase();\n return h === \"127.0.0.1\" || h === \"::1\" || h === \"[::1]\" || h === \"localhost\";\n}\n\n/**\n * Default `Host` allowlist for a given bind host. A loopback bind accepts\n * the loopback names a browser would send; a non-loopback bind defaults to\n * just the bound host (callers can widen via `allowedHosts`).\n */\nfunction defaultAllowedHosts(host: string): string[] {\n if (isLoopbackHost(host)) {\n return [\"localhost\", \"127.0.0.1\", \"[::1]\", \"::1\"];\n }\n return [host];\n}\n\n/**\n * Normalize a caller `basePath` to a leading-and-trailing-slash form the\n * router can prefix routes with. `undefined` / `\"\"` → `\"/\"`.\n */\nfunction normalizeBasePath(basePath?: string): string {\n if (basePath === undefined || basePath.length === 0 || basePath === \"/\") {\n return \"/\";\n }\n\n const withLeading = basePath.startsWith(\"/\") ? basePath : `/${basePath}`;\n\n return withLeading.endsWith(\"/\") ? withLeading : `${withLeading}/`;\n}\n\n/**\n * Best-effort open of the default browser at `url`. Fire-and-forget and\n * fully swallowed — failing to open a browser must never reject the\n * dashboard start. Uses the platform's native opener via a lazy\n * `node:child_process` import so the dependency is paid only when\n * `open: true`.\n */\nfunction openBrowser(url: string): void {\n void import(\"node:child_process\")\n .then(({ spawn }) => {\n const command =\n process.platform === \"win32\" ? \"cmd\" : process.platform === \"darwin\" ? \"open\" : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n\n const child = spawn(command, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {\n // Swallow — opening a browser is best-effort.\n });\n child.unref();\n })\n .catch(() => {\n // Swallow — opening a browser is best-effort.\n });\n}\n"],"mappings":";;;;AAKA,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;AA0BtB,SAAgB,UACd,OACA,UAA4B,CAAC,GACH;CAC1B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,WAAW,kBAAkB,QAAQ,QAAQ;CAKnD,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,QAAQ,WACpC,OAAO,QAAQ,uBACb,IAAI,MACF,wDAAwD,KAAK,mHAE/D,CACF;CAGF,MAAM,gBAAgB,QAAQ,gBAAgB,oBAAoB,IAAI,EAAC,CAAE,KAAI,MAC3E,EAAE,YAAY,CAChB;CAQA,MAAM,SAAS,aANC,qBAAqB,OAAO;EAC1C;EACA;EACA,WAAW,QAAQ;EACnB;CACF,CACkC,CAAC;CAEnC,OAAO,IAAI,SAA0B,SAAS,WAAW;EACvD,MAAM,WAAW,UAAuC;GACtD,OAAO,IAAI,SAAS,OAAO;GAE3B,IAAI,MAAM,SAAS,cAAc;IAC/B,uBACE,IAAI,MACF,4BAA4B,KAAK,gDACnC,CACF;IAEA;GACF;GAEA,OAAO,KAAK;EACd;EAEA,OAAO,GAAG,SAAS,OAAO;EAE1B,OAAO,OAAO,MAAM,YAAY;GAC9B,OAAO,IAAI,SAAS,OAAO;GAE3B,MAAM,UAAU,OAAO,QAAQ;GAC/B,MAAM,eAAe,OAAO,YAAY,YAAY,YAAY,OAAO,QAAQ,OAAO;GACtF,MAAM,MAAM,UAAU,KAAK,GAAG,eAAe;GAE7C,IAAI,QAAQ,MACV,YAAY,GAAG;GAGjB,QAAQ;IACN;IACA,MAAM;IACN,QAAuB;KACrB,OAAO,IAAI,SAAe,cAAc,gBAAgB;MACtD,OAAO,OAAO,eAAe;OAC3B,IAAI,YAAY;QACd,YAAY,UAAU;QAEtB;OACF;OAEA,aAAa;MACf,CAAC;KACH,CAAC;IACH;GACF,CAAC;EACH,CAAC;CACH,CAAC;AACH;;AAGA,SAAS,eAAe,MAAuB;CAC7C,MAAM,IAAI,KAAK,YAAY;CAC3B,OAAO,MAAM,eAAe,MAAM,SAAS,MAAM,WAAW,MAAM;AACpE;;;;;;AAOA,SAAS,oBAAoB,MAAwB;CACnD,IAAI,eAAe,IAAI,GACrB,OAAO;EAAC;EAAa;EAAa;EAAS;CAAK;CAElD,OAAO,CAAC,IAAI;AACd;;;;;AAMA,SAAS,kBAAkB,UAA2B;CACpD,IAAI,aAAa,UAAa,SAAS,WAAW,KAAK,aAAa,KAClE,OAAO;CAGT,MAAM,cAAc,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI;CAE9D,OAAO,YAAY,SAAS,GAAG,IAAI,cAAc,GAAG,YAAY;AAClE;;;;;;;;AASA,SAAS,YAAY,KAAmB;CACtC,AAAK,OAAO,qBAAqB,CAC9B,MAAM,EAAE,YAAY;EAKnB,MAAM,QAAQ,MAHZ,QAAQ,aAAa,UAAU,QAAQ,QAAQ,aAAa,WAAW,SAAS,YACrE,QAAQ,aAAa,UAAU;GAAC;GAAM;GAAS;GAAI;EAAG,IAAI,CAAC,GAAG,GAExC;GAAE,OAAO;GAAU,UAAU;EAAK,CAAC;EACtE,MAAM,GAAG,eAAe,CAExB,CAAC;EACD,MAAM,MAAM;CACd,CAAC,CAAC,CACD,YAAY,CAEb,CAAC;AACL"}
@@ -0,0 +1,63 @@
1
+ //#region ../@warlock.js/ai-panoptic/src/dashboard/dashboard.type.d.ts
2
+ /**
3
+ * Options for the local Panoptic {@link dashboard} server.
4
+ *
5
+ * Every field is optional and has a safe default. The dashboard is a dev
6
+ * tool over an in-memory trace store: it binds loopback-only by default
7
+ * (so prompt content is never accidentally exposed to the LAN), serves a
8
+ * single self-contained HTML page, and exposes a tiny read-only JSON API
9
+ * the page polls. There is no auth, persistence, or write surface.
10
+ */
11
+ type DashboardOptions = {
12
+ /**
13
+ * TCP port to listen on. Default `4319`. Pass `0` for an ephemeral
14
+ * port — the OS-assigned port is then returned on
15
+ * {@link DashboardHandle.port}.
16
+ */
17
+ port?: number;
18
+ /**
19
+ * Host / interface to bind. Default `"127.0.0.1"` — loopback only, so
20
+ * the dashboard (and the prompt content it can surface) is never
21
+ * exposed beyond the local machine unless you opt in explicitly.
22
+ */
23
+ host?: string;
24
+ /**
25
+ * Mount-path prefix for the JSON API + UI. Default `"/"`. A non-root
26
+ * value (e.g. `"/panoptic"`) prefixes both the page and every
27
+ * `/api/...` route.
28
+ */
29
+ basePath?: string;
30
+ /**
31
+ * Open the default browser at the resolved URL once the server is
32
+ * listening. Default `false`.
33
+ */
34
+ open?: boolean; /** Title shown in the dashboard header. Default `"Panoptic"`. */
35
+ title?: string;
36
+ /**
37
+ * Bearer token required on every request (S4). When set, a request must
38
+ * present `Authorization: Bearer <token>` (or `?token=<token>`), else
39
+ * `401`. **Required** when binding to a non-loopback `host` — starting
40
+ * the dashboard on a public interface without a token throws.
41
+ */
42
+ authToken?: string;
43
+ /**
44
+ * `Host` header allowlist (S4) — defends against DNS-rebinding. A
45
+ * request whose `Host` is not listed gets `403`. Defaults to the
46
+ * loopback names (`localhost`, `127.0.0.1`, `[::1]`) for a loopback
47
+ * bind; for a non-loopback bind you must list the expected host(s).
48
+ */
49
+ allowedHosts?: string[];
50
+ };
51
+ /**
52
+ * Handle returned by {@link dashboard} once the server is listening.
53
+ * Carries the resolved URL + port (useful when `port: 0` requested an
54
+ * ephemeral one) and a `close()` to release the port.
55
+ */
56
+ type DashboardHandle = {
57
+ /** Resolved URL the UI is served at (`http://host:port{basePath}`). */readonly url: string; /** Resolved port — the real OS-assigned port when `port: 0` was passed. */
58
+ readonly port: number; /** Stop the server and release the port. Resolves once fully closed. */
59
+ close(): Promise<void>;
60
+ };
61
+ //#endregion
62
+ export { DashboardHandle, DashboardOptions };
63
+ //# sourceMappingURL=dashboard.type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/dashboard/dashboard.type.ts"],"mappings":";;AASA;;;;;;;;KAAY,gBAAA;EAgCV;;;AAOY;AAQd;EAzCE,IAAA;;;;;;EAMA,IAAA;EAyCgB;AAAA;;;;EAnChB,QAAA;;;;;EAKA,IAAA;EAEA,KAAA;;;;;;;EAOA,SAAA;;;;;;;EAOA,YAAA;AAAA;;;;;;KAQU,eAAA;kFAED,GAAA;WAEA,IAAA;EAET,KAAA,IAAS,OAAO;AAAA"}
@@ -0,0 +1,3 @@
1
+ import { DashboardHandle, DashboardOptions } from "./dashboard.type.mjs";
2
+ import { dashboard } from "./dashboard.mjs";
3
+ import { NO_PROMPT_KEY, NO_SESSION_KEY, NO_TYPE_KEY, PromptGroup, SessionGroup, TraceFilter, TypeGroup, TypeStat, aggregateByType, filterTraces, groupByPrompt, groupBySession, groupByType, heatIntensity, matchesFilter, maxNodeCost, percentile, rollupCost, traceCost, tracePromptKey } from "./trace-filter.mjs";
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,54 @@
1
+ //#region ../@warlock.js/ai-panoptic/src/dashboard/parse-query.ts
2
+ /**
3
+ * The set of terminal statuses a stored trace can carry. Used to keep
4
+ * `parseQuery` from forwarding arbitrary `?status=` junk into the store.
5
+ */
6
+ const KNOWN_STATUSES = [
7
+ "completed",
8
+ "failed",
9
+ "cancelled",
10
+ "max-iterations",
11
+ "awaiting-input",
12
+ "awaiting-approval"
13
+ ];
14
+ /**
15
+ * Map a request's query string onto a {@link TraceQuery} the trace store
16
+ * understands. Every field is optional — an absent param is "don't care",
17
+ * so an empty query string yields an empty filter that matches every
18
+ * stored trace.
19
+ *
20
+ * - `traceId` / `sessionId` — passed through verbatim (first value wins).
21
+ * - `status` — **repeatable**: `?status=failed&status=cancelled` becomes
22
+ * `["failed", "cancelled"]`; a single value stays a scalar. Unknown
23
+ * status tokens are dropped so a typo never silently matches nothing in
24
+ * a confusing way (it simply isn't filtered on).
25
+ * - `startedAfter` / `startedBefore` — forwarded as ISO strings; the
26
+ * store accepts either a string or `Date`, and `matchTrace` does the
27
+ * inclusive bound comparison.
28
+ *
29
+ * Unknown params are ignored. Pure — takes a `URLSearchParams`, returns a
30
+ * plain object — so it's trivially testable without a live server.
31
+ *
32
+ * @example
33
+ * parseQuery(new URLSearchParams("status=failed&status=cancelled&sessionId=s1"));
34
+ * // → { status: ["failed", "cancelled"], sessionId: "s1" }
35
+ */
36
+ function parseQuery(params) {
37
+ const query = {};
38
+ const traceId = params.get("traceId");
39
+ if (traceId !== null && traceId.length > 0) query.traceId = traceId;
40
+ const sessionId = params.get("sessionId");
41
+ if (sessionId !== null && sessionId.length > 0) query.sessionId = sessionId;
42
+ const statuses = params.getAll("status").filter((value) => KNOWN_STATUSES.includes(value));
43
+ if (statuses.length === 1) query.status = statuses[0];
44
+ else if (statuses.length > 1) query.status = statuses;
45
+ const startedAfter = params.get("startedAfter");
46
+ if (startedAfter !== null && startedAfter.length > 0) query.startedAfter = startedAfter;
47
+ const startedBefore = params.get("startedBefore");
48
+ if (startedBefore !== null && startedBefore.length > 0) query.startedBefore = startedBefore;
49
+ return query;
50
+ }
51
+
52
+ //#endregion
53
+ export { parseQuery };
54
+ //# sourceMappingURL=parse-query.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-query.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/dashboard/parse-query.ts"],"sourcesContent":["import type { ReportStatus } from \"@warlock.js/ai\";\nimport type { TraceQuery } from \"../store/trace-query.type\";\n\n/**\n * The set of terminal statuses a stored trace can carry. Used to keep\n * `parseQuery` from forwarding arbitrary `?status=` junk into the store.\n */\nconst KNOWN_STATUSES: readonly ReportStatus[] = [\n \"completed\",\n \"failed\",\n \"cancelled\",\n \"max-iterations\",\n \"awaiting-input\",\n \"awaiting-approval\",\n] as const;\n\n/**\n * Map a request's query string onto a {@link TraceQuery} the trace store\n * understands. Every field is optional — an absent param is \"don't care\",\n * so an empty query string yields an empty filter that matches every\n * stored trace.\n *\n * - `traceId` / `sessionId` — passed through verbatim (first value wins).\n * - `status` — **repeatable**: `?status=failed&status=cancelled` becomes\n * `[\"failed\", \"cancelled\"]`; a single value stays a scalar. Unknown\n * status tokens are dropped so a typo never silently matches nothing in\n * a confusing way (it simply isn't filtered on).\n * - `startedAfter` / `startedBefore` — forwarded as ISO strings; the\n * store accepts either a string or `Date`, and `matchTrace` does the\n * inclusive bound comparison.\n *\n * Unknown params are ignored. Pure — takes a `URLSearchParams`, returns a\n * plain object — so it's trivially testable without a live server.\n *\n * @example\n * parseQuery(new URLSearchParams(\"status=failed&status=cancelled&sessionId=s1\"));\n * // → { status: [\"failed\", \"cancelled\"], sessionId: \"s1\" }\n */\nexport function parseQuery(params: URLSearchParams): TraceQuery {\n const query: TraceQuery = {};\n\n const traceId = params.get(\"traceId\");\n if (traceId !== null && traceId.length > 0) {\n query.traceId = traceId;\n }\n\n const sessionId = params.get(\"sessionId\");\n if (sessionId !== null && sessionId.length > 0) {\n query.sessionId = sessionId;\n }\n\n const statuses = params\n .getAll(\"status\")\n .filter((value): value is ReportStatus => (KNOWN_STATUSES as readonly string[]).includes(value));\n if (statuses.length === 1) {\n query.status = statuses[0];\n } else if (statuses.length > 1) {\n query.status = statuses;\n }\n\n const startedAfter = params.get(\"startedAfter\");\n if (startedAfter !== null && startedAfter.length > 0) {\n query.startedAfter = startedAfter;\n }\n\n const startedBefore = params.get(\"startedBefore\");\n if (startedBefore !== null && startedBefore.length > 0) {\n query.startedBefore = startedBefore;\n }\n\n return query;\n}\n"],"mappings":";;;;;AAOA,MAAM,iBAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,WAAW,QAAqC;CAC9D,MAAM,QAAoB,CAAC;CAE3B,MAAM,UAAU,OAAO,IAAI,SAAS;CACpC,IAAI,YAAY,QAAQ,QAAQ,SAAS,GACvC,MAAM,UAAU;CAGlB,MAAM,YAAY,OAAO,IAAI,WAAW;CACxC,IAAI,cAAc,QAAQ,UAAU,SAAS,GAC3C,MAAM,YAAY;CAGpB,MAAM,WAAW,OACd,OAAO,QAAQ,CAAC,CAChB,QAAQ,UAAkC,eAAqC,SAAS,KAAK,CAAC;CACjG,IAAI,SAAS,WAAW,GACtB,MAAM,SAAS,SAAS;MACnB,IAAI,SAAS,SAAS,GAC3B,MAAM,SAAS;CAGjB,MAAM,eAAe,OAAO,IAAI,cAAc;CAC9C,IAAI,iBAAiB,QAAQ,aAAa,SAAS,GACjD,MAAM,eAAe;CAGvB,MAAM,gBAAgB,OAAO,IAAI,eAAe;CAChD,IAAI,kBAAkB,QAAQ,cAAc,SAAS,GACnD,MAAM,gBAAgB;CAGxB,OAAO;AACT"}
@@ -0,0 +1 @@
1
+ export { };