theokit 0.1.0-alpha.8 → 0.2.1

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 (138) 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/add-2ZFFGGE4.js +0 -0
  6. package/dist/app-typed-client-4L4L4BWD.js +289 -0
  7. package/dist/app-typed-client-4L4L4BWD.js.map +1 -0
  8. package/dist/app-typed-client-6PAT6VOL.js +289 -0
  9. package/dist/app-typed-client-6PAT6VOL.js.map +1 -0
  10. package/dist/aws-lambda-PONVF3UX.js +0 -0
  11. package/dist/broadcast-QOQGUNNK.js +0 -0
  12. package/dist/{build-IQQYCA3B.js → build-EWZDEDT3.js} +37 -11
  13. package/dist/build-EWZDEDT3.js.map +1 -0
  14. package/dist/build-request-body-preview-EOP2Y4F3.js +52 -0
  15. package/dist/build-request-body-preview-EOP2Y4F3.js.map +1 -0
  16. package/dist/build-request-body-preview-II2235E6.js +54 -0
  17. package/dist/build-request-body-preview-II2235E6.js.map +1 -0
  18. package/dist/bun-DITMZMKL.js +0 -0
  19. package/dist/check-PZR67OY5.js +0 -0
  20. package/dist/{chunk-GQSTRXXB.js → chunk-2BQYEBCK.js} +77 -68
  21. package/dist/chunk-2BQYEBCK.js.map +1 -0
  22. package/dist/{chunk-FRBYJANT.js → chunk-4W447H7M.js} +195 -41
  23. package/dist/chunk-4W447H7M.js.map +1 -0
  24. package/dist/chunk-5ODOE6EF.js +0 -0
  25. package/dist/chunk-7DHGV77J.js +140 -0
  26. package/dist/chunk-7DHGV77J.js.map +1 -0
  27. package/dist/{chunk-WQCPKA2L.js → chunk-C27AZN46.js} +570 -165
  28. package/dist/chunk-C27AZN46.js.map +1 -0
  29. package/dist/chunk-ERAZNHHR.js +73 -0
  30. package/dist/chunk-ERAZNHHR.js.map +1 -0
  31. package/dist/chunk-F3TG5FJV.js +97 -0
  32. package/dist/chunk-F3TG5FJV.js.map +1 -0
  33. package/dist/chunk-KJVEJILQ.js +109 -0
  34. package/dist/chunk-KJVEJILQ.js.map +1 -0
  35. package/dist/chunk-KT3MWNZG.js +0 -0
  36. package/dist/{chunk-MCP6Y5BA.js → chunk-M23FP2AS.js} +25 -1
  37. package/dist/chunk-M23FP2AS.js.map +1 -0
  38. package/dist/{chunk-S3JBIFKS.js → chunk-MG7H3LAW.js} +2 -2
  39. package/dist/chunk-MOT42NAH.js +165 -0
  40. package/dist/chunk-MOT42NAH.js.map +1 -0
  41. package/dist/chunk-O7335ZGH.js +0 -0
  42. package/dist/{chunk-OEB3OIYP.js → chunk-OJHO2X7C.js} +497 -168
  43. package/dist/chunk-OJHO2X7C.js.map +1 -0
  44. package/dist/chunk-QLGES2HC.js +189 -0
  45. package/dist/chunk-QLGES2HC.js.map +1 -0
  46. package/dist/{chunk-JZ5WEG3S.js → chunk-SEVYARIU.js} +9 -5
  47. package/dist/chunk-SEVYARIU.js.map +1 -0
  48. package/dist/{chunk-ZK5JCE5J.js → chunk-TFECHXR5.js} +201 -43
  49. package/dist/chunk-TFECHXR5.js.map +1 -0
  50. package/dist/chunk-WSJKACWB.js +17 -0
  51. package/dist/chunk-WSJKACWB.js.map +1 -0
  52. package/dist/chunk-WZ7CPVQB.js +0 -0
  53. package/dist/chunk-XMS5VRIK.js +0 -0
  54. package/dist/chunk-Y5KE6YFP.js +0 -0
  55. package/dist/{chunk-3Q36SEDK.js → chunk-ZBWROIIO.js} +43 -2
  56. package/dist/{chunk-3Q36SEDK.js.map → chunk-ZBWROIIO.js.map} +1 -1
  57. package/dist/chunk-ZOXNJV2O.js +358 -0
  58. package/dist/chunk-ZOXNJV2O.js.map +1 -0
  59. package/dist/cli/index.js +22 -7
  60. package/dist/cli/index.js.map +1 -1
  61. package/dist/client/index.d.ts +31 -1
  62. package/dist/client/index.js +100 -0
  63. package/dist/client/index.js.map +1 -1
  64. package/dist/cloudflare-RBANU3SE.js +0 -0
  65. package/dist/configure-agent-registry-6YGTJYBC.js +0 -0
  66. package/dist/deno-deploy-J7LI6HNL.js +0 -0
  67. package/dist/{dev-VN6WS3CM.js → dev-74KPI5C5.js} +15 -7
  68. package/dist/dev-74KPI5C5.js.map +1 -0
  69. package/dist/dev-emit-3CHM73TV.js +390 -0
  70. package/dist/dev-emit-3CHM73TV.js.map +1 -0
  71. package/dist/dev-emit-I3CKOHML.js +44 -0
  72. package/dist/dev-emit-I3CKOHML.js.map +1 -0
  73. package/dist/devtools/entry.js +653 -243
  74. package/dist/devtools/entry.js.map +1 -1
  75. package/dist/dispatcher-AQJGI3HU.js +8 -0
  76. package/dist/dispatcher-E6QV32BO.js +83 -0
  77. package/dist/dispatcher-E6QV32BO.js.map +1 -0
  78. package/dist/{dist-UZV7J3YI.js → dist-R2Q7GVDW.js} +10412 -11961
  79. package/dist/dist-R2Q7GVDW.js.map +1 -0
  80. package/dist/docker-EZDA5FT7.js +0 -0
  81. package/dist/{generate-SIO4JSKJ.js → generate-DT4IIAHF.js} +88 -22
  82. package/dist/generate-DT4IIAHF.js.map +1 -0
  83. package/dist/index.d.ts +108 -1
  84. package/dist/index.js +5 -2
  85. package/dist/index.js.map +1 -1
  86. package/dist/{info-B3UOXFUF.js → info-VM7EK4JC.js} +3 -3
  87. package/dist/lib-NST3PNZX.js +0 -0
  88. package/dist/netlify-IFY6ZGFV.js +0 -0
  89. package/dist/node-KWZI3CKV.js +0 -0
  90. package/dist/openapi-CBASKC7W.js +84 -0
  91. package/dist/openapi-CBASKC7W.js.map +1 -0
  92. package/dist/proper-lockfile-MVKMQGFK.js +0 -0
  93. package/dist/{registry-BB3UYAOG.js → registry-QGSNLJKJ.js} +2 -2
  94. package/dist/{routes-CAGFZKUK.js → routes-7O4NUDYS.js} +9 -7
  95. package/dist/{routes-CAGFZKUK.js.map → routes-7O4NUDYS.js.map} +1 -1
  96. package/dist/scan-C2BOKLSY.js +0 -0
  97. package/dist/{schema-Cxq2qqwh.d.ts → schema-CjVly9c_.d.ts} +43 -1
  98. package/dist/{schema-MVB2FVK6.js → schema-JAKF23GR.js} +2 -2
  99. package/dist/server/cost/index.d.ts +7 -0
  100. package/dist/server/cost/index.js +1 -1
  101. package/dist/server/index.d.ts +219 -47
  102. package/dist/server/index.js +101 -114
  103. package/dist/server/index.js.map +1 -1
  104. package/dist/services-typed-client-ZX5JUHH3.js +0 -0
  105. package/dist/sqlite-vec-54KB4RD3.js +0 -0
  106. package/dist/{start-CSLJQXV3.js → start-5JTFWF4T.js} +20 -14
  107. package/dist/start-5JTFWF4T.js.map +1 -0
  108. package/dist/{static-BH6JQNM2.js → static-6PZZBY7G.js} +4 -4
  109. package/dist/storage-manager-D5KBXXKB.js +0 -0
  110. package/dist/theo-cloud-3K4DGB3S.js +0 -0
  111. package/dist/{upgrade-readiness-OHL36FQL.js → upgrade-readiness-Q46KTG2J.js} +6 -1
  112. package/dist/upgrade-readiness-Q46KTG2J.js.map +1 -0
  113. package/dist/vercel-RHADE5IP.js +0 -0
  114. package/dist/vite-plugin/index.d.ts +25 -1
  115. package/dist/vite-plugin/index.js +5 -2
  116. package/dist/{vite-plugin-WA3G45YY.js → vite-plugin-3EQP2JYM.js} +7 -6
  117. package/dist/vite-plugin-3EQP2JYM.js.map +1 -0
  118. package/package.json +10 -8
  119. package/LICENSE +0 -201
  120. package/dist/build-IQQYCA3B.js.map +0 -1
  121. package/dist/chunk-FRBYJANT.js.map +0 -1
  122. package/dist/chunk-GQSTRXXB.js.map +0 -1
  123. package/dist/chunk-JZ5WEG3S.js.map +0 -1
  124. package/dist/chunk-MCP6Y5BA.js.map +0 -1
  125. package/dist/chunk-OEB3OIYP.js.map +0 -1
  126. package/dist/chunk-WQCPKA2L.js.map +0 -1
  127. package/dist/chunk-ZK5JCE5J.js.map +0 -1
  128. package/dist/dev-VN6WS3CM.js.map +0 -1
  129. package/dist/dist-UZV7J3YI.js.map +0 -1
  130. package/dist/generate-SIO4JSKJ.js.map +0 -1
  131. package/dist/start-CSLJQXV3.js.map +0 -1
  132. package/dist/upgrade-readiness-OHL36FQL.js.map +0 -1
  133. /package/dist/{chunk-S3JBIFKS.js.map → chunk-MG7H3LAW.js.map} +0 -0
  134. /package/dist/{schema-MVB2FVK6.js.map → dispatcher-AQJGI3HU.js.map} +0 -0
  135. /package/dist/{info-B3UOXFUF.js.map → info-VM7EK4JC.js.map} +0 -0
  136. /package/dist/{registry-BB3UYAOG.js.map → registry-QGSNLJKJ.js.map} +0 -0
  137. /package/dist/{vite-plugin-WA3G45YY.js.map → schema-JAKF23GR.js.map} +0 -0
  138. /package/dist/{static-BH6JQNM2.js.map → static-6PZZBY7G.js.map} +0 -0
