theokit 0.2.0 → 0.2.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.
Files changed (130) hide show
  1. package/dist/actions-virtual-module-E6PU47GV.js +175 -0
  2. package/dist/actions-virtual-module-E6PU47GV.js.map +1 -0
  3. package/dist/actions-virtual-module-E7IELVHL.js +174 -0
  4. package/dist/actions-virtual-module-E7IELVHL.js.map +1 -0
  5. package/dist/app-typed-client-RZMVWHJ3.js +289 -0
  6. package/dist/app-typed-client-RZMVWHJ3.js.map +1 -0
  7. package/dist/app-typed-client-YIBP6JWH.js +289 -0
  8. package/dist/app-typed-client-YIBP6JWH.js.map +1 -0
  9. package/dist/{aws-lambda-PONVF3UX.js → aws-lambda-XYCGZH3I.js} +2 -2
  10. package/dist/{build-IQQYCA3B.js → build-NCGIV4LL.js} +41 -14
  11. package/dist/build-NCGIV4LL.js.map +1 -0
  12. package/dist/build-request-body-preview-EOP2Y4F3.js +52 -0
  13. package/dist/build-request-body-preview-EOP2Y4F3.js.map +1 -0
  14. package/dist/build-request-body-preview-II2235E6.js +54 -0
  15. package/dist/build-request-body-preview-II2235E6.js.map +1 -0
  16. package/dist/{bun-DITMZMKL.js → bun-K73TQEWC.js} +2 -2
  17. package/dist/{chunk-GQSTRXXB.js → chunk-2BQYEBCK.js} +77 -68
  18. package/dist/chunk-2BQYEBCK.js.map +1 -0
  19. package/dist/chunk-7DHGV77J.js +140 -0
  20. package/dist/chunk-7DHGV77J.js.map +1 -0
  21. package/dist/{chunk-WQCPKA2L.js → chunk-C27AZN46.js} +570 -165
  22. package/dist/chunk-C27AZN46.js.map +1 -0
  23. package/dist/chunk-ERAZNHHR.js +73 -0
  24. package/dist/chunk-ERAZNHHR.js.map +1 -0
  25. package/dist/{chunk-ZK5JCE5J.js → chunk-EXJWO2HB.js} +201 -43
  26. package/dist/chunk-EXJWO2HB.js.map +1 -0
  27. package/dist/chunk-F3TG5FJV.js +97 -0
  28. package/dist/chunk-F3TG5FJV.js.map +1 -0
  29. package/dist/{chunk-OK2PJOLK.js → chunk-KJVEJILQ.js} +7 -3
  30. package/dist/chunk-KJVEJILQ.js.map +1 -0
  31. package/dist/{chunk-JBC7PFV4.js → chunk-M23FP2AS.js} +2 -2
  32. package/dist/chunk-M23FP2AS.js.map +1 -0
  33. package/dist/{chunk-FRBYJANT.js → chunk-M6MYZSD2.js} +195 -41
  34. package/dist/chunk-M6MYZSD2.js.map +1 -0
  35. package/dist/{chunk-S3JBIFKS.js → chunk-MG7H3LAW.js} +2 -2
  36. package/dist/chunk-MOT42NAH.js +165 -0
  37. package/dist/chunk-MOT42NAH.js.map +1 -0
  38. package/dist/{chunk-OEB3OIYP.js → chunk-OJHO2X7C.js} +497 -168
  39. package/dist/chunk-OJHO2X7C.js.map +1 -0
  40. package/dist/chunk-QLGES2HC.js +189 -0
  41. package/dist/chunk-QLGES2HC.js.map +1 -0
  42. package/dist/{chunk-JZ5WEG3S.js → chunk-SEVYARIU.js} +9 -5
  43. package/dist/chunk-SEVYARIU.js.map +1 -0
  44. package/dist/chunk-WSJKACWB.js +17 -0
  45. package/dist/chunk-WSJKACWB.js.map +1 -0
  46. package/dist/{chunk-3Q36SEDK.js → chunk-ZBWROIIO.js} +43 -2
  47. package/dist/{chunk-3Q36SEDK.js.map → chunk-ZBWROIIO.js.map} +1 -1
  48. package/dist/{chunk-Y5KE6YFP.js → chunk-ZJW5FFYJ.js} +3 -3
  49. package/dist/chunk-ZJW5FFYJ.js.map +1 -0
  50. package/dist/chunk-ZOXNJV2O.js +358 -0
  51. package/dist/chunk-ZOXNJV2O.js.map +1 -0
  52. package/dist/cli/index.js +22 -7
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/client/index.d.ts +31 -1
  55. package/dist/client/index.js +100 -0
  56. package/dist/client/index.js.map +1 -1
  57. package/dist/{cloudflare-RBANU3SE.js → cloudflare-LCAOTRYZ.js} +2 -2
  58. package/dist/{deno-deploy-J7LI6HNL.js → deno-deploy-ZN4RE5HF.js} +2 -2
  59. package/dist/{dev-VN6WS3CM.js → dev-WDXC74CK.js} +15 -7
  60. package/dist/dev-WDXC74CK.js.map +1 -0
  61. package/dist/dev-emit-3CHM73TV.js +390 -0
  62. package/dist/dev-emit-3CHM73TV.js.map +1 -0
  63. package/dist/dev-emit-I3CKOHML.js +44 -0
  64. package/dist/dev-emit-I3CKOHML.js.map +1 -0
  65. package/dist/devtools/entry.js +545 -181
  66. package/dist/devtools/entry.js.map +1 -1
  67. package/dist/{dispatcher-IK3FVMX5.js → dispatcher-AQJGI3HU.js} +2 -2
  68. package/dist/dispatcher-E6QV32BO.js +83 -0
  69. package/dist/dispatcher-E6QV32BO.js.map +1 -0
  70. package/dist/{dist-Q5VBFWVL.js → dist-R2Q7GVDW.js} +10357 -12068
  71. package/dist/dist-R2Q7GVDW.js.map +1 -0
  72. package/dist/{generate-SIO4JSKJ.js → generate-DT4IIAHF.js} +88 -22
  73. package/dist/generate-DT4IIAHF.js.map +1 -0
  74. package/dist/index.d.ts +108 -1
  75. package/dist/index.js +5 -2
  76. package/dist/index.js.map +1 -1
  77. package/dist/{info-B3UOXFUF.js → info-VM7EK4JC.js} +3 -3
  78. package/dist/{netlify-IFY6ZGFV.js → netlify-5VQ22Z2P.js} +2 -2
  79. package/dist/{node-KWZI3CKV.js → node-3APTLGWB.js} +2 -2
  80. package/dist/openapi-CBASKC7W.js +84 -0
  81. package/dist/openapi-CBASKC7W.js.map +1 -0
  82. package/dist/registry-5QFTLOL2.js +25 -0
  83. package/dist/{routes-CAGFZKUK.js → routes-7O4NUDYS.js} +9 -7
  84. package/dist/{routes-CAGFZKUK.js.map → routes-7O4NUDYS.js.map} +1 -1
  85. package/dist/{schema-Cxq2qqwh.d.ts → schema-CjVly9c_.d.ts} +43 -1
  86. package/dist/{schema-MVB2FVK6.js → schema-JAKF23GR.js} +2 -2
  87. package/dist/server/cost/index.js +1 -1
  88. package/dist/server/index.d.ts +219 -47
  89. package/dist/server/index.js +35 -116
  90. package/dist/server/index.js.map +1 -1
  91. package/dist/{start-ZGWW5MOL.js → start-5JTFWF4T.js} +20 -14
  92. package/dist/start-5JTFWF4T.js.map +1 -0
  93. package/dist/{static-BH6JQNM2.js → static-TZBVBDTB.js} +4 -4
  94. package/dist/{upgrade-readiness-OHL36FQL.js → upgrade-readiness-Q46KTG2J.js} +6 -1
  95. package/dist/upgrade-readiness-Q46KTG2J.js.map +1 -0
  96. package/dist/{vercel-RHADE5IP.js → vercel-MWW24ABC.js} +2 -2
  97. package/dist/vite-plugin/index.d.ts +25 -1
  98. package/dist/vite-plugin/index.js +5 -2
  99. package/dist/{vite-plugin-WA3G45YY.js → vite-plugin-4WZ3C2AC.js} +7 -6
  100. package/package.json +4 -2
  101. package/dist/build-IQQYCA3B.js.map +0 -1
  102. package/dist/chunk-FRBYJANT.js.map +0 -1
  103. package/dist/chunk-GQSTRXXB.js.map +0 -1
  104. package/dist/chunk-JBC7PFV4.js.map +0 -1
  105. package/dist/chunk-JZ5WEG3S.js.map +0 -1
  106. package/dist/chunk-OEB3OIYP.js.map +0 -1
  107. package/dist/chunk-OK2PJOLK.js.map +0 -1
  108. package/dist/chunk-WQCPKA2L.js.map +0 -1
  109. package/dist/chunk-Y5KE6YFP.js.map +0 -1
  110. package/dist/chunk-ZK5JCE5J.js.map +0 -1
  111. package/dist/dev-VN6WS3CM.js.map +0 -1
  112. package/dist/dist-Q5VBFWVL.js.map +0 -1
  113. package/dist/generate-SIO4JSKJ.js.map +0 -1
  114. package/dist/registry-BB3UYAOG.js +0 -25
  115. package/dist/start-ZGWW5MOL.js.map +0 -1
  116. package/dist/upgrade-readiness-OHL36FQL.js.map +0 -1
  117. /package/dist/{aws-lambda-PONVF3UX.js.map → aws-lambda-XYCGZH3I.js.map} +0 -0
  118. /package/dist/{bun-DITMZMKL.js.map → bun-K73TQEWC.js.map} +0 -0
  119. /package/dist/{chunk-S3JBIFKS.js.map → chunk-MG7H3LAW.js.map} +0 -0
  120. /package/dist/{cloudflare-RBANU3SE.js.map → cloudflare-LCAOTRYZ.js.map} +0 -0
  121. /package/dist/{deno-deploy-J7LI6HNL.js.map → deno-deploy-ZN4RE5HF.js.map} +0 -0
  122. /package/dist/{dispatcher-IK3FVMX5.js.map → dispatcher-AQJGI3HU.js.map} +0 -0
  123. /package/dist/{info-B3UOXFUF.js.map → info-VM7EK4JC.js.map} +0 -0
  124. /package/dist/{netlify-IFY6ZGFV.js.map → netlify-5VQ22Z2P.js.map} +0 -0
  125. /package/dist/{node-KWZI3CKV.js.map → node-3APTLGWB.js.map} +0 -0
  126. /package/dist/{registry-BB3UYAOG.js.map → registry-5QFTLOL2.js.map} +0 -0
  127. /package/dist/{schema-MVB2FVK6.js.map → schema-JAKF23GR.js.map} +0 -0
  128. /package/dist/{static-BH6JQNM2.js.map → static-TZBVBDTB.js.map} +0 -0
  129. /package/dist/{vercel-RHADE5IP.js.map → vercel-MWW24ABC.js.map} +0 -0
  130. /package/dist/{vite-plugin-WA3G45YY.js.map → vite-plugin-4WZ3C2AC.js.map} +0 -0
