dsh-decision-layer 0.5.0 → 0.5.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/lib/client.js CHANGED
@@ -24,6 +24,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
24
24
  // src/client/index.tsx
25
25
  var index_exports = {};
26
26
  __export(index_exports, {
27
+ AnalysisView: () => AnalysisView,
27
28
  ConfigForm: () => ConfigForm,
28
29
  apply: () => apply,
29
30
  inject: () => inject
@@ -68,7 +69,7 @@ var dictionaries = {
68
69
  checkPassRate: "\u901A\u8FC7\u7387",
69
70
  narrowApplyRate: "\u6536\u7A84\u7387",
70
71
  rateNa: "\u2014",
71
- enabled: "\u542F\u7528\u81EA\u52A8\u4ECB\u5165",
72
+ enabled: "\u672C\u4F1A\u8BDD\u5185\u542F\u7528\u81EA\u52A8\u4ECB\u5165",
72
73
  backendStatus: "\u540E\u7AEF\u8FDE\u63A5",
73
74
  statusChecking: "\u68C0\u6D4B\u4E2D\u2026",
74
75
  statusIdle: "\u672A\u68C0\u6D4B",
@@ -165,7 +166,21 @@ var dictionaries = {
165
166
  error: "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
166
167
  loading: "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u72B6\u6001",
167
168
  destination: "\u88C1\u51B3\u65F6\u4F1A\u5411\u4EE5\u4E0B\u540E\u7AEF\u53D1\u9001\u5FC5\u8981\u7684\u8BC4\u4F30\u4E0A\u4E0B\u6587\uFF1A",
168
- httpWarning: "\u6B64\u5730\u5740\u4F7F\u7528\u672A\u52A0\u5BC6\u7684 HTTP\u3002\u786E\u8BA4\u540E\uFF0CAPI Key \u4E0E\u8BC4\u4F30\u4E0A\u4E0B\u6587\u5C06\u4EE5\u660E\u6587\u53D1\u9001\u81F3\u8BE5\u5730\u5740\u3002"
169
+ httpWarning: "\u6B64\u5730\u5740\u4F7F\u7528\u672A\u52A0\u5BC6\u7684 HTTP\u3002\u786E\u8BA4\u540E\uFF0CAPI Key \u4E0E\u8BC4\u4F30\u4E0A\u4E0B\u6587\u5C06\u4EE5\u660E\u6587\u53D1\u9001\u81F3\u8BE5\u5730\u5740\u3002",
170
+ analysisTab: "\u51B3\u7B56\u5206\u6790",
171
+ analysisTitle: "\u51B3\u7B56\u5206\u6790",
172
+ analysisIntro: "\u57FA\u4E8E\u672C\u4F1A\u8BDD\u5DF2\u843D\u76D8\u7684\u51B3\u7B56\u65E5\u5FD7\u505A\u53EA\u8BFB\u805A\u5408\u4E0E\u4EBA\u5DE5\u6807\u6CE8\uFF0C\u4E0D\u6539\u53D8\u4EFB\u4F55 agent \u884C\u4E3A\u3002",
173
+ analysisProfile: "\u8FC7\u7A0B\u753B\u50CF",
174
+ analysisMeanScore: "\u5E73\u5747\u5206",
175
+ analysisTrend: "\u8D28\u91CF\u8D8B\u52BF\u56DE\u6D4B",
176
+ analysisTrendWarn: "\u544A\u8B66\u89E6\u53D1",
177
+ analysisTrendSevere: "\u4E25\u91CD\u89E6\u53D1",
178
+ analysisTrendMaxRun: "\u6700\u957F\u8FDE\u7EED\u4F4E\u5206",
179
+ analysisTrendNote: "\u7528\u5F53\u524D\u9608\u503C\u628A\u672C\u4F1A\u8BDD\u7684\u81EA\u68C0\u5206\u6570\u6309\u65F6\u95F4\u91CD\u653E\uFF0C\u7EDF\u8BA1\u544A\u8B66\u672C\u4F1A\u300C\u4F1A\u300D\u89E6\u53D1\u51E0\u6B21\uFF0C\u7528\u4E8E\u6821\u51C6\u9608\u503C\uFF0C\u4E0D\u662F\u5B9E\u65F6\u4FE1\u53F7\u3002",
180
+ analysisAccuracy: "\u5224\u65AD\u51C6\u786E\u5EA6",
181
+ analysisAccuracyEmpty: "\u5C1A\u672A\u6807\u6CE8\u4EFB\u4F55\u51B3\u7B56\u3002\u5728\u4E0B\u65B9\u51B3\u7B56\u5217\u8868\u9010\u6761\u6807\u6CE8\u300C\u5224\u5BF9 / \u5224\u9519 / \u8BF4\u4E0D\u51C6\u300D\u540E\uFF0C\u8FD9\u91CC\u7ED9\u51FA\u6C47\u603B\u3002",
182
+ analysisLogNote: "\u6807\u6CE8\u5728\u6B64\u4E0E\u4ECB\u5165\u9762\u677F\u540C\u6E90\uFF1A\u4EFB\u4E00\u5904\u6807\u6CE8\u90FD\u5199\u5165\u540C\u4E00\u4EFD\u65E5\u5FD7\u3001\u6309\u51B3\u7B56 id \u5173\u8054\u3001\u53D6\u6700\u65B0\u3002",
183
+ analysisLogTruncated: "\u672C\u4F1A\u8BDD\u6682\u65E0\u53EF\u5C55\u793A\u7684\u51B3\u7B56\u8BB0\u5F55\uFF08\u5185\u5B58\u65E5\u5FD7\u53EF\u80FD\u5DF2\u6EDA\u52A8\u6E05\u7406\uFF09\u3002"
169
184
  },
170
185
  en: {
171
186
  summary: "A danger-call gate, output self-check, and loop guard driven by a pluggable adjudication model.",
@@ -203,7 +218,7 @@ var dictionaries = {
203
218
  checkPassRate: "Pass rate",
204
219
  narrowApplyRate: "Narrow rate",
205
220
  rateNa: "\u2014",
206
- enabled: "Enable automatic interventions",
221
+ enabled: "Enable automatic interventions in this session",
207
222
  backendStatus: "Backend",
208
223
  statusChecking: "Checking\u2026",
209
224
  statusIdle: "Not checked",
@@ -300,46 +315,314 @@ var dictionaries = {
300
315
  error: "Request failed. Please retry.",
301
316
  loading: "Loading session settings",
302
317
  destination: "Decisions send the necessary evaluation context to this backend:",
303
- httpWarning: "This address uses unencrypted HTTP. Your API key and evaluation context will be sent in plaintext to this address."
318
+ httpWarning: "This address uses unencrypted HTTP. Your API key and evaluation context will be sent in plaintext to this address.",
319
+ analysisTab: "Decision analysis",
320
+ analysisTitle: "Decision analysis",
321
+ analysisIntro: "A read-only aggregate and manual annotation over this session\u2019s persisted decision log. It changes no agent behavior.",
322
+ analysisProfile: "Process profile",
323
+ analysisMeanScore: "Mean score",
324
+ analysisTrend: "Quality-trend backtest",
325
+ analysisTrendWarn: "Warn fires",
326
+ analysisTrendSevere: "Severe fires",
327
+ analysisTrendMaxRun: "Longest low run",
328
+ analysisTrendNote: "Replays this session\u2019s self-check scores in time order under the current thresholds to count how often the alarm would fire \u2014 a calibration aid, not a live signal.",
329
+ analysisAccuracy: "Decision accuracy",
330
+ analysisAccuracyEmpty: "No decisions rated yet. Rate rows below as correct / wrong / unsure and a summary appears here.",
331
+ analysisLogNote: "Ratings here share one source with the intervention panel: a rating on either surface writes to the same log, keyed by decision id, latest wins.",
332
+ analysisLogTruncated: "No decision rows to show for this session (the in-memory log may have rolled over)."
304
333
  }
305
334
  };
306
335
 
307
336
  // src/client/panel.tsx
308
- var import_react = require("react");
309
- var import_jsx_runtime = require("react/jsx-runtime");
310
- var api = "/plugins/dsh-decision-layer/api";
337
+ var import_react2 = require("react");
338
+
339
+ // src/client/api.ts
340
+ var API_BASE = "/plugins/dsh-decision-layer/api";
311
341
  async function request(path, init) {
312
- const response = await fetch(`${api}/${path}`, init);
342
+ const response = await fetch(`${API_BASE}/${path}`, init);
313
343
  const body = await response.json();
314
344
  if (!response.ok || !body.ok || body.value === void 0) throw new Error(body.error || "Request failed");
315
345
  return body.value;
316
346
  }
347
+
348
+ // src/client/decision-log.tsx
349
+ var import_jsx_runtime = require("react/jsx-runtime");
350
+ var time = (at) => new Date(at).toLocaleTimeString();
351
+ function reasonText(t, reason) {
352
+ return reason === "unavailable" || reason === "unreachable" ? t("reasonUnavailable") : reason === "http-error" ? t("reasonHttpError") : reason === "invalid-response" ? t("reasonInvalidResponse") : reason === "low-confidence" ? t("reasonLowConfidence") : reason === "too-many-candidates" ? t("reasonTooMany") : reason === "low-keep" ? t("reasonLowKeep") : reason === "config" ? t("reasonConfig") : reason;
353
+ }
354
+ function actionText(t, action) {
355
+ return action === "deny" ? t("actionDeny") : action === "ask" ? t("actionAsk") : action === "pass" ? t("actionPass") : action;
356
+ }
357
+ function failDetail(t, entry) {
358
+ const reason = reasonText(t, entry.reason);
359
+ if (entry.reason === "low-confidence" && typeof entry.confidence === "number") return `${reason}\uFF08${t("logConfidence")} ${entry.confidence}\uFF09`;
360
+ return reason;
361
+ }
362
+ function renderGate({ t }, entry) {
363
+ const tool = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "decision-log-tool", children: entry.tool ?? "\u2014" });
364
+ if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
365
+ t("logGate"),
366
+ " \xB7 ",
367
+ tool,
368
+ " ",
369
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
370
+ t("gatePassThrough"),
371
+ entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
372
+ ] })
373
+ ] });
374
+ if (entry.outcome === "deny") {
375
+ const suggestion2 = entry.suggestion && entry.suggestion !== "deny" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
376
+ " \xB7 ",
377
+ t("logSuggested"),
378
+ " ",
379
+ actionText(t, entry.suggestion)
380
+ ] }) : null;
381
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
382
+ t("logGate"),
383
+ " \xB7 ",
384
+ tool,
385
+ " ",
386
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
387
+ "\u2192 ",
388
+ t("actionDeny")
389
+ ] }),
390
+ suggestion2
391
+ ] });
392
+ }
393
+ const suggestion = entry.suggestion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
394
+ " \xB7 ",
395
+ t("logSuggested"),
396
+ " ",
397
+ actionText(t, entry.suggestion)
398
+ ] }) : null;
399
+ const lowConf = entry.reason === "low-confidence" && typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
400
+ " \xB7 ",
401
+ t("reasonLowConfidence"),
402
+ " ",
403
+ entry.confidence
404
+ ] }) : null;
405
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
406
+ t("logGate"),
407
+ " \xB7 ",
408
+ tool,
409
+ " ",
410
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("gatePassThrough") }),
411
+ suggestion,
412
+ lowConf
413
+ ] });
414
+ }
415
+ function renderCheck({ t }, entry) {
416
+ const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") });
417
+ if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
418
+ label,
419
+ " ",
420
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
421
+ t("logEvalFailed"),
422
+ entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
423
+ ] })
424
+ ] });
425
+ const result = entry.outcome === "low" ? t("checkResultLow") : t("checkResultOk");
426
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
427
+ label,
428
+ " \xB7 ",
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
430
+ entry.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", title: t("logScoreHint"), children: [
431
+ " \xB7 ",
432
+ t("logScore"),
433
+ " ",
434
+ entry.score,
435
+ "/2"
436
+ ] }) : null,
437
+ typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
438
+ " \xB7 ",
439
+ t("logConfidence"),
440
+ " ",
441
+ entry.confidence
442
+ ] }) : null
443
+ ] });
444
+ }
445
+ function renderNarrow({ t }, entry) {
446
+ const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logNarrowHint"), children: t("logNarrow") });
447
+ if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
448
+ label,
449
+ " ",
450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
451
+ t("logEvalFailed"),
452
+ entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
453
+ ] })
454
+ ] });
455
+ const dropped = entry.dropped ?? 0;
456
+ const kept = typeof entry.kept === "number" ? entry.kept : void 0;
457
+ const guardText = entry.reason === "too-many-candidates" ? typeof entry.candidates === "number" ? t("reasonTooManyCount", { count: entry.candidates }) : t("reasonTooMany") : entry.reason === "low-keep" ? t("reasonLowKeep") : void 0;
458
+ const guard = guardText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
459
+ " \xB7 ",
460
+ guardText
461
+ ] }) : null;
462
+ const modeTag = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
463
+ " \xB7 ",
464
+ entry.mode === "enforce" ? t("narrowEnforce") : t("narrowObserve")
465
+ ] });
466
+ if (dropped === 0) {
467
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
468
+ label,
469
+ " \xB7 ",
470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("narrowKept") }),
471
+ kept !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
472
+ " \xB7 ",
473
+ t("narrowKeptCount"),
474
+ " ",
475
+ kept
476
+ ] }) : null,
477
+ guard,
478
+ modeTag
479
+ ] });
480
+ }
481
+ const names = Array.isArray(entry.tools) && entry.tools.length > 0 ? entry.tools.join("\u3001") : void 0;
482
+ const overflow = names && Array.isArray(entry.tools) && entry.tools.length < dropped ? t("narrowMore", { count: dropped }) : "";
483
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
484
+ label,
485
+ " \xB7 ",
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
487
+ t("narrowDropped"),
488
+ " ",
489
+ dropped,
490
+ kept !== void 0 ? ` / ${t("narrowKeptCount")} ${kept}` : ""
491
+ ] }),
492
+ names ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
493
+ " \xB7 ",
494
+ names,
495
+ overflow
496
+ ] }) : null,
497
+ guard,
498
+ modeTag
499
+ ] });
500
+ }
501
+ function renderComplete({ t }, entry) {
502
+ const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("logComplete") });
503
+ if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
504
+ label,
505
+ " ",
506
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
507
+ t("logEvalFailed"),
508
+ entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
509
+ ] })
510
+ ] });
511
+ const result = entry.outcome === "unsatisfied" ? t("completeResultUnsatisfied") : t("completeResultOk");
512
+ const parts = [];
513
+ if (typeof entry.satisfied === "number") parts.push(`${t("completeSatisfied")} ${entry.satisfied}`);
514
+ if (typeof entry.unsatisfied === "number" && entry.unsatisfied > 0) parts.push(`${t("completeUnsatisfied")} ${entry.unsatisfied}`);
515
+ if (typeof entry.insufficient === "number" && entry.insufficient > 0) parts.push(`${t("completeInsufficient")} ${entry.insufficient}`);
516
+ const tally = parts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
517
+ " \xB7 ",
518
+ parts.join(" / ")
519
+ ] }) : null;
520
+ const steer = entry.steered ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
521
+ " \xB7 ",
522
+ t("completeSteered")
523
+ ] }) : null;
524
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
525
+ label,
526
+ " \xB7 ",
527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
528
+ typeof entry.conditions === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
529
+ " \xB7 ",
530
+ t("completeConditions"),
531
+ " ",
532
+ entry.conditions
533
+ ] }) : null,
534
+ tally,
535
+ steer
536
+ ] });
537
+ }
538
+ function outcomeTagOf(entry) {
539
+ if (entry.kind === "narrow") return entry.outcome === "error" ? "error" : "ok";
540
+ if (entry.kind === "complete") return entry.outcome === "error" ? "error" : entry.outcome === "unsatisfied" ? "warn" : "ok";
541
+ return entry.outcome === "deny" ? "deny" : entry.outcome === "error" ? "error" : entry.outcome === "low" ? "warn" : "ok";
542
+ }
543
+ function renderBody(props, entry) {
544
+ return entry.kind === "gate" ? renderGate(props, entry) : entry.kind === "narrow" ? renderNarrow(props, entry) : entry.kind === "complete" ? renderComplete(props, entry) : renderCheck(props, entry);
545
+ }
546
+ function RatingButtons({ id, t, ratings, onRate }) {
547
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-rate", role: "group", "aria-label": t("rateGroup"), children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[id] === "good" ? " decision-rate-on decision-rate-good" : ""}`, "aria-label": t("rateGood"), title: t("rateGood"), "aria-pressed": ratings[id] === "good", onClick: () => onRate(id, "good"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
549
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7.2 8.1 2.4c.6-.9 2-.5 2 .6V6h3.2c.9 0 1.5.8 1.3 1.6l-1.1 4.6c-.2.8-.9 1.3-1.7 1.3H5V7.2Z", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" }),
550
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7.2H2.6v6.3H5", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" })
551
+ ] }) }),
552
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[id] === "bad" ? " decision-rate-on decision-rate-bad" : ""}`, "aria-label": t("rateBad"), title: t("rateBad"), "aria-pressed": ratings[id] === "bad", onClick: () => onRate(id, "bad"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
553
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8.8 7.9 13.6c-.6.9-2 .5-2-.6V10H2.7c-.9 0-1.5-.8-1.3-1.6l1.1-4.6C2.7 3 3.4 2.5 4.2 2.5H11v6.3Z", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" }),
554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8.8h2.4V2.5H11", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" })
555
+ ] }) }),
556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[id] === "unsure" ? " decision-rate-on decision-rate-unsure" : ""}`, "aria-label": t("rateUnsure"), title: t("rateUnsure"), "aria-pressed": ratings[id] === "unsure", onClick: () => onRate(id, "unsure"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
557
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6.2", stroke: "currentColor", "stroke-width": "1.2" }),
558
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.3 6.2c0-1 .8-1.7 1.7-1.7s1.7.7 1.7 1.6c0 1.3-1.6 1.4-1.7 2.6", stroke: "currentColor", "stroke-width": "1.2", "stroke-linecap": "round", "stroke-linejoin": "round" }),
559
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "11.4", r: ".8", fill: "currentColor" })
560
+ ] }) })
561
+ ] });
562
+ }
563
+ function DecisionLogList({ entries, ratings, onRate, t, newestFirst = true }) {
564
+ const rows = newestFirst ? entries.slice().reverse() : entries.slice();
565
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "decision-log", children: rows.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: `decision-log-item decision-log-${outcomeTagOf(entry)}`, children: [
566
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-time", children: time(entry.at) }),
567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-body", children: renderBody({ t }, entry) }),
568
+ entry.id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingButtons, { id: entry.id, t, ratings, onRate })
569
+ ] }, entry.id ?? `${entry.at}-${index}`)) });
570
+ }
571
+
572
+ // src/client/use-annotations.ts
573
+ var import_react = require("react");
574
+ function useAnnotations(sessionId, onFail) {
575
+ const [ratings, setRatings] = (0, import_react.useState)({});
576
+ const seed = (0, import_react.useCallback)((analysis) => {
577
+ setRatings(analysis?.ratings ?? {});
578
+ }, []);
579
+ const annotate = (0, import_react.useCallback)(async (id, rating) => {
580
+ setRatings((prev) => ({ ...prev, [id]: rating }));
581
+ try {
582
+ await request("annotate", {
583
+ method: "POST",
584
+ headers: { "content-type": "application/json" },
585
+ body: JSON.stringify({ sessionId, target: id, rating })
586
+ });
587
+ } catch {
588
+ onFail();
589
+ setRatings((prev) => {
590
+ const next = { ...prev };
591
+ delete next[id];
592
+ return next;
593
+ });
594
+ }
595
+ }, [sessionId, onFail]);
596
+ return { ratings, seed, annotate };
597
+ }
598
+
599
+ // src/client/panel.tsx
600
+ var import_jsx_runtime2 = require("react/jsx-runtime");
317
601
  function DecisionPanel(props) {
318
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SessionPanel, { ...props }, props.sessionId);
602
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SessionPanel, { ...props }, props.sessionId);
319
603
  }
