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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/preflight-node-version.ts"],"sourcesContent":["/**\n * Node version + native-binding ABI preflight for `theokit dev` / `theokit build` / `theokit start`.\n *\n * Why this exists:\n * In multi-Node-version setups (developer workflow: Node 22 for one\n * project, Node 20 for another, nvm switching frequently), the\n * `nohup env ...` invocation pattern strips the nvm PATH prefix and\n * the dev server launches under whichever `node` binary is FIRST in\n * PATH at spawn time — NOT the version the calling shell reported.\n *\n * When `theokit dev` boots under the wrong Node, every native binding\n * in `node_modules` (`better-sqlite3`, optionally `@lancedb/lancedb`,\n * etc.) fails with NODE_MODULE_VERSION mismatch on first use. The\n * error message references a low-level binary path; the dev wastes\n * time investigating a process-management issue.\n *\n * This preflight runs UPFRONT (before any module that touches native\n * bindings loads) and emits an actionable error referencing nvm + the\n * pinned `.nvmrc` floor.\n *\n * What it does:\n * 1. Reads `process.versions.node` + `process.versions.modules` (ABI).\n * 2. If Node major < 22, refuse to boot with a nvm install/use hint.\n * 3. If at least one native binding's compiled ABI != current ABI,\n * refuse to boot with a `pnpm rebuild <pkg>` hint.\n *\n * What it does NOT do:\n * * Auto-rebuild — that's the SDK's preflight (vitest setup, sentinel\n * cached). Auto-rebuild in a dev CLI is risky (slow, can run during\n * `pnpm dev` watch loops).\n * * Probe every native dep — only checks the ones the framework KNOWS\n * are required (currently `better-sqlite3` because of the cost\n * ledger + cron persistence + memory index DB).\n *\n * @internal\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\n\nconst NODE_MIN_MAJOR = 22\nconst NODE_MIN_FULL = '22.12.0'\n\ninterface BindingProbe {\n pkg: string\n /** path under <cwd>/node_modules — used only for the diagnostic message */\n hintPath?: string\n required: boolean\n}\n\nconst BINDINGS_TO_CHECK: readonly BindingProbe[] = [\n // better-sqlite3 is the only HARD-required native dep at framework boot\n // (cost ledger + cron persistence + memory index). Other native deps\n // (@lancedb/lancedb) are opt-in peers — preflight skips them when absent.\n { pkg: 'better-sqlite3', required: true },\n { pkg: '@lancedb/lancedb', required: false },\n]\n\nfunction checkNodeFloor(): { ok: true } | { ok: false; reason: string } {\n const major = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n if (major >= NODE_MIN_MAJOR) return { ok: true }\n return {\n ok: false,\n reason:\n `theokit requires Node >= ${NODE_MIN_FULL} (you are running v${process.versions.node}).\\n\\n` +\n 'Quick fix:\\n' +\n ' nvm use 22 # switch to a Node 22.x already installed\\n' +\n \" nvm install 22.12 # install + switch if you don't have it\\n\\n\" +\n 'Why this matters:\\n' +\n ' theokit ships AsyncLocalStorage + native fetch + structured WHATWG\\n' +\n ' Streams optimizations that depend on Node 22 internals. Older Node\\n' +\n ' versions silently miscompile SSR streaming and break native\\n' +\n ' bindings (NODE_MODULE_VERSION mismatch).\\n\\n' +\n 'Production tip: pin your CI runner to `actions/setup-node` v4 with\\n' +\n \" `node-version-file: .nvmrc` — that's what `create-theokit` writes.\",\n }\n}\n\nfunction checkBindingAbi(cwd: string): { ok: true } | { ok: false; reason: string } {\n const offenders: string[] = []\n // Build createRequire from the cwd so we resolve the consumer's deps,\n // NOT theokit's own node_modules.\n const cwdRequire = createRequire(join(cwd, 'package.json'))\n for (const probe of BINDINGS_TO_CHECK) {\n try {\n cwdRequire.resolve(probe.pkg)\n } catch {\n // not installed — required vs optional gates the behaviour\n if (probe.required) {\n offenders.push(`${probe.pkg} (required dep is NOT installed — run \\`pnpm install\\`)`)\n }\n continue\n }\n // Try a dry require — better-sqlite3 dlopens at load-time, so the ABI\n // mismatch surfaces here as a sync throw.\n try {\n cwdRequire(probe.pkg)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (/NODE_MODULE_VERSION|was compiled against|did not self-register/.test(msg)) {\n offenders.push(`${probe.pkg} (compiled against different Node ABI)`)\n } else if (probe.required) {\n // surface non-ABI errors verbatim for required deps\n offenders.push(`${probe.pkg} (require failed: ${msg.split('\\n')[0]})`)\n }\n }\n }\n if (offenders.length === 0) return { ok: true }\n return {\n ok: false,\n reason:\n `Native binding ABI mismatch detected (Node v${process.versions.node}, ABI ${process.versions.modules}).\\n\\n` +\n 'Offending packages:\\n' +\n offenders.map((o) => ` - ${o}`).join('\\n') +\n '\\n\\nQuick fix:\\n' +\n ' pnpm rebuild # rebuild ALL native bindings for current Node\\n' +\n ' pnpm rebuild better-sqlite3 # rebuild just one\\n\\n' +\n 'Why this happened:\\n' +\n ' Your `theokit dev` (or build/start) launched under a different\\n' +\n ' Node version than the one used to install/build node_modules.\\n' +\n ' Common cause: `nohup env ...` strips the nvm PATH prefix, so the\\n' +\n ' dev server picked the system `node` instead of `nvm use 22`.\\n\\n' +\n 'Permanent fix:\\n' +\n ' Prefix your dev/start command with the absolute Node 22 path:\\n' +\n ' PATH=\"$(nvm which 22 | xargs dirname):$PATH\" theokit dev\\n' +\n ' Or use direnv with `.envrc` containing `use node 22.12.0`.',\n }\n}\n\n/**\n * Read `.nvmrc` from cwd if present — used only to enrich error messages\n * with the project's pinned floor. Falls back to the framework default.\n *\n * @internal\n */\nfunction readNvmrcFloor(cwd: string): string | undefined {\n const path = join(cwd, '.nvmrc')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI reads consumer's own .nvmrc; consumer owns cwd\n if (!existsSync(path)) return undefined\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- same\n return readFileSync(path, 'utf8').trim()\n } catch {\n return undefined\n }\n}\n\n/**\n * Run the Node + ABI preflight. Throws a descriptive Error on mismatch\n * BEFORE any module that imports better-sqlite3 loads. The error message\n * is the only user-facing artefact — caller can rely on `error.message`\n * being a full actionable diagnostic.\n *\n * @throws Error with multi-line actionable message when Node version or\n * native binding ABI is wrong.\n * @internal\n */\nexport function preflightNodeAndBindings(cwd: string): void {\n const nodeCheck = checkNodeFloor()\n if (!nodeCheck.ok) {\n const pinned = readNvmrcFloor(cwd)\n const suffix = pinned !== undefined ? `\\n\\nYour project pins Node ${pinned} in .nvmrc.` : ''\n throw new Error(`[theokit preflight] ${nodeCheck.reason}${suffix}`)\n }\n const abiCheck = checkBindingAbi(cwd)\n if (!abiCheck.ok) {\n throw new Error(`[theokit preflight] ${abiCheck.reason}`)\n }\n}\n"],"mappings":";;;;AAqCA,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAStB,IAAM,oBAA6C;AAAA;AAAA;AAAA;AAAA,EAIjD,EAAE,KAAK,kBAAkB,UAAU,KAAK;AAAA,EACxC,EAAE,KAAK,oBAAoB,UAAU,MAAM;AAC7C;AAEA,SAAS,iBAA+D;AACtE,QAAM,QAAQ,OAAO,SAAS,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE;AAC5E,MAAI,SAAS,eAAgB,QAAO,EAAE,IAAI,KAAK;AAC/C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,4BAA4B,aAAa,sBAAsB,QAAQ,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxF;AACF;AAEA,SAAS,gBAAgB,KAA2D;AAClF,QAAM,YAAsB,CAAC;AAG7B,QAAM,aAAa,cAAc,KAAK,KAAK,cAAc,CAAC;AAC1D,aAAW,SAAS,mBAAmB;AACrC,QAAI;AACF,iBAAW,QAAQ,MAAM,GAAG;AAAA,IAC9B,QAAQ;AAEN,UAAI,MAAM,UAAU;AAClB,kBAAU,KAAK,GAAG,MAAM,GAAG,8DAAyD;AAAA,MACtF;AACA;AAAA,IACF;AAGA,QAAI;AACF,iBAAW,MAAM,GAAG;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAI,iEAAiE,KAAK,GAAG,GAAG;AAC9E,kBAAU,KAAK,GAAG,MAAM,GAAG,wCAAwC;AAAA,MACrE,WAAW,MAAM,UAAU;AAEzB,kBAAU,KAAK,GAAG,MAAM,GAAG,qBAAqB,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,IAAI,KAAK;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,+CAA+C,QAAQ,SAAS,IAAI,SAAS,QAAQ,SAAS,OAAO;AAAA;AAAA;AAAA,IAErG,UAAU,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAC1C;AAAA,EAYJ;AACF;AAQA,SAAS,eAAe,KAAiC;AACvD,QAAM,OAAO,KAAK,KAAK,QAAQ;AAE/B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AAEF,WAAO,aAAa,MAAM,MAAM,EAAE,KAAK;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,yBAAyB,KAAmB;AAC1D,QAAM,YAAY,eAAe;AACjC,MAAI,CAAC,UAAU,IAAI;AACjB,UAAM,SAAS,eAAe,GAAG;AACjC,UAAM,SAAS,WAAW,SAAY;AAAA;AAAA,yBAA8B,MAAM,gBAAgB;AAC1F,UAAM,IAAI,MAAM,uBAAuB,UAAU,MAAM,GAAG,MAAM,EAAE;AAAA,EACpE;AACA,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAAA,EAC1D;AACF;","names":[]}
@@ -1,16 +1,17 @@
1
1
  // src/devtools/shared.ts
2
2
  var RING_BUFFER_CAP = 50;
3
3
  var MAX_QUEUE_SIZE = 100;
4
- var STORAGE_VERSION = 1;
4
+ var STORAGE_VERSION = 2;
5
5
  var initialState = {
6
6
  open: false,
7
7
  visible: true,
8
8
  position: "bottom-right",
9
- theme: "system",
9
+ theme: "dark",
10
10
  activeTab: "requests",
11
11
  requests: [],
12
12
  errors: [],
13
13
  agentRuns: [],
14
+ actionCalls: [],
14
15
  routeManifest: null,
15
16
  activeRoutePath: null,
16
17
  activeChain: []
@@ -70,6 +71,9 @@ var dispatcher = {
70
71
  onAgentRun: queuable((d, run) => {
71
72
  d({ type: "AGENT_RUN_ADD", run });
72
73
  }),
74
+ onActionCall: queuable((d, record) => {
75
+ d({ type: "ACTION_CALL_ADD", record });
76
+ }),
73
77
  /**
74
78
  * Wire React's dispatch function in. Idempotent (EC-24): only the
75
79
  * NULL → non-null transition flushes the queue; subsequent non-null
@@ -102,4 +106,4 @@ export {
102
106
  CHANNEL_ROUTE_MATCHED,
103
107
  dispatcher
104
108
  };
105
- //# sourceMappingURL=chunk-OK2PJOLK.js.map
109
+ //# sourceMappingURL=chunk-KJVEJILQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/devtools/shared.ts","../src/devtools/dispatcher.ts"],"sourcesContent":["/**\n * Devtools — shared types.\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\n\nexport type DevtoolsPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\nexport type DevtoolsTab =\n | 'requests'\n | 'routes'\n | 'agents'\n | 'actions'\n | 'errors'\n | 'csrf-readiness'\n | 'settings'\nexport type DevtoolsTheme = 'light' | 'dark' | 'system'\n\nexport interface RequestRecord {\n id: string\n traceId: string\n method: string\n path: string\n status: number\n durationMs: number\n startedAt: number\n csrfWarn?: { code: string; docsUrl: string }\n headers?: Record<string, string>\n bodyPreview?: string\n bodyLength?: number\n bodyTruncated?: boolean\n}\n\nexport interface ErrorRecord {\n id: string\n type: 'console' | 'unhandled' | 'csrf.warn'\n message: string\n stack?: string\n code?: string\n docsUrl?: string\n timestamp: number\n}\n\nexport interface CsrfWarnPayload {\n event: 'csrf.warn'\n code: string\n docsUrl: string\n method: string\n path: string\n reason: string\n}\n\nexport interface RouteInfo {\n path: string\n absoluteFilePath: string\n layoutChain: string[]\n hasLoading: boolean\n hasError: boolean\n hasNotFound: boolean\n}\n\nexport interface RouteManifest {\n routes: RouteInfo[]\n}\n\n/**\n * Per-agent-run telemetry surfaced to the Agents devtools tab.\n *\n * Emitted by `trackAgentRun` (server-side) via the dispatcher in dev mode;\n * prod tree-shakes the entire wire (v1.1 EC-4 `__IS_DEV` IIFE guard).\n * Type lives here (devtools/shared) since both producer (server/cost) and\n * consumer (AgentsTab) need the shape; following architecture v3 ADR-0001\n * rule \"shared types in core/contracts\" is the canonical home — for this\n * one we keep it in devtools/shared to minimize churn in core/contracts/.\n */\nexport interface AgentRunRecord {\n id: string\n timestamp: number\n userId: string\n model: string\n tokensInput: number\n tokensOutput: number\n costUsd: number\n status: 'finished' | 'error' | 'aborted'\n}\n\n/**\n * T5.1 — Per-action-call telemetry surfaced to the Actions devtools tab.\n *\n * Emitted by `action-execute.ts` (server-side) via the dispatcher in dev\n * mode; prod tree-shakes the wire (mirrors AgentRunRecord pattern).\n * Field-level error pins consume `error.fields` directly when status='error'\n * (per plan ADR D6).\n */\nexport interface ActionCallRecord {\n id: string\n timestamp: number\n name: string\n input: unknown\n output?: unknown\n error?: {\n code: string\n message: string\n fields?: Record<string, string[]>\n issues?: unknown[]\n }\n durationMs: number\n status: 'success' | 'error'\n}\n\nexport interface DevtoolsState {\n open: boolean\n visible: boolean\n position: DevtoolsPosition\n theme: DevtoolsTheme\n activeTab: DevtoolsTab\n requests: RequestRecord[]\n errors: ErrorRecord[]\n agentRuns: AgentRunRecord[]\n actionCalls: ActionCallRecord[]\n routeManifest: RouteManifest | null\n activeRoutePath: string | null\n activeChain: string[]\n}\n\nexport type DevtoolsAction =\n | { type: 'TOGGLE_PANEL' }\n | { type: 'TOGGLE_VISIBLE' }\n | { type: 'SET_TAB'; tab: DevtoolsTab }\n | { type: 'SET_POSITION'; position: DevtoolsPosition }\n | { type: 'SET_THEME'; theme: DevtoolsTheme }\n | { type: 'REQUEST_ADD'; request: RequestRecord }\n | { type: 'ERROR_ADD'; error: ErrorRecord }\n | { type: 'CSRF_WARN'; payload: CsrfWarnPayload }\n | { type: 'MANIFEST_UPDATED'; manifest: RouteManifest }\n | { type: 'ROUTE_MATCHED'; path: string; chain: string[] }\n | { type: 'AGENT_RUN_ADD'; run: AgentRunRecord }\n | { type: 'ACTION_CALL_ADD'; record: ActionCallRecord }\n | { type: 'RESET_REQUESTS' }\n | { type: 'RESET_ERRORS' }\n | { type: 'RESET_AGENT_RUNS' }\n | { type: 'RESET_ACTION_CALLS' }\n\nexport const RING_BUFFER_CAP = 50\nexport const MAX_QUEUE_SIZE = 100\nexport const STORAGE_VERSION = 2\n\nexport const initialState: DevtoolsState = {\n open: false,\n visible: true,\n position: 'bottom-right',\n theme: 'dark',\n activeTab: 'requests',\n requests: [],\n errors: [],\n agentRuns: [],\n actionCalls: [],\n routeManifest: null,\n activeRoutePath: null,\n activeChain: [],\n}\n\nexport const CHANNEL_REQUEST = 'theo:devtools:request' as const\nexport const CHANNEL_ERROR = 'theo:devtools:error' as const\nexport const CHANNEL_CSRF_WARN = 'theo:devtools:csrf.warn' as const\nexport const CHANNEL_MANIFEST = 'theo:devtools:manifest' as const\nexport const CHANNEL_ROUTE_MATCHED = 'theo:devtools:route-matched' as const\nexport const CHANNEL_AGENT_RUN = 'theo:devtools:agent.run' as const\nexport const CHANNEL_ACTION_CALL = 'theo:devtools:action.call' as const\n","/**\n * T2.1 — Devtools dispatcher with pre-mount event queue.\n *\n * Pattern: events emitted from anywhere (server WS, browser console.error,\n * dispatcher.onCsrfWarn) can fire BEFORE the React tree mounts. Each call\n * either dispatches immediately (when `setDispatch` registered a dispatch\n * fn) or queues until React is ready, then flushes on first mount.\n *\n * EC-23: queue capped at MAX_QUEUE_SIZE (FIFO eviction).\n * EC-24: setDispatch is idempotent — flush only on NULL → non-null\n * transition (StrictMode re-mount safe).\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\nimport {\n type ActionCallRecord,\n type AgentRunRecord,\n type CsrfWarnPayload,\n type DevtoolsAction,\n type ErrorRecord,\n MAX_QUEUE_SIZE,\n type RequestRecord,\n type RouteManifest,\n} from './shared.js'\n\ntype Dispatch = (action: DevtoolsAction) => void\ntype QueuedItem = (d: Dispatch) => void\n\nlet _dispatch: Dispatch | null = null\nconst _queue: QueuedItem[] = []\n\nfunction queuable<Args extends unknown[]>(\n fn: (d: Dispatch, ...args: Args) => void,\n): (...args: Args) => void {\n return (...args: Args) => {\n if (_dispatch) {\n try {\n fn(_dispatch, ...args)\n } catch (err) {\n // EC-25: an error inside reducer-bound dispatch path MUST NOT bubble\n // to the original event source (e.g. logger / HMR callback / global\n // error handler). Log and continue.\n\n console.error('[theo devtools] dispatch failed', err)\n }\n } else {\n // EC-23: cap queue, FIFO-evict if full\n if (_queue.length >= MAX_QUEUE_SIZE) _queue.shift()\n _queue.push((d) => {\n fn(d, ...args)\n })\n }\n }\n}\n\nfunction flushQueue(): void {\n while (_queue.length) {\n const fn = _queue.shift()\n if (!fn || !_dispatch) break\n try {\n fn(_dispatch)\n } catch (err) {\n console.error('[theo devtools] queued event failed', err)\n }\n }\n}\n\nexport const dispatcher = {\n onRequest: queuable((d: Dispatch, req: RequestRecord) => {\n d({ type: 'REQUEST_ADD', request: req })\n }),\n onError: queuable((d: Dispatch, err: ErrorRecord) => {\n d({ type: 'ERROR_ADD', error: err })\n }),\n onCsrfWarn: queuable((d: Dispatch, payload: CsrfWarnPayload) => {\n d({ type: 'CSRF_WARN', payload })\n }),\n onManifestUpdated: queuable((d: Dispatch, manifest: RouteManifest) => {\n d({ type: 'MANIFEST_UPDATED', manifest })\n }),\n onRouteMatched: queuable((d: Dispatch, path: string, chain: string[]) => {\n d({ type: 'ROUTE_MATCHED', path, chain })\n }),\n onAgentRun: queuable((d: Dispatch, run: AgentRunRecord) => {\n d({ type: 'AGENT_RUN_ADD', run })\n }),\n onActionCall: queuable((d: Dispatch, record: ActionCallRecord) => {\n d({ type: 'ACTION_CALL_ADD', record })\n }),\n\n /**\n * Wire React's dispatch function in. Idempotent (EC-24): only the\n * NULL → non-null transition flushes the queue; subsequent non-null\n * sets just replace the reference (used for StrictMode re-mount).\n */\n setDispatch(d: Dispatch | null): void {\n const prev = _dispatch\n _dispatch = d\n if (d && !prev) flushQueue()\n },\n\n /** Testing helper — clear queue + dispatch reference between tests. */\n _reset(): void {\n _dispatch = null\n _queue.length = 0\n },\n\n /** Testing helper — observe queue length. */\n _queueLength(): number {\n return _queue.length\n },\n}\n\nexport type Dispatcher = typeof dispatcher\n"],"mappings":";AA8IO,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAExB,IAAM,eAA8B;AAAA,EACzC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,QAAQ,CAAC;AAAA,EACT,WAAW,CAAC;AAAA,EACZ,aAAa,CAAC;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa,CAAC;AAChB;AAEO,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AACtB,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;;;ACzIrC,IAAI,YAA6B;AACjC,IAAM,SAAuB,CAAC;AAE9B,SAAS,SACP,IACyB;AACzB,SAAO,IAAI,SAAe;AACxB,QAAI,WAAW;AACb,UAAI;AACF,WAAG,WAAW,GAAG,IAAI;AAAA,MACvB,SAAS,KAAK;AAKZ,gBAAQ,MAAM,mCAAmC,GAAG;AAAA,MACtD;AAAA,IACF,OAAO;AAEL,UAAI,OAAO,UAAU,eAAgB,QAAO,MAAM;AAClD,aAAO,KAAK,CAAC,MAAM;AACjB,WAAG,GAAG,GAAG,IAAI;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,aAAmB;AAC1B,SAAO,OAAO,QAAQ;AACpB,UAAM,KAAK,OAAO,MAAM;AACxB,QAAI,CAAC,MAAM,CAAC,UAAW;AACvB,QAAI;AACF,SAAG,SAAS;AAAA,IACd,SAAS,KAAK;AACZ,cAAQ,MAAM,uCAAuC,GAAG;AAAA,IAC1D;AAAA,EACF;AACF;AAEO,IAAM,aAAa;AAAA,EACxB,WAAW,SAAS,CAAC,GAAa,QAAuB;AACvD,MAAE,EAAE,MAAM,eAAe,SAAS,IAAI,CAAC;AAAA,EACzC,CAAC;AAAA,EACD,SAAS,SAAS,CAAC,GAAa,QAAqB;AACnD,MAAE,EAAE,MAAM,aAAa,OAAO,IAAI,CAAC;AAAA,EACrC,CAAC;AAAA,EACD,YAAY,SAAS,CAAC,GAAa,YAA6B;AAC9D,MAAE,EAAE,MAAM,aAAa,QAAQ,CAAC;AAAA,EAClC,CAAC;AAAA,EACD,mBAAmB,SAAS,CAAC,GAAa,aAA4B;AACpE,MAAE,EAAE,MAAM,oBAAoB,SAAS,CAAC;AAAA,EAC1C,CAAC;AAAA,EACD,gBAAgB,SAAS,CAAC,GAAa,MAAc,UAAoB;AACvE,MAAE,EAAE,MAAM,iBAAiB,MAAM,MAAM,CAAC;AAAA,EAC1C,CAAC;AAAA,EACD,YAAY,SAAS,CAAC,GAAa,QAAwB;AACzD,MAAE,EAAE,MAAM,iBAAiB,IAAI,CAAC;AAAA,EAClC,CAAC;AAAA,EACD,cAAc,SAAS,CAAC,GAAa,WAA6B;AAChE,MAAE,EAAE,MAAM,mBAAmB,OAAO,CAAC;AAAA,EACvC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOD,YAAY,GAA0B;AACpC,UAAM,OAAO;AACb,gBAAY;AACZ,QAAI,KAAK,CAAC,KAAM,YAAW;AAAA,EAC7B;AAAA;AAAA,EAGA,SAAe;AACb,gBAAY;AACZ,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA,EAGA,eAAuB;AACrB,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
@@ -9,7 +9,7 @@ var __IS_DEV = (() => {
9
9
  async function trackAgentRun(input, opts) {
10
10
  if (__IS_DEV) {
11
11
  try {
12
- const mod = await import("./dispatcher-IK3FVMX5.js");
12
+ const mod = await import("./dispatcher-AQJGI3HU.js");
13
13
  mod.dispatcher.onAgentRun({
14
14
  // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id
15
15
  id: `run-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,
@@ -182,4 +182,4 @@ export {
182
182
  InMemoryUsageStorage,
183
183
  trackAgentTools
184
184
  };
185
- //# sourceMappingURL=chunk-JBC7PFV4.js.map
185
+ //# sourceMappingURL=chunk-M23FP2AS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/cost/track-agent-run.ts","../src/server/cost/usage-storage-memory.ts","../src/server/cost/track-agent-tools.ts"],"sourcesContent":["import type { AgentRunRecord } from '../../devtools/shared.js'\n\nimport type { UsageRecord, UsageStorageAdapter } from './cost-types.js'\n\nexport interface TrackAgentRunInput {\n userId: string\n model: string\n tokens: { input: number; output: number }\n costUsd: number\n /** Defaults to `new Date()`. */\n timestamp?: Date\n /** Optional execution status surfaced to devtools (default 'finished'). */\n status?: 'finished' | 'error' | 'aborted'\n}\n\nexport interface TrackAgentRunOptions {\n /** Adapter resolved from `theo.config.ts > cost.storage`. */\n storage: UsageStorageAdapter | undefined\n}\n\n// v1.1 EC-4 MUST FIX — universal dev gate (Vite OR tsup-bundled).\n// Both checks are statically replaceable by bundlers in prod build,\n// so the entire dispatcher import is tree-shaken from the prod bundle.\nconst __IS_DEV = (() => {\n try {\n return (import.meta as { env?: { DEV?: boolean } }).env?.DEV === true\n } catch {\n return process.env.NODE_ENV !== 'production'\n }\n})()\n\n/**\n * Record a single agent run's usage + cost. Companion to the client-side\n * `<CostMeter>` from `@usetheo/ui`.\n *\n * EC-14: this function NEVER bubbles errors back to the caller. Adapter\n * failures (network outage, DB down, etc.) are logged via `console.warn`\n * and swallowed. The agent response MUST NOT fail because cost tracking\n * is degraded.\n *\n * No-op when `storage` is undefined (cost tracking unconfigured).\n *\n * In dev mode (Vite OR tsup-built with NODE_ENV != 'production'), fires a\n * `theokit-evolution-ci-and-dx` Phase 3 dispatcher event so the devtools\n * Agents tab can render the run. Prod tree-shakes the entire dispatcher\n * import via the `__IS_DEV` IIFE guard.\n *\n * @see docs/concepts/cost-tracking.md (when implemented in T6.3)\n */\nexport async function trackAgentRun(\n input: TrackAgentRunInput,\n opts: TrackAgentRunOptions,\n): Promise<void> {\n // Dev-only: surface run to devtools Agents tab (T3.1)\n if (__IS_DEV) {\n try {\n const mod = (await import('../../devtools/dispatcher.js')) as {\n dispatcher: { onAgentRun: (r: AgentRunRecord) => void }\n }\n mod.dispatcher.onAgentRun({\n // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id\n id: `run-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,\n timestamp: (input.timestamp ?? new Date()).getTime(),\n userId: input.userId,\n model: input.model,\n tokensInput: input.tokens.input,\n tokensOutput: input.tokens.output,\n costUsd: input.costUsd,\n status: input.status ?? 'finished',\n })\n } catch {\n // devtools dispatcher missing in some prod-like bundle — silently skip\n }\n }\n\n if (!opts.storage) return\n const record: UsageRecord = {\n userId: input.userId,\n model: input.model,\n tokens: input.tokens,\n costUsd: input.costUsd,\n timestamp: input.timestamp ?? new Date(),\n }\n try {\n await opts.storage.record(record)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n\n console.warn(\n `[theokit:cost] usage storage record failed: ${message}. ` +\n '(Response is unaffected — cost tracking degraded.)',\n )\n }\n}\n","import type { StorageAdapter } from '../storage/storage-types.js'\n\nimport type {\n ToolUsageRecord,\n UsageQuery,\n UsageRecord,\n UsageResult,\n UsageStorageAdapter,\n} from './cost-types.js'\n\n/**\n * In-memory usage storage for dev/tests. Unbounded — production\n * deployments MUST plug in a durable adapter (Postgres/Redis recipes\n * land with R0.6.7; documented in EC-114).\n *\n * Phase 5 — Production-Readiness #4: accepts both `UsageRecord` (LLM call,\n * kind='llm' or omitted) AND `ToolUsageRecord` (tool call, kind='tool').\n * `getUsage` only sums LLM records (tools have no token/cost dimension).\n *\n * EC-9 (backward compat): input without `kind` is normalized to `kind:'llm'`\n * — adapters from older versions stay working.\n *\n * Concurrency: Node's single-threaded event loop guarantees that\n * `Array.push` is atomic, so concurrent record() calls cannot lose data.\n */\nexport class InMemoryUsageStorage implements UsageStorageAdapter, StorageAdapter {\n readonly name = 'memory'\n readonly #records: (UsageRecord | ToolUsageRecord)[] = []\n\n /**\n * T2.3 — `StorageAdapter` lifecycle hook (ADR-0007 D6). In-memory storage\n * has no real cleanup to perform — this is intentionally a noop so the\n * adapter can be registered with `StorageManager.register()` and\n * participate in graceful shutdown without changing behavior.\n *\n * Does NOT clear stored records (dispose ≠ reset). For test reset use\n * a fresh instance.\n */\n dispose(): Promise<void> {\n // Intentional noop — see jsdoc above\n return Promise.resolve()\n }\n\n async record(input: UsageRecord | ToolUsageRecord): Promise<void> {\n // EC-9: normalize legacy input (no `kind` field) to kind:'llm'.\n const normalized: UsageRecord | ToolUsageRecord =\n 'kind' in input && input.kind === 'tool'\n ? { ...input }\n : ({ ...input, kind: 'llm' as const } satisfies UsageRecord)\n this.#records.push(normalized)\n return Promise.resolve()\n }\n\n async getUsage(query: UsageQuery): Promise<UsageResult> {\n const from = query.period.from.getTime()\n const to = query.period.to.getTime()\n let totalTokens = 0\n let totalCostUsd = 0\n let runs = 0\n for (const r of this.#records) {\n if (r.kind === 'tool') continue\n if (r.userId !== query.userId) continue\n const t = r.timestamp.getTime()\n if (t < from || t > to) continue\n totalTokens += r.tokens.input + r.tokens.output\n totalCostUsd += r.costUsd\n runs += 1\n }\n return Promise.resolve({ totalTokens, totalCostUsd, runs })\n }\n\n /** @internal — test helper to inspect raw record stream */\n __getRecords(): readonly (UsageRecord | ToolUsageRecord)[] {\n return this.#records\n }\n}\n","import type { ToolUsageRecord, UsageStorageAdapter } from './cost-types.js'\n\n/**\n * Phase 5 — Production-Readiness #4: tool lifecycle hooks for cost tracking.\n *\n * The SDK calls `onToolStart` → handler runs → `onToolEnd` (success) or\n * `onToolError` (throw). This factory returns the three callbacks ready to\n * attach to `Agent.create({ tools, onToolStart, onToolEnd, onToolError })`.\n *\n * Each successful tool call writes a `ToolUsageRecord{success:true}`; each\n * failure writes `ToolUsageRecord{success:false}` with `errorMessage` from\n * the thrown Error.\n *\n * Invariants:\n * - `onToolStart` records nothing yet (no duration available). Only stashes\n * a `startedAt` timestamp by `callId` in a per-factory Map.\n * - `onToolEnd` reads the stashed timestamp, computes `durationMs`, writes\n * a successful record. Removes the Map entry.\n * - `onToolError` analog with `success:false`.\n * - Hook throws are SWALLOWED to stderr — observation hooks must not crash\n * the run (per the v1.1.0 release contract).\n * - EC-8 (SHOULD TEST) — orphan starts (no matching End/Error) are pruned\n * on every onToolStart if older than 5 minutes. Bounded memory.\n * - EC-16 (DOCUMENT) — callId uniqueness is SDK contract; duplicate Start\n * for the same callId uses last-write-wins.\n */\n\nconst ORPHAN_TTL_MS = 5 * 60 * 1000\n\ninterface PendingStart {\n startedAt: number\n userId: string\n conversationId: string\n}\n\nexport interface TrackAgentToolsOptions {\n storage: UsageStorageAdapter\n /** Defaults to () => new Date() — overridable for deterministic testing. */\n now?: () => Date\n /** Identifier for the user — passed through to ToolUsageRecord.userId. */\n userId?: string\n /** Identifier for the conversation — passed through to ToolUsageRecord.conversationId. */\n conversationId?: string\n}\n\nexport interface ToolHookEvent {\n callId: string\n name: string\n // SDK shape — we only read these structurally\n [key: string]: unknown\n}\n\nexport interface TrackAgentToolsHooks {\n onToolStart: (event: ToolHookEvent) => void\n onToolEnd: (event: ToolHookEvent) => void\n onToolError: (event: ToolHookEvent) => void\n}\n\n/**\n * Create the three tool-lifecycle callbacks. Hand the returned object's\n * methods to `Agent.create({ tools, onToolStart, onToolEnd, onToolError })`.\n */\nexport function trackAgentTools(opts: TrackAgentToolsOptions): TrackAgentToolsHooks {\n const pending = new Map<string, PendingStart>()\n const now = opts.now ?? ((): Date => new Date())\n const userId = opts.userId ?? 'anonymous'\n const conversationId = opts.conversationId ?? 'unknown'\n\n function pruneOrphans(): void {\n const cutoff = now().getTime() - ORPHAN_TTL_MS\n for (const [id, p] of pending) {\n if (p.startedAt < cutoff) pending.delete(id)\n }\n }\n\n function safeRecord(record: ToolUsageRecord): void {\n Promise.resolve(opts.storage.record(record)).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit:trackAgentTools] storage.record failed: ${msg}`)\n })\n }\n\n return {\n onToolStart(event: ToolHookEvent): void {\n try {\n pruneOrphans()\n pending.set(event.callId, {\n startedAt: now().getTime(),\n userId,\n conversationId,\n })\n } catch (err) {\n console.warn(\n `[theokit:trackAgentTools] onToolStart hook crashed (swallowed): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n },\n onToolEnd(event: ToolHookEvent): void {\n try {\n const start = pending.get(event.callId)\n const startedAt = start?.startedAt ?? now().getTime()\n const durationMs = start ? now().getTime() - startedAt : 0\n pending.delete(event.callId)\n if (!start) {\n console.warn(\n `[theokit:trackAgentTools] orphan onToolEnd for callId=${event.callId}; durationMs:0 recorded`,\n )\n }\n safeRecord({\n kind: 'tool',\n userId: start?.userId ?? userId,\n conversationId: start?.conversationId ?? conversationId,\n toolName: event.name,\n callId: event.callId,\n success: true,\n durationMs,\n timestamp: now(),\n })\n } catch (err) {\n console.warn(\n `[theokit:trackAgentTools] onToolEnd hook crashed (swallowed): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n },\n onToolError(event: ToolHookEvent): void {\n try {\n const start = pending.get(event.callId)\n const startedAt = start?.startedAt ?? now().getTime()\n const durationMs = start ? now().getTime() - startedAt : 0\n pending.delete(event.callId)\n const error = event.error\n let errorMessage = 'unknown error'\n if (error instanceof Error) errorMessage = error.message\n else if (typeof error === 'string') errorMessage = error\n safeRecord({\n kind: 'tool',\n userId: start?.userId ?? userId,\n conversationId: start?.conversationId ?? conversationId,\n toolName: event.name,\n callId: event.callId,\n success: false,\n durationMs,\n errorMessage,\n timestamp: now(),\n })\n } catch (err) {\n console.warn(\n `[theokit:trackAgentTools] onToolError hook crashed (swallowed): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n },\n }\n}\n\n/**\n * @internal — testing helper. Inspects the size of the pending-starts Map of\n * a factory instance. Not in the public API.\n */\nexport const __internalsForTesting = {\n ORPHAN_TTL_MS,\n}\n"],"mappings":"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","names":[]}
@@ -8,6 +8,7 @@ import {
8
8
  import {
9
9
  BATCH_PATH,
10
10
  CSP_REPORT_PATH,
11
+ CsrfReadinessStore,
11
12
  TRACE_HEADER,
12
13
  applySecurityHeaders,
13
14
  createCorsHandler,
@@ -23,13 +24,17 @@ import {
23
24
  handleCsrfReadiness,
24
25
  loadEnv,
25
26
  logRequest,
26
- matchRoute,
27
27
  resolveTransformer,
28
- scanServerActions,
29
- scanServerRoutes,
30
- scanWebSocketRoutes,
31
28
  sendError
32
- } from "./chunk-OEB3OIYP.js";
29
+ } from "./chunk-OJHO2X7C.js";
30
+ import {
31
+ matchRoute,
32
+ scanServerRoutes,
33
+ scanWebSocketRoutes
34
+ } from "./chunk-2BQYEBCK.js";
35
+ import {
36
+ scanServerActions
37
+ } from "./chunk-7DHGV77J.js";
33
38
  import {
34
39
  generateNonce
35
40
  } from "./chunk-PB4GR7EP.js";
@@ -612,13 +617,17 @@ function createActionMiddleware(vite, serverDir, options) {
612
617
  csrfMode,
613
618
  disallowed
614
619
  );
615
- logRequest({
616
- method: req.method ?? "POST",
617
- url,
618
- status: res.statusCode,
619
- duration: Date.now() - start,
620
- requestId
621
- });
620
+ logRequest(
621
+ {
622
+ method: req.method ?? "POST",
623
+ url,
624
+ status: res.statusCode,
625
+ duration: Date.now() - start,
626
+ requestId
627
+ },
628
+ void 0,
629
+ req
630
+ );
622
631
  })();
623
632
  };
624
633
  }
@@ -698,6 +707,35 @@ async function handleBatchInline(req, res, batching, requestId) {
698
707
  sendError(res, "BATCH_ERROR", message, 400, void 0, requestId);
699
708
  }
700
709
  }
710
+ async function runPluginsBeforeRouteMatch(pluginRunner, reqCtx) {
711
+ const { req, res, requestId, start, url } = reqCtx;
712
+ try {
713
+ const hookResult = await pluginRunner.runOnRequest({
714
+ request: req,
715
+ response: res,
716
+ ctx: {},
717
+ requestId
718
+ });
719
+ if (hookResult.shortCircuited) {
720
+ logRequest(
721
+ {
722
+ method: req.method ?? "GET",
723
+ url,
724
+ status: res.statusCode,
725
+ duration: Date.now() - start,
726
+ requestId
727
+ },
728
+ void 0,
729
+ req
730
+ );
731
+ return true;
732
+ }
733
+ } catch (err) {
734
+ sendError(res, "PLUGIN_ERROR", err.message, 500, void 0, requestId);
735
+ return true;
736
+ }
737
+ return false;
738
+ }
701
739
  function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
702
740
  const opts = rateLimitConfigOrOptions && "windowMs" in rateLimitConfigOrOptions ? { rateLimitConfig: rateLimitConfigOrOptions } : rateLimitConfigOrOptions ?? {};
703
741
  const loadModule = createViteLoader(vite);
@@ -715,7 +753,7 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
715
753
  const csrfReadinessStore = opts.csrfReadinessStore;
716
754
  const servicesProxyPrefixes = opts.servicesProxyPrefixes ?? [];
717
755
  return (req, res, next) => {
718
- void (async () => {
756
+ const handle = async () => {
719
757
  const url = req.url ?? "";
720
758
  if (await handleCspReportIfMatch(req, res, url, { auditLogger, onCspViolation })) {
721
759
  return;
@@ -739,6 +777,9 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
739
777
  if (applyRateLimitOr429(req, res, { rateLimiter, requestId, url, start })) {
740
778
  return;
741
779
  }
780
+ if (pluginRunner && await runPluginsBeforeRouteMatch(pluginRunner, { req, res, requestId, start, url })) {
781
+ return;
782
+ }
742
783
  if (await handleBatchIfMatch(req, res, { url, batching, requestId, start })) {
743
784
  return;
744
785
  }
@@ -750,13 +791,17 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
750
791
  const suggestion = findSuggestion(urlPath, routePaths);
751
792
  const msg = suggestion ? `API route not found: ${urlPath}. Did you mean: ${suggestion}?` : "API route not found";
752
793
  sendError(res, "NOT_FOUND", msg, 404, void 0, requestId);
753
- logRequest({
754
- method: req.method ?? "GET",
755
- url,
756
- status: 404,
757
- duration: Date.now() - start,
758
- requestId
759
- });
794
+ logRequest(
795
+ {
796
+ method: req.method ?? "GET",
797
+ url,
798
+ status: 404,
799
+ duration: Date.now() - start,
800
+ requestId
801
+ },
802
+ void 0,
803
+ req
804
+ );
760
805
  return;
761
806
  }
762
807
  const method = (req.method ?? "GET").toUpperCase();
@@ -774,8 +819,13 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
774
819
  csrfMode,
775
820
  disallowed
776
821
  });
777
- logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId });
778
- })();
822
+ logRequest(
823
+ { method, url, status: res.statusCode, duration: Date.now() - start, requestId },
824
+ void 0,
825
+ req
826
+ );
827
+ };
828
+ void handle();
779
829
  };