@@ -1,3 +1,14 @@
1
+ import {
2
+ CHANNEL_CSRF_WARN,
3
+ CHANNEL_ERROR,
4
+ CHANNEL_MANIFEST,
5
+ CHANNEL_REQUEST,
6
+ CHANNEL_ROUTE_MATCHED,
7
+ RING_BUFFER_CAP,
8
+ STORAGE_VERSION,
9
+ dispatcher,
10
+ initialState
11
+ } from "../chunk-KJVEJILQ.js";
1
12
  import "../chunk-DGUM43GV.js";
2
13
 
3
14
  // src/devtools/entry.tsx
@@ -433,9 +444,448 @@ function Indicator() {
433
444
  );
434
445
  }
435
446
 
436
- // src/devtools/components/Tabs/CsrfReadinessTab.tsx
437
- import { useEffect as useEffect2, useState, useCallback as useCallback2 } from "react";
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
438
484
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
485
+ function formatTime(ts) {
486
+ const d = new Date(ts);
487
+ return d.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
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
+ }
719
+ function formatCost(usd) {
720
+ if (usd === 0) return "$0.0000";
721
+ if (usd < 1e-4) return `<$0.0001`;
722
+ return `$${usd.toFixed(4)}`;
723
+ }
724
+ function AgentsTab() {
725
+ const { state, dispatch, styles } = useDevtoolsContext();
726
+ if (state.agentRuns.length === 0) {
727
+ return /* @__PURE__ */ jsxs2("div", { style: { color: tokens.colors.textMuted, padding: tokens.spacing.md }, children: [
728
+ "No agent runs yet. Trigger a request to an endpoint using ",
729
+ /* @__PURE__ */ jsx4("code", { children: "trackAgentRun" }),
730
+ " to see it here. (Wired in the saas template; opt-in elsewhere.)"
731
+ ] });
732
+ }
733
+ const totalCost = state.agentRuns.reduce((sum, r) => sum + r.costUsd, 0);
734
+ const totalIn = state.agentRuns.reduce((sum, r) => sum + r.tokensInput, 0);
735
+ const totalOut = state.agentRuns.reduce((sum, r) => sum + r.tokensOutput, 0);
736
+ const headerClass = styles.css`
737
+ display: flex;
738
+ align-items: center;
739
+ justify-content: space-between;
740
+ padding: ${tokens.spacing.xs} ${tokens.spacing.sm};
741
+ color: ${tokens.colors.textMuted};
742
+ font-size: ${tokens.font.sizeXs};
743
+ border-bottom: 1px solid ${tokens.colors.borderSubtle};
744
+ `;
745
+ const clearBtn = styles.css`
746
+ appearance: none;
747
+ background: transparent;
748
+ color: ${tokens.colors.textMuted};
749
+ border: 1px solid ${tokens.colors.borderSubtle};
750
+ padding: 2px 6px;
751
+ border-radius: ${tokens.radius.sm};
752
+ cursor: pointer;
753
+ font-size: ${tokens.font.sizeXs};
754
+ &:hover { color: ${tokens.colors.text}; }
755
+ `;
756
+ const tableClass = styles.css`
757
+ width: 100%;
758
+ border-collapse: collapse;
759
+ font-size: ${tokens.font.sizeXs};
760
+ th, td {
761
+ text-align: left;
762
+ padding: 4px 8px;
763
+ border-bottom: 1px solid ${tokens.colors.borderSubtle};
764
+ }
765
+ th {
766
+ color: ${tokens.colors.textMuted};
767
+ font-weight: 500;
768
+ font-size: ${tokens.font.sizeXs};
769
+ }
770
+ tr.status-error td { color: #c92a2a; }
771
+ tr.status-aborted td { color: ${tokens.colors.textMuted}; }
772
+ `;
773
+ return /* @__PURE__ */ jsxs2("div", { "data-testid": "devtools-agents-tab", children: [
774
+ /* @__PURE__ */ jsxs2("div", { className: headerClass, children: [
775
+ /* @__PURE__ */ jsxs2("span", { children: [
776
+ state.agentRuns.length,
777
+ " run",
778
+ state.agentRuns.length === 1 ? "" : "s",
779
+ " \xB7 ",
780
+ "tokens ",
781
+ totalIn,
782
+ "/",
783
+ totalOut,
784
+ " \xB7 ",
785
+ formatCost(totalCost),
786
+ " total"
787
+ ] }),
788
+ /* @__PURE__ */ jsx4(
789
+ "button",
790
+ {
791
+ type: "button",
792
+ className: clearBtn,
793
+ onClick: () => {
794
+ dispatch({ type: "RESET_AGENT_RUNS" });
795
+ },
796
+ children: "clear"
797
+ }
798
+ )
799
+ ] }),
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" })
808
+ ] }) }),
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: [
814
+ run.tokensInput,
815
+ "/",
816
+ run.tokensOutput
817
+ ] }),
818
+ /* @__PURE__ */ jsx4("td", { children: formatCost(run.costUsd) }),
819
+ /* @__PURE__ */ jsx4("td", { children: run.status })
820
+ ] }, run.id)) })
821
+ ] })
822
+ ] });
823
+ }
824
+
825
+ // src/devtools/components/Tabs/CsrfReadinessTab.tsx
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";
439
889
  function buildTabStyles(styles) {
440
890
  return {
441
891
  container: styles.css`
@@ -477,13 +927,13 @@ function ReadinessRow({
477
927
  rowClass,
478
928
  route
479
929
  }) {
480
- return /* @__PURE__ */ jsxs("div", { className: rowClass, children: [
481
- /* @__PURE__ */ jsx3("span", { style: { color: tokens.colors.accent }, children: route.method }),
482
- /* @__PURE__ */ jsxs("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: [
483
933
  route.path,
484
- /* @__PURE__ */ jsx3("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 })
485
935
  ] }),
486
- /* @__PURE__ */ jsxs("span", { style: { color: tokens.colors.textMuted }, children: [
936
+ /* @__PURE__ */ jsxs3("span", { style: { color: tokens.colors.textMuted }, children: [
487
937
  "\xD7",
488
938
  String(route.count)
489
939
  ] })
@@ -492,21 +942,23 @@ function ReadinessRow({
492
942
  function CsrfReadinessTab() {
493
943
  const { styles } = useDevtoolsContext();
494
944
  const cls = buildTabStyles(styles);
495
- const [data, setData] = useState(null);
496
- const [error, setError] = useState(null);
497
- const [busy, setBusy] = useState(false);
945
+ const [data, setData] = useState2(null);
946
+ const [error, setError] = useState2(null);
947
+ const [busy, setBusy] = useState2(false);
498
948
  const fetchData = useCallback2(async () => {
499
949
  try {
500
950
  const res = await fetch("/__theo/csrf-readiness");
501
951
  if (!res.ok) {
502
- setError(`Endpoint returned ${String(res.status)} \u2014 is csrfReadinessStore wired?`);
952
+ setError(classifyCsrfReadinessError({ kind: "http", status: res.status }));
503
953
  return;
504
954
  }
505
955
  const json = await res.json();
506
956
  setData(json);
507
957
  setError(null);
508
958
  } catch (e) {
509
- 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);
510
962
  }
511
963
  }, []);
512
964
  useEffect2(() => {
@@ -529,24 +981,28 @@ function CsrfReadinessTab() {
529
981
  }
530
982
  }, [fetchData]);
531
983
  if (error) {
532
- return /* @__PURE__ */ jsxs("div", { className: cls.container, "data-testid": "devtools-csrf-readiness-error", children: [
533
- /* @__PURE__ */ jsx3("div", { style: { color: tokens.colors.error.csrf }, children: error }),
534
- /* @__PURE__ */ jsxs("p", { style: { color: tokens.colors.textMuted, marginTop: tokens.spacing.sm }, children: [
535
- "Wire ",
536
- /* @__PURE__ */ jsx3("code", { children: "csrfReadinessStore" }),
537
- " in the api-middleware options to enable this tab."
538
- ] })
539
- ] });
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
+ );
540
996
  }
541
997
  if (data === null) {
542
- return /* @__PURE__ */ jsx3("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" });
543
999
  }
544
1000
  const isEmpty = data.totalEvents === 0;
545
1001
  const testid = isEmpty ? "devtools-csrf-readiness-empty" : "devtools-csrf-readiness";
546
- return /* @__PURE__ */ jsxs("div", { className: cls.container, "data-testid": testid, children: [
547
- /* @__PURE__ */ jsxs("div", { className: cls.headerRow, children: [
548
- /* @__PURE__ */ jsx3("strong", { children: isEmpty ? "CSRF Readiness" : `CSRF Readiness \u2014 ${String(data.totalEvents)} event(s) across ${String(data.routes.length)} route(s)` }),
549
- /* @__PURE__ */ jsx3(
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(
550
1006
  "button",
551
1007
  {
552
1008
  type: "button",
@@ -559,13 +1015,13 @@ function CsrfReadinessTab() {
559
1015
  }
560
1016
  )
561
1017
  ] }),
562
- isEmpty ? /* @__PURE__ */ jsx3("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__ */ jsx3(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}`))
563
1019
  ] });