320
604
  function Bar({ allow, ask, deny }) {
321
605
  const total = allow + ask + deny;
322
- if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "decision-bar decision-bar-empty", "aria-hidden": "true" });
606
+ if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "decision-bar decision-bar-empty", "aria-hidden": "true" });
323
607
  const pct = (n) => `${n / total * 100}%`;
324
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-bar", "aria-hidden": "true", children: [
325
- allow > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-bar-allow", style: { width: pct(allow) } }),
326
- ask > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-bar-ask", style: { width: pct(ask) } }),
327
- deny > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-bar-deny", style: { width: pct(deny) } })
608
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-bar", "aria-hidden": "true", children: [
609
+ allow > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-allow", style: { width: pct(allow) } }),
610
+ ask > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-ask", style: { width: pct(ask) } }),
611
+ deny > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-deny", style: { width: pct(deny) } })
328
612
  ] });
329
613
  }
330
614
  function SessionPanel({ sessionId, t }) {
331
- const dialog = (0, import_react.useRef)(null);
332
- const trigger = (0, import_react.useRef)(null);
333
- const [open, setOpen] = (0, import_react.useState)(false);
334
- const [enabled, setEnabled] = (0, import_react.useState)(null);
335
- const [busy, setBusy] = (0, import_react.useState)(false);
336
- const [metrics, setMetrics] = (0, import_react.useState)(null);
337
- const [log, setLog] = (0, import_react.useState)([]);
338
- const [analysis, setAnalysis] = (0, import_react.useState)(null);
339
- const [ratings, setRatings] = (0, import_react.useState)({});
340
- const [status, setStatus] = (0, import_react.useState)("idle");
341
- const [message, setMessage] = (0, import_react.useState)("");
342
- (0, import_react.useEffect)(() => {
615
+ const dialog = (0, import_react2.useRef)(null);
616
+ const trigger = (0, import_react2.useRef)(null);
617
+ const [open, setOpen] = (0, import_react2.useState)(false);
618
+ const [enabled, setEnabled] = (0, import_react2.useState)(null);
619
+ const [busy, setBusy] = (0, import_react2.useState)(false);
620
+ const [metrics, setMetrics] = (0, import_react2.useState)(null);
621
+ const [log, setLog] = (0, import_react2.useState)([]);
622
+ const [status, setStatus] = (0, import_react2.useState)("idle");
623
+ const [message, setMessage] = (0, import_react2.useState)("");
624
+ const { ratings, seed, annotate } = useAnnotations(sessionId, () => setMessage(t("annotateFailed")));
625
+ (0, import_react2.useEffect)(() => {
343
626
  let live = true;
344
627
  void request(`session?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
345
628
  if (live) setEnabled(value.enabled);
@@ -363,7 +646,7 @@ function SessionPanel({ sessionId, t }) {
363
646
  if (live()) setStatus("down");
364
647
  });
365
648
  };
366
- (0, import_react.useEffect)(() => {
649
+ (0, import_react2.useEffect)(() => {
367
650
  if (!open) return;
368
651
  const element = dialog.current;
369
652
  if (!element) return;
@@ -379,34 +662,14 @@ function SessionPanel({ sessionId, t }) {
379
662
  }).catch(() => {
380
663
  });
381
664
  void request(`logs?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
382
- if (alive) {
383
- setAnalysis(value);
384
- setRatings(value.ratings ?? {});
385
- }
665
+ if (alive) seed(value);
386
666
  }).catch(() => {
387
667
  });
388
668
  return () => {
389
669
  alive = false;
390
670
  element.close();
391
671
  };
392
- }, [open, t, sessionId]);
393
- const annotate = async (id, rating) => {
394
- setRatings((prev) => ({ ...prev, [id]: rating }));
395
- try {
396
- await request("annotate", {
397
- method: "POST",
398
- headers: { "content-type": "application/json" },
399
- body: JSON.stringify({ sessionId, target: id, rating })
400
- });
401
- } catch {
402
- setMessage(t("annotateFailed"));
403
- setRatings((prev) => {
404
- const next = { ...prev };
405
- delete next[id];
406
- return next;
407
- });
408
- }
409
- };
672
+ }, [open, t, sessionId, seed]);
410
673
  const updateEnabled = async (next) => {
411
674
  setBusy(true);
412
675
  try {
@@ -422,192 +685,7 @@ function SessionPanel({ sessionId, t }) {
422
685
  setBusy(false);
423
686
  }
424
687
  };
425
- const time = (at) => new Date(at).toLocaleTimeString();
426
- const reasonText = (reason) => reason === "unavailable" || reason === "unreachable" ? t("reasonUnavailable") : reason === "http-error" ? t("reasonHttpError") : reason === "invalid-response" ? t("reasonInvalidResponse") : reason === "low-confidence" ? t("reasonLowConfidence") : reason === "too-many-candidates" ? t("reasonTooMany") : reason === "low-keep" ? t("reasonLowKeep") : reason === "config" ? t("reasonConfig") : reason;
427
- const actionText = (action) => action === "deny" ? t("actionDeny") : action === "ask" ? t("actionAsk") : action === "pass" ? t("actionPass") : action;
428
- const failDetail = (entry) => {
429
- const reason = reasonText(entry.reason);
430
- if (entry.reason === "low-confidence" && typeof entry.confidence === "number") return `${reason}\uFF08${t("logConfidence")} ${entry.confidence}\uFF09`;
431
- return reason;
432
- };
433
- const outcomeTag = (entry) => entry.outcome === "deny" ? "deny" : entry.outcome === "error" ? "error" : entry.outcome === "low" ? "warn" : "ok";
434
- const renderGate = (entry) => {
435
- const tool = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "decision-log-tool", children: entry.tool ?? "\u2014" });
436
- if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
437
- t("logGate"),
438
- " \xB7 ",
439
- tool,
440
- " ",
441
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
442
- t("gatePassThrough"),
443
- entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
444
- ] })
445
- ] });
446
- if (entry.outcome === "deny") {
447
- const suggestion2 = entry.suggestion && entry.suggestion !== "deny" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
448
- " \xB7 ",
449
- t("logSuggested"),
450
- " ",
451
- actionText(entry.suggestion)
452
- ] }) : null;
453
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
454
- t("logGate"),
455
- " \xB7 ",
456
- tool,
457
- " ",
458
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
459
- "\u2192 ",
460
- t("actionDeny")
461
- ] }),
462
- suggestion2
463
- ] });
464
- }
465
- const suggestion = entry.suggestion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
466
- " \xB7 ",
467
- t("logSuggested"),
468
- " ",
469
- actionText(entry.suggestion)
470
- ] }) : null;
471
- const lowConf = entry.reason === "low-confidence" && typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
472
- " \xB7 ",
473
- t("reasonLowConfidence"),
474
- " ",
475
- entry.confidence
476
- ] }) : null;
477
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
478
- t("logGate"),
479
- " \xB7 ",
480
- tool,
481
- " ",
482
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("gatePassThrough") }),
483
- suggestion,
484
- lowConf
485
- ] });
486
- };
487
- const renderCheck = (entry) => {
488
- const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") });
489
- if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
490
- label,
491
- " ",
492
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
493
- t("logEvalFailed"),
494
- entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
495
- ] })
496
- ] });
497
- const result = entry.outcome === "low" ? t("checkResultLow") : t("checkResultOk");
498
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
499
- label,
500
- " \xB7 ",
501
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
502
- entry.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", title: t("logScoreHint"), children: [
503
- " \xB7 ",
504
- t("logScore"),
505
- " ",
506
- entry.score,
507
- "/2"
508
- ] }) : null,
509
- typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
510
- " \xB7 ",
511
- t("logConfidence"),
512
- " ",
513
- entry.confidence
514
- ] }) : null
515
- ] });
516
- };
517
- const renderNarrow = (entry) => {
518
- const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logNarrowHint"), children: t("logNarrow") });
519
- if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
520
- label,
521
- " ",
522
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
523
- t("logEvalFailed"),
524
- entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
525
- ] })
526
- ] });
527
- const dropped = entry.dropped ?? 0;
528
- const kept = typeof entry.kept === "number" ? entry.kept : void 0;
529
- const guardText = entry.reason === "too-many-candidates" ? typeof entry.candidates === "number" ? t("reasonTooManyCount", { count: entry.candidates }) : t("reasonTooMany") : entry.reason === "low-keep" ? t("reasonLowKeep") : void 0;
530
- const guard = guardText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
531
- " \xB7 ",
532
- guardText
533
- ] }) : null;
534
- const modeTag = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
535
- " \xB7 ",
536
- entry.mode === "enforce" ? t("narrowEnforce") : t("narrowObserve")
537
- ] });
538
- if (dropped === 0) {
539
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
540
- label,
541
- " \xB7 ",
542
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("narrowKept") }),
543
- kept !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
544
- " \xB7 ",
545
- t("narrowKeptCount"),
546
- " ",
547
- kept
548
- ] }) : null,
549
- guard,
550
- modeTag
551
- ] });
552
- }
553
- const names = Array.isArray(entry.tools) && entry.tools.length > 0 ? entry.tools.join("\u3001") : void 0;
554
- const overflow = names && Array.isArray(entry.tools) && entry.tools.length < dropped ? t("narrowMore", { count: dropped }) : "";
555
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
556
- label,
557
- " \xB7 ",
558
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
559
- t("narrowDropped"),
560
- " ",
561
- dropped,
562
- kept !== void 0 ? ` / ${t("narrowKeptCount")} ${kept}` : ""
563
- ] }),
564
- names ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
565
- " \xB7 ",
566
- names,
567
- overflow
568
- ] }) : null,
569
- guard,
570
- modeTag
571
- ] });
572
- };
573
- const renderComplete = (entry) => {
574
- const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("logComplete") });
575
- if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
576
- label,
577
- " ",
578
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
579
- t("logEvalFailed"),
580
- entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
581
- ] })
582
- ] });
583
- const result = entry.outcome === "unsatisfied" ? t("completeResultUnsatisfied") : t("completeResultOk");
584
- const parts = [];
585
- if (typeof entry.satisfied === "number") parts.push(`${t("completeSatisfied")} ${entry.satisfied}`);
586
- if (typeof entry.unsatisfied === "number" && entry.unsatisfied > 0) parts.push(`${t("completeUnsatisfied")} ${entry.unsatisfied}`);
587
- if (typeof entry.insufficient === "number" && entry.insufficient > 0) parts.push(`${t("completeInsufficient")} ${entry.insufficient}`);
588
- const tally = parts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
589
- " \xB7 ",
590
- parts.join(" / ")
591
- ] }) : null;
592
- const steer = entry.steered ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
593
- " \xB7 ",
594
- t("completeSteered")
595
- ] }) : null;
596
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
597
- label,
598
- " \xB7 ",
599
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
600
- typeof entry.conditions === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
601
- " \xB7 ",
602
- t("completeConditions"),
603
- " ",
604
- entry.conditions
605
- ] }) : null,
606
- tally,
607
- steer
608
- ] });
609
- };
610
- const outcomeTagOf = (entry) => entry.kind === "narrow" ? entry.outcome === "error" ? "error" : entry.outcome === "applied" ? "ok" : "ok" : entry.kind === "complete" ? entry.outcome === "error" ? "error" : entry.outcome === "unsatisfied" ? "warn" : "ok" : outcomeTag(entry);
688
+ const time2 = (at) => new Date(at).toLocaleTimeString();
611
689
  const statusLabel = status === "ok" ? t("statusOk") : status === "down" ? t("statusDown") : status === "checking" ? t("statusChecking") : t("statusIdle");