780
830
  }
781
831
 
@@ -1039,6 +1089,21 @@ var theoConfigSchema = z.object({
1039
1089
  * matching `--target`, `theokit build` emits a cross-reference note.
1040
1090
  */
1041
1091
  adapters: z.array(z.string()).optional(),
1092
+ /**
1093
+ * Extra package names to pre-bundle via Vite's `optimizeDeps.include`.
1094
+ *
1095
+ * Framework auto-includes `@usetheo/ui` and `lucide-react` when present.
1096
+ * Apps adding plugin peer-deps that rely on a runtime `import('<pkg>')`
1097
+ * (dynamic specifier) — e.g. `@theokit/plugin-canvas` consumers
1098
+ * installing `mermaid` for Mermaid SVG rendering — must declare those
1099
+ * here so Vite can resolve the literal in dev mode without
1100
+ * "Failed to resolve module specifier" errors. Production builds use
1101
+ * tsup/esbuild bundling and are not affected.
1102
+ *
1103
+ * Example:
1104
+ * viteOptimizeDeps: ['mermaid']
1105
+ */
1106
+ viteOptimizeDeps: z.array(z.string()).optional(),
1042
1107
  /**
1043
1108
  * Jobs backend (ADR D3 + T2.1). When configured, every request gets
1044
1109
  * `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a
@@ -1065,7 +1130,33 @@ var theoConfigSchema = z.object({
1065
1130
  * that boot alongside the TheoKit TS app. Empty `services: {}` is the
1066
1131
  * default and preserves Wave 1 behavior.
1067
1132
  */
1068
- services: servicesConfigSchema
1133
+ services: servicesConfigSchema,
1134
+ /**
1135
+ * G2 — OpenAPI emit (build-time only).
1136
+ *
1137
+ * When present, `theokit build` writes a generated `openapi.json` from
1138
+ * every `defineRoute()` body/query/params Zod schema. `undefined` keeps
1139
+ * the framework backward-compatible (no emit). Pass `openapi: {}` to
1140
+ * opt in with defaults.
1141
+ *
1142
+ * Defaults: spec 3.1.0 · servers `http://localhost:3000` · title
1143
+ * "TheoKit App" · version "0.0.0" · outDir ".theo" (next to dist).
1144
+ *
1145
+ * The env var `THEOKIT_OPENAPI_SERVERS` (CSV of URLs) overrides
1146
+ * `servers[].url` at emit time without rebuilding the config.
1147
+ */
1148
+ openapi: z.object({
1149
+ servers: z.array(
1150
+ z.object({
1151
+ url: z.string().url(),
1152
+ description: z.string().optional()
1153
+ })
1154
+ ).default([{ url: "http://localhost:3000", description: "Local development" }]),
1155
+ specVersion: z.enum(["3.1.0", "3.0.3"]).default("3.1.0"),
1156
+ title: z.string().default("TheoKit App"),
1157
+ version: z.string().default("0.0.0"),
1158
+ outDir: z.string().default(".theo")
1159
+ }).optional()
1069
1160
  }).refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {
1070
1161
  message: "service.port collides with TheoKit web port \u2014 change one of them",
1071
1162
  path: ["services"]
@@ -1164,7 +1255,7 @@ function resolveExportSubpath(pkgRoot, subpath) {
1164
1255
  }
1165
1256
  let target;
1166
1257
  if (typeof exp === "string") target = exp;
1167
- else if (exp && typeof exp === "object") {
1258
+ else if (typeof exp === "object") {
1168
1259
  const e = exp;
1169
1260
  target = e.import ?? e.default;
1170
1261
  }
@@ -1234,7 +1325,9 @@ var DEFAULT_RESOLVED = {
1234
1325
  disallowed: void 0,
1235
1326
  cors: void 0,
1236
1327
  auditLogger: void 0,
1237
- devtoolsEnabled: true
1328
+ devtoolsEnabled: true,
1329
+ openapi: void 0,
1330
+ distDir: ".theo"
1238
1331
  };
1239
1332
  async function resolvePluginConfig(projectRoot) {
1240
1333
  try {
@@ -1263,7 +1356,9 @@ async function resolvePluginConfig(projectRoot) {
1263
1356
  disallowed: userConfig.security?.disallowed,
1264
1357
  cors: userConfig.security?.cors,
1265
1358
  auditLogger,
1266
- devtoolsEnabled: userConfig.devtools !== false
1359
+ devtoolsEnabled: userConfig.devtools !== false,
1360
+ openapi: userConfig.openapi,
1361
+ distDir: userConfig.distDir
1267
1362
  };
1268
1363
  } catch {
1269
1364
  return DEFAULT_RESOLVED;
@@ -1600,9 +1695,7 @@ async function integrateUseTheoUI(cwd, opts) {
1600
1695
  }
1601
1696
  const tailwindPlugins = normalizePluginReturn(tailwindPlugin);
1602
1697
  if (tailwindPlugins === null) {
1603
- console.warn(
1604
- `[theokit] @tailwindcss/vite returned unexpected shape. Skipping auto-config.`
1605
- );
1698
+ console.warn(`[theokit] @tailwindcss/vite returned unexpected shape. Skipping auto-config.`);
1606
1699
  return [];
1607
1700
  }
1608
1701
  const uiPlugins = normalizePluginReturn(uiPlugin);
@@ -1876,7 +1969,42 @@ async function theoPluginAsync(rootOrOptions) {
1876
1969
  })
1877
1970
  );
1878
1971
  }
1879
- return [theoPlugin(rootOrOptions), ...uiPlugins, ...servicesPlugins];
1972
+ const { appTypedClientPlugin } = await import("./app-typed-client-RZMVWHJ3.js");
1973
+ const appClientPlugin = appTypedClientPlugin({
1974
+ cwd: projectRoot,
1975
+ serverDir: resolve6(projectRoot, "server"),
1976
+ distDir: resolve6(projectRoot, ".theo")
1977
+ });
1978
+ const { actionsVirtualModule } = await import("./actions-virtual-module-E7IELVHL.js");
1979
+ const actionsPlugin = actionsVirtualModule({
1980
+ serverDir: resolve6(projectRoot, "server"),
1981
+ distDir: resolve6(projectRoot, ".theo")
1982
+ });
1983
+ return [
1984
+ theoPlugin(rootOrOptions),
1985
+ ...uiPlugins,
1986
+ ...servicesPlugins,
1987
+ appClientPlugin,
1988
+ actionsPlugin
1989
+ ];
1990
+ }
1991
+ function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
1992
+ const include = [];
1993
+ if (existsSync7(resolve6(projectRoot, "node_modules", "@usetheo", "ui"))) {
1994
+ include.push("@usetheo/ui");
1995
+ }
1996
+ if (existsSync7(resolve6(projectRoot, "node_modules", "lucide-react"))) {
1997
+ include.push("lucide-react");
1998
+ }
1999
+ include.push("devalue");
2000
+ if (Array.isArray(viteOptimizeDeps)) {
2001
+ for (const pkg of viteOptimizeDeps) {
2002
+ if (typeof pkg === "string" && pkg.length > 0 && !include.includes(pkg)) {
2003
+ include.push(pkg);
2004
+ }
2005
+ }
2006
+ }
2007
+ return include;
1880
2008
  }
