@warlock.js/ai-panoptic 4.8.1 → 4.8.2
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.
- package/CHANGELOG.md +12 -0
- package/cjs/index.cjs +312 -14
- package/cjs/index.cjs.map +1 -1
- package/esm/config/apply-panoptic-config.d.mts.map +1 -1
- package/esm/config/apply-panoptic-config.mjs +3 -1
- package/esm/config/apply-panoptic-config.mjs.map +1 -1
- package/esm/config/panoptic-config.type.d.mts +11 -0
- package/esm/config/panoptic-config.type.d.mts.map +1 -1
- package/esm/dashboard/dashboard.mjs +2 -1
- package/esm/dashboard/dashboard.mjs.map +1 -1
- package/esm/dashboard/dashboard.type.d.mts +15 -2
- package/esm/dashboard/dashboard.type.d.mts.map +1 -1
- package/esm/dashboard/index.mjs +7 -0
- package/esm/dashboard/serve.mjs +123 -8
- package/esm/dashboard/serve.mjs.map +1 -1
- package/esm/dashboard/ui.html.mjs +133 -4
- package/esm/dashboard/ui.html.mjs.map +1 -1
- package/esm/evaluate/evaluate-system-prompt.d.mts +19 -0
- package/esm/evaluate/evaluate-system-prompt.d.mts.map +1 -0
- package/esm/evaluate/evaluate-system-prompt.mjs +22 -0
- package/esm/evaluate/evaluate-system-prompt.mjs.map +1 -0
- package/esm/evaluate/evaluate.type.d.mts +47 -0
- package/esm/evaluate/evaluate.type.d.mts.map +1 -0
- package/esm/evaluate/extract-last-system-prompt.d.mts +19 -0
- package/esm/evaluate/extract-last-system-prompt.d.mts.map +1 -0
- package/esm/evaluate/extract-last-system-prompt.mjs +24 -0
- package/esm/evaluate/extract-last-system-prompt.mjs.map +1 -0
- package/esm/evaluate/find-span-by-id.d.mts +8 -0
- package/esm/evaluate/find-span-by-id.d.mts.map +1 -0
- package/esm/evaluate/find-span-by-id.mjs +13 -0
- package/esm/evaluate/find-span-by-id.mjs.map +1 -0
- package/esm/evaluate/index.d.mts +4 -0
- package/esm/evaluate/index.mjs +5 -0
- package/esm/index.d.mts +5 -1
- package/esm/index.mjs +6 -1
- package/llms-full.txt +95 -3
- package/llms.txt +1 -0
- package/package.json +4 -3
- package/skills/README.md +4 -0
- package/skills/evaluate-system-prompt/SKILL.md +86 -0
- package/skills/use-local-dashboard/SKILL.md +5 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-span-by-id.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/evaluate/find-span-by-id.ts"],"mappings":";;;;iBAGgB,YAAA,CAAa,IAAA,EAAM,SAAA,EAAW,MAAA,WAAiB,SAAS"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai-panoptic/src/evaluate/find-span-by-id.ts
|
|
2
|
+
/** Depth-first search for the span with `spanId` inside a trace's span tree. */
|
|
3
|
+
function findSpanById(root, spanId) {
|
|
4
|
+
if (root.spanId === spanId) return root;
|
|
5
|
+
for (const child of root.children) {
|
|
6
|
+
const found = findSpanById(child, spanId);
|
|
7
|
+
if (found !== void 0) return found;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { findSpanById };
|
|
13
|
+
//# sourceMappingURL=find-span-by-id.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-span-by-id.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/evaluate/find-span-by-id.ts"],"sourcesContent":["import type { TraceSpan } from \"../contracts/trace.type\";\n\n/** Depth-first search for the span with `spanId` inside a trace's span tree. */\nexport function findSpanById(root: TraceSpan, spanId: string): TraceSpan | undefined {\n if (root.spanId === spanId) {\n return root;\n }\n\n for (const child of root.children) {\n const found = findSpanById(child, spanId);\n\n if (found !== undefined) {\n return found;\n }\n }\n\n return undefined;\n}\n"],"mappings":";;AAGA,SAAgB,aAAa,MAAiB,QAAuC;CACnF,IAAI,KAAK,WAAW,QAClB,OAAO;CAGT,KAAK,MAAM,SAAS,KAAK,UAAU;EACjC,MAAM,QAAQ,aAAa,OAAO,MAAM;EAExC,IAAI,UAAU,QACZ,OAAO;CAEX;AAGF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { EvaluateConfig, EvaluateVerdict } from "./evaluate.type.mjs";
|
|
2
|
+
import { evaluateSystemPrompt } from "./evaluate-system-prompt.mjs";
|
|
3
|
+
import { extractLastSystemPrompt } from "./extract-last-system-prompt.mjs";
|
|
4
|
+
import { findSpanById } from "./find-span-by-id.mjs";
|
package/esm/index.d.mts
CHANGED
|
@@ -31,10 +31,14 @@ import { CompletedEventPayload, PanopticTarget } from "./panoptic/panoptic-targe
|
|
|
31
31
|
import { Panoptic, PanopticOptions } from "./panoptic/panoptic.type.mjs";
|
|
32
32
|
import { panoptic } from "./panoptic/panoptic.mjs";
|
|
33
33
|
import { createPanopticMiddleware } from "./panoptic/panoptic-middleware.mjs";
|
|
34
|
+
import { EvaluateConfig, EvaluateVerdict } from "./evaluate/evaluate.type.mjs";
|
|
35
|
+
import { evaluateSystemPrompt } from "./evaluate/evaluate-system-prompt.mjs";
|
|
36
|
+
import { extractLastSystemPrompt } from "./evaluate/extract-last-system-prompt.mjs";
|
|
37
|
+
import { findSpanById } from "./evaluate/find-span-by-id.mjs";
|
|
34
38
|
import { DashboardHandle, DashboardOptions } from "./dashboard/dashboard.type.mjs";
|
|
35
39
|
import { dashboard } from "./dashboard/dashboard.mjs";
|
|
36
40
|
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 "./dashboard/trace-filter.mjs";
|
|
37
41
|
import { PanopticConfig } from "./config/panoptic-config.type.mjs";
|
|
38
42
|
import { applyPanopticConfig } from "./config/apply-panoptic-config.mjs";
|
|
39
|
-
export { type AttributeValue, type CacheDriverInput, type CacheTraceStoreHandle, type CacheTraceStoreOptions, type CollectorContract, type CollectorOptions, type CompletedEventPayload, type ConsoleExporterOptions, type ConsoleLike, type ContentCaptureOptions, type ContentRedactor, type DashboardHandle, type DashboardOptions, type ExporterContract, type ExporterErrorHandler, type FileExporterOptions, GEN_AI_ATTRIBUTES, type InMemoryTraceStoreOptions, type LangfuseClientLike, type LangfuseExporterOptions, type LangfuseObservationBody, type LangfuseObservationEndBody, type LangfuseObservationLevel, type LangfuseObservationLike, type LangfuseTraceBody, type LangfuseTraceLike, type LangfuseUsageBody, NO_PROMPT_KEY, NO_SESSION_KEY, NO_TYPE_KEY, type OtelExporterOptions, type Panoptic, type PanopticConfig, type PanopticOptions, type PanopticTarget, type PromptGroup, type SessionGroup, type Trace, type TraceAggregate, type TraceFilter, type TraceQuery, type TraceRecord, type TraceSpan, type TraceSpanError, type TraceStoreContract, type TypeGroup, type TypeStat, WARLOCK_ATTRIBUTES, aggregateByType, applyPanopticConfig, consoleExporter, createCacheTraceStore, createCollector, createInMemoryTraceStore, createPanopticMiddleware, dashboard, emptyUsage, extractSpanAttributes, fileExporter, filterTraces, formatSpanIO, formatSpanLine, groupByPrompt, groupBySession, groupByType, heatIntensity, langfuseExporter, matchTrace, matchesFilter, maxNodeCost, normalizeError, otelExporter, panoptic, percentile, reportToSpan, reportToTrace, rollupCost, sumUsage, toGenAiAttributes, totalCostUsd, traceCost, tracePromptKey, walkSpans };
|
|
43
|
+
export { type AttributeValue, type CacheDriverInput, type CacheTraceStoreHandle, type CacheTraceStoreOptions, type CollectorContract, type CollectorOptions, type CompletedEventPayload, type ConsoleExporterOptions, type ConsoleLike, type ContentCaptureOptions, type ContentRedactor, type DashboardHandle, type DashboardOptions, type EvaluateConfig, type EvaluateVerdict, type ExporterContract, type ExporterErrorHandler, type FileExporterOptions, GEN_AI_ATTRIBUTES, type InMemoryTraceStoreOptions, type LangfuseClientLike, type LangfuseExporterOptions, type LangfuseObservationBody, type LangfuseObservationEndBody, type LangfuseObservationLevel, type LangfuseObservationLike, type LangfuseTraceBody, type LangfuseTraceLike, type LangfuseUsageBody, NO_PROMPT_KEY, NO_SESSION_KEY, NO_TYPE_KEY, type OtelExporterOptions, type Panoptic, type PanopticConfig, type PanopticOptions, type PanopticTarget, type PromptGroup, type SessionGroup, type Trace, type TraceAggregate, type TraceFilter, type TraceQuery, type TraceRecord, type TraceSpan, type TraceSpanError, type TraceStoreContract, type TypeGroup, type TypeStat, WARLOCK_ATTRIBUTES, aggregateByType, applyPanopticConfig, consoleExporter, createCacheTraceStore, createCollector, createInMemoryTraceStore, createPanopticMiddleware, dashboard, emptyUsage, evaluateSystemPrompt, extractLastSystemPrompt, extractSpanAttributes, fileExporter, filterTraces, findSpanById, formatSpanIO, formatSpanLine, groupByPrompt, groupBySession, groupByType, heatIntensity, langfuseExporter, matchTrace, matchesFilter, maxNodeCost, normalizeError, otelExporter, panoptic, percentile, reportToSpan, reportToTrace, rollupCost, sumUsage, toGenAiAttributes, totalCostUsd, traceCost, tracePromptKey, walkSpans };
|
|
40
44
|
import "./config/panoptic-config.type.mjs";
|
package/esm/index.mjs
CHANGED
|
@@ -22,10 +22,15 @@ import "./exporters/index.mjs";
|
|
|
22
22
|
import { createPanopticMiddleware } from "./panoptic/panoptic-middleware.mjs";
|
|
23
23
|
import { panoptic } from "./panoptic/panoptic.mjs";
|
|
24
24
|
import "./panoptic/index.mjs";
|
|
25
|
+
import { evaluateSystemPrompt } from "./evaluate/evaluate-system-prompt.mjs";
|
|
26
|
+
import { extractLastSystemPrompt } from "./evaluate/extract-last-system-prompt.mjs";
|
|
27
|
+
import { findSpanById } from "./evaluate/find-span-by-id.mjs";
|
|
28
|
+
import "./evaluate/index.mjs";
|
|
25
29
|
import { dashboard } from "./dashboard/dashboard.mjs";
|
|
26
30
|
import { NO_PROMPT_KEY, NO_SESSION_KEY, NO_TYPE_KEY, aggregateByType, filterTraces, groupByPrompt, groupBySession, groupByType, heatIntensity, matchesFilter, maxNodeCost, percentile, rollupCost, traceCost, tracePromptKey } from "./dashboard/trace-filter.mjs";
|
|
31
|
+
import "./dashboard/index.mjs";
|
|
27
32
|
import { applyPanopticConfig } from "./config/apply-panoptic-config.mjs";
|
|
28
33
|
import "./config/index.mjs";
|
|
29
34
|
import "./register.mjs";
|
|
30
35
|
|
|
31
|
-
export { GEN_AI_ATTRIBUTES, NO_PROMPT_KEY, NO_SESSION_KEY, NO_TYPE_KEY, WARLOCK_ATTRIBUTES, aggregateByType, applyPanopticConfig, consoleExporter, createCacheTraceStore, createCollector, createInMemoryTraceStore, createPanopticMiddleware, dashboard, emptyUsage, extractSpanAttributes, fileExporter, filterTraces, formatSpanIO, formatSpanLine, groupByPrompt, groupBySession, groupByType, heatIntensity, langfuseExporter, matchTrace, matchesFilter, maxNodeCost, normalizeError, otelExporter, panoptic, percentile, reportToSpan, reportToTrace, rollupCost, sumUsage, toGenAiAttributes, totalCostUsd, traceCost, tracePromptKey, walkSpans };
|
|
36
|
+
export { GEN_AI_ATTRIBUTES, NO_PROMPT_KEY, NO_SESSION_KEY, NO_TYPE_KEY, WARLOCK_ATTRIBUTES, aggregateByType, applyPanopticConfig, consoleExporter, createCacheTraceStore, createCollector, createInMemoryTraceStore, createPanopticMiddleware, dashboard, emptyUsage, evaluateSystemPrompt, extractLastSystemPrompt, extractSpanAttributes, fileExporter, filterTraces, findSpanById, formatSpanIO, formatSpanLine, groupByPrompt, groupBySession, groupByType, heatIntensity, langfuseExporter, matchTrace, matchesFilter, maxNodeCost, normalizeError, otelExporter, panoptic, percentile, reportToSpan, reportToTrace, rollupCost, sumUsage, toGenAiAttributes, totalCostUsd, traceCost, tracePromptKey, walkSpans };
|
package/llms-full.txt
CHANGED
|
@@ -4,6 +4,96 @@
|
|
|
4
4
|
|
|
5
5
|
> Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/ai-panoptic/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.
|
|
6
6
|
|
|
7
|
+
## evaluate-system-prompt `@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
name: evaluate-system-prompt
|
|
11
|
+
description: 'Grade a single trace''s LAST captured system prompt from the dashboard drawer — the dashboard''s only write-capable feature, config-gated via `DashboardOptions.evaluate` (`ai.config({ panoptic: { dashboard: { evaluate: { model, instructions } } } })`). Triggers: `evaluate`, `EvaluateConfig`, `EvaluateVerdict`, `evaluateSystemPrompt`, `extractLastSystemPrompt`, `findSpanById`, "Evaluate system prompt" button, `POST .../spans/:spanId/evaluate`, grading a prompt from a real trace, judging a captured system prompt, per-run instructions override, judge model factory; ''evaluate this trace''s prompt'', ''grade the system prompt from the dashboard'', ''judge a captured run'', ''score a live system prompt''. Skip: grading raw prompt TEXT you already have in code (no trace involved) — `@warlock.js/ai`''s `ai.prompts().validate({ criteria })`, the same judge machinery this wraps; a DATASET of test cases run through an agent — `@warlock.js/ai`''s `ai.dataset` + `agent.eval`; viewing/filtering/grouping traces without grading them — `@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`; capturing the content this feature reads — `@warlock.js/ai-panoptic/export-traces/SKILL.md` (`captureContent`).'
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Evaluate a trace's system prompt from the dashboard
|
|
15
|
+
|
|
16
|
+
A drawer button that grades the LAST `{role: "system"}` message captured on the selected span — not the whole conversation, not the tool code, just the system prompt that was actually in effect for that run. It's the dashboard's only write-capable feature: every other route is `GET`-only and read-only by design (see `use-local-dashboard/SKILL.md`'s security note); this one POSTs, and it's off unless you explicitly opt in.
|
|
17
|
+
|
|
18
|
+
## Enable it
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { ai } from "@warlock.js/ai";
|
|
22
|
+
import "@warlock.js/ai-panoptic";
|
|
23
|
+
|
|
24
|
+
ai.config({
|
|
25
|
+
panoptic: {
|
|
26
|
+
dashboard: {
|
|
27
|
+
evaluate: {
|
|
28
|
+
model: openai.model("gpt-4o-mini"),
|
|
29
|
+
instructions: "Must address the user by name. Never invent prices.",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
observeAll: true,
|
|
33
|
+
captureContent: true, // required — evaluate reads the captured span.input
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Absent `evaluate` ⇒ the drawer never renders the button and a `POST` to its route 405s like any other route would (the path is never even pattern-matched) — the dashboard stays fully read-only unless you opt in.
|
|
39
|
+
|
|
40
|
+
### `EvaluateConfig`
|
|
41
|
+
|
|
42
|
+
| Field | Required | Notes |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `model` | yes | A `ModelContract`, or a `() => ModelContract \| Promise<ModelContract>` factory — mirrors `panoptic.cache`'s literal-or-factory ergonomics. Resolved fresh on every evaluate click (a deliberate, infrequent, human-triggered action — not the collector's hot path — so no memoization, unlike the cache store's driver). |
|
|
45
|
+
| `instructions` | no | Default grading rubric/criteria, verbatim — the same shape `ai.prompts().validate({ criteria })` accepts. Seeds the dashboard's editable textarea (baked into the served page at boot, so the operator sees and can extend the actual default, not a blank box); the built-in prompt-quality rubric is used when neither this nor a per-run override is supplied. |
|
|
46
|
+
|
|
47
|
+
## The UI
|
|
48
|
+
|
|
49
|
+
Open a trace, select the span whose prompt you want graded (any span carrying a captured `[system, user]` pair or `fullHistory` message array — an agent span, not a tool span). An "Evaluate system prompt" button appears under the input/output block **only when that span actually has a system prompt to grade** (`extractLastSystemPrompt` finds one) — no dead button on tool spans or system-prompt-less runs.
|
|
50
|
+
|
|
51
|
+
Click it to open an inline panel: a textarea pre-filled with the configured `instructions` (fully editable — type over it, append to it, or clear it to fall back to the built-in rubric) and a "Run" button. The result — `score` (0–1, shown as a percentage) and `issues` (the judge's reasoning, as a list) — renders inline once it lands. State (open/closed, typed text, last result) is kept per-span in client memory, so switching to a sibling span and back doesn't lose it; it does NOT persist to the trace store or survive a page reload.
|
|
52
|
+
|
|
53
|
+
## The building blocks (for embedding elsewhere)
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { evaluateSystemPrompt, extractLastSystemPrompt, findSpanById } from "@warlock.js/ai-panoptic";
|
|
57
|
+
|
|
58
|
+
const span = findSpanById(trace.root, spanId); // walk the span tree
|
|
59
|
+
const prompt = extractLastSystemPrompt(span); // undefined if none captured
|
|
60
|
+
if (prompt) {
|
|
61
|
+
const verdict = await evaluateSystemPrompt(prompt, evaluateConfig, "Per-run override, optional");
|
|
62
|
+
// verdict: { score?: number; issues: string[] }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- **`extractLastSystemPrompt(span)`** — reads `span.input` (only present under `captureContent`). Handles both shapes `report-to-span.ts` produces: the `[system, user]` first-trip pair, and — under `fullHistory` — the full `CapturedMessage[]` conversation, where it takes the LAST system-role turn (a long-running agent can carry more than one; the most recent one is the one actually in effect). Returns `undefined` for a tool span, an agent with no system prompt, or when content capture is off.
|
|
67
|
+
- **`findSpanById(root, spanId)`** — depth-first lookup inside a trace's span tree; the route uses it to resolve which node the drawer had selected.
|
|
68
|
+
- **`evaluateSystemPrompt(text, config, instructionsOverride?)`** — the grading call. `instructionsOverride` (trimmed; blank-after-trim is treated as absent, never as "grade against nothing") wins over `config.instructions`. Reuses `@warlock.js/ai`'s `judgePromptBody` verbatim — the SAME LLM-as-judge machinery `ai.prompts().validate({ criteria })` runs — so there's exactly one judging implementation in the whole framework, not two. `judgePromptBody` itself never throws (a broken judge degrades to an issues-only outcome with no `score`); the only step that CAN throw is resolving `config.model` when it's a factory (e.g. constructing an SDK client) — the dashboard route catches that and returns `502`.
|
|
69
|
+
|
|
70
|
+
## The route
|
|
71
|
+
|
|
72
|
+
`POST {basePath}api/traces/:traceId/spans/:spanId/evaluate`, body `{ instructions?: string }` (all optional — an empty body is valid). Gated by the same `authToken` / `allowedHosts` checks as every other route (S4) — off-loopback still requires a token, exactly like the rest of the dashboard. Body reads are capped at 64 KB (`413` past that) so a hostile/oversized payload can't hold the connection open.
|
|
73
|
+
|
|
74
|
+
| Status | Meaning |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `200` | `EvaluateVerdict` — `{ score?: number; issues: string[] }` |
|
|
77
|
+
| `404` | trace or span not found |
|
|
78
|
+
| `422` | the span carried no captured system prompt to grade |
|
|
79
|
+
| `413` | request body over 64 KB |
|
|
80
|
+
| `400` | request body isn't valid JSON |
|
|
81
|
+
| `502` | `config.model` (a factory) threw resolving the judge model |
|
|
82
|
+
| `405` | POST hit this path but `evaluate` isn't configured |
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
curl -X POST 'http://127.0.0.1:4319/api/traces/tr_1/spans/sp_1/evaluate' \
|
|
86
|
+
-H 'content-type: application/json' \
|
|
87
|
+
-d '{"instructions": "Must never invent prices."}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## See also
|
|
91
|
+
|
|
92
|
+
- [`@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`](@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md) — the dashboard this feature is a drawer action of; its security note now says "every route is GET-only and read-only EXCEPT the one opt-in write route `evaluate` enables."
|
|
93
|
+
- [`@warlock.js/ai-panoptic/export-traces/SKILL.md`](@warlock.js/ai-panoptic/export-traces/SKILL.md) — `captureContent` (and `fullHistory`), the prerequisite for `span.input` to carry anything gradable.
|
|
94
|
+
- `ai.prompts().validate({ criteria })` in `@warlock.js/ai` — the sibling capability this reuses: grading raw prompt TEXT you already have in code, no trace or dashboard involved.
|
|
95
|
+
|
|
96
|
+
|
|
7
97
|
## export-traces `@warlock.js/ai-panoptic/export-traces/SKILL.md`
|
|
8
98
|
|
|
9
99
|
---
|
|
@@ -748,7 +838,7 @@ type DashboardHandle = {
|
|
|
748
838
|
|
|
749
839
|
## The JSON API
|
|
750
840
|
|
|
751
|
-
|
|
841
|
+
Every route is **`GET`-only and read-only** (non-`GET` → `405`), mounted under `basePath` — EXCEPT the one opt-in `POST .../spans/:spanId/evaluate` route `DashboardOptions.evaluate` enables (see [`evaluate-system-prompt/SKILL.md`](@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md)); absent that config, it 405s like everything else. The page polls the `GET` routes; you can curl them too. Store shapes are already JSON-safe, so responses are a plain `JSON.stringify`.
|
|
752
842
|
|
|
753
843
|
| Route | Returns |
|
|
754
844
|
|---|---|
|
|
@@ -756,6 +846,7 @@ All routes are **`GET`-only and read-only** (non-`GET` → `405`), mounted under
|
|
|
756
846
|
| `GET /api/traces/:id` | `store.get(id)` — one full trace, or `404 { error: "trace_not_found" }`. |
|
|
757
847
|
| `GET /api/aggregate` | `store.aggregate(...)` — the usage + cost + status rollup over the same query filter. |
|
|
758
848
|
| `GET /` (basePath) | the self-contained HTML page. |
|
|
849
|
+
| `POST /api/traces/:traceId/spans/:spanId/evaluate` | grades the span's last captured system prompt — only when `evaluate` is configured. |
|
|
759
850
|
|
|
760
851
|
Query-string filters map 1:1 onto `TraceQuery`:
|
|
761
852
|
|
|
@@ -782,16 +873,17 @@ const handler = createRequestHandler(store, {
|
|
|
782
873
|
createServer(handler).listen(4319, "127.0.0.1");
|
|
783
874
|
```
|
|
784
875
|
|
|
785
|
-
`dashboardHtml(basePath, title)` returns the served page as a string if you embed it elsewhere. `ServeConfig` is `{ basePath; title; allowedHosts; authToken? }`.
|
|
876
|
+
`dashboardHtml(basePath, title, evaluateEnabled?, evaluateDefaultInstructions?)` returns the served page as a string if you embed it elsewhere. `ServeConfig` is `{ basePath; title; allowedHosts; authToken?; evaluate? }`.
|
|
786
877
|
|
|
787
878
|
## Security note
|
|
788
879
|
|
|
789
|
-
The dashboard surfaces whatever the store holds — including captured prompt/response content when content capture is on (`captureContent`). It binds **loopback-only** by default precisely so that content never leaves the machine. Binding a non-loopback host is gated — it requires an `authToken` (and checks a `Host` allowlist, sending `nosniff` / CSP / `X-Frame-Options: DENY` on every response) — but still prefer loopback for dev, and for production ship to a real backend via an exporter rather than exposing the dashboard.
|
|
880
|
+
The dashboard surfaces whatever the store holds — including captured prompt/response content when content capture is on (`captureContent`). It binds **loopback-only** by default precisely so that content never leaves the machine. Binding a non-loopback host is gated — it requires an `authToken` (and checks a `Host` allowlist, sending `nosniff` / CSP / `X-Frame-Options: DENY` on every response) — but still prefer loopback for dev, and for production ship to a real backend via an exporter rather than exposing the dashboard. The dashboard has exactly one write-capable route (`evaluate`, off by default) — everything else stays read-only regardless of configuration.
|
|
790
881
|
|
|
791
882
|
## See also
|
|
792
883
|
|
|
793
884
|
- [`@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md`](@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md) — the `ai.config({ panoptic })` wiring and the underlying `panoptic(...)` subscriber that fills the store.
|
|
794
885
|
- [`@warlock.js/ai-panoptic/query-traces/SKILL.md`](@warlock.js/ai-panoptic/query-traces/SKILL.md) — the `TraceStoreContract` (`query` / `get` / `aggregate`) the dashboard reads, the `TraceQuery` filter, and capacity eviction.
|
|
795
886
|
- [`@warlock.js/ai-panoptic/export-traces/SKILL.md`](@warlock.js/ai-panoptic/export-traces/SKILL.md) — shipping traces to OTel / Langfuse / a file instead of (or alongside) the local dashboard.
|
|
887
|
+
- [`@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md`](@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md) — the drawer's "Evaluate system prompt" action, the dashboard's one write route.
|
|
796
888
|
|
|
797
889
|
|
package/llms.txt
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
|
+
- [evaluate-system-prompt](@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md): Grade a single trace's LAST captured system prompt from the dashboard drawer — the dashboard's only write-capable feature, config-gated via `DashboardOptions.evaluate` (`ai.config({ panoptic: { dashboard: { evaluate: { model, instructions } } } })`). Triggers: `evaluate`, `EvaluateConfig`, `EvaluateVerdict`, `evaluateSystemPrompt`, `extractLastSystemPrompt`, `findSpanById`, "Evaluate system prompt" button, `POST .../spans/:spanId/evaluate`, grading a prompt from a real trace, judging a captured system prompt, per-run instructions override, judge model factory; 'evaluate this trace's prompt', 'grade the system prompt from the dashboard', 'judge a captured run', 'score a live system prompt'. Skip: grading raw prompt TEXT you already have in code (no trace involved) — `@warlock.js/ai`'s `ai.prompts().validate({ criteria })`, the same judge machinery this wraps; a DATASET of test cases run through an agent — `@warlock.js/ai`'s `ai.dataset` + `agent.eval`; viewing/filtering/grouping traces without grading them — `@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`; capturing the content this feature reads — `@warlock.js/ai-panoptic/export-traces/SKILL.md` (`captureContent`).
|
|
9
10
|
- [export-traces](@warlock.js/ai-panoptic/export-traces/SKILL.md): Send @warlock.js/ai-panoptic traces to an observability backend via pluggable exporters, including capturing prompt/response content onto spans. Triggers: `consoleExporter`, `fileExporter`, `otelExporter`, `langfuseExporter`, `ExporterContract`, `ContentCaptureOptions`, `captureContent`, `redactContent`, `fullHistory`, `ContentRedactor`, `toGenAiAttributes`, `walkSpans`, `totalCostUsd`, `GEN_AI_ATTRIBUTES`, `WARLOCK_ATTRIBUTES`, `reportToTrace`, `reportToSpan`, `extractSpanAttributes`, `normalizeError`, `formatSpanLine`, `createPanopticMiddleware`; 'export AI traces to OpenTelemetry', 'send traces to Langfuse', 'log traces to console / a file', 'capture the full prompt/conversation onto a span', 'gen_ai semantic conventions', 'write a custom exporter'; typical import `import { otelExporter, langfuseExporter, consoleExporter, fileExporter } from "@warlock.js/ai-panoptic"`. Skip: wiring the subscriber/collector into a run — `@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md`; the core report shape — `@warlock.js/ai/run-ai-agent/SKILL.md`.
|
|
10
11
|
- [observe-with-panoptic](@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md): Wire @warlock.js/ai-panoptic into an agent/workflow/supervisor/orchestrator — declaratively via `ai.config({ panoptic })` (registers panoptic on core's Observer seam) or with the one-call `panoptic({ exporters })` subscriber. Triggers: `panoptic`, `Panoptic`, `PanopticOptions`, `PanopticConfig`, `ai.config({ panoptic })`, `observeAll`, per-flow `observe`, `FlowObserveOption`, `registerObserver`, `Observer`, `.attach`, `.middleware`, `.collect`, `.toTrace`, `observe.attach(agent)`, `panoptic().middleware()`, `completedEvents`, `middlewareName`; 'observe a warlock agent', 'trace an agent run', 'attach observability to a workflow/supervisor', 'observe every flow by default', 'add a tracing middleware', 'collect an orchestrator turn report', 'wire OTel/Langfuse into my agent'; typical import `import { ai } from "@warlock.js/ai"` + `import "@warlock.js/ai-panoptic"` (or `import { panoptic } from "@warlock.js/ai-panoptic"`). Skip: the zero-setup local dashboard — `@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`; writing/choosing an exporter (the sink end) — `@warlock.js/ai-panoptic/export-traces/SKILL.md`; the core report shape / running the agent itself — `@warlock.js/ai/run-ai-agent/SKILL.md`.
|
|
11
12
|
- [query-traces](@warlock.js/ai-panoptic/query-traces/SKILL.md): Retain @warlock.js/ai-panoptic traces in a queryable store — in-memory, or cache-backed so they survive a process restart — and slice them after the fact: get one run by id, list a session, filter failed runs in a window, roll up usage + cost. Triggers: `createInMemoryTraceStore`, `createCacheTraceStore`, `CacheTraceStoreHandle`, `CacheTraceStoreOptions`, `CacheDriverInput`, `store.ready`, `TraceStoreContract`, `TraceQuery`, `TraceAggregate`, `InMemoryTraceStoreOptions`, `store.query`, `store.aggregate`, `store.get`, `store.add`, `store.clear`, `store.size`, `capacity`, `prefix`, `onError`, `sumUsage`, `emptyUsage`, `matchTrace`; 'query collected traces', 'aggregate AI cost per session', 'how much did this session spend', 'list failed runs', 'retain traces in memory', 'persist traces across a restart', 'cache-backed trace store', 'Redis trace store', 'roll up token usage'; typical import `import { createInMemoryTraceStore, createCacheTraceStore } from "@warlock.js/ai-panoptic"`. Skip: sending traces to an external backend (OTel/Langfuse/console/file) — `@warlock.js/ai-panoptic/export-traces/SKILL.md`; wiring the subscriber into a run — `@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md`.
|
package/package.json
CHANGED
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@opentelemetry/api": "*",
|
|
23
23
|
"@opentelemetry/sdk-trace-base": "*",
|
|
24
|
-
"@warlock.js/ai": "4.8.
|
|
25
|
-
"@warlock.js/cache": "4.8.
|
|
24
|
+
"@warlock.js/ai": "4.8.2",
|
|
25
|
+
"@warlock.js/cache": "4.8.2",
|
|
26
|
+
"@warlock.js/logger": "4.8.2",
|
|
26
27
|
"langfuse": "*"
|
|
27
28
|
},
|
|
28
|
-
"version": "4.8.
|
|
29
|
+
"version": "4.8.2",
|
|
29
30
|
"main": "./cjs/index.cjs",
|
|
30
31
|
"module": "./esm/index.mjs",
|
|
31
32
|
"types": "./esm/index.d.mts",
|
package/skills/README.md
CHANGED
|
@@ -19,3 +19,7 @@ Retain traces in a queryable store — in-memory (`createInMemoryTraceStore`) or
|
|
|
19
19
|
### [`use-local-dashboard/`](./use-local-dashboard/SKILL.md)
|
|
20
20
|
|
|
21
21
|
Run the zero-setup local Panoptic dashboard — a loopback `node:http` server over a trace store served from `ai.config({ panoptic: { dashboard } })`. Covers the redesigned UI (light/dark/system theme, two-pane nested call tree + metadata panel, colour-coded Title-case type labels including the first-class `Team` type, started/ended wall-clock timestamps, arrow-coded `↓in · ↑out · total` tokens, per-node rollup cost, hide-zero rows, Warlock logo), the new dashboard features (client-side search/filter, group-by-session + group-by-prompt + group-by-type, per-type stats panel, cost heatmap, Gantt timeline, errors-only toggle, hash deep-links) and their pure `trace-filter.ts` helpers (`filterTraces` / `groupBySession` / `groupByPrompt` / `rollupCost` / `maxNodeCost` / `heatIntensity` / `tracePromptKey`), the persistent `cache` store wiring, the declarative path (which store it reads, idempotent wiring), the low-level `dashboard(store, options)` building block, `DashboardOptions` (port `4319`, loopback host, `basePath`, `open`, `title`), the `DashboardHandle`, the read-only `/api/traces` / `/api/aggregate` / `/api/traces/:id` JSON API, `createRequestHandler` / `dashboardHtml` for embedding, prompt-version linkage, and the loopback-only security posture. Load when opening a local AI trace viewer in the browser, theming/filtering it, inspecting traces without a backend, persisting them across a restart, or spinning up a dev observability server.
|
|
22
|
+
|
|
23
|
+
### [`evaluate-system-prompt/`](./evaluate-system-prompt/SKILL.md)
|
|
24
|
+
|
|
25
|
+
Grade a trace's LAST captured system prompt from the drawer — the dashboard's only write-capable feature, config-gated via `DashboardOptions.evaluate` (`{ model, instructions }`). Covers `evaluateSystemPrompt` / `extractLastSystemPrompt` / `findSpanById`, the "Evaluate system prompt" button + editable-instructions panel, the `POST .../spans/:spanId/evaluate` route and its status codes, and how it reuses `@warlock.js/ai`'s `judgePromptBody` (the same machinery `ai.prompts().validate()` runs) rather than a second judging implementation. Load when grading a live/captured system prompt from the dashboard, wiring `evaluate` into `ai.config`, or embedding the evaluate building blocks elsewhere.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: evaluate-system-prompt
|
|
3
|
+
description: 'Grade a single trace''s LAST captured system prompt from the dashboard drawer — the dashboard''s only write-capable feature, config-gated via `DashboardOptions.evaluate` (`ai.config({ panoptic: { dashboard: { evaluate: { model, instructions } } } })`). Triggers: `evaluate`, `EvaluateConfig`, `EvaluateVerdict`, `evaluateSystemPrompt`, `extractLastSystemPrompt`, `findSpanById`, "Evaluate system prompt" button, `POST .../spans/:spanId/evaluate`, grading a prompt from a real trace, judging a captured system prompt, per-run instructions override, judge model factory; ''evaluate this trace''s prompt'', ''grade the system prompt from the dashboard'', ''judge a captured run'', ''score a live system prompt''. Skip: grading raw prompt TEXT you already have in code (no trace involved) — `@warlock.js/ai`''s `ai.prompts().validate({ criteria })`, the same judge machinery this wraps; a DATASET of test cases run through an agent — `@warlock.js/ai`''s `ai.dataset` + `agent.eval`; viewing/filtering/grouping traces without grading them — `@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`; capturing the content this feature reads — `@warlock.js/ai-panoptic/export-traces/SKILL.md` (`captureContent`).'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Evaluate a trace's system prompt from the dashboard
|
|
7
|
+
|
|
8
|
+
A drawer button that grades the LAST `{role: "system"}` message captured on the selected span — not the whole conversation, not the tool code, just the system prompt that was actually in effect for that run. It's the dashboard's only write-capable feature: every other route is `GET`-only and read-only by design (see `use-local-dashboard/SKILL.md`'s security note); this one POSTs, and it's off unless you explicitly opt in.
|
|
9
|
+
|
|
10
|
+
## Enable it
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { ai } from "@warlock.js/ai";
|
|
14
|
+
import "@warlock.js/ai-panoptic";
|
|
15
|
+
|
|
16
|
+
ai.config({
|
|
17
|
+
panoptic: {
|
|
18
|
+
dashboard: {
|
|
19
|
+
evaluate: {
|
|
20
|
+
model: openai.model("gpt-4o-mini"),
|
|
21
|
+
instructions: "Must address the user by name. Never invent prices.",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
observeAll: true,
|
|
25
|
+
captureContent: true, // required — evaluate reads the captured span.input
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Absent `evaluate` ⇒ the drawer never renders the button and a `POST` to its route 405s like any other route would (the path is never even pattern-matched) — the dashboard stays fully read-only unless you opt in.
|
|
31
|
+
|
|
32
|
+
### `EvaluateConfig`
|
|
33
|
+
|
|
34
|
+
| Field | Required | Notes |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `model` | yes | A `ModelContract`, or a `() => ModelContract \| Promise<ModelContract>` factory — mirrors `panoptic.cache`'s literal-or-factory ergonomics. Resolved fresh on every evaluate click (a deliberate, infrequent, human-triggered action — not the collector's hot path — so no memoization, unlike the cache store's driver). |
|
|
37
|
+
| `instructions` | no | Default grading rubric/criteria, verbatim — the same shape `ai.prompts().validate({ criteria })` accepts. Seeds the dashboard's editable textarea (baked into the served page at boot, so the operator sees and can extend the actual default, not a blank box); the built-in prompt-quality rubric is used when neither this nor a per-run override is supplied. |
|
|
38
|
+
|
|
39
|
+
## The UI
|
|
40
|
+
|
|
41
|
+
Open a trace, select the span whose prompt you want graded (any span carrying a captured `[system, user]` pair or `fullHistory` message array — an agent span, not a tool span). An "Evaluate system prompt" button appears under the input/output block **only when that span actually has a system prompt to grade** (`extractLastSystemPrompt` finds one) — no dead button on tool spans or system-prompt-less runs.
|
|
42
|
+
|
|
43
|
+
Click it to open an inline panel: a textarea pre-filled with the configured `instructions` (fully editable — type over it, append to it, or clear it to fall back to the built-in rubric) and a "Run" button. The result — `score` (0–1, shown as a percentage) and `issues` (the judge's reasoning, as a list) — renders inline once it lands. State (open/closed, typed text, last result) is kept per-span in client memory, so switching to a sibling span and back doesn't lose it; it does NOT persist to the trace store or survive a page reload.
|
|
44
|
+
|
|
45
|
+
## The building blocks (for embedding elsewhere)
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { evaluateSystemPrompt, extractLastSystemPrompt, findSpanById } from "@warlock.js/ai-panoptic";
|
|
49
|
+
|
|
50
|
+
const span = findSpanById(trace.root, spanId); // walk the span tree
|
|
51
|
+
const prompt = extractLastSystemPrompt(span); // undefined if none captured
|
|
52
|
+
if (prompt) {
|
|
53
|
+
const verdict = await evaluateSystemPrompt(prompt, evaluateConfig, "Per-run override, optional");
|
|
54
|
+
// verdict: { score?: number; issues: string[] }
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- **`extractLastSystemPrompt(span)`** — reads `span.input` (only present under `captureContent`). Handles both shapes `report-to-span.ts` produces: the `[system, user]` first-trip pair, and — under `fullHistory` — the full `CapturedMessage[]` conversation, where it takes the LAST system-role turn (a long-running agent can carry more than one; the most recent one is the one actually in effect). Returns `undefined` for a tool span, an agent with no system prompt, or when content capture is off.
|
|
59
|
+
- **`findSpanById(root, spanId)`** — depth-first lookup inside a trace's span tree; the route uses it to resolve which node the drawer had selected.
|
|
60
|
+
- **`evaluateSystemPrompt(text, config, instructionsOverride?)`** — the grading call. `instructionsOverride` (trimmed; blank-after-trim is treated as absent, never as "grade against nothing") wins over `config.instructions`. Reuses `@warlock.js/ai`'s `judgePromptBody` verbatim — the SAME LLM-as-judge machinery `ai.prompts().validate({ criteria })` runs — so there's exactly one judging implementation in the whole framework, not two. `judgePromptBody` itself never throws (a broken judge degrades to an issues-only outcome with no `score`); the only step that CAN throw is resolving `config.model` when it's a factory (e.g. constructing an SDK client) — the dashboard route catches that and returns `502`.
|
|
61
|
+
|
|
62
|
+
## The route
|
|
63
|
+
|
|
64
|
+
`POST {basePath}api/traces/:traceId/spans/:spanId/evaluate`, body `{ instructions?: string }` (all optional — an empty body is valid). Gated by the same `authToken` / `allowedHosts` checks as every other route (S4) — off-loopback still requires a token, exactly like the rest of the dashboard. Body reads are capped at 64 KB (`413` past that) so a hostile/oversized payload can't hold the connection open.
|
|
65
|
+
|
|
66
|
+
| Status | Meaning |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `200` | `EvaluateVerdict` — `{ score?: number; issues: string[] }` |
|
|
69
|
+
| `404` | trace or span not found |
|
|
70
|
+
| `422` | the span carried no captured system prompt to grade |
|
|
71
|
+
| `413` | request body over 64 KB |
|
|
72
|
+
| `400` | request body isn't valid JSON |
|
|
73
|
+
| `502` | `config.model` (a factory) threw resolving the judge model |
|
|
74
|
+
| `405` | POST hit this path but `evaluate` isn't configured |
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
curl -X POST 'http://127.0.0.1:4319/api/traces/tr_1/spans/sp_1/evaluate' \
|
|
78
|
+
-H 'content-type: application/json' \
|
|
79
|
+
-d '{"instructions": "Must never invent prices."}'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## See also
|
|
83
|
+
|
|
84
|
+
- [`@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`](@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md) — the dashboard this feature is a drawer action of; its security note now says "every route is GET-only and read-only EXCEPT the one opt-in write route `evaluate` enables."
|
|
85
|
+
- [`@warlock.js/ai-panoptic/export-traces/SKILL.md`](@warlock.js/ai-panoptic/export-traces/SKILL.md) — `captureContent` (and `fullHistory`), the prerequisite for `span.input` to carry anything gradable.
|
|
86
|
+
- `ai.prompts().validate({ criteria })` in `@warlock.js/ai` — the sibling capability this reuses: grading raw prompt TEXT you already have in code, no trace or dashboard involved.
|
|
@@ -166,7 +166,7 @@ type DashboardHandle = {
|
|
|
166
166
|
|
|
167
167
|
## The JSON API
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
Every route is **`GET`-only and read-only** (non-`GET` → `405`), mounted under `basePath` — EXCEPT the one opt-in `POST .../spans/:spanId/evaluate` route `DashboardOptions.evaluate` enables (see [`evaluate-system-prompt/SKILL.md`](@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md)); absent that config, it 405s like everything else. The page polls the `GET` routes; you can curl them too. Store shapes are already JSON-safe, so responses are a plain `JSON.stringify`.
|
|
170
170
|
|
|
171
171
|
| Route | Returns |
|
|
172
172
|
|---|---|
|
|
@@ -174,6 +174,7 @@ All routes are **`GET`-only and read-only** (non-`GET` → `405`), mounted under
|
|
|
174
174
|
| `GET /api/traces/:id` | `store.get(id)` — one full trace, or `404 { error: "trace_not_found" }`. |
|
|
175
175
|
| `GET /api/aggregate` | `store.aggregate(...)` — the usage + cost + status rollup over the same query filter. |
|
|
176
176
|
| `GET /` (basePath) | the self-contained HTML page. |
|
|
177
|
+
| `POST /api/traces/:traceId/spans/:spanId/evaluate` | grades the span's last captured system prompt — only when `evaluate` is configured. |
|
|
177
178
|
|
|
178
179
|
Query-string filters map 1:1 onto `TraceQuery`:
|
|
179
180
|
|
|
@@ -200,14 +201,15 @@ const handler = createRequestHandler(store, {
|
|
|
200
201
|
createServer(handler).listen(4319, "127.0.0.1");
|
|
201
202
|
```
|
|
202
203
|
|
|
203
|
-
`dashboardHtml(basePath, title)` returns the served page as a string if you embed it elsewhere. `ServeConfig` is `{ basePath; title; allowedHosts; authToken? }`.
|
|
204
|
+
`dashboardHtml(basePath, title, evaluateEnabled?, evaluateDefaultInstructions?)` returns the served page as a string if you embed it elsewhere. `ServeConfig` is `{ basePath; title; allowedHosts; authToken?; evaluate? }`.
|
|
204
205
|
|
|
205
206
|
## Security note
|
|
206
207
|
|
|
207
|
-
The dashboard surfaces whatever the store holds — including captured prompt/response content when content capture is on (`captureContent`). It binds **loopback-only** by default precisely so that content never leaves the machine. Binding a non-loopback host is gated — it requires an `authToken` (and checks a `Host` allowlist, sending `nosniff` / CSP / `X-Frame-Options: DENY` on every response) — but still prefer loopback for dev, and for production ship to a real backend via an exporter rather than exposing the dashboard.
|
|
208
|
+
The dashboard surfaces whatever the store holds — including captured prompt/response content when content capture is on (`captureContent`). It binds **loopback-only** by default precisely so that content never leaves the machine. Binding a non-loopback host is gated — it requires an `authToken` (and checks a `Host` allowlist, sending `nosniff` / CSP / `X-Frame-Options: DENY` on every response) — but still prefer loopback for dev, and for production ship to a real backend via an exporter rather than exposing the dashboard. The dashboard has exactly one write-capable route (`evaluate`, off by default) — everything else stays read-only regardless of configuration.
|
|
208
209
|
|
|
209
210
|
## See also
|
|
210
211
|
|
|
211
212
|
- [`@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md`](@warlock.js/ai-panoptic/observe-with-panoptic/SKILL.md) — the `ai.config({ panoptic })` wiring and the underlying `panoptic(...)` subscriber that fills the store.
|
|
212
213
|
- [`@warlock.js/ai-panoptic/query-traces/SKILL.md`](@warlock.js/ai-panoptic/query-traces/SKILL.md) — the `TraceStoreContract` (`query` / `get` / `aggregate`) the dashboard reads, the `TraceQuery` filter, and capacity eviction.
|
|
213
214
|
- [`@warlock.js/ai-panoptic/export-traces/SKILL.md`](@warlock.js/ai-panoptic/export-traces/SKILL.md) — shipping traces to OTel / Langfuse / a file instead of (or alongside) the local dashboard.
|
|
215
|
+
- [`@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md`](@warlock.js/ai-panoptic/evaluate-system-prompt/SKILL.md) — the drawer's "Evaluate system prompt" action, the dashboard's one write route.
|