612
690
  const attempts = metrics?.attempts ?? 0;
613
691
  const severity = metrics?.check?.trend?.severity ?? "normal";
@@ -616,19 +694,19 @@ function SessionPanel({ sessionId, t }) {
616
694
  const checkPassRate = metrics?.check && checkValid > 0 ? Math.round((checkValid - metrics.check.low) / checkValid * 100) : null;
617
695
  const narrowValid = metrics?.narrow ? metrics.narrow.attempts - metrics.narrow.failures : 0;
618
696
  const narrowApplyRate = metrics?.narrow && narrowValid > 0 ? Math.round(metrics.narrow.applied / narrowValid * 100) : null;
619
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
620
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { ref: trigger, className: `decision-trigger decision-trend-${severity}`, type: "button", "aria-haspopup": "dialog", "aria-expanded": open, "aria-busy": enabled === null, "aria-label": t("button"), title: trendTitle, onClick: () => setOpen(true), children: [
621
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "decision-trigger-icon", width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
622
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6.4", stroke: "currentColor", "stroke-width": "1.3" }),
623
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 4.6 11.3 11H4.7L8 4.6Z", fill: "currentColor", stroke: "none" })
697
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
698
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { ref: trigger, className: `decision-trigger decision-trend-${severity}`, type: "button", "aria-haspopup": "dialog", "aria-expanded": open, "aria-busy": enabled === null, "aria-label": t("button"), title: trendTitle, onClick: () => setOpen(true), children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "decision-trigger-icon", width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
700
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6.4", stroke: "currentColor", "stroke-width": "1.3" }),
701
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 4.6 11.3 11H4.7L8 4.6Z", fill: "currentColor", stroke: "none" })
624
702
  ] }),
625
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-trigger-text", children: [
703
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-trigger-text", children: [
626
704
  t("triggerDecisions"),
627
705
  " ",
628
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: attempts })
706
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: attempts })
629
707
  ] })