@@ -8,7 +8,7 @@ import {
8
8
  STORAGE_VERSION,
9
9
  dispatcher,
10
10
  initialState
11
- } from "../chunk-OK2PJOLK.js";
11
+ } from "../chunk-KJVEJILQ.js";
12
12
  import "../chunk-DGUM43GV.js";
13
13
 
14
14
  // src/devtools/entry.tsx
@@ -444,12 +444,278 @@ function Indicator() {
444
444
  );
445
445
  }
446
446
 
447
- // src/devtools/components/Tabs/AgentsTab.tsx
447
+ // src/devtools/components/Tabs/ActionsTab.tsx
448
+ import { useState } from "react";
449
+
450
+ // src/devtools/actions-row-state.ts
451
+ function toggleExpandedIds(prev, id) {
452
+ const next = new Set(prev);
453
+ if (next.has(id)) {
454
+ next.delete(id);
455
+ } else {
456
+ next.add(id);
457
+ }
458
+ return next;
459
+ }
460
+
461
+ // src/devtools/pii-mask.ts
462
+ var SENSITIVE_KEY_PATTERN = /(password|token|secret|api_?key|apikey|credit_?card|ssn|cpf|cnpj)/i;
463
+ function isSensitiveKey(key) {
464
+ return SENSITIVE_KEY_PATTERN.test(key);
465
+ }
466
+ function maskPiiFields(value) {
467
+ if (value === null || value === void 0) return value;
468
+ if (typeof value !== "object") return value;
469
+ if (Array.isArray(value)) {
470
+ return value.map((item) => maskPiiFields(item));
471
+ }
472
+ const out = {};
473
+ for (const [key, val] of Object.entries(value)) {
474
+ if (isSensitiveKey(key)) {
475
+ out[key] = "***";
476
+ } else {
477
+ out[key] = maskPiiFields(val);
478
+ }
479
+ }
480
+ return out;
481
+ }
482
+
483
+ // src/devtools/components/Tabs/ActionsTab.tsx
448
484
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
449
485
  function formatTime(ts) {
450
486
  const d = new Date(ts);
451
487
  return d.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
452
488
  }