564
1020
  }
565
1021
 
566
1022
  // src/devtools/components/ErrorRow.tsx
567
- import { useState as useState2 } from "react";
568
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1023
+ import { useState as useState3 } from "react";
1024
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
569
1025
  var STACK_DISPLAY_LIMIT = 4096;
570
1026
  function truncateStackForDisplay(stack, limit = STACK_DISPLAY_LIMIT) {
571
1027
  if (!stack) return null;
@@ -596,7 +1052,7 @@ function typeIcon(type) {
596
1052
  }
597
1053
  }
598
1054
  function ErrorRow({ error }) {
599
- const [expanded, setExpanded] = useState2(false);
1055
+ const [expanded, setExpanded] = useState3(false);
600
1056
  const { styles } = useDevtoolsContext();
601
1057
  const containerClass = styles.css`
602
1058
  display: block;
@@ -643,8 +1099,8 @@ function ErrorRow({ error }) {
643
1099
  `;
644
1100
  const displayedStack = truncateStackForDisplay(error.stack);
645
1101
  const showDocsLink = typeof error.docsUrl === "string" && error.docsUrl.length > 0;
646
- return /* @__PURE__ */ jsxs2("div", { className: containerClass, "data-testid": "devtools-error-row", children: [
647
- /* @__PURE__ */ jsxs2(
1102
+ return /* @__PURE__ */ jsxs4("div", { className: containerClass, "data-testid": "devtools-error-row", children: [
1103
+ /* @__PURE__ */ jsxs4(
648
1104
  "button",
649
1105
  {
650
1106
  type: "button",
@@ -655,9 +1111,9 @@ function ErrorRow({ error }) {
655
1111
  "aria-expanded": expanded,
656
1112
  title: error.message,
657
1113
  children: [
658
- /* @__PURE__ */ jsx4("span", { className: iconClass, "aria-hidden": "true", children: typeIcon(error.type) }),
659
- /* @__PURE__ */ jsx4("span", { className: msgClass, children: error.message }),
660
- error.code && /* @__PURE__ */ jsx4(
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(
661
1117
  "span",
662
1118
  {
663
1119
  style: {
@@ -671,8 +1127,8 @@ function ErrorRow({ error }) {
671
1127
  ]
672
1128
  }
673
1129
  ),
674
- expanded && /* @__PURE__ */ jsxs2("div", { className: detailsClass, children: [
675
- showDocsLink && /* @__PURE__ */ jsx4("div", { style: { marginBottom: tokens.spacing.sm }, children: /* @__PURE__ */ jsx4(
1130
+ expanded && /* @__PURE__ */ jsxs4("div", { className: detailsClass, children: [
1131
+ showDocsLink && /* @__PURE__ */ jsx6("div", { style: { marginBottom: tokens.spacing.sm }, children: /* @__PURE__ */ jsx6(
676
1132
  "a",
677
1133
  {
678
1134
  href: error.docsUrl,
@@ -682,7 +1138,7 @@ function ErrorRow({ error }) {
682
1138
  children: "Open migration guide"
683
1139
  }
684
1140
  ) }),
685
- displayedStack && /* @__PURE__ */ jsx4(
1141
+ displayedStack && /* @__PURE__ */ jsx6(
686
1142
  "pre",
687
1143
  {
688
1144
  style: {
@@ -706,11 +1162,11 @@ function ErrorRow({ error }) {
706
1162
  }
707
1163
 
708
1164
  // src/devtools/components/Tabs/ErrorsTab.tsx
709
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1165
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
710
1166
  function ErrorsTab() {
711
1167
  const { state, dispatch, styles } = useDevtoolsContext();
712
1168
  if (state.errors.length === 0) {
713
- return /* @__PURE__ */ jsx5("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." });
714
1170
  }
715
1171
  const headerClass = styles.css`
716
1172
  display: flex;
@@ -732,14 +1188,14 @@ function ErrorsTab() {
732
1188
  font-size: ${tokens.font.sizeXs};
733
1189
  &:hover { color: ${tokens.colors.text}; }
734
1190
  `;
735
- return /* @__PURE__ */ jsxs3("div", { "data-testid": "devtools-errors-tab", children: [
736
- /* @__PURE__ */ jsxs3("div", { className: headerClass, children: [
737
- /* @__PURE__ */ jsxs3("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: [
738
1194
  state.errors.length,
739
1195
  " error",
740
1196
  state.errors.length === 1 ? "" : "s"
741
1197
  ] }),
742
- /* @__PURE__ */ jsx5(
1198
+ /* @__PURE__ */ jsx7(
743
1199
  "button",
744
1200
  {
745
1201
  type: "button",
@@ -751,16 +1207,16 @@ function ErrorsTab() {
751
1207
  }
752
1208
  )
753
1209
  ] }),
754
- state.errors.map((err) => /* @__PURE__ */ jsx5(ErrorRow, { error: err }, err.id))
1210
+ state.errors.map((err) => /* @__PURE__ */ jsx7(ErrorRow, { error: err }, err.id))
755
1211
  ] });
756
1212
  }
757
1213
 
758
1214
  // src/devtools/components/RequestRow.tsx
759
- import { useState as useState4 } from "react";
1215
+ import { useState as useState5 } from "react";
760
1216
 
761
1217
  // src/devtools/components/JSONExplorer.tsx
762
- import { useState as useState3 } from "react";
763
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1218
+ import { useState as useState4 } from "react";
1219
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
764
1220
  var PAGE_SIZE = 100;
765
1221
  var MAX_STRING_DISPLAY = 256;
766
1222
  function describe(value) {
@@ -782,8 +1238,8 @@ function JSONExplorer({
782
1238
  defaultExpanded = false,
783
1239
  depth = 0
784
1240
  }) {
785
- const [expanded, setExpanded] = useState3(defaultExpanded);
786
- const [pageIndex, setPageIndex] = useState3(0);
1241
+ const [expanded, setExpanded] = useState4(defaultExpanded);
1242
+ const [pageIndex, setPageIndex] = useState4(0);
787
1243
  const { styles } = useDevtoolsContext();
788
1244
  const rowClass = styles.css`
789
1245
  font-family: ${tokens.font.mono};
@@ -808,26 +1264,26 @@ function JSONExplorer({
808
1264
  const keyClass = styles.css`color: ${tokens.colors.accent};`;
809
1265
  const valClass = styles.css`color: ${tokens.colors.textMuted};`;
810
1266
  if (value === null || value === void 0 || typeof value !== "object") {
811
- return /* @__PURE__ */ jsxs4("div", { className: rowClass, children: [
812
- /* @__PURE__ */ jsx6("span", { className: keyClass, children: label }),
1267
+ return /* @__PURE__ */ jsxs6("div", { className: rowClass, children: [
1268
+ /* @__PURE__ */ jsx8("span", { className: keyClass, children: label }),
813
1269
  ":",
814
1270
  " ",
815
- /* @__PURE__ */ jsx6("span", { className: valClass, children: describe(value) })
1271
+ /* @__PURE__ */ jsx8("span", { className: valClass, children: describe(value) })
816
1272
  ] });
817
1273
  }
818
1274
  const entries = Array.isArray(value) ? value.map((v, i) => [String(i), v]) : Object.entries(value);
819
1275
  if (entries.length === 0) {
820
- return /* @__PURE__ */ jsxs4("div", { className: rowClass, children: [
821
- /* @__PURE__ */ jsx6("span", { className: keyClass, children: label }),
1276
+ return /* @__PURE__ */ jsxs6("div", { className: rowClass, children: [
1277
+ /* @__PURE__ */ jsx8("span", { className: keyClass, children: label }),
822
1278
  ":",
823
1279
  " ",
824
- /* @__PURE__ */ jsx6("span", { className: valClass, children: Array.isArray(value) ? "[]" : "{}" })
1280
+ /* @__PURE__ */ jsx8("span", { className: valClass, children: Array.isArray(value) ? "[]" : "{}" })
825
1281
  ] });
826
1282
  }
827
1283
  const totalPages = Math.ceil(entries.length / PAGE_SIZE);
828
1284
  const slice = entries.slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);
829
- return /* @__PURE__ */ jsxs4("div", { className: rowClass, children: [
830
- /* @__PURE__ */ jsxs4(
1285
+ return /* @__PURE__ */ jsxs6("div", { className: rowClass, children: [
1286
+ /* @__PURE__ */ jsxs6(
831
1287
  "button",
832
1288
  {
833
1289
  type: "button",
@@ -837,15 +1293,15 @@ function JSONExplorer({
837
1293
  },
838
1294
  "aria-expanded": expanded,
839
1295
  children: [
840
- /* @__PURE__ */ jsx6("span", { children: expanded ? "\u25BE" : "\u25B8" }),
841
- /* @__PURE__ */ jsx6("span", { className: keyClass, children: label }),
842
- /* @__PURE__ */ jsx6("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) })
843
1299
  ]
844
1300
  }
845
1301
  ),
846
- expanded && /* @__PURE__ */ jsxs4("div", { children: [
847
- slice.map(([k, v]) => /* @__PURE__ */ jsx6(JSONExplorer, { label: k, value: v, depth: depth + 1 }, k)),
848
- totalPages > 1 && /* @__PURE__ */ jsxs4(
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(
849
1305
  "div",
850
1306
  {
851
1307
  className: styles.css`padding-left: ${(depth + 1) * 12}px; color: ${tokens.colors.textMuted}; font-size: ${tokens.font.sizeXs};`,
@@ -854,7 +1310,7 @@ function JSONExplorer({
854
1310
  pageIndex + 1,
855
1311
  "/",
856
1312
  totalPages,
857
- pageIndex > 0 && /* @__PURE__ */ jsx6(
1313
+ pageIndex > 0 && /* @__PURE__ */ jsx8(
858
1314
  "button",
859
1315
  {
860
1316
  type: "button",
@@ -865,7 +1321,7 @@ function JSONExplorer({
865
1321
  children: "prev"
866
1322
  }
867
1323
  ),
868
- pageIndex < totalPages - 1 && /* @__PURE__ */ jsx6(
1324
+ pageIndex < totalPages - 1 && /* @__PURE__ */ jsx8(
869
1325
  "button",
870
1326
  {
871
1327
  type: "button",
@@ -884,7 +1340,7 @@ function JSONExplorer({
884
1340
  }
885
1341
 
886
1342
  // src/devtools/components/RequestRow.tsx
887
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1343
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
888
1344
  function buildRequestRowStyles(styles, request) {
889
1345
  return {
890
1346
  container: styles.css`
@@ -960,7 +1416,7 @@ function formatDuration(ms) {
960
1416
  return `${Math.round(ms)}ms`;
961
1417
  }
962
1418
  function RequestRow({ request }) {
963
- const [expanded, setExpanded] = useState4(false);
1419
+ const [expanded, setExpanded] = useState5(false);
964
1420
  const { styles } = useDevtoolsContext();
965
1421
  const cls = buildRequestRowStyles(styles, request);
966
1422
  const containerClass = cls.container;
@@ -973,8 +1429,8 @@ function RequestRow({ request }) {
973
1429
  const detailsClass = cls.details;
974
1430
  const displayDuration = formatDuration(request.durationMs);
975
1431
  const displayStatus = request.status === 0 ? "failed" : String(request.status);
976
- return /* @__PURE__ */ jsxs5("div", { className: containerClass, "data-testid": "devtools-request-row", children: [
977
- /* @__PURE__ */ jsxs5(
1432
+ return /* @__PURE__ */ jsxs7("div", { className: containerClass, "data-testid": "devtools-request-row", children: [
1433
+ /* @__PURE__ */ jsxs7(
978
1434
  "button",
979
1435
  {
980
1436
  type: "button",
@@ -985,19 +1441,19 @@ function RequestRow({ request }) {
985
1441
  "aria-expanded": expanded,
986
1442
  title: request.path,
987
1443
  children: [
988
- /* @__PURE__ */ jsx7("span", { className: methodBadge, children: request.method }),
989
- /* @__PURE__ */ jsx7("span", { className: pathClass, children: request.path }),
990
- /* @__PURE__ */ jsx7("span", { className: statusClass, children: displayStatus }),
991
- /* @__PURE__ */ jsx7("span", { className: durClass, children: displayDuration }),
992
- /* @__PURE__ */ jsx7("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) })
993
1449
  ]
994
1450
  }
995
1451
  ),
996
- expanded && /* @__PURE__ */ jsxs5("div", { className: detailsClass, children: [
997
- request.csrfWarn && /* @__PURE__ */ jsxs5("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: [
998
1454
  "CSRF warn:",
999
1455
  " ",
1000
- /* @__PURE__ */ jsx7(
1456
+ /* @__PURE__ */ jsx9(
1001
1457
  "a",
1002
1458
  {
1003
1459
  href: request.csrfWarn.docsUrl,
@@ -1008,8 +1464,8 @@ function RequestRow({ request }) {
1008
1464
  }
1009
1465
  )
1010
1466
  ] }),
1011
- request.headers && /* @__PURE__ */ jsx7(JSONExplorer, { label: "headers", value: request.headers, defaultExpanded: false }),
1012
- request.bodyPreview !== void 0 && request.bodyPreview !== "" && /* @__PURE__ */ jsxs5(
1467
+ request.headers && /* @__PURE__ */ jsx9(JSONExplorer, { label: "headers", value: request.headers, defaultExpanded: false }),
1468
+ request.bodyPreview !== void 0 && request.bodyPreview !== "" && /* @__PURE__ */ jsxs7(
1013
1469
  "div",
1014
1470
  {
1015
1471
  style: {
@@ -1022,7 +1478,7 @@ function RequestRow({ request }) {
1022
1478
  "body",
1023
1479
  request.bodyTruncated ? ` (truncated, ${request.bodyLength} bytes)` : "",
1024
1480
  ":",
1025
- /* @__PURE__ */ jsx7("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 })
1026
1482
  ]
1027
1483
  }
1028
1484
  )
@@ -1031,11 +1487,11 @@ function RequestRow({ request }) {
1031
1487
  }
1032
1488
 
1033
1489
  // src/devtools/components/Tabs/RequestsTab.tsx
1034
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1490
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1035
1491
  function RequestsTab() {
1036
1492
  const { state, dispatch, styles } = useDevtoolsContext();
1037
1493
  if (state.requests.length === 0) {
1038
- return /* @__PURE__ */ jsx8("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." });
1039
1495
  }
1040
1496
  const headerClass = styles.css`
1041
1497
  display: flex;
@@ -1057,14 +1513,14 @@ function RequestsTab() {
1057
1513
  font-size: ${tokens.font.sizeXs};
1058
1514
  &:hover { color: ${tokens.colors.text}; }
1059
1515
  `;
1060
- return /* @__PURE__ */ jsxs6("div", { "data-testid": "devtools-requests-tab", children: [
1061
- /* @__PURE__ */ jsxs6("div", { className: headerClass, children: [
1062
- /* @__PURE__ */ jsxs6("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: [
1063
1519
  state.requests.length,
1064
1520
  " request",
1065
1521
  state.requests.length === 1 ? "" : "s"
1066
1522
  ] }),
1067
- /* @__PURE__ */ jsx8(
1523
+ /* @__PURE__ */ jsx10(
1068
1524
  "button",
1069
1525
  {
1070
1526
  type: "button",
@@ -1076,16 +1532,16 @@ function RequestsTab() {
1076
1532
  }
1077
1533
  )
1078
1534
  ] }),
1079
- state.requests.map((req) => /* @__PURE__ */ jsx8(RequestRow, { request: req }, req.id))
1535
+ state.requests.map((req) => /* @__PURE__ */ jsx10(RequestRow, { request: req }, req.id))
1080
1536
  ] });
1081
1537
  }
1082
1538
 
1083
1539
  // src/devtools/components/Tabs/RoutesTab.tsx
1084
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1540
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1085
1541
  function RoutesTab() {
1086
1542
  const { state, styles } = useDevtoolsContext();
1087
1543
  if (!state.routeManifest || state.routeManifest.routes.length === 0) {
1088
- return /* @__PURE__ */ jsx9("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." });
1089
1545
  }
1090
1546
  const rowClass = styles.css`
1091
1547
  display: flex;
@@ -1114,9 +1570,9 @@ function RoutesTab() {
1114
1570
  console.warn("[theo devtools] Editor not configured");
1115
1571
  });
1116
1572
  }
1117
- return /* @__PURE__ */ jsx9("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) => {
1118
1574
  const active = state.activeRoutePath === route.path || state.activeChain.includes(route.absoluteFilePath);
1119
- return /* @__PURE__ */ jsxs7(
1575
+ return /* @__PURE__ */ jsxs9(
1120
1576
  "button",
1121
1577
  {
1122
1578
  type: "button",
@@ -1127,7 +1583,7 @@ function RoutesTab() {
1127
1583
  },
1128
1584
  title: `Open ${route.absoluteFilePath} in editor`,
1129
1585
  children: [
1130
- /* @__PURE__ */ jsx9(
1586
+ /* @__PURE__ */ jsx11(
1131
1587
  "span",
1132
1588
  {
1133
1589
  style: {
@@ -1137,8 +1593,8 @@ function RoutesTab() {
1137
1593
  children: active ? "\u25B8" : " "
1138
1594
  }
1139
1595
  ),
1140
- /* @__PURE__ */ jsx9("span", { style: { flex: 1 }, children: route.path }),
1141
- /* @__PURE__ */ jsx9("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("/") })
1142
1598
  ]
1143
1599
  },
1144
1600
  route.absoluteFilePath
@@ -1147,7 +1603,7 @@ function RoutesTab() {
1147
1603
  }
1148
1604
 
1149
1605
  // src/devtools/components/Tabs/SettingsTab.tsx
1150
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1606
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1151
1607
  var POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"];
1152
1608
  var THEMES = ["light", "dark", "system"];
1153
1609
  function SettingsTab() {
@@ -1168,11 +1624,11 @@ function SettingsTab() {
1168
1624
  color: ${tokens.colors.text};
1169
1625
  font-size: ${tokens.font.sizeSm};
1170
1626
  `;
1171
- return /* @__PURE__ */ jsxs8("div", { "data-testid": "devtools-settings-tab", style: { padding: tokens.spacing.md }, children: [
1172
- /* @__PURE__ */ jsxs8("fieldset", { className: sectionClass, style: { border: 0, padding: 0, margin: 0 }, children: [
1173
- /* @__PURE__ */ jsx10("legend", { className: headingClass, children: "Position" }),
1174
- POSITIONS.map((p) => /* @__PURE__ */ jsxs8("label", { className: radioRowClass, children: [
1175
- /* @__PURE__ */ jsx10(
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(
1176
1632
  "input",
1177
1633
  {
1178
1634
  type: "radio",
@@ -1184,13 +1640,13 @@ function SettingsTab() {
1184
1640
  }
1185
1641
  }
1186
1642
  ),
1187
- /* @__PURE__ */ jsx10("span", { children: p })
1643
+ /* @__PURE__ */ jsx12("span", { children: p })
1188
1644
  ] }, p))
1189
1645
  ] }),
1190
- /* @__PURE__ */ jsxs8("fieldset", { className: sectionClass, style: { border: 0, padding: 0, margin: 0 }, children: [
1191
- /* @__PURE__ */ jsx10("legend", { className: headingClass, children: "Theme" }),
1192
- THEMES.map((t) => /* @__PURE__ */ jsxs8("label", { className: radioRowClass, children: [
1193
- /* @__PURE__ */ jsx10(
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(
1194
1650
  "input",
1195
1651
  {
1196
1652
  type: "radio",
@@ -1202,15 +1658,23 @@ function SettingsTab() {
1202
1658
  }
1203
1659
  }
1204
1660
  ),
1205
- /* @__PURE__ */ jsx10("span", { children: t })
1661
+ /* @__PURE__ */ jsx12("span", { children: t })
1206
1662
  ] }, t))
1207
1663
  ] })
1208
1664
  ] });
1209
1665
  }
1210
1666
 
1211
1667
  // src/devtools/components/Panel.tsx
1212
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1213
- var TABS = ["requests", "routes", "errors", "csrf-readiness", "settings"];
1668
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1669
+ var TABS = [
1670
+ "requests",
1671
+ "routes",
1672
+ "agents",
1673
+ "actions",
1674
+ "errors",
1675
+ "csrf-readiness",
1676
+ "settings"
1677
+ ];
1214
1678
  function buildPanelStyles(styles, vertical, horizontal, verticalOffset) {
1215
1679
  return {
1216
1680
  panel: styles.css`
@@ -1295,7 +1759,7 @@ function Panel() {
1295
1759
  const tabBtnClass = cls.tabBtn;
1296
1760
  const closeBtnClass = cls.closeBtn;
1297
1761
  const bodyClass = cls.body;
1298
- return /* @__PURE__ */ jsxs9(
1762
+ return /* @__PURE__ */ jsxs11(
1299
1763
  "div",
1300
1764
  {
1301
1765
  role: "dialog",
@@ -1303,9 +1767,9 @@ function Panel() {
1303
1767
  "data-theo-devtools-panel": "",
1304
1768
  className: panelClass,
1305
1769
  children: [
1306
- /* @__PURE__ */ jsxs9("div", { className: headerClass, children: [
1307
- /* @__PURE__ */ jsxs9("div", { className: tabsClass, role: "tablist", children: [
1308
- /* @__PURE__ */ jsx11(
1770
+ /* @__PURE__ */ jsxs11("div", { className: headerClass, children: [
1771
+ /* @__PURE__ */ jsxs11("div", { className: tabsClass, role: "tablist", children: [
1772
+ /* @__PURE__ */ jsx13(
1309
1773
  "span",
1310
1774
  {
1311
1775
  className: styles.css`
@@ -1315,10 +1779,10 @@ function Panel() {
1315
1779
  opacity: 0.9;
1316
1780
  `,
1317
1781
  "aria-hidden": "true",
1318
- children: /* @__PURE__ */ jsx11(TheoLogo, { size: 16 })
1782
+ children: /* @__PURE__ */ jsx13(TheoLogo, { size: 16 })
1319
1783
  }
1320
1784
  ),
1321
- TABS.map((t) => /* @__PURE__ */ jsx11(
1785
+ TABS.map((t) => /* @__PURE__ */ jsx13(
1322
1786
  "button",
1323
1787
  {
1324
1788
  type: "button",
@@ -1334,7 +1798,7 @@ function Panel() {
1334
1798
  t
1335
1799
  ))
1336
1800
  ] }),
1337
- /* @__PURE__ */ jsx11(
1801
+ /* @__PURE__ */ jsx13(
1338
1802
  "button",
1339
1803
  {
1340
1804
  type: "button",
@@ -1347,107 +1811,20 @@ function Panel() {
1347
1811
  }
1348
1812
  )
1349
1813
  ] }),
1350
- /* @__PURE__ */ jsxs9("div", { className: bodyClass, "data-testid": "devtools-tab-body", children: [
1351
- state.activeTab === "requests" && /* @__PURE__ */ jsx11(RequestsTab, {}),
1352
- state.activeTab === "routes" && /* @__PURE__ */ jsx11(RoutesTab, {}),
1353
- state.activeTab === "errors" && /* @__PURE__ */ jsx11(ErrorsTab, {}),
1354
- state.activeTab === "csrf-readiness" && /* @__PURE__ */ jsx11(CsrfReadinessTab, {}),
1355
- state.activeTab === "settings" && /* @__PURE__ */ jsx11(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, {})
1356
1822
  ] })
1357
1823
  ]
1358
1824
  }
1359
1825
  );
1360
1826
  }
1361
1827
 
1362
- // src/devtools/shared.ts
1363
- var RING_BUFFER_CAP = 50;
1364
- var MAX_QUEUE_SIZE = 100;
1365
- var STORAGE_VERSION = 1;
1366
- var initialState = {
1367
- open: false,
1368
- visible: true,
1369
- position: "bottom-right",
1370
- theme: "system",
1371
- activeTab: "requests",
1372
- requests: [],
1373
- errors: [],
1374
- routeManifest: null,
1375
- activeRoutePath: null,
1376
- activeChain: []
1377
- };
1378
- var CHANNEL_REQUEST = "theo:devtools:request";
1379
- var CHANNEL_ERROR = "theo:devtools:error";
1380
- var CHANNEL_CSRF_WARN = "theo:devtools:csrf.warn";
1381
- var CHANNEL_MANIFEST = "theo:devtools:manifest";
1382
- var CHANNEL_ROUTE_MATCHED = "theo:devtools:route-matched";
1383
-
1384
- // src/devtools/dispatcher.ts
1385
- var _dispatch = null;
1386
- var _queue = [];
1387
- function queuable(fn) {
1388
- return (...args) => {
1389
- if (_dispatch) {
1390
- try {
1391
- fn(_dispatch, ...args);
1392
- } catch (err) {
1393
- console.error("[theo devtools] dispatch failed", err);
1394
- }
1395
- } else {
1396
- if (_queue.length >= MAX_QUEUE_SIZE) _queue.shift();
1397
- _queue.push((d) => {
1398
- fn(d, ...args);
1399
- });
1400
- }
1401
- };
1402
- }
1403
- function flushQueue() {
1404
- while (_queue.length) {
1405
- const fn = _queue.shift();
1406
- if (!fn || !_dispatch) break;
1407
- try {
1408
- fn(_dispatch);
1409
- } catch (err) {
1410
- console.error("[theo devtools] queued event failed", err);
1411
- }
1412
- }
1413
- }
1414
- var dispatcher = {
1415
- onRequest: queuable((d, req) => {
1416
- d({ type: "REQUEST_ADD", request: req });
1417
- }),
1418
- onError: queuable((d, err) => {
1419
- d({ type: "ERROR_ADD", error: err });
1420
- }),
1421
- onCsrfWarn: queuable((d, payload) => {
1422
- d({ type: "CSRF_WARN", payload });
1423
- }),
1424
- onManifestUpdated: queuable((d, manifest) => {
1425
- d({ type: "MANIFEST_UPDATED", manifest });
1426
- }),
1427
- onRouteMatched: queuable((d, path, chain) => {
1428
- d({ type: "ROUTE_MATCHED", path, chain });
1429
- }),
1430
- /**
1431
- * Wire React's dispatch function in. Idempotent (EC-24): only the
1432
- * NULL → non-null transition flushes the queue; subsequent non-null
1433
- * sets just replace the reference (used for StrictMode re-mount).
1434
- */
1435
- setDispatch(d) {
1436
- const prev = _dispatch;
1437
- _dispatch = d;
1438
- if (d && !prev) flushQueue();
1439
- },
1440
- /** Testing helper — clear queue + dispatch reference between tests. */
1441
- _reset() {
1442
- _dispatch = null;
1443
- _queue.length = 0;
1444
- },
1445
- /** Testing helper — observe queue length. */
1446
- _queueLength() {
1447
- return _queue.length;
1448
- }
1449
- };
1450
-
1451
1828
  // src/devtools/hmr-bridge.ts
1452
1829
  function getHot() {
1453
1830
  try {
@@ -1577,13 +1954,13 @@ function useActiveRoute() {
1577
1954
  }
1578
1955
 
1579
1956
  // src/devtools/hooks/useResolvedTheme.ts
1580
- import { useEffect as useEffect4, useState as useState5 } from "react";
1957
+ import { useEffect as useEffect4, useState as useState6 } from "react";
1581
1958
  function getSystemTheme() {
1582
1959
  if (typeof window === "undefined") return "dark";
1583
1960
  return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
1584
1961
  }
1585
1962
  function useResolvedTheme(theme) {
1586
- const [systemTheme, setSystemTheme] = useState5(() => getSystemTheme());
1963
+ const [systemTheme, setSystemTheme] = useState6(() => getSystemTheme());
1587
1964
  useEffect4(() => {
1588
1965
  if (theme !== "system") return;
1589
1966
  if (typeof window === "undefined") return;
@@ -1640,6 +2017,16 @@ function useShortcuts() {
1640
2017
  }, [dispatch, state.open]);
1641
2018
  }
1642
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
+
1643
2030
  // src/devtools/persistence.ts
1644
2031
  var STORAGE_KEYS = {
1645
2032
  version: "theo-devtools-storage-version",
@@ -1731,6 +2118,45 @@ function writeToStorage(s) {
1731
2118
  function appendCapped(arr, item, cap = RING_BUFFER_CAP) {
1732
2119
  return [item, ...arr].slice(0, cap);
1733
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
+ }
1734
2160
  function devtoolsReducer(state, action) {
1735
2161
  switch (action.type) {
1736
2162
  case "TOGGLE_PANEL":
@@ -1743,32 +2169,14 @@ function devtoolsReducer(state, action) {
1743
2169
  return { ...state, position: action.position };
1744
2170
  case "SET_THEME":
1745
2171
  return { ...state, theme: action.theme };
1746
- case "REQUEST_ADD":
1747
- return { ...state, requests: appendCapped(state.requests, action.request) };
1748
- case "ERROR_ADD":
1749
- return { ...state, errors: appendCapped(state.errors, action.error) };
1750
- case "CSRF_WARN": {
1751
- const errorRecord = {
1752
- // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id for devtools UI
1753
- id: `csrf-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,
1754
- type: "csrf.warn",
1755
- message: `CSRF warn: ${action.payload.method} ${action.payload.path} \u2014 ${action.payload.reason}`,
1756
- code: action.payload.code,
1757
- docsUrl: action.payload.docsUrl,
1758
- timestamp: Date.now()
1759
- };
1760
- return { ...state, errors: appendCapped(state.errors, errorRecord) };
1761
- }
2172
+ case "CSRF_WARN":
2173
+ return { ...state, errors: appendCapped(state.errors, csrfWarnToErrorRecord(action.payload)) };
1762
2174
  case "MANIFEST_UPDATED":
1763
2175
  return { ...state, routeManifest: action.manifest };
1764
2176
  case "ROUTE_MATCHED":
1765
2177
  return { ...state, activeRoutePath: action.path, activeChain: action.chain };
1766
- case "RESET_REQUESTS":
1767
- return { ...state, requests: [] };
1768
- case "RESET_ERRORS":
1769
- return { ...state, errors: [] };
1770
2178
  default:
1771
- return state;
2179
+ return reduceAppend(state, action) ?? reduceReset(state, action) ?? state;
1772
2180
  }
1773
2181
  }
1774
2182
 
@@ -1799,7 +2207,7 @@ function createStyles(shadowRoot) {
1799
2207
  }
1800
2208
 
1801
2209
  // src/devtools/Overlay.tsx
1802
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2210
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1803
2211
  function ActiveRouteTracker() {
1804
2212
  useActiveRoute();
1805
2213
  return null;
@@ -1810,7 +2218,7 @@ function ShortcutsTracker() {
1810
2218
  }
1811
2219
  function ThemeVars({ resolved }) {
1812
2220
  const css = `:host { color-scheme: ${resolved}; ${buildThemeCssVars(resolved)} }`;
1813
- return /* @__PURE__ */ jsx12("style", { "data-theo-devtools-theme": resolved, children: css });
2221
+ return /* @__PURE__ */ jsx14("style", { "data-theo-devtools-theme": resolved, children: css });
1814
2222
  }
1815
2223
  function Overlay({ shadowRoot }) {
1816
2224
  const [state, dispatch] = useReducer(
@@ -1832,8 +2240,10 @@ function Overlay({ shadowRoot }) {
1832
2240
  }, [state.position, state.theme, state.open, state.activeTab, state.visible]);
1833
2241
  useInsertionEffect(() => {
1834
2242
  dispatcher.setDispatch(dispatch);
2243
+ const uninstall = installDispatcherGlobal();
1835
2244
  return () => {
1836
2245
  dispatcher.setDispatch(null);
2246
+ uninstall();
1837
2247
  };
1838
2248
  }, []);
1839
2249
  useEffect6(() => {
@@ -1842,17 +2252,17 @@ function Overlay({ shadowRoot }) {
1842
2252
  sub.unsubscribe();
1843
2253
  };
1844
2254
  }, []);
1845
- return /* @__PURE__ */ jsx12(DevtoolsContext.Provider, { value: { shadowRoot, state, dispatch, styles }, children: /* @__PURE__ */ jsxs10(ShadowPortal, { children: [
1846
- /* @__PURE__ */ jsx12(ThemeVars, { resolved: resolvedTheme }),
1847
- /* @__PURE__ */ jsx12(ActiveRouteTracker, {}),
1848
- /* @__PURE__ */ jsx12(ShortcutsTracker, {}),
1849
- /* @__PURE__ */ jsx12(Indicator, {}),
1850
- /* @__PURE__ */ jsx12(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, {})
1851
2261
  ] }) });
1852
2262
  }
1853
2263
 
1854
2264
  // src/devtools/entry.tsx
1855
- import { jsx as jsx13 } from "react/jsx-runtime";
2265
+ import { jsx as jsx15 } from "react/jsx-runtime";
1856
2266
  var PORTAL_TAG = "theo-devtools-portal";
1857
2267
  function definePortalElement() {
1858
2268
  if (typeof customElements === "undefined") return;
@@ -1879,7 +2289,7 @@ function mount() {
1879
2289
  shadowRoot.appendChild(reactRootHost);
1880
2290
  const root = createRoot(reactRootHost);
1881
2291
  root.render(
1882
- /* @__PURE__ */ jsx13(StrictMode, { children: /* @__PURE__ */ jsx13(Overlay, { shadowRoot }) })
2292
+ /* @__PURE__ */ jsx15(StrictMode, { children: /* @__PURE__ */ jsx15(Overlay, { shadowRoot }) })
1883
2293
  );
1884
2294
  }
1885
2295
  if (typeof document !== "undefined") {