630
708
  ] }),
631
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
709
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
632
710
  "dialog",
633
711
  {
634
712
  ref: dialog,
@@ -653,19 +731,19 @@ function SessionPanel({ sessionId, t }) {
653
731
  trigger.current?.focus();
654
732
  },
655
733
  children: [
656
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-head", children: [
657
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "decision-panel-title", children: t("title") }),
658
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => {
734
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-head", children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { id: "decision-panel-title", children: t("title") }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: () => {
659
737
  dialog.current?.close();
660
738
  }, "aria-label": t("close"), children: "\xD7" })
661
739
  ] }),
662
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-body", children: [
663
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
664
- enabled === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", children: t("loading") }),
665
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "decision-toggle", children: [
666
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-toggle-text", children: t("enabled") }),
667
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-toggle-box", children: [
668
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
740
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-body", children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
742
+ enabled === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", children: t("loading") }),
743
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-toggle", children: [
744
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-text", children: t("enabled") }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-toggle-box", children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
669
747
  "input",
670
748
  {
671
749
  className: "decision-toggle-input",
@@ -675,170 +753,160 @@ function SessionPanel({ sessionId, t }) {
675
753
  onChange: (event) => void updateEnabled(event.target.checked)
676
754
  }
677
755
  ),
678
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
756
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
679
757
  ] })
680
758
  ] }),
681
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-status", children: [
682
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `decision-status-dot decision-status-${status}`, "aria-hidden": "true" }),
683
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-status-label", children: [
759
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-status", children: [
760
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `decision-status-dot decision-status-${status}`, "aria-hidden": "true" }),
761
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-status-label", children: [
684
762
  t("backendStatus"),
685
763
  "\uFF1A",
686
764
  statusLabel
687
765
  ] }),
688
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "decision-status-recheck", disabled: status === "checking", onClick: () => checkStatus(() => true), children: t("recheck") })
766
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "decision-status-recheck", disabled: status === "checking", onClick: () => checkStatus(() => true), children: t("recheck") })
689
767
  ] })
690
768
  ] }),
691
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
692
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("metrics") }),
693
- !metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-cards", children: [
694
- metrics.gate && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "decision-card decision-card-wide", children: [
695
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "decision-card-head", children: [
696
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
697
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-card-total", children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("metrics") }),
771
+ !metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-cards", children: [
772
+ metrics.gate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card decision-card-wide", children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
698
776
  metrics.gate.attempts,
699
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t("cardTimes") })
777
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
700
778
  ] })
701
779
  ] }),
702
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bar, { allow: metrics.gate.allow, ask: metrics.gate.ask, deny: metrics.gate.deny }),
703
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ul", { className: "decision-legend", children: [
704
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
705
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-legend-dot decision-dot-allow", "aria-hidden": "true" }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Bar, { allow: metrics.gate.allow, ask: metrics.gate.ask, deny: metrics.gate.deny }),
781
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "decision-legend", children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
783
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-allow", "aria-hidden": "true" }),
706
784
  t("allow"),