489
+ function formatPayload(value, reveal) {
490
+ const display = reveal ? value : maskPiiFields(value);
491
+ try {
492
+ return JSON.stringify(display, null, 2);
493
+ } catch {
494
+ return "<unserializable>";
495
+ }
496
+ }
497
+ function buildActionsStyles(styles) {
498
+ return {
499
+ header: styles.css`
500
+ display: flex;
501
+ align-items: center;
502
+ justify-content: space-between;
503
+ padding: ${tokens.spacing.xs} ${tokens.spacing.sm};
504
+ color: ${tokens.colors.textMuted};
505
+ font-size: ${tokens.font.sizeXs};
506
+ border-bottom: 1px solid ${tokens.colors.borderSubtle};
507
+ `,
508
+ clearBtn: styles.css`
509
+ appearance: none;
510
+ background: transparent;
511
+ color: ${tokens.colors.textMuted};
512
+ border: 1px solid ${tokens.colors.borderSubtle};
513
+ padding: 2px 6px;
514
+ border-radius: ${tokens.radius.sm};
515
+ cursor: pointer;
516
+ font-size: ${tokens.font.sizeXs};
517
+ &:hover { color: ${tokens.colors.text}; }
518
+ `,
519
+ rowContainer: styles.css`
520
+ border-bottom: 1px solid ${tokens.colors.borderSubtle};
521
+ font-size: ${tokens.font.sizeXs};
522
+ `,
523
+ rowSummary: styles.css`
524
+ display: grid;
525
+ grid-template-columns: 24px 80px 1fr 80px 80px;
526
+ gap: ${tokens.spacing.sm};
527
+ align-items: center;
528
+ width: 100%;
529
+ padding: ${tokens.spacing.xs} ${tokens.spacing.sm};
530
+ background: transparent;
531
+ border: none;
532
+ color: ${tokens.colors.text};
533
+ text-align: left;
534
+ cursor: pointer;
535
+ font-family: ${tokens.font.family};
536
+ font-size: ${tokens.font.sizeXs};
537
+ &:hover { background: ${tokens.colors.bgPanelHover}; }
538
+ &[aria-expanded='true'] { background: ${tokens.colors.bgPanelHover}; }
539
+ .chev { color: ${tokens.colors.textMuted}; font-family: ${tokens.font.mono}; }
540
+ .name { font-family: ${tokens.font.mono}; }
541
+ .time { color: ${tokens.colors.textMuted}; font-family: ${tokens.font.mono}; }
542
+ .status-success { color: ${tokens.colors.accent}; }
543
+ .status-error { color: #c92a2a; }
544
+ .duration { color: ${tokens.colors.textMuted}; text-align: right; }
545
+ `,
546
+ detail: styles.css`
547
+ padding: ${tokens.spacing.sm} ${tokens.spacing.md};
548
+ background: ${tokens.colors.bgPanelHover};
549
+ border-top: 1px dashed ${tokens.colors.borderSubtle};
550
+ font-size: ${tokens.font.sizeXs};
551
+ pre {
552
+ margin: 0;
553
+ padding: ${tokens.spacing.xs};
554
+ background: ${tokens.colors.borderSubtle};
555
+ border-radius: ${tokens.radius.sm};
556
+ overflow: auto;
557
+ max-height: 200px;
558
+ font-family: ${tokens.font.mono};
559
+ }
560
+ .reveal-btn {
561
+ appearance: none;
562
+ background: transparent;
563
+ color: ${tokens.colors.textMuted};
564
+ border: 1px solid ${tokens.colors.borderSubtle};
565
+ padding: 2px 8px;
566
+ margin-bottom: ${tokens.spacing.xs};
567
+ border-radius: ${tokens.radius.sm};
568
+ cursor: pointer;
569
+ font-size: ${tokens.font.sizeXs};
570
+ }
571
+ .reveal-btn:hover { color: ${tokens.colors.text}; }
572
+ .section { margin-top: ${tokens.spacing.sm}; }
573
+ .field-errors { margin-top: ${tokens.spacing.xs}; color: #c92a2a; }
574
+ .field-errors li { margin-left: ${tokens.spacing.md}; }
575
+ `
576
+ };
577
+ }
578
+ function ActionsEmptyState() {
579
+ return /* @__PURE__ */ jsxs("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: [
580
+ "No action calls yet. Trigger a server action (via ",
581
+ /* @__PURE__ */ jsx3("code", { children: "useAction" }),
582
+ " or direct",
583
+ " ",
584
+ /* @__PURE__ */ jsx3("code", { children: "POST /_actions/<name>" }),
585
+ ") to see it here. Production builds tree-shake this telemetry path."
586
+ ] });
587
+ }
588
+ function ActionsTab() {
589
+ const { state, dispatch, styles } = useDevtoolsContext();
590
+ const [revealedIds, setRevealedIds] = useState(() => /* @__PURE__ */ new Set());
591
+ const [expandedIds, setExpandedIds] = useState(() => /* @__PURE__ */ new Set());
592
+ if (state.actionCalls.length === 0) return /* @__PURE__ */ jsx3(ActionsEmptyState, {});
593
+ const cls = buildActionsStyles(styles);
594
+ const toggleReveal = (id) => {
595
+ setRevealedIds((prev) => toggleExpandedIds(prev, id));
596
+ };
597
+ const toggleExpand = (id) => {
598
+ setExpandedIds((prev) => toggleExpandedIds(prev, id));
599
+ };
600
+ return /* @__PURE__ */ jsxs("div", { "data-testid": "devtools-actions-tab", children: [
601
+ /* @__PURE__ */ jsx3(
602
+ ActionsHeader,
603
+ {
604
+ className: cls.header,
605
+ clearBtnClass: cls.clearBtn,
606
+ count: state.actionCalls.length,
607
+ onClear: () => {
608
+ dispatch({ type: "RESET_ACTION_CALLS" });
609
+ setRevealedIds(/* @__PURE__ */ new Set());
610
+ setExpandedIds(/* @__PURE__ */ new Set());
611
+ }
612
+ }
613
+ ),
614
+ /* @__PURE__ */ jsx3("div", { children: state.actionCalls.map((r) => /* @__PURE__ */ jsx3(
615
+ ActionRow,
616
+ {
617
+ record: r,
618
+ containerClass: cls.rowContainer,
619
+ summaryClass: cls.rowSummary,
620
+ detailClass: cls.detail,
621
+ expanded: expandedIds.has(r.id),
622
+ revealed: revealedIds.has(r.id),
623
+ onToggleExpand: () => {
624
+ toggleExpand(r.id);
625
+ },
626
+ onToggleReveal: () => {
627
+ toggleReveal(r.id);
628
+ }
629
+ },
630
+ r.id
631
+ )) })
632
+ ] });
633
+ }
634
+ function ActionsHeader({
635
+ className,
636
+ clearBtnClass,
637
+ count,
638
+ onClear
639
+ }) {
640
+ return /* @__PURE__ */ jsxs("div", { className, children: [
641
+ /* @__PURE__ */ jsxs("span", { children: [
642
+ count,
643
+ " call",
644
+ count === 1 ? "" : "s"
645
+ ] }),
646
+ /* @__PURE__ */ jsx3("button", { type: "button", className: clearBtnClass, onClick: onClear, children: "Clear" })
647
+ ] });
648
+ }
649
+ function ActionRow({
650
+ record,
651
+ containerClass,
652
+ summaryClass,
653
+ detailClass,
654
+ expanded,
655
+ revealed,
656
+ onToggleExpand,
657
+ onToggleReveal
658
+ }) {
659
+ const statusClass = record.status === "success" ? "status-success" : "status-error";
660
+ return /* @__PURE__ */ jsxs("div", { className: containerClass, "data-testid": "devtools-action-row", children: [
661
+ /* @__PURE__ */ jsxs(
662
+ "button",
663
+ {
664
+ type: "button",
665
+ className: summaryClass,
666
+ onClick: onToggleExpand,
667
+ "aria-expanded": expanded,
668
+ title: `${record.name} \u2014 click to ${expanded ? "collapse" : "expand"} details`,
669
+ children: [
670
+ /* @__PURE__ */ jsx3("span", { className: "chev", children: expanded ? "\u25BC" : "\u25B6" }),
671
+ /* @__PURE__ */ jsx3("span", { className: "time", children: formatTime(record.timestamp) }),
672
+ /* @__PURE__ */ jsx3("span", { className: "name", children: record.name }),
673
+ /* @__PURE__ */ jsx3("span", { className: statusClass, children: record.status }),
674
+ /* @__PURE__ */ jsxs("span", { className: "duration", children: [
675
+ record.durationMs,
676
+ "ms"
677
+ ] })
678
+ ]
679
+ }
680
+ ),
681
+ expanded && /* @__PURE__ */ jsxs("div", { className: detailClass, children: [
682
+ /* @__PURE__ */ jsx3("button", { type: "button", className: "reveal-btn", onClick: onToggleReveal, children: revealed ? "Hide values" : "Reveal values" }),
683
+ /* @__PURE__ */ jsxs("div", { className: "section", children: [
684
+ /* @__PURE__ */ jsx3("strong", { children: "Input" }),
685
+ /* @__PURE__ */ jsx3("pre", { children: formatPayload(record.input, revealed) })
686
+ ] }),
687
+ record.status === "success" && record.output !== void 0 && /* @__PURE__ */ jsxs("div", { className: "section", children: [
688
+ /* @__PURE__ */ jsx3("strong", { children: "Output" }),
689
+ /* @__PURE__ */ jsx3("pre", { children: formatPayload(record.output, revealed) })
690
+ ] }),
691
+ record.status === "error" && record.error && /* @__PURE__ */ jsx3(ActionDetailError, { error: record.error })
692
+ ] })
693
+ ] });
694
+ }
695
+ function ActionDetailError({ error }) {
696
+ return /* @__PURE__ */ jsxs("div", { className: "section", children: [
697
+ /* @__PURE__ */ jsxs("strong", { children: [
698
+ "Error: ",
699
+ error.code
700
+ ] }),
701
+ /* @__PURE__ */ jsx3("p", { children: error.message }),
702
+ error.fields && Object.keys(error.fields).length > 0 && /* @__PURE__ */ jsx3("ul", { className: "field-errors", children: Object.entries(error.fields).map(([field, msgs]) => /* @__PURE__ */ jsxs("li", { children: [
703
+ /* @__PURE__ */ jsxs("strong", { children: [
704
+ field || "(root)",
705
+ ":"
706
+ ] }),
707
+ " ",
708
+ msgs.join("; ")
709
+ ] }, field)) })
710
+ ] });
711
+ }
712
+
713
+ // src/devtools/components/Tabs/AgentsTab.tsx
714
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
715
+ function formatTime2(ts) {
716
+ const d = new Date(ts);
717
+ return d.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
718
+ }
453
719
  function formatCost(usd) {
454
720
  if (usd === 0) return "$0.0000";
455
721
  if (usd < 1e-4) return `<$0.0001`;
@@ -458,9 +724,9 @@ function formatCost(usd) {
458
724
  function AgentsTab() {
459
725
  const { state, dispatch, styles } = useDevtoolsContext();
460
726
  if (state.agentRuns.length === 0) {
461
- return /* @__PURE__ */ jsxs("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: [
727
+ return /* @__PURE__ */ jsxs2("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: [
462
728
  "No agent runs yet. Trigger a request to an endpoint using ",
463
- /* @__PURE__ */ jsx3("code", { children: "trackAgentRun" }),
729
+ /* @__PURE__ */ jsx4("code", { children: "trackAgentRun" }),
464
730
  " to see it here. (Wired in the saas template; opt-in elsewhere.)"
465
731
  ] });
466
732
  }
@@ -504,9 +770,9 @@ function AgentsTab() {
504
770
  tr.status-error td { color: #c92a2a; }
505
771
  tr.status-aborted td { color: ${tokens.colors.textMuted}; }
506
772
  `;
507
- return /* @__PURE__ */ jsxs("div", { "data-testid": "devtools-agents-tab", children: [
508
- /* @__PURE__ */ jsxs("div", { className: headerClass, children: [
509
- /* @__PURE__ */ jsxs("span", { children: [
773
+ return /* @__PURE__ */ jsxs2("div", { "data-testid": "devtools-agents-tab", children: [
774
+ /* @__PURE__ */ jsxs2("div", { className: headerClass, children: [
775
+ /* @__PURE__ */ jsxs2("span", { children: [
510
776
  state.agentRuns.length,
511
777
  " run",
512
778
  state.agentRuns.length === 1 ? "" : "s",
@@ -519,7 +785,7 @@ function AgentsTab() {
519
785
  formatCost(totalCost),
520
786
  " total"
521
787
  ] }),
522
- /* @__PURE__ */ jsx3(
788
+ /* @__PURE__ */ jsx4(
523
789
  "button",
524
790
  {
525
791
  type: "button",
@@ -531,34 +797,95 @@ function AgentsTab() {
531
797
  }
532
798
  )
533
799
  ] }),
534
- /* @__PURE__ */ jsxs("table", { className: tableClass, children: [
535
- /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
536
- /* @__PURE__ */ jsx3("th", { children: "Time" }),
537
- /* @__PURE__ */ jsx3("th", { children: "User" }),
538
- /* @__PURE__ */ jsx3("th", { children: "Model" }),
539
- /* @__PURE__ */ jsx3("th", { children: "Tokens (in/out)" }),
540
- /* @__PURE__ */ jsx3("th", { children: "Cost" }),
541
- /* @__PURE__ */ jsx3("th", { children: "Status" })
800
+ /* @__PURE__ */ jsxs2("table", { className: tableClass, children: [
801
+ /* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
802
+ /* @__PURE__ */ jsx4("th", { children: "Time" }),
803
+ /* @__PURE__ */ jsx4("th", { children: "User" }),
804
+ /* @__PURE__ */ jsx4("th", { children: "Model" }),
805
+ /* @__PURE__ */ jsx4("th", { children: "Tokens (in/out)" }),
806
+ /* @__PURE__ */ jsx4("th", { children: "Cost" }),
807
+ /* @__PURE__ */ jsx4("th", { children: "Status" })
542
808
  ] }) }),
543
- /* @__PURE__ */ jsx3("tbody", { children: state.agentRuns.map((run) => /* @__PURE__ */ jsxs("tr", { className: `status-${run.status}`, children: [
544
- /* @__PURE__ */ jsx3("td", { children: formatTime(run.timestamp) }),
545
- /* @__PURE__ */ jsx3("td", { children: run.userId }),
546
- /* @__PURE__ */ jsx3("td", { children: run.model }),
547
- /* @__PURE__ */ jsxs("td", { children: [
809
+ /* @__PURE__ */ jsx4("tbody", { children: state.agentRuns.map((run) => /* @__PURE__ */ jsxs2("tr", { className: `status-${run.status}`, children: [
810
+ /* @__PURE__ */ jsx4("td", { children: formatTime2(run.timestamp) }),
811
+ /* @__PURE__ */ jsx4("td", { children: run.userId }),
812
+ /* @__PURE__ */ jsx4("td", { children: run.model }),
813
+ /* @__PURE__ */ jsxs2("td", { children: [
548
814
  run.tokensInput,
549
815
  "/",
550
816
  run.tokensOutput
551
817
  ] }),
552
- /* @__PURE__ */ jsx3("td", { children: formatCost(run.costUsd) }),
553
- /* @__PURE__ */ jsx3("td", { children: run.status })
818
+ /* @__PURE__ */ jsx4("td", { children: formatCost(run.costUsd) }),
819
+ /* @__PURE__ */ jsx4("td", { children: run.status })
554
820
  ] }, run.id)) })
555
821
  ] })
556
822
  ] });
557
823
  }
558
824
 
559
825
  // src/devtools/components/Tabs/CsrfReadinessTab.tsx
560
- import { useEffect as useEffect2, useState, useCallback as useCallback2 } from "react";
561
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
826
+ import { useEffect as useEffect2, useState as useState2, useCallback as useCallback2 } from "react";
827
+
828
+ // src/devtools/csrf-readiness-classify.ts
829
+ var NETWORK_ERROR_PATTERN = /failed to fetch|networkerror|network request failed/i;
830
+ function classifyCsrfReadinessError(input) {
831
+ if (input.kind === "http") {
832
+ return classifyHttpStatus(input.status);
833
+ }
834
+ return classifyThrown(input.error);
835
+ }
836
+ function classifyHttpStatus(status) {
837
+ if (status === 404) {
838
+ return {
839
+ kind: "store-not-wired",
840
+ summary: `Endpoint returned 404 \u2014 /__theo/csrf-readiness not registered.`,
841
+ hint: "Wire `csrfReadinessStore` in the api-middleware options to enable this tab."
842
+ };
843
+ }
844
+ if (status === 401 || status === 403) {
845
+ return {
846
+ kind: "unauthorized",
847
+ summary: `Endpoint returned ${String(status)} \u2014 the readiness route is auth-gated.`,
848
+ hint: "Authenticate (e.g. via session) before opening this tab, or move the route outside of requireAuth."
849
+ };
850
+ }
851
+ if (status >= 500 && status < 600) {
852
+ return {
853
+ kind: "server-crash",
854
+ summary: `Endpoint returned ${String(status)} \u2014 the readiness handler threw.`,
855
+ hint: "Check dev server logs (terminal running `theokit dev`) for the underlying stack trace."
856
+ };
857
+ }
858
+ return {
859
+ kind: "unexpected-status",
860
+ summary: `Endpoint returned unexpected status ${String(status)}.`,
861
+ hint: "This is not a status the readiness route normally emits. Inspect the response body in Network tab."
862
+ };
863
+ }
864
+ function classifyThrown(error) {
865
+ if (error instanceof Error && error.name === "AbortError") {
866
+ return {
867
+ kind: "aborted",
868
+ summary: "Request aborted.",
869
+ hint: ""
870
+ };
871
+ }
872
+ const message = error instanceof Error ? error.message : String(error);
873
+ if (error instanceof TypeError && NETWORK_ERROR_PATTERN.test(message)) {
874
+ return {
875
+ kind: "server-unreachable",
876
+ summary: "Dev server unreachable \u2014 request failed before any HTTP response.",
877
+ hint: "Restart the dev server (`theokit dev`) and verify the port matches the browser URL."
878
+ };
879
+ }
880
+ return {
881
+ kind: "unknown-error",
882
+ summary: message.length > 0 ? message : "Unknown error",
883
+ hint: "Open the browser DevTools Network panel to inspect the failed request."
884
+ };
885
+ }
886
+
887
+ // src/devtools/components/Tabs/CsrfReadinessTab.tsx
888
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
562
889
  function buildTabStyles(styles) {
563
890
  return {
564
891
  container: styles.css`
@@ -600,13 +927,13 @@ function ReadinessRow({
600
927
  rowClass,
601
928
  route
602
929
  }) {
603
- return /* @__PURE__ */ jsxs2("div", { className: rowClass, children: [
604
- /* @__PURE__ */ jsx4("span", { style: { color: tokens.colors.accent }, children: route.method }),
605
- /* @__PURE__ */ jsxs2("span", { children: [
930
+ return /* @__PURE__ */ jsxs3("div", { className: rowClass, children: [
931
+ /* @__PURE__ */ jsx5("span", { style: { color: tokens.colors.accent }, children: route.method }),
932
+ /* @__PURE__ */ jsxs3("span", { children: [
606
933
  route.path,
607
- /* @__PURE__ */ jsx4("span", { style: { color: tokens.colors.textMuted, marginLeft: tokens.spacing.sm }, children: route.reason })
934
+ /* @__PURE__ */ jsx5("span", { style: { color: tokens.colors.textMuted, marginLeft: tokens.spacing.sm }, children: route.reason })
608
935
  ] }),
609
- /* @__PURE__ */ jsxs2("span", { style: { color: tokens.colors.textMuted }, children: [
936
+ /* @__PURE__ */ jsxs3("span", { style: { color: tokens.colors.textMuted }, children: [
610
937
  "\xD7",
611
938
  String(route.count)
612
939
  ] })
@@ -615,21 +942,23 @@ function ReadinessRow({
615
942
  function CsrfReadinessTab() {
616
943
  const { styles } = useDevtoolsContext();
617
944
  const cls = buildTabStyles(styles);
618
- const [data, setData] = useState(null);
619
- const [error, setError] = useState(null);
620
- const [busy, setBusy] = useState(false);
945
+ const [data, setData] = useState2(null);
946
+ const [error, setError] = useState2(null);
947
+ const [busy, setBusy] = useState2(false);
621
948
  const fetchData = useCallback2(async () => {
622
949
  try {
623
950
  const res = await fetch("/__theo/csrf-readiness");
624
951
  if (!res.ok) {
625
- setError(`Endpoint returned ${String(res.status)} \u2014 is csrfReadinessStore wired?`);
952
+ setError(classifyCsrfReadinessError({ kind: "http", status: res.status }));
626
953
  return;
627
954
  }
628
955
  const json = await res.json();
629
956
  setData(json);
630
957
  setError(null);
631
958
  } catch (e) {
632
- setError(e instanceof Error ? e.message : "fetch failed");
959
+ const classified = classifyCsrfReadinessError({ kind: "thrown", error: e });
960
+ if (classified.kind === "aborted") return;
961
+ setError(classified);
633
962
  }
634
963
  }, []);
635
964
  useEffect2(() => {
@@ -652,24 +981,28 @@ function CsrfReadinessTab() {
652
981
  }
653
982
  }, [fetchData]);
654
983
  if (error) {
655
- return /* @__PURE__ */ jsxs2("div", { className: cls.container, "data-testid": "devtools-csrf-readiness-error", children: [
656
- /* @__PURE__ */ jsx4("div", { style: { color: tokens.colors.error.csrf }, children: error }),
657
- /* @__PURE__ */ jsxs2("p", { style: { color: tokens.colors.textMuted, marginTop: tokens.spacing.sm }, children: [
658
- "Wire ",
659
- /* @__PURE__ */ jsx4("code", { children: "csrfReadinessStore" }),
660
- " in the api-middleware options to enable this tab."
661
- ] })
662
- ] });
984
+ return /* @__PURE__ */ jsxs3(
985
+ "div",
986
+ {
987
+ className: cls.container,
988
+ "data-testid": "devtools-csrf-readiness-error",
989
+ "data-error-kind": error.kind,
990
+ children: [
991
+ /* @__PURE__ */ jsx5("div", { style: { color: tokens.colors.error.csrf }, children: error.summary }),
992
+ error.hint.length > 0 && /* @__PURE__ */ jsx5("p", { style: { color: tokens.colors.textMuted, marginTop: tokens.spacing.sm }, children: error.hint })
993
+ ]
994
+ }
995
+ );
663
996
  }
664
997
  if (data === null) {
665
- return /* @__PURE__ */ jsx4("div", { className: cls.container, "data-testid": "devtools-csrf-readiness-loading", children: "Loading\u2026" });
998
+ return /* @__PURE__ */ jsx5("div", { className: cls.container, "data-testid": "devtools-csrf-readiness-loading", children: "Loading\u2026" });
666
999
  }
667
1000
  const isEmpty = data.totalEvents === 0;
668
1001
  const testid = isEmpty ? "devtools-csrf-readiness-empty" : "devtools-csrf-readiness";
669
- return /* @__PURE__ */ jsxs2("div", { className: cls.container, "data-testid": testid, children: [
670
- /* @__PURE__ */ jsxs2("div", { className: cls.headerRow, children: [
671
- /* @__PURE__ */ jsx4("strong", { children: isEmpty ? "CSRF Readiness" : `CSRF Readiness \u2014 ${String(data.totalEvents)} event(s) across ${String(data.routes.length)} route(s)` }),
672
- /* @__PURE__ */ jsx4(
1002
+ return /* @__PURE__ */ jsxs3("div", { className: cls.container, "data-testid": testid, children: [
1003
+ /* @__PURE__ */ jsxs3("div", { className: cls.headerRow, children: [
1004
+ /* @__PURE__ */ jsx5("strong", { children: isEmpty ? "CSRF Readiness" : `CSRF Readiness \u2014 ${String(data.totalEvents)} event(s) across ${String(data.routes.length)} route(s)` }),
1005
+ /* @__PURE__ */ jsx5(
673
1006
  "button",
674
1007
  {
675
1008
  type: "button",
@@ -682,13 +1015,13 @@ function CsrfReadinessTab() {
682
1015
  }
683
1016
  )
684
1017
  ] }),
685
- isEmpty ? /* @__PURE__ */ jsx4("div", { style: { color: tokens.colors.textMuted }, children: "No csrf.warn events recorded. Endpoints are ready for 0.3.0 strict CSRF." }) : data.routes.map((r) => /* @__PURE__ */ jsx4(ReadinessRow, { rowClass: cls.row, route: r }, `${r.method} ${r.path} ${r.reason}`))
1018
+ isEmpty ? /* @__PURE__ */ jsx5("div", { style: { color: tokens.colors.textMuted }, children: "No csrf.warn events recorded. Endpoints are ready for 0.3.0 strict CSRF." }) : data.routes.map((r) => /* @__PURE__ */ jsx5(ReadinessRow, { rowClass: cls.row, route: r }, `${r.method} ${r.path} ${r.reason}`))
686
1019
  ] });
687
1020
  }
688
1021
 
689
1022
  // src/devtools/components/ErrorRow.tsx
690
- import { useState as useState2 } from "react";
691
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1023
+ import { useState as useState3 } from "react";
1024
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
692
1025
  var STACK_DISPLAY_LIMIT = 4096;
693
1026
  function truncateStackForDisplay(stack, limit = STACK_DISPLAY_LIMIT) {
694
1027
  if (!stack) return null;
@@ -719,7 +1052,7 @@ function typeIcon(type) {
719
1052
  }
720
1053
  }
721
1054
  function ErrorRow({ error }) {
722
- const [expanded, setExpanded] = useState2(false);
1055
+ const [expanded, setExpanded] = useState3(false);
723
1056
  const { styles } = useDevtoolsContext();
724
1057
  const containerClass = styles.css`
725
1058
  display: block;
@@ -766,8 +1099,8 @@ function ErrorRow({ error }) {
766
1099
  `;
767
1100
  const displayedStack = truncateStackForDisplay(error.stack);
768
1101
  const showDocsLink = typeof error.docsUrl === "string" && error.docsUrl.length > 0;
769
- return /* @__PURE__ */ jsxs3("div", { className: containerClass, "data-testid": "devtools-error-row", children: [
770
- /* @__PURE__ */ jsxs3(
1102
+ return /* @__PURE__ */ jsxs4("div", { className: containerClass, "data-testid": "devtools-error-row", children: [
1103
+ /* @__PURE__ */ jsxs4(
771
1104
  "button",
772
1105
  {
773
1106
  type: "button",
@@ -778,9 +1111,9 @@ function ErrorRow({ error }) {
778
1111
  "aria-expanded": expanded,
779
1112
  title: error.message,
780
1113
  children: [
781
- /* @__PURE__ */ jsx5("span", { className: iconClass, "aria-hidden": "true", children: typeIcon(error.type) }),
782
- /* @__PURE__ */ jsx5("span", { className: msgClass, children: error.message }),
783
- error.code && /* @__PURE__ */ jsx5(
1114
+ /* @__PURE__ */ jsx6("span", { className: iconClass, "aria-hidden": "true", children: typeIcon(error.type) }),
1115
+ /* @__PURE__ */ jsx6("span", { className: msgClass, children: error.message }),
1116
+ error.code && /* @__PURE__ */ jsx6(
784
1117
  "span",
785
1118
  {
786
1119
  style: {
@@ -794,8 +1127,8 @@ function ErrorRow({ error }) {
794
1127
  ]
795
1128
  }
796
1129
  ),
797
- expanded && /* @__PURE__ */ jsxs3("div", { className: detailsClass, children: [
798
- showDocsLink && /* @__PURE__ */ jsx5("div", { style: { marginBottom: tokens.spacing.sm }, children: /* @__PURE__ */ jsx5(
1130
+ expanded && /* @__PURE__ */ jsxs4("div", { className: detailsClass, children: [
1131
+ showDocsLink && /* @__PURE__ */ jsx6("div", { style: { marginBottom: tokens.spacing.sm }, children: /* @__PURE__ */ jsx6(
799
1132
  "a",
800
1133
  {
801
1134
  href: error.docsUrl,
@@ -805,7 +1138,7 @@ function ErrorRow({ error }) {
805
1138
  children: "Open migration guide"
806
1139
  }
807
1140
  ) }),
808
- displayedStack && /* @__PURE__ */ jsx5(
1141
+ displayedStack && /* @__PURE__ */ jsx6(
809
1142
  "pre",
810
1143
  {
811
1144
  style: {
@@ -829,11 +1162,11 @@ function ErrorRow({ error }) {
829
1162
  }
830
1163
 
831
1164
  // src/devtools/components/Tabs/ErrorsTab.tsx
832
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1165
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
833
1166
  function ErrorsTab() {
834
1167
  const { state, dispatch, styles } = useDevtoolsContext();
835
1168
  if (state.errors.length === 0) {
836
- return /* @__PURE__ */ jsx6("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: "No errors yet. csrf.warn, console.error and unhandled rejections will surface here." });
1169
+ return /* @__PURE__ */ jsx7("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: "No errors yet. csrf.warn, console.error and unhandled rejections will surface here." });
837
1170
  }
838
1171
  const headerClass = styles.css`
839
1172
  display: flex;
@@ -855,14 +1188,14 @@ function ErrorsTab() {
855
1188
  font-size: ${tokens.font.sizeXs};
856
1189
  &:hover { color: ${tokens.colors.text}; }
857
1190
  `;
858
- return /* @__PURE__ */ jsxs4("div", { "data-testid": "devtools-errors-tab", children: [
859
- /* @__PURE__ */ jsxs4("div", { className: headerClass, children: [
860
- /* @__PURE__ */ jsxs4("span", { children: [
1191
+ return /* @__PURE__ */ jsxs5("div", { "data-testid": "devtools-errors-tab", children: [
1192
+ /* @__PURE__ */ jsxs5("div", { className: headerClass, children: [
1193
+ /* @__PURE__ */ jsxs5("span", { children: [
861
1194
  state.errors.length,
862
1195
  " error",
863
1196
  state.errors.length === 1 ? "" : "s"
864
1197
  ] }),
865
- /* @__PURE__ */ jsx6(
1198
+ /* @__PURE__ */ jsx7(
866
1199
  "button",
867
1200
  {
868
1201
  type: "button",
@@ -874,16 +1207,16 @@ function ErrorsTab() {
874
1207
  }
875
1208
  )
876
1209
  ] }),
877
- state.errors.map((err) => /* @__PURE__ */ jsx6(ErrorRow, { error: err }, err.id))
1210
+ state.errors.map((err) => /* @__PURE__ */ jsx7(ErrorRow, { error: err }, err.id))
878
1211
  ] });
879
1212
  }
880
1213
 
881
1214
  // src/devtools/components/RequestRow.tsx
882
- import { useState as useState4 } from "react";
1215
+ import { useState as useState5 } from "react";
883
1216
 
884
1217
  // src/devtools/components/JSONExplorer.tsx
885
- import { useState as useState3 } from "react";
886
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1218
+ import { useState as useState4 } from "react";
1219
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
887
1220
  var PAGE_SIZE = 100;
888
1221
  var MAX_STRING_DISPLAY = 256;
889
1222
  function describe(value) {
@@ -905,8 +1238,8 @@ function JSONExplorer({
905
1238
  defaultExpanded = false,
906
1239
  depth = 0
907
1240
  }) {
908
- const [expanded, setExpanded] = useState3(defaultExpanded);
909
- const [pageIndex, setPageIndex] = useState3(0);
1241
+ const [expanded, setExpanded] = useState4(defaultExpanded);
1242
+ const [pageIndex, setPageIndex] = useState4(0);
910
1243
  const { styles } = useDevtoolsContext();
911
1244
  const rowClass = styles.css`
912
1245
  font-family: ${tokens.font.mono};
@@ -931,26 +1264,26 @@ function JSONExplorer({
931
1264
  const keyClass = styles.css`color: ${tokens.colors.accent};`;
932
1265
  const valClass = styles.css`color: ${tokens.colors.textMuted};`;
933
1266
  if (value === null || value === void 0 || typeof value !== "object") {
934
- return /* @__PURE__ */ jsxs5("div", { className: rowClass, children: [
935
- /* @__PURE__ */ jsx7("span", { className: keyClass, children: label }),
1267
+ return /* @__PURE__ */ jsxs6("div", { className: rowClass, children: [
1268
+ /* @__PURE__ */ jsx8("span", { className: keyClass, children: label }),
936
1269
  ":",
937
1270
  " ",
938
- /* @__PURE__ */ jsx7("span", { className: valClass, children: describe(value) })
1271
+ /* @__PURE__ */ jsx8("span", { className: valClass, children: describe(value) })
939
1272
  ] });
940
1273
  }
941
1274
  const entries = Array.isArray(value) ? value.map((v, i) => [String(i), v]) : Object.entries(value);
942
1275
  if (entries.length === 0) {
943
- return /* @__PURE__ */ jsxs5("div", { className: rowClass, children: [
944
- /* @__PURE__ */ jsx7("span", { className: keyClass, children: label }),
1276
+ return /* @__PURE__ */ jsxs6("div", { className: rowClass, children: [
1277
+ /* @__PURE__ */ jsx8("span", { className: keyClass, children: label }),
945
1278
  ":",
946
1279
  " ",
947
- /* @__PURE__ */ jsx7("span", { className: valClass, children: Array.isArray(value) ? "[]" : "{}" })
1280
+ /* @__PURE__ */ jsx8("span", { className: valClass, children: Array.isArray(value) ? "[]" : "{}" })
948
1281
  ] });
949
1282
  }
950
1283
  const totalPages = Math.ceil(entries.length / PAGE_SIZE);
951
1284
  const slice = entries.slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);
952
- return /* @__PURE__ */ jsxs5("div", { className: rowClass, children: [
953
- /* @__PURE__ */ jsxs5(
1285
+ return /* @__PURE__ */ jsxs6("div", { className: rowClass, children: [
1286
+ /* @__PURE__ */ jsxs6(
954
1287
  "button",
955
1288
  {
956
1289
  type: "button",
@@ -960,15 +1293,15 @@ function JSONExplorer({
960
1293
  },
961
1294
  "aria-expanded": expanded,
962
1295
  children: [
963
- /* @__PURE__ */ jsx7("span", { children: expanded ? "\u25BE" : "\u25B8" }),
964
- /* @__PURE__ */ jsx7("span", { className: keyClass, children: label }),
965
- /* @__PURE__ */ jsx7("span", { className: valClass, children: describe(value) })
1296
+ /* @__PURE__ */ jsx8("span", { children: expanded ? "\u25BE" : "\u25B8" }),
1297
+ /* @__PURE__ */ jsx8("span", { className: keyClass, children: label }),
1298
+ /* @__PURE__ */ jsx8("span", { className: valClass, children: describe(value) })
966
1299
  ]
967
1300
  }
968
1301
  ),
969
- expanded && /* @__PURE__ */ jsxs5("div", { children: [
970
- slice.map(([k, v]) => /* @__PURE__ */ jsx7(JSONExplorer, { label: k, value: v, depth: depth + 1 }, k)),
971
- totalPages > 1 && /* @__PURE__ */ jsxs5(
1302
+ expanded && /* @__PURE__ */ jsxs6("div", { children: [
1303
+ slice.map(([k, v]) => /* @__PURE__ */ jsx8(JSONExplorer, { label: k, value: v, depth: depth + 1 }, k)),
1304
+ totalPages > 1 && /* @__PURE__ */ jsxs6(
972
1305
  "div",
973
1306
  {
974
1307
  className: styles.css`padding-left: ${(depth + 1) * 12}px; color: ${tokens.colors.textMuted}; font-size: ${tokens.font.sizeXs};`,
@@ -977,7 +1310,7 @@ function JSONExplorer({
977
1310
  pageIndex + 1,
978
1311
  "/",
979
1312
  totalPages,
980
- pageIndex > 0 && /* @__PURE__ */ jsx7(
1313
+ pageIndex > 0 && /* @__PURE__ */ jsx8(
981
1314
  "button",
982
1315
  {
983
1316
  type: "button",
@@ -988,7 +1321,7 @@ function JSONExplorer({
988
1321
  children: "prev"
989
1322
  }
990
1323
  ),
991
- pageIndex < totalPages - 1 && /* @__PURE__ */ jsx7(
1324
+ pageIndex < totalPages - 1 && /* @__PURE__ */ jsx8(
992
1325
  "button",
993
1326
  {
994
1327
  type: "button",
@@ -1007,7 +1340,7 @@ function JSONExplorer({
1007
1340
  }
1008
1341
 
1009
1342
  // src/devtools/components/RequestRow.tsx
1010
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1343
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1011
1344
  function buildRequestRowStyles(styles, request) {
1012
1345
  return {
1013
1346
  container: styles.css`
@@ -1083,7 +1416,7 @@ function formatDuration(ms) {
1083
1416
  return `${Math.round(ms)}ms`;
1084
1417
  }
1085
1418
  function RequestRow({ request }) {
1086
- const [expanded, setExpanded] = useState4(false);
1419
+ const [expanded, setExpanded] = useState5(false);
1087
1420
  const { styles } = useDevtoolsContext();
1088
1421
  const cls = buildRequestRowStyles(styles, request);
1089
1422
  const containerClass = cls.container;
@@ -1096,8 +1429,8 @@ function RequestRow({ request }) {
1096
1429
  const detailsClass = cls.details;
1097
1430
  const displayDuration = formatDuration(request.durationMs);
1098
1431
  const displayStatus = request.status === 0 ? "failed" : String(request.status);
1099
- return /* @__PURE__ */ jsxs6("div", { className: containerClass, "data-testid": "devtools-request-row", children: [
1100
- /* @__PURE__ */ jsxs6(
1432
+ return /* @__PURE__ */ jsxs7("div", { className: containerClass, "data-testid": "devtools-request-row", children: [
1433
+ /* @__PURE__ */ jsxs7(
1101
1434
  "button",
1102
1435
  {
1103
1436
  type: "button",
@@ -1108,19 +1441,19 @@ function RequestRow({ request }) {
1108
1441
  "aria-expanded": expanded,
1109
1442
  title: request.path,
1110
1443
  children: [
1111
- /* @__PURE__ */ jsx8("span", { className: methodBadge, children: request.method }),
1112
- /* @__PURE__ */ jsx8("span", { className: pathClass, children: request.path }),
1113
- /* @__PURE__ */ jsx8("span", { className: statusClass, children: displayStatus }),
1114
- /* @__PURE__ */ jsx8("span", { className: durClass, children: displayDuration }),
1115
- /* @__PURE__ */ jsx8("span", { className: traceClass, children: request.traceId.slice(0, 8) })
1444
+ /* @__PURE__ */ jsx9("span", { className: methodBadge, children: request.method }),
1445
+ /* @__PURE__ */ jsx9("span", { className: pathClass, children: request.path }),
1446
+ /* @__PURE__ */ jsx9("span", { className: statusClass, children: displayStatus }),
1447
+ /* @__PURE__ */ jsx9("span", { className: durClass, children: displayDuration }),
1448
+ /* @__PURE__ */ jsx9("span", { className: traceClass, children: request.traceId.slice(0, 8) })
1116
1449
  ]
1117
1450
  }
1118
1451
  ),
1119
- expanded && /* @__PURE__ */ jsxs6("div", { className: detailsClass, children: [
1120
- request.csrfWarn && /* @__PURE__ */ jsxs6("div", { style: { color: tokens.colors.error.csrf, marginBottom: tokens.spacing.sm }, children: [
1452
+ expanded && /* @__PURE__ */ jsxs7("div", { className: detailsClass, children: [
1453
+ request.csrfWarn && /* @__PURE__ */ jsxs7("div", { style: { color: tokens.colors.error.csrf, marginBottom: tokens.spacing.sm }, children: [
1121
1454
  "CSRF warn:",
1122
1455
  " ",
1123
- /* @__PURE__ */ jsx8(
1456
+ /* @__PURE__ */ jsx9(
1124
1457
  "a",
1125
1458
  {
1126
1459
  href: request.csrfWarn.docsUrl,
@@ -1131,8 +1464,8 @@ function RequestRow({ request }) {
1131
1464
  }
1132
1465
  )
1133
1466
  ] }),
1134
- request.headers && /* @__PURE__ */ jsx8(JSONExplorer, { label: "headers", value: request.headers, defaultExpanded: false }),
1135
- request.bodyPreview !== void 0 && request.bodyPreview !== "" && /* @__PURE__ */ jsxs6(
1467
+ request.headers && /* @__PURE__ */ jsx9(JSONExplorer, { label: "headers", value: request.headers, defaultExpanded: false }),
1468
+ request.bodyPreview !== void 0 && request.bodyPreview !== "" && /* @__PURE__ */ jsxs7(
1136
1469
  "div",
1137
1470
  {
1138
1471
  style: {
@@ -1145,7 +1478,7 @@ function RequestRow({ request }) {
1145
1478
  "body",
1146
1479
  request.bodyTruncated ? ` (truncated, ${request.bodyLength} bytes)` : "",
1147
1480
  ":",
1148
- /* @__PURE__ */ jsx8("pre", { style: { marginTop: 4, whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: request.bodyPreview })
1481
+ /* @__PURE__ */ jsx9("pre", { style: { marginTop: 4, whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: request.bodyPreview })
1149
1482
  ]
1150
1483
  }
1151
1484
  )
@@ -1154,11 +1487,11 @@ function RequestRow({ request }) {
1154
1487
  }
1155
1488
 
1156
1489
  // src/devtools/components/Tabs/RequestsTab.tsx
1157
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1490
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1158
1491
  function RequestsTab() {
1159
1492
  const { state, dispatch, styles } = useDevtoolsContext();
1160
1493
  if (state.requests.length === 0) {
1161
- return /* @__PURE__ */ jsx9("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: "No requests yet. Make a fetch call in your app to see it here." });
1494
+ return /* @__PURE__ */ jsx10("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: "No requests yet. Make a fetch call in your app to see it here." });
1162
1495
  }
1163
1496
  const headerClass = styles.css`
1164
1497
  display: flex;
@@ -1180,14 +1513,14 @@ function RequestsTab() {
1180
1513
  font-size: ${tokens.font.sizeXs};
1181
1514
  &:hover { color: ${tokens.colors.text}; }
1182
1515
  `;
1183
- return /* @__PURE__ */ jsxs7("div", { "data-testid": "devtools-requests-tab", children: [
1184
- /* @__PURE__ */ jsxs7("div", { className: headerClass, children: [
1185
- /* @__PURE__ */ jsxs7("span", { children: [
1516
+ return /* @__PURE__ */ jsxs8("div", { "data-testid": "devtools-requests-tab", children: [
1517
+ /* @__PURE__ */ jsxs8("div", { className: headerClass, children: [
1518
+ /* @__PURE__ */ jsxs8("span", { children: [
1186
1519
  state.requests.length,
1187
1520
  " request",
1188
1521
  state.requests.length === 1 ? "" : "s"
1189
1522
  ] }),
1190
- /* @__PURE__ */ jsx9(
1523
+ /* @__PURE__ */ jsx10(
1191
1524
  "button",
1192
1525
  {
1193
1526
  type: "button",
@@ -1199,16 +1532,16 @@ function RequestsTab() {
1199
1532
  }
1200
1533
  )
1201
1534
  ] }),
1202
- state.requests.map((req) => /* @__PURE__ */ jsx9(RequestRow, { request: req }, req.id))
1535
+ state.requests.map((req) => /* @__PURE__ */ jsx10(RequestRow, { request: req }, req.id))
1203
1536
  ] });
1204
1537
  }
1205
1538
 
1206
1539
  // src/devtools/components/Tabs/RoutesTab.tsx
1207
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1540
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1208
1541
  function RoutesTab() {
1209
1542
  const { state, styles } = useDevtoolsContext();
1210
1543
  if (!state.routeManifest || state.routeManifest.routes.length === 0) {
1211
- return /* @__PURE__ */ jsx10("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: "Routes will appear when the manifest loads." });
1544
+ return /* @__PURE__ */ jsx11("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: "Routes will appear when the manifest loads." });
1212
1545
  }
1213
1546
  const rowClass = styles.css`
1214
1547
  display: flex;
@@ -1237,9 +1570,9 @@ function RoutesTab() {
1237
1570
  console.warn("[theo devtools] Editor not configured");
1238
1571
  });
1239
1572
  }
1240
- return /* @__PURE__ */ jsx10("div", { "data-testid": "devtools-routes-tab", children: state.routeManifest.routes.map((route) => {
1573
+ return /* @__PURE__ */ jsx11("div", { "data-testid": "devtools-routes-tab", children: state.routeManifest.routes.map((route) => {
1241
1574
  const active = state.activeRoutePath === route.path || state.activeChain.includes(route.absoluteFilePath);
1242
- return /* @__PURE__ */ jsxs8(
1575
+ return /* @__PURE__ */ jsxs9(
1243
1576
  "button",
1244
1577
  {
1245
1578
  type: "button",
@@ -1250,7 +1583,7 @@ function RoutesTab() {
1250
1583
  },
1251
1584
  title: `Open ${route.absoluteFilePath} in editor`,
1252
1585
  children: [
1253
- /* @__PURE__ */ jsx10(
1586
+ /* @__PURE__ */ jsx11(
1254
1587
  "span",
1255
1588
  {
1256
1589
  style: {
@@ -1260,8 +1593,8 @@ function RoutesTab() {
1260
1593
  children: active ? "\u25B8" : " "
1261
1594
  }
1262
1595
  ),
1263
- /* @__PURE__ */ jsx10("span", { style: { flex: 1 }, children: route.path }),
1264
- /* @__PURE__ */ jsx10("span", { style: { color: tokens.colors.textDim, fontSize: tokens.font.sizeXs }, children: route.absoluteFilePath.split("/").slice(-2).join("/") })
1596
+ /* @__PURE__ */ jsx11("span", { style: { flex: 1 }, children: route.path }),
1597
+ /* @__PURE__ */ jsx11("span", { style: { color: tokens.colors.textDim, fontSize: tokens.font.sizeXs }, children: route.absoluteFilePath.split("/").slice(-2).join("/") })
1265
1598
  ]
1266
1599
  },
1267
1600
  route.absoluteFilePath
@@ -1270,7 +1603,7 @@ function RoutesTab() {
1270
1603
  }
1271
1604
 
1272
1605
  // src/devtools/components/Tabs/SettingsTab.tsx
1273
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1606
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1274
1607
  var POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"];
1275
1608
  var THEMES = ["light", "dark", "system"];
1276
1609
  function SettingsTab() {
@@ -1291,11 +1624,11 @@ function SettingsTab() {
1291
1624
  color: ${tokens.colors.text};
1292
1625
  font-size: ${tokens.font.sizeSm};
1293
1626
  `;
1294
- return /* @__PURE__ */ jsxs9("div", { "data-testid": "devtools-settings-tab", style: { padding: tokens.spacing.md }, children: [
1295
- /* @__PURE__ */ jsxs9("fieldset", { className: sectionClass, style: { border: 0, padding: 0, margin: 0 }, children: [
1296
- /* @__PURE__ */ jsx11("legend", { className: headingClass, children: "Position" }),
1297
- POSITIONS.map((p) => /* @__PURE__ */ jsxs9("label", { className: radioRowClass, children: [
1298
- /* @__PURE__ */ jsx11(
1627
+ return /* @__PURE__ */ jsxs10("div", { "data-testid": "devtools-settings-tab", style: { padding: tokens.spacing.md }, children: [
1628
+ /* @__PURE__ */ jsxs10("fieldset", { className: sectionClass, style: { border: 0, padding: 0, margin: 0 }, children: [
1629
+ /* @__PURE__ */ jsx12("legend", { className: headingClass, children: "Position" }),
1630
+ POSITIONS.map((p) => /* @__PURE__ */ jsxs10("label", { className: radioRowClass, children: [
1631
+ /* @__PURE__ */ jsx12(
1299
1632
  "input",
1300
1633
  {
1301
1634
  type: "radio",
@@ -1307,13 +1640,13 @@ function SettingsTab() {
1307
1640
  }
1308
1641
  }
1309
1642
  ),
1310
- /* @__PURE__ */ jsx11("span", { children: p })
1643
+ /* @__PURE__ */ jsx12("span", { children: p })
1311
1644
  ] }, p))
1312
1645
  ] }),
1313
- /* @__PURE__ */ jsxs9("fieldset", { className: sectionClass, style: { border: 0, padding: 0, margin: 0 }, children: [
1314
- /* @__PURE__ */ jsx11("legend", { className: headingClass, children: "Theme" }),
1315
- THEMES.map((t) => /* @__PURE__ */ jsxs9("label", { className: radioRowClass, children: [
1316
- /* @__PURE__ */ jsx11(
1646
+ /* @__PURE__ */ jsxs10("fieldset", { className: sectionClass, style: { border: 0, padding: 0, margin: 0 }, children: [
1647
+ /* @__PURE__ */ jsx12("legend", { className: headingClass, children: "Theme" }),
1648
+ THEMES.map((t) => /* @__PURE__ */ jsxs10("label", { className: radioRowClass, children: [
1649
+ /* @__PURE__ */ jsx12(
1317
1650
  "input",
1318
1651
  {
1319
1652
  type: "radio",
@@ -1325,18 +1658,19 @@ function SettingsTab() {
1325
1658
  }
1326
1659
  }
1327
1660
  ),
1328
- /* @__PURE__ */ jsx11("span", { children: t })
1661
+ /* @__PURE__ */ jsx12("span", { children: t })
1329
1662
  ] }, t))
1330
1663
  ] })
1331
1664
  ] });
1332
1665
  }
1333
1666
 
1334
1667
  // src/devtools/components/Panel.tsx
1335
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1668
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1336
1669
  var TABS = [
1337
1670
  "requests",
1338
1671
  "routes",
1339
1672
  "agents",
1673
+ "actions",
1340
1674
  "errors",
1341
1675
  "csrf-readiness",
1342
1676
  "settings"
@@ -1425,7 +1759,7 @@ function Panel() {
1425
1759
  const tabBtnClass = cls.tabBtn;
1426
1760
  const closeBtnClass = cls.closeBtn;
1427
1761
  const bodyClass = cls.body;
1428
- return /* @__PURE__ */ jsxs10(
1762
+ return /* @__PURE__ */ jsxs11(
1429
1763
  "div",
1430
1764
  {
1431
1765
  role: "dialog",
@@ -1433,9 +1767,9 @@ function Panel() {
1433
1767
  "data-theo-devtools-panel": "",
1434
1768
  className: panelClass,
1435
1769
  children: [
1436
- /* @__PURE__ */ jsxs10("div", { className: headerClass, children: [
1437
- /* @__PURE__ */ jsxs10("div", { className: tabsClass, role: "tablist", children: [
1438
- /* @__PURE__ */ jsx12(
1770
+ /* @__PURE__ */ jsxs11("div", { className: headerClass, children: [
1771
+ /* @__PURE__ */ jsxs11("div", { className: tabsClass, role: "tablist", children: [
1772
+ /* @__PURE__ */ jsx13(
1439
1773
  "span",
1440
1774
  {
1441
1775
  className: styles.css`
@@ -1445,10 +1779,10 @@ function Panel() {
1445
1779
  opacity: 0.9;
1446
1780
  `,
1447
1781
  "aria-hidden": "true",
1448
- children: /* @__PURE__ */ jsx12(TheoLogo, { size: 16 })
1782
+ children: /* @__PURE__ */ jsx13(TheoLogo, { size: 16 })
1449
1783
  }
1450
1784
  ),
1451
- TABS.map((t) => /* @__PURE__ */ jsx12(
1785
+ TABS.map((t) => /* @__PURE__ */ jsx13(
1452
1786
  "button",
1453
1787
  {
1454
1788
  type: "button",
@@ -1464,7 +1798,7 @@ function Panel() {
1464
1798
  t
1465
1799
  ))
1466
1800
  ] }),
1467
- /* @__PURE__ */ jsx12(
1801
+ /* @__PURE__ */ jsx13(
1468
1802
  "button",
1469
1803
  {
1470
1804
  type: "button",
@@ -1477,13 +1811,14 @@ function Panel() {
1477
1811
  }
1478
1812
  )
1479
1813
  ] }),
1480
- /* @__PURE__ */ jsxs10("div", { className: bodyClass, "data-testid": "devtools-tab-body", children: [
1481
- state.activeTab === "requests" && /* @__PURE__ */ jsx12(RequestsTab, {}),
1482
- state.activeTab === "routes" && /* @__PURE__ */ jsx12(RoutesTab, {}),
1483
- state.activeTab === "agents" && /* @__PURE__ */ jsx12(AgentsTab, {}),
1484
- state.activeTab === "errors" && /* @__PURE__ */ jsx12(ErrorsTab, {}),
1485
- state.activeTab === "csrf-readiness" && /* @__PURE__ */ jsx12(CsrfReadinessTab, {}),
1486
- state.activeTab === "settings" && /* @__PURE__ */ jsx12(SettingsTab, {})
1814
+ /* @__PURE__ */ jsxs11("div", { className: bodyClass, "data-testid": "devtools-tab-body", children: [
1815
+ state.activeTab === "requests" && /* @__PURE__ */ jsx13(RequestsTab, {}),
1816
+ state.activeTab === "routes" && /* @__PURE__ */ jsx13(RoutesTab, {}),
1817
+ state.activeTab === "agents" && /* @__PURE__ */ jsx13(AgentsTab, {}),
1818
+ state.activeTab === "actions" && /* @__PURE__ */ jsx13(ActionsTab, {}),
1819
+ state.activeTab === "errors" && /* @__PURE__ */ jsx13(ErrorsTab, {}),
1820
+ state.activeTab === "csrf-readiness" && /* @__PURE__ */ jsx13(CsrfReadinessTab, {}),
1821
+ state.activeTab === "settings" && /* @__PURE__ */ jsx13(SettingsTab, {})
1487
1822
  ] })
1488
1823
  ]
1489
1824
  }
@@ -1619,13 +1954,13 @@ function useActiveRoute() {
1619
1954
  }
1620
1955
 
1621
1956
  // src/devtools/hooks/useResolvedTheme.ts
1622
- import { useEffect as useEffect4, useState as useState5 } from "react";
1957
+ import { useEffect as useEffect4, useState as useState6 } from "react";
1623
1958
  function getSystemTheme() {
1624
1959
  if (typeof window === "undefined") return "dark";
1625
1960
  return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
1626
1961
  }
1627
1962
  function useResolvedTheme(theme) {
1628
- const [systemTheme, setSystemTheme] = useState5(() => getSystemTheme());
1963
+ const [systemTheme, setSystemTheme] = useState6(() => getSystemTheme());
1629
1964
  useEffect4(() => {
1630
1965
  if (theme !== "system") return;
1631
1966
  if (typeof window === "undefined") return;
@@ -1682,6 +2017,16 @@ function useShortcuts() {
1682
2017
  }, [dispatch, state.open]);
1683
2018
  }
1684
2019
 
2020
+ // src/devtools/install-global.ts
2021
+ function noopCleanup() {
2022
+ }
2023
+ function installDispatcherGlobal() {
2024
+ if (typeof window === "undefined") return noopCleanup;
2025
+ const w = window;
2026
+ w.__theoDevtoolsDispatcher = dispatcher;
2027
+ return noopCleanup;
2028
+ }
2029
+
1685
2030
  // src/devtools/persistence.ts
1686
2031
  var STORAGE_KEYS = {
1687
2032
  version: "theo-devtools-storage-version",
@@ -1773,6 +2118,45 @@ function writeToStorage(s) {
1773
2118
  function appendCapped(arr, item, cap = RING_BUFFER_CAP) {
1774
2119
  return [item, ...arr].slice(0, cap);
1775
2120
  }
2121
+ function reduceAppend(state, action) {
2122
+ switch (action.type) {
2123
+ case "REQUEST_ADD":
2124
+ return { ...state, requests: appendCapped(state.requests, action.request) };
2125
+ case "ERROR_ADD":
2126
+ return { ...state, errors: appendCapped(state.errors, action.error) };
2127
+ case "AGENT_RUN_ADD":
2128
+ return { ...state, agentRuns: appendCapped(state.agentRuns, action.run) };
2129
+ case "ACTION_CALL_ADD":
2130
+ return { ...state, actionCalls: appendCapped(state.actionCalls, action.record) };
2131
+ default:
2132
+ return null;
2133
+ }
2134
+ }
2135
+ function reduceReset(state, action) {
2136
+ switch (action.type) {
2137
+ case "RESET_REQUESTS":
2138
+ return { ...state, requests: [] };
2139
+ case "RESET_ERRORS":
2140
+ return { ...state, errors: [] };
2141
+ case "RESET_AGENT_RUNS":
2142
+ return { ...state, agentRuns: [] };
2143
+ case "RESET_ACTION_CALLS":
2144
+ return { ...state, actionCalls: [] };
2145
+ default:
2146
+ return null;
2147
+ }
2148
+ }
2149
+ function csrfWarnToErrorRecord(payload) {
2150
+ return {
2151
+ // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id for devtools UI
2152
+ id: `csrf-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,
2153
+ type: "csrf.warn",
2154
+ message: `CSRF warn: ${payload.method} ${payload.path} \u2014 ${payload.reason}`,
2155
+ code: payload.code,
2156
+ docsUrl: payload.docsUrl,
2157
+ timestamp: Date.now()
2158
+ };
2159
+ }
1776
2160
  function devtoolsReducer(state, action) {
1777
2161
  switch (action.type) {
1778
2162
  case "TOGGLE_PANEL":
@@ -1785,36 +2169,14 @@ function devtoolsReducer(state, action) {
1785
2169
  return { ...state, position: action.position };
1786
2170
  case "SET_THEME":
1787
2171
  return { ...state, theme: action.theme };
1788
- case "REQUEST_ADD":
1789
- return { ...state, requests: appendCapped(state.requests, action.request) };
1790
- case "ERROR_ADD":
1791
- return { ...state, errors: appendCapped(state.errors, action.error) };
1792
- case "CSRF_WARN": {
1793
- const errorRecord = {
1794
- // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id for devtools UI
1795
- id: `csrf-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,
1796
- type: "csrf.warn",
1797
- message: `CSRF warn: ${action.payload.method} ${action.payload.path} \u2014 ${action.payload.reason}`,
1798
- code: action.payload.code,
1799
- docsUrl: action.payload.docsUrl,
1800
- timestamp: Date.now()
1801
- };
1802
- return { ...state, errors: appendCapped(state.errors, errorRecord) };
1803
- }
2172
+ case "CSRF_WARN":
2173
+ return { ...state, errors: appendCapped(state.errors, csrfWarnToErrorRecord(action.payload)) };
1804
2174
  case "MANIFEST_UPDATED":
1805
2175
  return { ...state, routeManifest: action.manifest };
1806
2176
  case "ROUTE_MATCHED":
1807
2177
  return { ...state, activeRoutePath: action.path, activeChain: action.chain };
1808
- case "AGENT_RUN_ADD":
1809
- return { ...state, agentRuns: appendCapped(state.agentRuns, action.run) };
1810
- case "RESET_REQUESTS":
1811
- return { ...state, requests: [] };
1812
- case "RESET_ERRORS":
1813
- return { ...state, errors: [] };
1814
- case "RESET_AGENT_RUNS":
1815
- return { ...state, agentRuns: [] };
1816
2178
  default:
1817
- return state;
2179
+ return reduceAppend(state, action) ?? reduceReset(state, action) ?? state;
1818
2180
  }
1819
2181
  }
1820
2182
 
@@ -1845,7 +2207,7 @@ function createStyles(shadowRoot) {
1845
2207
  }
1846
2208
 
1847
2209
  // src/devtools/Overlay.tsx
1848
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2210
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1849
2211
  function ActiveRouteTracker() {
1850
2212
  useActiveRoute();
1851
2213
  return null;
@@ -1856,7 +2218,7 @@ function ShortcutsTracker() {
1856
2218
  }
1857
2219
  function ThemeVars({ resolved }) {
1858
2220
  const css = `:host { color-scheme: ${resolved}; ${buildThemeCssVars(resolved)} }`;
1859
- return /* @__PURE__ */ jsx13("style", { "data-theo-devtools-theme": resolved, children: css });
2221
+ return /* @__PURE__ */ jsx14("style", { "data-theo-devtools-theme": resolved, children: css });
1860
2222
  }
1861
2223
  function Overlay({ shadowRoot }) {
1862
2224
  const [state, dispatch] = useReducer(
@@ -1878,8 +2240,10 @@ function Overlay({ shadowRoot }) {
1878
2240
  }, [state.position, state.theme, state.open, state.activeTab, state.visible]);
1879
2241
  useInsertionEffect(() => {
1880
2242
  dispatcher.setDispatch(dispatch);
2243
+ const uninstall = installDispatcherGlobal();
1881
2244
  return () => {
1882
2245
  dispatcher.setDispatch(null);
2246
+ uninstall();
1883
2247
  };
1884
2248
  }, []);
1885
2249
  useEffect6(() => {
@@ -1888,17 +2252,17 @@ function Overlay({ shadowRoot }) {
1888
2252
  sub.unsubscribe();
1889
2253
  };
1890
2254
  }, []);
1891
- return /* @__PURE__ */ jsx13(DevtoolsContext.Provider, { value: { shadowRoot, state, dispatch, styles }, children: /* @__PURE__ */ jsxs11(ShadowPortal, { children: [
1892
- /* @__PURE__ */ jsx13(ThemeVars, { resolved: resolvedTheme }),
1893
- /* @__PURE__ */ jsx13(ActiveRouteTracker, {}),
1894
- /* @__PURE__ */ jsx13(ShortcutsTracker, {}),
1895
- /* @__PURE__ */ jsx13(Indicator, {}),
1896
- /* @__PURE__ */ jsx13(Panel, {})
2255
+ return /* @__PURE__ */ jsx14(DevtoolsContext.Provider, { value: { shadowRoot, state, dispatch, styles }, children: /* @__PURE__ */ jsxs12(ShadowPortal, { children: [
2256
+ /* @__PURE__ */ jsx14(ThemeVars, { resolved: resolvedTheme }),
2257
+ /* @__PURE__ */ jsx14(ActiveRouteTracker, {}),
2258
+ /* @__PURE__ */ jsx14(ShortcutsTracker, {}),
2259
+ /* @__PURE__ */ jsx14(Indicator, {}),
2260
+ /* @__PURE__ */ jsx14(Panel, {})
1897
2261
  ] }) });
1898
2262
  }
1899
2263
 
1900
2264
  // src/devtools/entry.tsx
1901
- import { jsx as jsx14 } from "react/jsx-runtime";
2265
+ import { jsx as jsx15 } from "react/jsx-runtime";
1902
2266
  var PORTAL_TAG = "theo-devtools-portal";
1903
2267
  function definePortalElement() {
1904
2268
  if (typeof customElements === "undefined") return;
@@ -1925,7 +2289,7 @@ function mount() {
1925
2289
  shadowRoot.appendChild(reactRootHost);
1926
2290
  const root = createRoot(reactRootHost);
1927
2291
  root.render(
1928
- /* @__PURE__ */ jsx14(StrictMode, { children: /* @__PURE__ */ jsx14(Overlay, { shadowRoot }) })
2292
+ /* @__PURE__ */ jsx15(StrictMode, { children: /* @__PURE__ */ jsx15(Overlay, { shadowRoot }) })
1929
2293
  );
1930
2294
  }
1931
2295
  if (typeof document !== "undefined") {