1881
2009
  function theoPlugin(rootOrOptions) {
1882
2010
  const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
@@ -1895,6 +2023,8 @@ function theoPlugin(rootOrOptions) {
1895
2023
  let cors;
1896
2024
  let auditLogger;
1897
2025
  let devtoolsEnabled = true;
2026
+ let resolvedOpenApi;
2027
+ let resolvedDistDir = ".theo";
1898
2028
  let isDevMode = false;
1899
2029
  let configLoadedOnce = false;
1900
2030
  return {
@@ -1913,6 +2043,8 @@ function theoPlugin(rootOrOptions) {
1913
2043
  cors = resolved.cors;
1914
2044
  auditLogger = resolved.auditLogger;
1915
2045
  devtoolsEnabled = resolved.devtoolsEnabled;
2046
+ resolvedOpenApi = resolved.openapi;
2047
+ resolvedDistDir = resolved.distDir;
1916
2048
  },
1917
2049
  // Vite calls this hook (sync return is fine — no awaits left after
1918
2050
  // the auto-chain moved to theoPluginAsync). The @usetheo/ui +
@@ -1920,13 +2052,7 @@ function theoPlugin(rootOrOptions) {
1920
2052
  // Vite drops plugins returned from a child plugin's config() hook.
1921
2053
  config() {
1922
2054
  const ext = existsSync7(resolve6(theoSrcDir, "index.ts")) ? ".ts" : ".js";
1923
- const optimizeDepsInclude = [];
1924
- if (existsSync7(resolve6(projectRoot, "node_modules", "@usetheo", "ui"))) {
1925
- optimizeDepsInclude.push("@usetheo/ui");
1926
- }
1927
- if (existsSync7(resolve6(projectRoot, "node_modules", "lucide-react"))) {
1928
- optimizeDepsInclude.push("lucide-react");
1929
- }
2055
+ const optimizeDepsInclude = buildOptimizeDepsInclude(projectRoot, options.viteOptimizeDeps);
1930
2056
  const warmupClientFiles = [];
1931
2057
  for (const candidate of ["app/layout.tsx", "app/page.tsx", "app/page.jsx"]) {
1932
2058
  const full = resolve6(projectRoot, candidate);
@@ -1935,7 +2061,10 @@ function theoPlugin(rootOrOptions) {
1935
2061
  const servicesProxy = options.services && Object.keys(options.services).length > 0 ? buildServicesProxyConfig(options.services) : void 0;
1936
2062
  return {
1937
2063
  envPrefix: "THEO_PUBLIC_",
1938
- optimizeDeps: optimizeDepsInclude.length > 0 ? { include: optimizeDepsInclude } : void 0,
2064
+ optimizeDeps: {
2065
+ ...optimizeDepsInclude.length > 0 ? { include: optimizeDepsInclude } : {},
2066
+ force: true
2067
+ },
1939
2068
  server: {
1940
2069
  ...warmupClientFiles.length > 0 ? { warmup: { clientFiles: warmupClientFiles } } : {},
1941
2070
  ...servicesProxy !== void 0 ? { proxy: servicesProxy } : {},
@@ -2050,7 +2179,7 @@ function theoPlugin(rootOrOptions) {
2050
2179
  ].join("\n");
2051
2180
  }
2052
2181
  },
2053
- configureServer(server) {
2182
+ async configureServer(server) {
2054
2183
  isDevMode = true;
2055
2184
  globalThis.__theoViteHotServer = server;
2056
2185
  server.ws.on("theo:devtools:request-manifest", () => {
@@ -2065,6 +2194,7 @@ function theoPlugin(rootOrOptions) {
2065
2194
  createActionMiddleware(server, serverDir, { pluginRunner, csrfMode, disallowed })
2066
2195
  );
2067
2196
  const servicesProxyPrefixes = options.services && Object.keys(options.services).length > 0 ? Object.values(options.services).map((s) => s.proxy) : [];
2197
+ const csrfReadinessStore = options.csrfReadinessStore ?? new CsrfReadinessStore();
2068
2198
  server.middlewares.use(
2069
2199
  createApiMiddleware(server, serverDir, {
2070
2200
  rateLimitConfig: options.rateLimit,
@@ -2076,7 +2206,8 @@ function theoPlugin(rootOrOptions) {
2076
2206
  disallowed,
2077
2207
  cors,
2078
2208
  auditLogger,
2079
- servicesProxyPrefixes
2209
+ servicesProxyPrefixes,
2210
+ csrfReadinessStore
2080
2211
  })
2081
2212
  );
2082
2213
  const configPath = resolve6(projectRoot, "theo.config.ts");
@@ -2105,6 +2236,29 @@ function theoPlugin(rootOrOptions) {
2105
2236
  }
2106
2237
  server.watcher.on("add", handleRouteChange);
2107
2238
  server.watcher.on("unlink", handleRouteChange);
2239
+ if (resolvedOpenApi !== void 0) {
2240
+ const openApiCfg = resolvedOpenApi;
2241
+ const { reEmitOpenApi } = await import("./dev-emit-3CHM73TV.js");
2242
+ const openApiServerDir = resolve6(projectRoot, "server");
2243
+ const openApiDistDir = resolve6(projectRoot, resolvedDistDir);
2244
+ const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
2245
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2246
+ server.watcher.on("change", (file) => {
2247
+ if (isRouteFileForOpenApi(file)) {
2248
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2249
+ }
2250
+ });
2251
+ server.watcher.on("add", (file) => {
2252
+ if (isRouteFileForOpenApi(file)) {
2253
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2254
+ }
2255
+ });
2256
+ server.watcher.on("unlink", (file) => {
2257
+ if (isRouteFileForOpenApi(file)) {
2258
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2259
+ }
2260
+ });
2261
+ }
2108
2262
  setupWsUpgrade(server, projectRoot);
2109
2263
  server.httpServer?.once("close", () => {
2110
2264
  ;
@@ -2130,4 +2284,4 @@ export {
2130
2284
  theoPluginAsync,
2131
2285
  theoPlugin
2132
2286
  };
2133
- //# sourceMappingURL=chunk-FRBYJANT.js.map
2287
+ //# sourceMappingURL=chunk-M6MYZSD2.js.map