707
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: metrics.gate.allow })
785
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.allow })
708
786
  ] }),
709
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
710
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-legend-dot decision-dot-ask", "aria-hidden": "true" }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
788
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-ask", "aria-hidden": "true" }),
711
789
  t("ask"),
712
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: metrics.gate.ask })
790
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.ask })
713
791
  ] }),
714
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
715
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-legend-dot decision-dot-deny", "aria-hidden": "true" }),
792
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-deny", "aria-hidden": "true" }),
716
794
  t("deny"),
717
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: metrics.gate.deny })
795
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.deny })
718
796
  ] })
719
797
  ] }),
720
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("footer", { className: "decision-card-foot", children: [
721
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("footer", { className: "decision-card-foot", children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
722
800
  t("actualShort"),
723
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("b", { children: [
801
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("b", { children: [
724
802
  metrics.gate.actual.allow,
725
803
  " / ",
726
804
  metrics.gate.actual.deny
727
805
  ] })
728
806
  ] }),
729
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
807
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
730
808
  t("failures"),
731
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: metrics.gate.failures })
809
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.failures })
732
810
  ] })
733
811
  ] })
734
812
  ] }),
735
- metrics.check && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "decision-card", children: [
736
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "decision-card-head", children: [
737
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
738
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-card-total", children: [
739
- metrics.check.attempts,
740
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t("cardTimes") })
813
+ metrics.check && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
814
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
816
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-head-metrics", children: [
817
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-rate-inline", children: [
818
+ t("checkPassRate"),
819
+ " ",
820
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: checkPassRate === null ? t("rateNa") : `${checkPassRate}%` })
821
+ ] }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
823
+ metrics.check.attempts,
824
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
825
+ ] })
741
826
  ] })
742
827
  ] }),
743
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-card-stats", children: [
744
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
745
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.check.low }),
746
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("checkLow") })
828
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
829
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
830
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.check.low }),
831
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("checkLow") })
747
832
  ] }),
748
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
749
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num", children: metrics.check.attempts - metrics.check.low - metrics.check.failures }),
750
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("checkOk") })
833
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.check.attempts - metrics.check.low - metrics.check.failures }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("checkOk") })
751
836
  ] }),
752
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
753
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.check.failures }),
754
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("failures") })
837
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.check.failures }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("failures") })
755
840
  ] })
756
- ] }),
757
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
758
- t("checkPassRate"),
759
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: checkPassRate === null ? t("rateNa") : `${checkPassRate}%` })
760
- ] }) })
841
+ ] })
761
842
  ] }),
762
- metrics.narrow && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "decision-card", children: [
763
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "decision-card-head", children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
765
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-card-total", children: [
766
- metrics.narrow.attempts,
767
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t("cardTimes") })
843
+ metrics.narrow && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
844
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-head-metrics", children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-rate-inline", children: [
848
+ t("narrowApplyRate"),
849
+ " ",
850
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: narrowApplyRate === null ? t("rateNa") : `${narrowApplyRate}%` })
851
+ ] }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
853
+ metrics.narrow.attempts,
854
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
855
+ ] })
768
856
  ] })
769
857
  ] }),
770
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-card-stats", children: [
771
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
772
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.applied }),
773
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("narrowAppliedLabel") })
858
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
859
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
860
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.applied }),
861
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("narrowAppliedLabel") })
774
862
  ] }),
775
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
776
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.dropped }),
777
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("narrowDroppedLabel") })
863
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
864
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.dropped }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("narrowDroppedLabel") })
778
866
  ] }),
779
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
780
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.narrow.failures }),
781
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("failures") })
867
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
868
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.narrow.failures }),
869
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("failures") })
782
870
  ] })
783
- ] }),
784
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
785
- t("narrowApplyRate"),
786
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: narrowApplyRate === null ? t("rateNa") : `${narrowApplyRate}%` })
787
- ] }) })
871
+ ] })
788
872
  ] }),
789
- metrics.complete && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "decision-card", children: [
790
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "decision-card-head", children: [
791
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
792
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-card-total", children: [
793
- metrics.complete.attempts,
794
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t("cardTimes") })
873
+ metrics.complete && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-head-metrics", children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-rate-inline", children: [
878
+ t("failures"),
879
+ " ",
880
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.complete.failures })
881
+ ] }),
882
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
883
+ metrics.complete.attempts,
884
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
885
+ ] })
795
886
  ] })
796
887
  ] }),
797
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-card-stats", children: [
798
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
799
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num", children: metrics.complete.satisfied }),
800
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("completeSatisfied") })
888
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.complete.satisfied }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeSatisfied") })
801
892
  ] }),
802
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
803
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.complete.unsatisfied }),
804
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("completeUnsatisfied") })
893
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.complete.unsatisfied }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeUnsatisfied") })
805
896
  ] }),
806
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-stat", children: [
807
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.complete.insufficient }),
808
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-stat-label", children: t("completeInsufficient") })
897
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
898
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.complete.insufficient }),
899
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeInsufficient") })
809
900
  ] })
810
- ] }),
811
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
812
- t("failures"),
813
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: metrics.complete.failures })
814
- ] }) })
901
+ ] })
815
902
  ] })
816
903
  ] })
817
904
  ] }),
818
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("log") }),
820
- log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "decision-log", children: log.slice().reverse().map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: `decision-log-item decision-log-${outcomeTagOf(entry)}`, children: [
821
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-time", children: time(entry.at) }),
822
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-body", children: entry.kind === "gate" ? renderGate(entry) : entry.kind === "narrow" ? renderNarrow(entry) : entry.kind === "complete" ? renderComplete(entry) : renderCheck(entry) }),
823
- entry.id && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-rate", role: "group", "aria-label": t("rateGroup"), children: [
824
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[entry.id] === "good" ? " decision-rate-on decision-rate-good" : ""}`, "aria-label": t("rateGood"), title: t("rateGood"), "aria-pressed": ratings[entry.id] === "good", onClick: () => void annotate(entry.id, "good"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
825
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7.2 8.1 2.4c.6-.9 2-.5 2 .6V6h3.2c.9 0 1.5.8 1.3 1.6l-1.1 4.6c-.2.8-.9 1.3-1.7 1.3H5V7.2Z", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" }),
826
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7.2H2.6v6.3H5", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" })
827
- ] }) }),
828
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[entry.id] === "bad" ? " decision-rate-on decision-rate-bad" : ""}`, "aria-label": t("rateBad"), title: t("rateBad"), "aria-pressed": ratings[entry.id] === "bad", onClick: () => void annotate(entry.id, "bad"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
829
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8.8 7.9 13.6c-.6.9-2 .5-2-.6V10H2.7c-.9 0-1.5-.8-1.3-1.6l1.1-4.6C2.7 3 3.4 2.5 4.2 2.5H11v6.3Z", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" }),
830
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8.8h2.4V2.5H11", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" })
831
- ] }) }),
832
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[entry.id] === "unsure" ? " decision-rate-on decision-rate-unsure" : ""}`, "aria-label": t("rateUnsure"), title: t("rateUnsure"), "aria-pressed": ratings[entry.id] === "unsure", onClick: () => void annotate(entry.id, "unsure"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
833
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6.2", stroke: "currentColor", "stroke-width": "1.2" }),
834
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.3 6.2c0-1 .8-1.7 1.7-1.7s1.7.7 1.7 1.6c0 1.3-1.6 1.4-1.7 2.6", stroke: "currentColor", "stroke-width": "1.2", "stroke-linecap": "round", "stroke-linejoin": "round" }),
835
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "11.4", r: ".8", fill: "currentColor" })
836
- ] }) })
837
- ] })
838
- ] }, entry.id ?? `${entry.at}-${index}`)) }),
839
- analysis && analysis.annotations.rated > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: analysis.annotations.rated, good: analysis.annotations.good, bad: analysis.annotations.bad, unsure: analysis.annotations.unsure }) })
905
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("log") }),
907
+ log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t })
840
908
  ] }),
841
- message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
909
+ message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
842
910
  ] })
843
911
  ]
844
912
  }
@@ -846,12 +914,150 @@ function SessionPanel({ sessionId, t }) {
846
914
  ] });
847
915
  }
848
916
 
917
+ // src/client/analysis-view.tsx
918
+ var import_react3 = require("react");
919
+ var import_jsx_runtime3 = require("react/jsx-runtime");
920
+ function Stat({ label, value, pct, tone }) {
921
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-stat", children: [
922
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `decision-stat-num${tone ? ` decision-stat-${tone}` : ""}`, children: value }),
923
+ typeof pct === "number" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-stat-pct", children: [
924
+ pct,
925
+ "%"
926
+ ] }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-stat-label", children: label })
928
+ ] });
929
+ }
930
+ var share = (n, total) => total > 0 ? Math.round(n / total * 100) : null;
931
+ function AnalysisView({ sessionId, t }) {
932
+ const [analysis, setAnalysis] = (0, import_react3.useState)(null);
933
+ const [log, setLog] = (0, import_react3.useState)([]);
934
+ const [loaded, setLoaded] = (0, import_react3.useState)(false);
935
+ const [message, setMessage] = (0, import_react3.useState)("");
936
+ const { ratings, seed, annotate } = useAnnotations(sessionId, () => setMessage(t("annotateFailed")));
937
+ (0, import_react3.useEffect)(() => {
938
+ let alive = true;
939
+ setMessage("");
940
+ setLoaded(false);
941
+ void Promise.allSettled([
942
+ request(`logs?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
943
+ if (alive) {
944
+ setAnalysis(value);
945
+ seed(value);
946
+ }
947
+ }),
948
+ request(`log?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
949
+ if (alive) setLog(value.entries);
950
+ })
951
+ ]).finally(() => {
952
+ if (alive) setLoaded(true);
953
+ });
954
+ return () => {
955
+ alive = false;
956
+ };
957
+ }, [sessionId, seed]);
958
+ const profile = analysis?.profile;
959
+ const backtest = analysis?.trendBacktest;
960
+ const annotations = analysis?.annotations;
961
+ const meanScore = profile && profile.check.scoreCount > 0 ? Math.round(profile.check.scoreSum / profile.check.scoreCount * 10) / 10 : null;
962
+ const completeTotal = profile ? profile.complete.satisfied + profile.complete.unsatisfied + profile.complete.insufficient : 0;
963
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "decision-analysis", role: "region", "aria-label": t("analysisTitle"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-analysis-inner", children: [
964
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "decision-analysis-title", children: t("analysisTitle") }),
965
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-intro", children: t("analysisIntro") }),
966
+ loaded && (!analysis || analysis.totalDecisions === 0) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
967
+ profile && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
968
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisProfile") }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-analysis-cards", children: [
970
+ profile.gate.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
971
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
972
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
973
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
974
+ profile.gate.attempts,
975
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
976
+ ] })
977
+ ] }),
978
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("deny"), value: profile.gate.deny, pct: share(profile.gate.deny, profile.gate.attempts), tone: "warn" }),
980
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("allow"), value: profile.gate.allow, pct: share(profile.gate.allow, profile.gate.attempts) }),
981
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("ask"), value: profile.gate.ask, pct: share(profile.gate.ask, profile.gate.attempts) }),
982
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.gate.error, pct: share(profile.gate.error, profile.gate.attempts), tone: "muted" })
983
+ ] })
984
+ ] }),
985
+ profile.check.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
986
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
987
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
988
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
989
+ profile.check.attempts,
990
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
991
+ ] })
992
+ ] }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
994
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkLow"), value: profile.check.low, pct: share(profile.check.low, profile.check.attempts), tone: "warn" }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkOk"), value: profile.check.ok, pct: share(profile.check.ok, profile.check.attempts) }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisMeanScore"), value: meanScore === null ? t("rateNa") : `${meanScore}/2` }),
997
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.check.error, pct: share(profile.check.error, profile.check.attempts), tone: "muted" })
998
+ ] })
999
+ ] }),
1000
+ profile.narrow.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
1002
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
1003
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
1004
+ profile.narrow.attempts,
1005
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
1006
+ ] })
1007
+ ] }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
1009
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowAppliedLabel"), value: profile.narrow.applied, pct: share(profile.narrow.applied, profile.narrow.attempts) }),
1010
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowDroppedLabel"), value: profile.narrow.droppedSum }),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.narrow.error, pct: share(profile.narrow.error, profile.narrow.attempts), tone: "muted" })
1012
+ ] })
1013
+ ] }),
1014
+ profile.complete.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
1015
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
1016
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
1018
+ profile.complete.attempts,
1019
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
1020
+ ] })
1021
+ ] }),
1022
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
1023
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeSatisfied"), value: profile.complete.satisfied, pct: share(profile.complete.satisfied, completeTotal) }),
1024
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeUnsatisfied"), value: profile.complete.unsatisfied, pct: share(profile.complete.unsatisfied, completeTotal), tone: "warn" }),
1025
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeInsufficient"), value: profile.complete.insufficient, pct: share(profile.complete.insufficient, completeTotal), tone: "muted" })
1026
+ ] })
1027
+ ] })
1028
+ ] })
1029
+ ] }),
1030
+ backtest && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
1031
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisTrend") }),
1032
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats decision-analysis-trend", children: [
1033
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendWarn"), value: backtest.warnHits }),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendSevere"), value: backtest.severeHits, tone: "warn" }),
1035
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendMaxRun"), value: backtest.maxRun })
1036
+ ] }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisTrendNote") })
1038
+ ] }),
1039
+ annotations && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
1040
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisAccuracy") }),
1041
+ annotations.rated === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted", children: t("analysisAccuracyEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: annotations.rated, good: annotations.good, bad: annotations.bad, unsure: annotations.unsure }) })
1042
+ ] }),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
1044
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("log") }),
1045
+ log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("analysisLogTruncated") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1046
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisLogNote") })
1048
+ ] })
1049
+ ] })
1050
+ ] }),
1051
+ message && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
1052
+ ] }) });
1053
+ }
1054
+
849
1055
  // src/client/config-form.tsx
850
- var import_react2 = require("react");
851
- var import_jsx_runtime2 = require("react/jsx-runtime");
852
- var api2 = "/plugins/dsh-decision-layer/api";
1056
+ var import_react4 = require("react");
1057
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1058
+ var api = "/plugins/dsh-decision-layer/api";
853
1059
  async function request2(path, fetchFn, init) {
854
- const response = await fetchFn(`${api2}/${path}`, init);
1060
+ const response = await fetchFn(`${api}/${path}`, init);
855
1061
  const body = await response.json();
856
1062
  if (!response.ok || !body.ok || body.value === void 0) throw new Error(body.error || "Request failed");
857
1063
  return body.value;
@@ -865,31 +1071,31 @@ var maxCandidatesOf = (settings) => settings?.maxCandidates ?? DEFAULT_MAX_CANDI
865
1071
  var thresholdOf = (settings) => settings?.threshold ?? DEFAULT_THRESHOLD;
866
1072
  function HeadIcon({ name }) {
867
1073
  const glyphs = {
868
- features: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
869
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "3", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
870
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11.9", cy: "5.1", r: "1.25", fill: "currentColor" }),
871
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "8.8", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
872
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "4.1", cy: "10.9", r: "1.25", fill: "currentColor" })
1074
+ features: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1075
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2", y: "3", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
1076
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "11.9", cy: "5.1", r: "1.25", fill: "currentColor" }),
1077
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2", y: "8.8", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
1078
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "4.1", cy: "10.9", r: "1.25", fill: "currentColor" })
873
1079
  ] }),
874
- advanced: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
875
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.5 5h11M2.5 11h11", stroke: "currentColor", "stroke-width": "1.3", "stroke-linecap": "round" }),
876
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "10.5", cy: "5", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" }),
877
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5.5", cy: "11", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" })
1080
+ advanced: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1081
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M2.5 5h11M2.5 11h11", stroke: "currentColor", "stroke-width": "1.3", "stroke-linecap": "round" }),
1082
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "10.5", cy: "5", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" }),
1083
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5.5", cy: "11", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" })
878
1084
  ] }),
879
- backend: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
880
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.3", y: "2.6", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
881
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.3", y: "8.8", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
882
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "4.9", r: ".7", fill: "currentColor" }),
883
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "11.1", r: ".7", fill: "currentColor" })
1085
+ backend: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1086
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2.3", y: "2.6", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2.3", y: "8.8", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
1088
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "4.9", r: ".7", fill: "currentColor" }),
1089
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "11.1", r: ".7", fill: "currentColor" })
884
1090
  ] })
885
1091
  };
886
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `decision-head-icon decision-head-icon-${name}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", children: glyphs[name] }) });
1092
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `decision-head-icon decision-head-icon-${name}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", children: glyphs[name] }) });
887
1093
  }
888
1094
  function Toggle({ label, checked, disabled, onChange }) {
889
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-toggle", children: [
890
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-text", children: label }),
891
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-toggle-box", children: [
892
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1095
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-toggle", children: [
1096
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-toggle-text", children: label }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "decision-toggle-box", children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
893
1099
  "input",
894
1100
  {
895
1101
  className: "decision-toggle-input",
@@ -899,29 +1105,29 @@ function Toggle({ label, checked, disabled, onChange }) {
899
1105
  onChange: (event) => onChange(event.target.checked)
900
1106
  }
901
1107
  ),
902
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
1108
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
903
1109
  ] })
904
1110
  ] });
905
1111
  }
906
1112
  function ConfigForm({ t, fetchFn = fetch }) {
907
- const [config, setConfig] = (0, import_react2.useState)({ url: "", model: "", apiKeySet: false, httpApprovedUrl: "", effectiveUrl: "https://api.typesafe.ai" });
908
- const [checkMode, setCheckMode] = (0, import_react2.useState)("observe");
909
- const [narrowMode, setNarrowMode] = (0, import_react2.useState)("enforce");
910
- const [keepPrefixes, setKeepPrefixes] = (0, import_react2.useState)(DEFAULT_KEEP_PREFIXES.join("\n"));
911
- const [maxCandidates, setMaxCandidates] = (0, import_react2.useState)(String(DEFAULT_MAX_CANDIDATES));
912
- const [threshold, setThreshold] = (0, import_react2.useState)(String(DEFAULT_THRESHOLD));
913
- const [gateOn, setGateOn] = (0, import_react2.useState)(true);
914
- const [checkOn, setCheckOn] = (0, import_react2.useState)(true);
915
- const [narrowOn, setNarrowOn] = (0, import_react2.useState)(true);
916
- const [completeOn, setCompleteOn] = (0, import_react2.useState)(true);
917
- const [completeMode, setCompleteMode] = (0, import_react2.useState)("observe");
918
- const [key, setKey] = (0, import_react2.useState)("");
919
- const [busy, setBusy] = (0, import_react2.useState)(false);
920
- const [advancedDirty, setAdvancedDirty] = (0, import_react2.useState)(false);
921
- const [backendDirty, setBackendDirty] = (0, import_react2.useState)(false);
922
- const [ready, setReady] = (0, import_react2.useState)(false);
923
- const [message, setMessage] = (0, import_react2.useState)(null);
924
- const [probeResult, setProbeResult] = (0, import_react2.useState)(null);
1113
+ const [config, setConfig] = (0, import_react4.useState)({ url: "", model: "", apiKeySet: false, httpApprovedUrl: "", effectiveUrl: "https://api.typesafe.ai" });
1114
+ const [checkMode, setCheckMode] = (0, import_react4.useState)("observe");
1115
+ const [narrowMode, setNarrowMode] = (0, import_react4.useState)("enforce");
1116
+ const [keepPrefixes, setKeepPrefixes] = (0, import_react4.useState)(DEFAULT_KEEP_PREFIXES.join("\n"));
1117
+ const [maxCandidates, setMaxCandidates] = (0, import_react4.useState)(String(DEFAULT_MAX_CANDIDATES));
1118
+ const [threshold, setThreshold] = (0, import_react4.useState)(String(DEFAULT_THRESHOLD));
1119
+ const [gateOn, setGateOn] = (0, import_react4.useState)(true);
1120
+ const [checkOn, setCheckOn] = (0, import_react4.useState)(true);
1121
+ const [narrowOn, setNarrowOn] = (0, import_react4.useState)(true);
1122
+ const [completeOn, setCompleteOn] = (0, import_react4.useState)(true);
1123
+ const [completeMode, setCompleteMode] = (0, import_react4.useState)("observe");
1124
+ const [key, setKey] = (0, import_react4.useState)("");
1125
+ const [busy, setBusy] = (0, import_react4.useState)(false);
1126
+ const [advancedDirty, setAdvancedDirty] = (0, import_react4.useState)(false);
1127
+ const [backendDirty, setBackendDirty] = (0, import_react4.useState)(false);
1128
+ const [ready, setReady] = (0, import_react4.useState)(false);
1129
+ const [message, setMessage] = (0, import_react4.useState)(null);
1130
+ const [probeResult, setProbeResult] = (0, import_react4.useState)(null);
925
1131
  const applyFeatures = (features) => {
926
1132
  setGateOn(features?.gate !== false);
927
1133
  setCheckOn(features?.check !== false);
@@ -938,7 +1144,7 @@ function ConfigForm({ t, fetchFn = fetch }) {
938
1144
  setThreshold(String(thresholdOf(value.narrowSettings)));
939
1145
  applyFeatures(value.features);
940
1146
  };
941
- (0, import_react2.useEffect)(() => {
1147
+ (0, import_react4.useEffect)(() => {
942
1148
  let live = true;
943
1149
  setMessage(null);
944
1150
  setReady(false);
@@ -1041,17 +1247,17 @@ ${url}`)) return;
1041
1247
  setBusy(false);
1042
1248
  }
1043
1249
  };
1044
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-config", children: [
1045
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: "decision-collapse", open: true, children: [
1046
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("summary", { className: "decision-collapse-head", children: [
1047
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { children: [
1048
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HeadIcon, { name: "features" }),
1250
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-config", children: [
1251
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "decision-collapse", open: true, children: [
1252
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "decision-collapse-head", children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
1254
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "features" }),
1049
1255
  t("basicFeatures")
1050
1256
  ] }),
1051
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1257
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1052
1258
  ] }),
1053
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-collapse-body", children: [
1054
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1259
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-collapse-body", children: [
1260
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1055
1261
  Toggle,
1056
1262
  {
1057
1263
  label: t("featureGate"),
@@ -1063,8 +1269,8 @@ ${url}`)) return;
1063
1269
  }
1064
1270
  }
1065
1271
  ),
1066
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("featureGateHint") }),
1067
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1272
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureGateHint") }),
1273
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1068
1274
  Toggle,
1069
1275
  {
1070
1276
  label: t("featureCheck"),
@@ -1076,8 +1282,8 @@ ${url}`)) return;
1076
1282
  }
1077
1283
  }
1078
1284
  ),
1079
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("featureCheckHint") }),
1080
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1285
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureCheckHint") }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1081
1287
  Toggle,
1082
1288
  {
1083
1289
  label: t("featureNarrow"),
@@ -1089,8 +1295,8 @@ ${url}`)) return;
1089
1295
  }
1090
1296
  }
1091
1297
  ),
1092
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("featureNarrowHint") }),
1093
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1298
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureNarrowHint") }),
1299
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1094
1300
  Toggle,
1095
1301
  {
1096
1302
  label: t("featureComplete"),
@@ -1102,22 +1308,22 @@ ${url}`)) return;
1102
1308
  }
1103
1309
  }
1104
1310
  ),
1105
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("featureCompleteHint") })
1311
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureCompleteHint") })
1106
1312
  ] })
1107
1313
  ] }),
1108
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: "decision-collapse", children: [
1109
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("summary", { className: "decision-collapse-head", children: [
1110
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { children: [
1111
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HeadIcon, { name: "advanced" }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "decision-collapse", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "decision-collapse-head", children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
1317
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "advanced" }),
1112
1318
  t("advancedConfig")
1113
1319
  ] }),
1114
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1320
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
1115
1321
  ] }),
1116
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { className: "decision-collapse-body", onSubmit: (event) => void saveAdvanced(event), children: [
1117
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-advanced-grid", children: [
1118
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-field-block", children: [
1119
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-subhead", children: t("advancedSwitches") }),
1120
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1322
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "decision-collapse-body", onSubmit: (event) => void saveAdvanced(event), children: [
1323
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-advanced-grid", children: [
1324
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-field-block", children: [
1325
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-subhead", children: t("advancedSwitches") }),
1326
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1121
1327
  Toggle,
1122
1328
  {
1123
1329
  label: t("selfCheckSteer"),
@@ -1129,8 +1335,8 @@ ${url}`)) return;
1129
1335
  }
1130
1336
  }
1131
1337
  ),
1132
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("selfCheckHint") }),
1133
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1338
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("selfCheckHint") }),
1339
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1134
1340
  Toggle,
1135
1341
  {
1136
1342
  label: t("narrowObserveLabel"),
@@ -1142,8 +1348,8 @@ ${url}`)) return;
1142
1348
  }
1143
1349
  }
1144
1350
  ),
1145
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("narrowHint") }),
1146
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1351
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("narrowHint") }),
1352
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1147
1353
  Toggle,
1148
1354
  {
1149
1355
  label: t("completeSteer"),
@@ -1155,13 +1361,13 @@ ${url}`)) return;
1155
1361
  }
1156
1362
  }
1157
1363
  ),
1158
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted", children: t("completeHint") })
1364
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("completeHint") })
1159
1365
  ] }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-field-block", children: [
1161
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-subhead", children: t("advancedParams") }),
1162
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-field", children: [
1366
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-field-block", children: [
1367
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-subhead", children: t("advancedParams") }),
1368
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1163
1369
  t("narrowKeepLabel"),
1164
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1370
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1165
1371
  "textarea",
1166
1372
  {
1167
1373
  className: "decision-prefixes",
@@ -1175,11 +1381,11 @@ ${url}`)) return;
1175
1381
  }
1176
1382
  }
1177
1383
  ),
1178
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { className: "decision-muted", children: t("narrowKeepHint") })
1384
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowKeepHint") })
1179
1385
  ] }),
1180
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1386
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1181
1387
  t("narrowMaxLabel"),
1182
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1388
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1183
1389
  "input",
1184
1390
  {
1185
1391
  className: "decision-narrow-cap",
@@ -1196,11 +1402,11 @@ ${url}`)) return;
1196
1402
  }
1197
1403
  }
1198
1404
  ),
1199
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { className: "decision-muted", children: t("narrowMaxHint") })
1405
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowMaxHint") })
1200
1406
  ] }),
1201
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1407
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field decision-field-inline", children: [
1202
1408
  t("narrowThresholdLabel"),
1203
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1409
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1204
1410
  "input",
1205
1411
  {
1206
1412
  className: "decision-narrow-cap",
@@ -1217,26 +1423,26 @@ ${url}`)) return;
1217
1423
  }
1218
1424
  }
1219
1425
  ),
1220
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { className: "decision-muted", children: t("narrowThresholdHint") })
1426
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowThresholdHint") })
1221
1427
  ] })
1222
1428
  ] })
1223
1429
  ] }),
1224
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "decision-actions", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { disabled: busy || !ready || !advancedDirty, type: "submit", children: t("saveAdvanced") }) })
1430
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "decision-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { disabled: busy || !ready || !advancedDirty, type: "submit", children: t("saveAdvanced") }) })
1225
1431
  ] })
1226
1432
  ] }),
1227
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
1228
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { children: [
1229
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HeadIcon, { name: "backend" }),
1433
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { children: [
1434
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "backend" }),
1230
1436
  t("backend")
1231
1437
  ] }),
1232
- ready && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "decision-muted decision-destination", children: [
1438
+ ready && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "decision-muted decision-destination", children: [
1233
1439
  t("destination"),
1234
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: config.effectiveUrl })
1440
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: config.effectiveUrl })
1235
1441
  ] }),
1236
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: (event) => void saveBackend(event), children: [
1237
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-field", children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { onSubmit: (event) => void saveBackend(event), children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1238
1444
  t("url"),
1239
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1240
1446
  "input",
1241
1447
  {
1242
1448
  type: "url",
@@ -1251,9 +1457,9 @@ ${url}`)) return;
1251
1457
  }
1252
1458
  )
1253
1459
  ] }),
1254
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-field", children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1255
1461
  t("model"),
1256
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1462
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1257
1463
  "input",
1258
1464
  {
1259
1465
  value: config.model,
@@ -1267,9 +1473,9 @@ ${url}`)) return;
1267
1473
  }
1268
1474
  )
1269
1475
  ] }),
1270
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-field", children: [
1476
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
1271
1477
  t("key"),
1272
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1478
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1273
1479
  "input",
1274
1480
  {
1275
1481
  type: "password",
@@ -1284,23 +1490,23 @@ ${url}`)) return;
1284
1490
  }
1285
1491
  }
1286
1492
  ),
1287
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { className: "decision-muted", children: config.apiKeySet ? t("keyHintSaved") : t("keyHintEmpty") })
1493
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: config.apiKeySet ? t("keyHintSaved") : t("keyHintEmpty") })
1288
1494
  ] }),
1289
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-actions", children: [
1290
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { disabled: busy || !ready, type: "submit", children: t("save") }),
1291
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "decision-secondary", disabled: busy || !ready || backendDirty, type: "button", onClick: () => void probe(), children: t("probe") }),
1292
- probeResult && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { role: "status", className: `decision-probe-result${probeResult.ok ? " decision-message-ok" : " decision-message-warn"}`, children: probeResult.text })
1495
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-actions", children: [
1496
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { disabled: busy || !ready, type: "submit", children: t("save") }),
1497
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "decision-secondary", disabled: busy || !ready || backendDirty, type: "button", onClick: () => void probe(), children: t("probe") }),
1498
+ probeResult && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { role: "status", className: `decision-probe-result${probeResult.ok ? " decision-message-ok" : " decision-message-warn"}`, children: probeResult.text })
1293
1499
  ] })
1294
1500
  ] }),
1295
- config.apiKeySet && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-danger-row", children: [
1296
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-danger-text", children: [
1297
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-danger-title", children: t("removeKey") }),
1298
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { className: "decision-muted", children: t("removeKeyHint") })
1501
+ config.apiKeySet && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-danger-row", children: [
1502
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-danger-text", children: [
1503
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-danger-title", children: t("removeKey") }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("removeKeyHint") })
1299
1505
  ] }),
1300
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "decision-danger-button", disabled: busy || !ready, onClick: () => void removeSavedKey(), children: t("removeKeyButton") })
1506
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "decision-danger-button", disabled: busy || !ready, onClick: () => void removeSavedKey(), children: t("removeKeyButton") })
1301
1507
  ] })
1302
1508
  ] }),
1303
- message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", className: `decision-message${message.ok ? " decision-message-ok" : " decision-message-warn"}`, children: message.text })
1509
+ message && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "status", className: `decision-message${message.ok ? " decision-message-ok" : " decision-message-warn"}`, children: message.text })
1304
1510
  ] });
1305
1511
  }
1306
1512
 
@@ -1342,6 +1548,9 @@ var css = `
1342
1548
  .decision-card-title{font-size:.8rem;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);letter-spacing:.02em}
1343
1549
  .decision-card-total{font-size:1.5rem;font-weight:700;line-height:1;color:var(--dsw-alias-label-primary,#202124);font-variant-numeric:tabular-nums}
1344
1550
  .decision-card-total small{font-size:.7rem;font-weight:500;color:var(--dsw-alias-label-tertiary,#77818b);margin-left:.15rem}
1551
+ .decision-card-head-metrics{display:flex;align-items:baseline;gap:.6rem}
1552
+ .decision-card-rate-inline{font-size:.76rem;color:var(--dsw-alias-label-secondary,#59636e);white-space:nowrap}
1553
+ .decision-card-rate-inline b{color:var(--dsw-alias-label-primary,#202124);font-variant-numeric:tabular-nums;margin-left:.2rem;font-weight:600}
1345
1554
  .decision-bar{display:flex;height:8px;border-radius:999px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,#eef1f3)}
1346
1555
  .decision-bar-empty{background:var(--dsw-alias-bg-layer-1,#eef1f3)}
1347
1556
  .decision-bar-allow{background:#1f8a70}
@@ -1360,6 +1569,7 @@ var css = `
1360
1569
  .decision-card-stats{display:flex;gap:.6rem}
1361
1570
  .decision-stat{flex:1 1 0;display:flex;flex-direction:column;align-items:center;gap:.2rem;padding:.5rem .3rem;border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f6f8fa)}
1362
1571
  .decision-stat-num{font-size:1.35rem;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary,#202124)}
1572
+ .decision-stat-pct{font-size:.74rem;font-weight:600;line-height:1;color:var(--dsw-alias-label-tertiary,#77818b);font-variant-numeric:tabular-nums}
1363
1573
  .decision-stat-warn{color:#c98a1e}
1364
1574
  .decision-stat-muted{color:var(--dsw-alias-label-tertiary,#9aa5b1)}
1365
1575
  .decision-stat-label{font-size:.72rem;color:var(--dsw-alias-label-secondary,#59636e)}
@@ -1467,6 +1677,22 @@ var css = `
1467
1677
  .decision-message{font-size:.82rem;margin:.2rem 0 0}
1468
1678
  .decision-message-ok{color:#1f6f57}
1469
1679
  .decision-message-warn{color:#9d4b1f}
1680
+ /* Conversation-view "Decision analysis" tab. It reuses the card / stat / log
1681
+ primitives above; this block only supplies the scroll container and section
1682
+ rhythm, since the tab is not inside the modal's .decision-body wrapper. */
1683
+ .decision-analysis{height:100%;overflow:auto;color:var(--dsw-alias-label-primary,#202124);font:inherit}
1684
+ .decision-analysis-inner{max-width:52rem;margin:0 auto;padding:1.4rem 1.5rem 3rem}
1685
+ .decision-analysis-title{font-size:1.15rem;margin:0 0 .3rem}
1686
+ .decision-analysis-intro{margin:0 0 .4rem}
1687
+ .decision-analysis-section{border-top:1px solid var(--dsw-alias-border-l2,#eceff1);margin-top:1.1rem;padding-top:1rem}
1688
+ .decision-analysis-section>h3{font-size:.92rem;margin:0 0 .6rem}
1689
+ .decision-analysis-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:.7rem}
1690
+ .decision-analysis-cards .decision-card-stats{flex-wrap:wrap}
1691
+ .decision-analysis-trend{max-width:26rem}
1692
+ .decision-analysis-note{margin:.5rem 0 0}
1693
+ @media (prefers-color-scheme:dark){
1694
+ .decision-analysis{color:var(--dsw-alias-label-primary,#e6eaee)}
1695
+ }
1470
1696
  `;
1471
1697
  function installStyles() {
1472
1698
  if (typeof document === "undefined" || document.querySelector('style[data-plugin="dsh-decision-layer"]')) return () => {
@@ -1479,9 +1705,10 @@ function installStyles() {
1479
1705
  }
1480
1706
 
1481
1707
  // src/client/index.tsx
1482
- var import_jsx_runtime3 = require("react/jsx-runtime");
1708
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1483
1709
  var inject = ["slots", "locale"];
1484
1710
  function apply(ctx) {
1711
+ const t = ctx.locale.bind("dsh-decision-layer");
1485
1712
  ctx.effect(installStyles);
1486
1713
  ctx.effect(() => ctx.locale.register("dsh-decision-layer", dictionaries));
1487
1714
  ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
@@ -1492,9 +1719,18 @@ function apply(ctx) {
1492
1719
  registrant: "dsh-decision-layer",
1493
1720
  inject: (sessionId) => ({ sessionId })
1494
1721
  }, DecisionPanel));
1722
+ ctx.slots.inject("conversation.view", () => ctx.slots.register({
1723
+ name: "conversation.view",
1724
+ id: "decision-layer-analysis",
1725
+ order: 40,
1726
+ locale: "dsh-decision-layer",
1727
+ registrant: "dsh-decision-layer",
1728
+ label: () => t("analysisTab"),
1729
+ inject: (sessionId) => ({ sessionId })
1730
+ }, AnalysisView));
1495
1731
  ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register(
1496
1732
  { name: "plugins.bundle.config", key: "dsh-decision-layer", locale: "dsh-decision-layer" },
1497
- (props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ConfigForm, { t: props.t })
1733
+ (props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConfigForm, { t: props.t })
1498
1734
  ));
1499
1735
  }
1500
1736