@ken-jo/agent-connector 0.1.0

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 (203) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +348 -0
  4. package/dist/amp-QSFH2V7Y.js +217 -0
  5. package/dist/amp-QSFH2V7Y.js.map +1 -0
  6. package/dist/amp-TULUPXXL.js +237 -0
  7. package/dist/amp-TULUPXXL.js.map +1 -0
  8. package/dist/antigravity-CX2YLSY7.js +17 -0
  9. package/dist/antigravity-CX2YLSY7.js.map +1 -0
  10. package/dist/antigravity-STHZWMP3.js +171 -0
  11. package/dist/antigravity-STHZWMP3.js.map +1 -0
  12. package/dist/antigravity-cli-P6HADOAO.js +121 -0
  13. package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
  14. package/dist/antigravity-cli-YQDFVV4U.js +86 -0
  15. package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
  16. package/dist/chunk-54EMKTTX.js +191 -0
  17. package/dist/chunk-54EMKTTX.js.map +1 -0
  18. package/dist/chunk-7L6ITC4T.js +54 -0
  19. package/dist/chunk-7L6ITC4T.js.map +1 -0
  20. package/dist/chunk-7MSDGHJW.js +316 -0
  21. package/dist/chunk-7MSDGHJW.js.map +1 -0
  22. package/dist/chunk-BCHWFTV4.js +196 -0
  23. package/dist/chunk-BCHWFTV4.js.map +1 -0
  24. package/dist/chunk-DBQMX75U.js +36 -0
  25. package/dist/chunk-DBQMX75U.js.map +1 -0
  26. package/dist/chunk-EXLS7BSI.js +29 -0
  27. package/dist/chunk-EXLS7BSI.js.map +1 -0
  28. package/dist/chunk-JNM3TPLX.js +593 -0
  29. package/dist/chunk-JNM3TPLX.js.map +1 -0
  30. package/dist/chunk-JWNT5KW7.js +197 -0
  31. package/dist/chunk-JWNT5KW7.js.map +1 -0
  32. package/dist/chunk-KHO5WNTP.js +30 -0
  33. package/dist/chunk-KHO5WNTP.js.map +1 -0
  34. package/dist/chunk-LTT56GI3.js +155 -0
  35. package/dist/chunk-LTT56GI3.js.map +1 -0
  36. package/dist/chunk-MEH7GC7R.js +111 -0
  37. package/dist/chunk-MEH7GC7R.js.map +1 -0
  38. package/dist/chunk-MQPNU2CY.js +64 -0
  39. package/dist/chunk-MQPNU2CY.js.map +1 -0
  40. package/dist/chunk-OPXAP3AC.js +12 -0
  41. package/dist/chunk-OPXAP3AC.js.map +1 -0
  42. package/dist/chunk-RK6VBLXP.js +50 -0
  43. package/dist/chunk-RK6VBLXP.js.map +1 -0
  44. package/dist/chunk-TIIP3RG2.js +420 -0
  45. package/dist/chunk-TIIP3RG2.js.map +1 -0
  46. package/dist/chunk-TRKWUNO4.js +702 -0
  47. package/dist/chunk-TRKWUNO4.js.map +1 -0
  48. package/dist/chunk-TRQKHP7D.js +93 -0
  49. package/dist/chunk-TRQKHP7D.js.map +1 -0
  50. package/dist/chunk-WCETQWWK.js +59 -0
  51. package/dist/chunk-WCETQWWK.js.map +1 -0
  52. package/dist/chunk-WV63X7IK.js +357 -0
  53. package/dist/chunk-WV63X7IK.js.map +1 -0
  54. package/dist/chunk-XD7MY6HB.js +343 -0
  55. package/dist/chunk-XD7MY6HB.js.map +1 -0
  56. package/dist/chunk-XNAM7F35.js +276 -0
  57. package/dist/chunk-XNAM7F35.js.map +1 -0
  58. package/dist/chunk-XUCWLJY5.js +238 -0
  59. package/dist/chunk-XUCWLJY5.js.map +1 -0
  60. package/dist/chunk-YNZU6PAV.js +800 -0
  61. package/dist/chunk-YNZU6PAV.js.map +1 -0
  62. package/dist/chunk-YT43MUSJ.js +146 -0
  63. package/dist/chunk-YT43MUSJ.js.map +1 -0
  64. package/dist/claude-code-HKDV7T32.js +637 -0
  65. package/dist/claude-code-HKDV7T32.js.map +1 -0
  66. package/dist/claude-code-W74MNTAC.js +652 -0
  67. package/dist/claude-code-W74MNTAC.js.map +1 -0
  68. package/dist/cli/sdk.d.ts +54 -0
  69. package/dist/cli/sdk.js +82 -0
  70. package/dist/cli/sdk.js.map +1 -0
  71. package/dist/cli.d.ts +2 -0
  72. package/dist/cli.js +15 -0
  73. package/dist/cli.js.map +1 -0
  74. package/dist/codebuff-3PMZJJHP.js +307 -0
  75. package/dist/codebuff-3PMZJJHP.js.map +1 -0
  76. package/dist/codebuff-QX57FJDN.js +220 -0
  77. package/dist/codebuff-QX57FJDN.js.map +1 -0
  78. package/dist/codex-JFQTS6LU.js +593 -0
  79. package/dist/codex-JFQTS6LU.js.map +1 -0
  80. package/dist/codex-Q3ATRRX3.js +364 -0
  81. package/dist/codex-Q3ATRRX3.js.map +1 -0
  82. package/dist/copilot-cli-MTCGHZ2T.js +622 -0
  83. package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
  84. package/dist/copilot-cli-SJGIXVSD.js +369 -0
  85. package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
  86. package/dist/crush-I7PK3FGI.js +360 -0
  87. package/dist/crush-I7PK3FGI.js.map +1 -0
  88. package/dist/crush-LN5QFFR2.js +183 -0
  89. package/dist/crush-LN5QFFR2.js.map +1 -0
  90. package/dist/cursor-4TMXKJOZ.js +603 -0
  91. package/dist/cursor-4TMXKJOZ.js.map +1 -0
  92. package/dist/cursor-QZ6MTO74.js +194 -0
  93. package/dist/cursor-QZ6MTO74.js.map +1 -0
  94. package/dist/detect-WIAGVYS4.js +68 -0
  95. package/dist/detect-WIAGVYS4.js.map +1 -0
  96. package/dist/doctor-EZW2K4NI.js +372 -0
  97. package/dist/doctor-EZW2K4NI.js.map +1 -0
  98. package/dist/droid-KN6VRMMT.js +424 -0
  99. package/dist/droid-KN6VRMMT.js.map +1 -0
  100. package/dist/droid-W5WF5XEX.js +224 -0
  101. package/dist/droid-W5WF5XEX.js.map +1 -0
  102. package/dist/gemini-cli-CIRW37TU.js +369 -0
  103. package/dist/gemini-cli-CIRW37TU.js.map +1 -0
  104. package/dist/gemini-cli-Y35FFK4M.js +705 -0
  105. package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
  106. package/dist/goose-A3M5GODH.js +212 -0
  107. package/dist/goose-A3M5GODH.js.map +1 -0
  108. package/dist/goose-ZTGTSUFJ.js +480 -0
  109. package/dist/goose-ZTGTSUFJ.js.map +1 -0
  110. package/dist/hermes-H2KV44GK.js +470 -0
  111. package/dist/hermes-H2KV44GK.js.map +1 -0
  112. package/dist/hermes-ZYLMOFLZ.js +196 -0
  113. package/dist/hermes-ZYLMOFLZ.js.map +1 -0
  114. package/dist/hook-YVDILAMZ.js +75 -0
  115. package/dist/hook-YVDILAMZ.js.map +1 -0
  116. package/dist/index.d.ts +22 -0
  117. package/dist/index.js +10 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/install-HQRYZOQ2.js +60 -0
  120. package/dist/install-HQRYZOQ2.js.map +1 -0
  121. package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
  122. package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
  123. package/dist/kilo-NSWAVP6P.js +235 -0
  124. package/dist/kilo-NSWAVP6P.js.map +1 -0
  125. package/dist/kilo-QXJRCSLT.js +384 -0
  126. package/dist/kilo-QXJRCSLT.js.map +1 -0
  127. package/dist/kilo-cli-EAQTHXUW.js +587 -0
  128. package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
  129. package/dist/kilo-cli-EVBKUODA.js +160 -0
  130. package/dist/kilo-cli-EVBKUODA.js.map +1 -0
  131. package/dist/kimi-EI3UM3ZV.js +155 -0
  132. package/dist/kimi-EI3UM3ZV.js.map +1 -0
  133. package/dist/kimi-GQ7VXNYT.js +423 -0
  134. package/dist/kimi-GQ7VXNYT.js.map +1 -0
  135. package/dist/kiro-UCLSLBW2.js +217 -0
  136. package/dist/kiro-UCLSLBW2.js.map +1 -0
  137. package/dist/kiro-VQTT6LO2.js +451 -0
  138. package/dist/kiro-VQTT6LO2.js.map +1 -0
  139. package/dist/leaderboard-K6ON3SWW.js +137 -0
  140. package/dist/leaderboard-K6ON3SWW.js.map +1 -0
  141. package/dist/mux-T2TV4CIQ.js +211 -0
  142. package/dist/mux-T2TV4CIQ.js.map +1 -0
  143. package/dist/mux-ZMLVAXOO.js +96 -0
  144. package/dist/mux-ZMLVAXOO.js.map +1 -0
  145. package/dist/omp-PB4VDKS6.js +586 -0
  146. package/dist/omp-PB4VDKS6.js.map +1 -0
  147. package/dist/openclaw-ETN2M63O.js +751 -0
  148. package/dist/openclaw-ETN2M63O.js.map +1 -0
  149. package/dist/openclaw-IYA7ASL6.js +150 -0
  150. package/dist/openclaw-IYA7ASL6.js.map +1 -0
  151. package/dist/opencode-FZ33JKUZ.js +734 -0
  152. package/dist/opencode-FZ33JKUZ.js.map +1 -0
  153. package/dist/opencode-HWT3RSMN.js +216 -0
  154. package/dist/opencode-HWT3RSMN.js.map +1 -0
  155. package/dist/package-DLOVE2E4.js +1132 -0
  156. package/dist/package-DLOVE2E4.js.map +1 -0
  157. package/dist/pi-CIU2QHT4.js +208 -0
  158. package/dist/pi-CIU2QHT4.js.map +1 -0
  159. package/dist/pi-UFX2OYFV.js +139 -0
  160. package/dist/pi-UFX2OYFV.js.map +1 -0
  161. package/dist/qwen-code-5POCNU6P.js +573 -0
  162. package/dist/qwen-code-5POCNU6P.js.map +1 -0
  163. package/dist/qwen-code-W7NUCDEN.js +134 -0
  164. package/dist/qwen-code-W7NUCDEN.js.map +1 -0
  165. package/dist/roo-code-IOIUXRDE.js +242 -0
  166. package/dist/roo-code-IOIUXRDE.js.map +1 -0
  167. package/dist/roo-code-ZLKH4A7N.js +229 -0
  168. package/dist/roo-code-ZLKH4A7N.js.map +1 -0
  169. package/dist/runtime/index.d.ts +157 -0
  170. package/dist/runtime/index.js +20 -0
  171. package/dist/runtime/index.js.map +1 -0
  172. package/dist/serve-HVWWY4B6.js +65 -0
  173. package/dist/serve-HVWWY4B6.js.map +1 -0
  174. package/dist/status-FE7TECZD.js +128 -0
  175. package/dist/status-FE7TECZD.js.map +1 -0
  176. package/dist/synthetic-UKR6SMGS.js +178 -0
  177. package/dist/synthetic-UKR6SMGS.js.map +1 -0
  178. package/dist/telemetry-36GESKQQ.js +349 -0
  179. package/dist/telemetry-36GESKQQ.js.map +1 -0
  180. package/dist/trae-JSNDZFKL.js +204 -0
  181. package/dist/trae-JSNDZFKL.js.map +1 -0
  182. package/dist/trae-PA26DPBW.js +157 -0
  183. package/dist/trae-PA26DPBW.js.map +1 -0
  184. package/dist/types-CYUB1OTr.d.ts +427 -0
  185. package/dist/uninstall-IGCKNOSF.js +72 -0
  186. package/dist/uninstall-IGCKNOSF.js.map +1 -0
  187. package/dist/upgrade-JV2VK242.js +104 -0
  188. package/dist/upgrade-JV2VK242.js.map +1 -0
  189. package/dist/usage-6ER6EMEY.js +324 -0
  190. package/dist/usage-6ER6EMEY.js.map +1 -0
  191. package/dist/usage-event-JKBI6XCF.js +48 -0
  192. package/dist/usage-event-JKBI6XCF.js.map +1 -0
  193. package/dist/vscode-copilot-CK3NO54M.js +715 -0
  194. package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
  195. package/dist/warp-JNQA7PBU.js +126 -0
  196. package/dist/warp-JNQA7PBU.js.map +1 -0
  197. package/dist/warp-QNVDXOG4.js +199 -0
  198. package/dist/warp-QNVDXOG4.js.map +1 -0
  199. package/dist/zed-NNNX5MNT.js +272 -0
  200. package/dist/zed-NNNX5MNT.js.map +1 -0
  201. package/dist/zed-VRJKWYUR.js +209 -0
  202. package/dist/zed-VRJKWYUR.js.map +1 -0
  203. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/readers/claude-code.ts"],"sourcesContent":["/**\n * usage/readers/claude-code — Claude Code native session-log reader.\n *\n * Faithful port of tokscale sessions/claudecode.rs. Reads\n * ~/.claude/projects/<key>/**.jsonl (one entry per line) plus the two subagent\n * layouts (nested projects/<key>/<session>/subagents/agent-*.jsonl and flat\n * projects/<key>/agent-*.jsonl) and headless .json transcripts.\n *\n * Token fields live under message.usage:\n * input_tokens → input\n * output_tokens → output\n * cache_read_input_tokens → cacheRead\n * cache_creation_input_tokens → cacheWrite\n * (reasoning is never reported → always 0)\n *\n * CRITICAL DEDUP — Claude Code's streaming API rewrites the SAME logical message\n * (keyed by message.id : requestId) multiple times as the response streams in,\n * each write carrying CUMULATIVE token counts. Summing them over-counts 5–10x.\n * We therefore keep one record per dedup key and MERGE duplicates with a PER-FIELD\n * MAX (each token dimension independently keeps the highest value seen). The dedup\n * key is \"<messageId>:<requestId>\" when both exist, \"message:<messageId>\" when\n * only the id exists, and absent otherwise (such records always pass through).\n *\n * tool_result lines (type \"user\"/\"tool_result\") contribute extra INPUT tokens\n * (explicit token fields, else chars/4 estimate), deduped per tool_use_id under\n * \"claude:tool_result:<session>:tool_result:<id>\" (per-field max on input).\n *\n * Headless transcripts: .json files parse a single message_start/message_delta/\n * message_stop stream OR a flat usage object; same usage paths, per-field max.\n *\n * Session id = the file stem; for sidechain transcripts the parent sessionId on\n * the line is used instead (so subagent rows roll up under the parent session,\n * not an inflated synthetic one). Subagent name resolved via meta.json sidecar →\n * parent tool_use lookup → \"claude-code-subagent\" fallback. Project key/label\n * decoded from the dir-name-encoded cwd (.claude/projects/<key>/ window).\n *\n * Provider inferred from message.model (anthropic for claude-*), overridable by\n * an explicit providerId/provider hint on the message or entry, and by cc-mirror\n * variant metadata. Confidence is always \"host-reported\" (real token counts).\n *\n * Fail-open: no root → []; unreadable/malformed file or line → skipped.\n */\n\nimport { basename, dirname, sep } from \"node:path\";\n\nimport type { TokenBreakdown, UsageReader, UsageRecord } from \"../types.js\";\nimport { emptyTokens } from \"../aggregate.js\";\nimport { fileMtimeMs, readJsonFile, readJsonlLines } from \"../jsonl.js\";\nimport { inferProvider, normalizeWorkspaceKey, workspaceLabelFromKey } from \"../normalize.js\";\nimport { firstExistingRoot, isFile, walkFiles } from \"../paths.js\";\n\nconst PLATFORM_ID = \"claude-code\" as const;\nconst DEFAULT_MODEL = \"unknown\";\nconst DEFAULT_PROVIDER = \"anthropic\";\nconst SUBAGENT_FALLBACK = \"claude-code-subagent\";\n\n/** Internal Claude Code user-content tags that are NOT genuine human turns. */\nconst INTERNAL_USER_TAGS = [\n \"<local-command-stdout>\",\n \"<local-command-stderr>\",\n \"<command-name>\",\n \"<command-message>\",\n \"<system-reminder>\",\n \"<bash-input>\",\n \"<bash-stdout>\",\n \"<bash-stderr>\",\n] as const;\n\n// ─────────────────────────────────────────────────────────────────────────\n// Coercion helpers\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Coerce an unknown to a non-negative integer (0 on absence/garbage). */\nfunction toNonNegInt(v: unknown): number {\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.trunc(n));\n}\n\n/** Coerce an unknown to an integer or null (port of extract_i64: number/string only). */\nfunction toIntOrNull(v: unknown): number | null {\n if (typeof v === \"number\" && Number.isFinite(v)) return Math.trunc(v);\n if (typeof v === \"string\") {\n const n = Number(v);\n if (Number.isFinite(n)) return Math.trunc(n);\n }\n return null;\n}\n\n/** A non-empty string, or undefined. */\nfunction asNonEmptyString(v: unknown): string | undefined {\n return typeof v === \"string\" && v !== \"\" ? v : undefined;\n}\n\n/** Parse an RFC3339 / numeric timestamp to epoch ms, or null when unusable. */\nfunction parseTs(v: unknown): number | null {\n if (typeof v === \"string\") {\n const ms = Date.parse(v);\n if (!Number.isNaN(ms)) return ms;\n const num = Number(v);\n if (Number.isFinite(num) && num > 0) return num >= 1e12 ? num : num * 1000;\n return null;\n }\n if (typeof v === \"number\" && Number.isFinite(v) && v > 0) {\n return v >= 1e12 ? v : v * 1000;\n }\n return null;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Provider / model resolution (port of claude_provider_choice + canonicalize)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve the provider id from a raw model and an optional provider hint.\n * Mirrors claude_provider_choice/_from_parts:\n * - an explicit hint that canonicalizes to a non-anthropic provider wins;\n * - a hint of \"anthropic\" yields the model-inferred provider when that differs\n * (e.g. an \"anthropic\" wrapper actually serving a non-claude model), else\n * \"anthropic\";\n * - with no hint, a \"provider/model\" prefix wins, then model inference, then\n * \"unknown\" (we surface the platform default DEFAULT_PROVIDER instead of a\n * bare \"unknown\" only when nothing at all is known and the model is absent).\n */\nfunction resolveProvider(rawModel: string | undefined, hint: string | undefined): string {\n const canonicalHint = canonicalProvider(hint);\n if (canonicalHint !== undefined) {\n if (canonicalHint === \"anthropic\") {\n const inferred = rawModel !== undefined ? inferProvider(rawModel) : null;\n if (inferred !== null && inferred !== \"anthropic\") return inferred;\n return \"anthropic\";\n }\n return canonicalHint;\n }\n\n if (rawModel === undefined) return DEFAULT_PROVIDER;\n\n // provider_from_model_prefix: a \"provider/model\" form canonicalizes the prefix.\n if (rawModel.trim().includes(\"/\")) {\n const prefixProvider = canonicalProvider(rawModel);\n if (prefixProvider !== undefined) return prefixProvider;\n }\n\n const inferred = inferProvider(rawModel);\n if (inferred !== null) return inferred;\n\n return DEFAULT_PROVIDER;\n}\n\n/**\n * Lightweight stand-in for provider_identity::canonical_provider. The fixed infra\n * exposes inferProvider (model→provider family); for an explicit provider hint we\n * canonicalize a \"vendor/model\" or bare-vendor token via the same family table,\n * with the documented cc-mirror \"mirror\"→anthropic special case folded in by the\n * caller. Returns undefined for an empty/unrecognized hint.\n */\nfunction canonicalProvider(hint: string | undefined): string | undefined {\n if (hint === undefined) return undefined;\n const trimmed = hint.trim();\n if (trimmed === \"\") return undefined;\n // A \"vendor/model\" hint: infer from the whole string (covers \"openrouter/anthropic\").\n const inferred = inferProvider(trimmed);\n if (inferred !== null) return inferred;\n // Bare vendor token: take the segment before any '/' as the provider id.\n const head = trimmed.split(\"/\")[0];\n if (head !== undefined && head !== \"\") return head.toLowerCase();\n return undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Workspace (project) from path (port of claude_workspace_from_path)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Decode the project key/label from a path. Scans for a `[.claude, projects, key]`\n * window, then a cc-mirror `[.cc-mirror, *, config, projects, key]` window, then a\n * trailing `[projects, key]` window (last match). The key is the dir-name-encoded\n * cwd (e.g. \"-Users-me-work\"); we keep it verbatim as the stable key, exactly as\n * the Rust normalize_workspace_key/workspace_label_from_key do.\n */\nfunction workspaceFromPath(path: string): { projectKey?: string; projectLabel?: string } {\n const components = path.split(/[\\\\/]+/).filter((c) => c !== \"\");\n\n for (let i = 0; i + 2 < components.length; i++) {\n if (components[i] === \".claude\" && components[i + 1] === \"projects\") {\n return keyAndLabel(components[i + 2]);\n }\n }\n\n for (let i = 0; i + 4 < components.length; i++) {\n if (\n components[i] === \".cc-mirror\" &&\n components[i + 2] === \"config\" &&\n components[i + 3] === \"projects\"\n ) {\n return keyAndLabel(components[i + 4]);\n }\n }\n\n for (let i = components.length - 2; i >= 0; i--) {\n if (components[i] === \"projects\") {\n return keyAndLabel(components[i + 1]);\n }\n }\n\n return {};\n}\n\nfunction keyAndLabel(raw: string | undefined): { projectKey?: string; projectLabel?: string } {\n if (raw === undefined) return {};\n const key = normalizeWorkspaceKey(raw);\n if (key === undefined) return {};\n const label = workspaceLabelFromKey(key);\n return label === undefined ? { projectKey: key } : { projectKey: key, projectLabel: label };\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Subagent name resolution (port of resolve_subagent_name)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** A small per-scan cache of parent-session agentId→subagent_type lookups. */\ntype ParentSubagentCache = Map<string, Map<string, string>>;\n\nfunction stripExt(name: string): string {\n const dot = name.lastIndexOf(\".\");\n return dot > 0 ? name.slice(0, dot) : name;\n}\n\n/**\n * Resolve the subagent display name for a sidechain transcript.\n * Tier 1: sibling `<stem>.meta.json` agentType.\n * Tier 2: scan the parent session JSONL for the spawning tool_use's subagent_type.\n * Tier 3: \"claude-code-subagent\".\n */\nfunction resolveSubagentName(\n path: string,\n parentSessionId: string | undefined,\n entryAgentId: string | undefined,\n cache: ParentSubagentCache,\n): string {\n const stem = stripExt(basename(path));\n if (stem === \"\") return SUBAGENT_FALLBACK;\n\n // Tier 1: sibling meta.json\n const metaPath = `${dirname(path)}${sep}${stem}.meta.json`;\n const meta = readJsonFile(metaPath);\n if (isObject(meta)) {\n const agentType = asNonEmptyString(meta[\"agentType\"]);\n if (agentType !== undefined && agentType.trim() !== \"\") return agentType;\n }\n\n // Tier 2: parent session tool_use inference\n const lookupAgentId =\n entryAgentId !== undefined && entryAgentId.trim() !== \"\"\n ? entryAgentId\n : agentIdFromStem(stem);\n if (parentSessionId !== undefined && lookupAgentId !== undefined) {\n const parentPath = findParentSessionPath(path, parentSessionId);\n if (parentPath !== undefined) {\n const subagentType = lookupSubagentTypeInParent(parentPath, lookupAgentId, cache);\n if (subagentType !== undefined) return subagentType;\n }\n }\n\n // Tier 3\n return SUBAGENT_FALLBACK;\n}\n\n/** Derive an agentId from an \"agent-<id>\" stem (port of sidechain_agent_id_from_stem). */\nfunction agentIdFromStem(stem: string): string | undefined {\n if (!stem.startsWith(\"agent-\")) return undefined;\n const agentStem = stem.slice(\"agent-\".length);\n if (!agentStem.includes(\"-\")) return agentStem;\n const trailing = agentStem.slice(agentStem.lastIndexOf(\"-\") + 1);\n if (trailing !== \"\" && /^[0-9a-fA-F]+$/.test(trailing)) return trailing;\n return agentStem;\n}\n\n/** Locate the parent main-session JSONL for a sidechain transcript. */\nfunction findParentSessionPath(sidechainPath: string, parentSessionId: string): string | undefined {\n const parentFilename = `${parentSessionId}.jsonl`;\n const dir = dirname(sidechainPath);\n\n // Nested layout: file → subagents → session-dir → project-dir\n if (basename(dir) === \"subagents\") {\n const projectDir = dirname(dirname(dir));\n const candidate = `${projectDir}${sep}${parentFilename}`;\n if (isFile(candidate)) return candidate;\n }\n\n // Flat layout: parent dir is one level up\n const flat = `${dir}${sep}${parentFilename}`;\n if (isFile(flat)) return flat;\n\n return undefined;\n}\n\n/** Cached agentId→subagent_type lookup for a parent session. */\nfunction lookupSubagentTypeInParent(\n parentPath: string,\n targetAgentId: string,\n cache: ParentSubagentCache,\n): string | undefined {\n let lookup = cache.get(parentPath);\n if (lookup === undefined) {\n lookup = buildParentSubagentLookup(parentPath);\n cache.set(parentPath, lookup);\n }\n return lookup.get(targetAgentId);\n}\n\n/**\n * Scan a parent session JSONL to recover subagent_type per agentId. Joins\n * tool_use.id→subagent_type with tool_result.tool_use_id→agentId (from result text).\n * Port of build_parent_subagent_type_lookup.\n */\nfunction buildParentSubagentLookup(parentPath: string): Map<string, string> {\n const out = new Map<string, string>();\n const toolUseTypes = new Map<string, string>(); // tool_use.id → subagent_type\n const agentIdLinks = new Map<string, string>(); // tool_use_id → agentId\n\n for (const raw of readJsonlLines(parentPath)) {\n if (!isObject(raw)) continue;\n const message = raw[\"message\"];\n if (!isObject(message)) continue;\n const content = message[\"content\"];\n if (!Array.isArray(content)) continue;\n\n for (const block of content) {\n if (!isObject(block)) continue;\n const blockType = block[\"type\"];\n if (blockType === \"tool_use\") {\n const id = asNonEmptyString(block[\"id\"]);\n const input = block[\"input\"];\n const subagentType = isObject(input) ? asNonEmptyString(input[\"subagent_type\"]) : undefined;\n if (id !== undefined && subagentType !== undefined) {\n toolUseTypes.set(id, subagentType);\n }\n } else if (blockType === \"tool_result\") {\n const toolUseId = asNonEmptyString(block[\"tool_use_id\"]);\n if (toolUseId === undefined) continue;\n const resultContent = block[\"content\"];\n if (!Array.isArray(resultContent)) continue;\n for (const cb of resultContent) {\n if (!isObject(cb)) continue;\n const text = cb[\"text\"];\n if (typeof text === \"string\") {\n const aid = extractAgentIdFromText(text);\n if (aid !== undefined) {\n agentIdLinks.set(toolUseId, aid);\n break;\n }\n }\n }\n }\n }\n }\n\n for (const [toolUseId, agentId] of agentIdLinks) {\n const subagentType = toolUseTypes.get(toolUseId);\n if (subagentType !== undefined) out.set(agentId, subagentType);\n }\n return out;\n}\n\n/** Extract `agentId: <alphanumeric>` from a tool_result text block. */\nfunction extractAgentIdFromText(text: string): string | undefined {\n const marker = \"agentId: \";\n const pos = text.indexOf(marker);\n if (pos < 0) return undefined;\n const rest = text.slice(pos + marker.length);\n let end = 0;\n while (end < rest.length && /[0-9a-zA-Z]/.test(rest[end] as string)) end++;\n return end > 0 ? rest.slice(0, end) : undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// tool_result input-token extraction (port of extract_claude_tool_result_usage)\n// ─────────────────────────────────────────────────────────────────────────\n\ninterface ToolResultUsage {\n inputTokens: number;\n /** \"tool_result:<id>\" when an id was present, else undefined. */\n dedupId?: string;\n}\n\n/** Sum input tokens across all tool_result blocks in a line, deduping by id. */\nfunction extractToolResultUsage(value: Record<string, unknown>): ToolResultUsage | undefined {\n let total = 0;\n let firstDedupId: string | undefined;\n const seen = new Set<string>();\n\n for (const toolResult of collectToolResultValues(value)) {\n const id = extractToolResultId(toolResult);\n if (id !== undefined) {\n if (seen.has(id)) continue;\n seen.add(id);\n }\n if (firstDedupId === undefined && id !== undefined) firstDedupId = `tool_result:${id}`;\n total += extractToolResultInputTokens(toolResult);\n }\n\n if (total <= 0) return undefined;\n return firstDedupId === undefined\n ? { inputTokens: total }\n : { inputTokens: total, dedupId: firstDedupId };\n}\n\n/** Gather every tool_result Value reachable from a line (port of claude_tool_result_values). */\nfunction collectToolResultValues(value: Record<string, unknown>): Array<Record<string, unknown>> {\n const results: Array<Record<string, unknown>> = [];\n\n if (value[\"type\"] === \"tool_result\") results.push(value);\n\n const directTr = value[\"tool_result\"];\n if (isObject(directTr)) results.push(directTr);\n\n const message = value[\"message\"];\n if (isObject(message)) {\n const msgTr = message[\"tool_result\"];\n if (isObject(msgTr)) results.push(msgTr);\n }\n\n const content = isObject(message) ? message[\"content\"] : value[\"content\"];\n if (Array.isArray(content)) {\n for (const block of content) {\n if (isObject(block) && block[\"type\"] === \"tool_result\") results.push(block);\n }\n }\n\n return results;\n}\n\nfunction extractToolResultId(tr: Record<string, unknown>): string | undefined {\n return (\n asNonEmptyString(tr[\"tool_use_id\"]) ??\n asNonEmptyString(tr[\"id\"]) ??\n asNonEmptyString(tr[\"tool_result_id\"])\n );\n}\n\n/** Explicit input-token count for a tool_result, else a chars/4 estimate. */\nfunction extractToolResultInputTokens(tr: Record<string, unknown>): number {\n const explicit = explicitToolResultInputTokens(tr);\n if (explicit !== undefined) return explicit;\n const chars = toolResultOutputCharCount(tr);\n return chars > 0 ? Math.ceil(chars / 4) : 0;\n}\n\nfunction explicitToolResultInputTokens(tr: Record<string, unknown>): number | undefined {\n const toolOutput = isObject(tr[\"tool_output\"]) ? (tr[\"tool_output\"] as Record<string, unknown>) : undefined;\n const usage = isObject(tr[\"usage\"]) ? (tr[\"usage\"] as Record<string, unknown>) : undefined;\n const toolOutputUsage =\n toolOutput !== undefined && isObject(toolOutput[\"usage\"])\n ? (toolOutput[\"usage\"] as Record<string, unknown>)\n : undefined;\n\n const candidates: unknown[] = [\n tr[\"input_tokens\"],\n tr[\"token_count\"],\n tr[\"tokens\"],\n usage?.[\"input_tokens\"],\n toolOutput?.[\"input_tokens\"],\n toolOutput?.[\"token_count\"],\n toolOutput?.[\"tokens\"],\n toolOutputUsage?.[\"input_tokens\"],\n ];\n for (const c of candidates) {\n const n = toIntOrNull(c);\n if (n !== null) return Math.max(0, n);\n }\n return undefined;\n}\n\n/** Count chars across a tool_result's output/content text (port of tool_result_output_char_count). */\nfunction toolResultOutputCharCount(tr: Record<string, unknown>): number {\n let chars = 0;\n\n const toolOutput = tr[\"tool_output\"];\n if (isObject(toolOutput)) {\n const output = toolOutput[\"output\"];\n if (typeof output === \"string\") chars += [...output].length;\n }\n\n const content = tr[\"content\"];\n if (typeof content === \"string\") {\n chars += [...content].length;\n } else if (Array.isArray(content)) {\n for (const block of content) {\n if (!isObject(block)) continue;\n const blockToolOutput = block[\"tool_output\"];\n const blockOutput = isObject(blockToolOutput) ? blockToolOutput[\"output\"] : undefined;\n const text =\n typeof blockOutput === \"string\"\n ? blockOutput\n : typeof block[\"text\"] === \"string\"\n ? (block[\"text\"] as string)\n : undefined;\n if (text !== undefined) chars += [...text].length;\n }\n }\n\n return chars;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// is_human_turn (kept for parity; does not affect token counts)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** True when a `type:\"user\"` line is genuine human input (not tool/system). */\nfunction isHumanTurn(rawLine: string): boolean {\n const pos = rawLine.indexOf('\"content\":');\n if (pos < 0) return false;\n const after = rawLine.slice(pos + '\"content\":'.length).trimStart();\n if (after.startsWith(\"[\")) return false;\n if (after.startsWith('\"')) {\n const contentStart = after.slice(1);\n for (const tag of INTERNAL_USER_TAGS) {\n if (contentStart.startsWith(tag)) return false;\n }\n return true;\n }\n return false;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Accumulator (one entry per dedup key, per-field MAX merge)\n// ─────────────────────────────────────────────────────────────────────────\n\ninterface Accum {\n record: UsageRecord;\n /** Provider confidence rank (0..3) so a stronger hint from a later duplicate wins. */\n providerConfidence: number;\n}\n\nconst PC_DEFAULT = 1; // hint canonicalized to anthropic\nconst PC_INFERRED = 2; // model-inferred / stored non-anthropic\nconst PC_EXPLICIT = 3; // explicit \"vendor/model\" hint or prefix\n\n/** Provider confidence for a freshly resolved choice (port of the confidence ranks). */\nfunction providerConfidence(rawModel: string | undefined, hint: string | undefined): number {\n const canonicalHint = canonicalProvider(hint);\n if (canonicalHint !== undefined) {\n if (canonicalHint === \"anthropic\") {\n const inferred = rawModel !== undefined ? inferProvider(rawModel) : null;\n if (inferred !== null && inferred !== \"anthropic\") return PC_INFERRED;\n return PC_DEFAULT;\n }\n return PC_EXPLICIT;\n }\n if (rawModel !== undefined) {\n if (rawModel.trim().includes(\"/\") && canonicalProvider(rawModel) !== undefined) return PC_EXPLICIT;\n if (inferProvider(rawModel) !== null) return PC_INFERRED;\n }\n return 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// JSONL file parse\n// ─────────────────────────────────────────────────────────────────────────\n\ninterface ClaudeUsage {\n input_tokens?: unknown;\n output_tokens?: unknown;\n cache_read_input_tokens?: unknown;\n cache_creation_input_tokens?: unknown;\n}\n\n/**\n * Parse a Claude Code session file (.jsonl, or headless .json) into usage records.\n * Each record carries a dedupKey; streaming duplicates are pre-merged here so the\n * dedupKey is unique per logical message (the aggregate-level dedup is then a\n * cross-source backstop, not the primary defense against streaming over-count).\n */\nfunction parseClaudeFile(path: string, parentCache: ParentSubagentCache): UsageRecord[] {\n const fallbackTs = fileMtimeMs(path);\n const { projectKey, projectLabel } = workspaceFromPath(path);\n\n // Headless .json: try the whole-file JSON shape first.\n if (path.endsWith(\".json\")) {\n const headless = parseHeadlessJson(path, fallbackTs, projectKey, projectLabel);\n if (headless.length > 0) return headless;\n }\n\n const lines = readJsonlLines(path);\n if (lines.length === 0) return [];\n\n // session id defaults to the file stem.\n let sessionId = stripExt(basename(path)) || \"unknown\";\n\n // Per-key accumulators, in insertion order.\n const byKey = new Map<string, Accum>();\n const ordered: Accum[] = [];\n\n let lastModel: string | undefined;\n let lastProviderHint: string | undefined;\n let sidechainAgent: string | undefined;\n let sidechainDetected = false;\n\n // Headless streaming state (for message_start/_delta/_stop lines inside a .jsonl).\n const headlessState = newHeadlessState();\n const headlessOut: UsageRecord[] = [];\n\n for (let li = 0; li < lines.length; li++) {\n const value = lines[li];\n if (!isObject(value)) continue;\n const rawLine = JSON.stringify(value); // for the is_human_turn / nothing-critical path\n\n const entryType = value[\"type\"];\n const message = isObject(value[\"message\"]) ? (value[\"message\"] as Record<string, unknown>) : undefined;\n const entryProviderHint =\n asNonEmptyString(value[\"providerId\"]) ??\n asNonEmptyString(value[\"provider_id\"]) ??\n asNonEmptyString(value[\"provider\"]);\n\n // Detect sidechain on the first parseable entry.\n if (!sidechainDetected) {\n sidechainDetected = true;\n if (value[\"isSidechain\"] === true) {\n const parentId = asNonEmptyString(value[\"sessionId\"]);\n if (parentId !== undefined) sessionId = parentId;\n sidechainAgent = resolveSubagentName(\n path,\n asNonEmptyString(value[\"sessionId\"]),\n asNonEmptyString(value[\"agentId\"]),\n parentCache,\n );\n }\n }\n\n // ── user / tool_result lines ──────────────────────────────────────────\n if (entryType === \"user\" || entryType === \"tool_result\") {\n const tr = extractToolResultUsage(value);\n if (tr !== undefined) {\n const rawModel =\n extractModel(value) ??\n (message !== undefined ? asNonEmptyString(message[\"model\"]) : undefined) ??\n lastModel;\n const providerHint =\n extractProviderHint(value) ??\n (message !== undefined ? messageProviderHint(message) : undefined) ??\n entryProviderHint ??\n lastProviderHint;\n const ts =\n parseTs(value[\"timestamp\"]) ?? extractClaudeTimestamp(value) ?? fallbackTs;\n\n const dedupKey =\n tr.dedupId !== undefined\n ? `claude:tool_result:${sessionId}:${tr.dedupId}`\n : undefined;\n\n if (dedupKey !== undefined) {\n const existing = byKey.get(dedupKey);\n if (existing !== undefined) {\n existing.record.tokens.input = Math.max(existing.record.tokens.input, tr.inputTokens);\n if (ts >= existing.record.ts) existing.record.ts = ts;\n continue;\n }\n }\n\n const tokens = emptyTokens();\n tokens.input = tr.inputTokens;\n const record = buildRecord({\n rawModel,\n providerHint,\n sessionId,\n ts,\n tokens,\n messageCount: 0,\n dedupKey,\n projectKey,\n projectLabel,\n agent: sidechainAgent,\n });\n const accum: Accum = { record, providerConfidence: providerConfidence(rawModel, providerHint) };\n if (dedupKey !== undefined) byKey.set(dedupKey, accum);\n ordered.push(accum);\n }\n continue;\n }\n\n // ── assistant lines (the token-bearing path) ─────────────────────────\n if (entryType === \"assistant\") {\n if (message === undefined) continue;\n\n const msgModel = asNonEmptyString(message[\"model\"]);\n if (msgModel !== undefined) {\n lastModel = msgModel;\n lastProviderHint = messageProviderHint(message) ?? entryProviderHint;\n }\n\n const usage = message[\"usage\"];\n if (!isObject(usage)) continue;\n const u = usage as ClaudeUsage;\n\n const msgId = asNonEmptyString(message[\"id\"]);\n const reqId = asNonEmptyString(value[\"requestId\"]);\n const providerHint = messageProviderHint(message) ?? entryProviderHint;\n\n // Dedup key: messageId:requestId, else message:messageId, else none.\n let dedupKey: string | undefined;\n if (msgId !== undefined && reqId !== undefined) dedupKey = `${msgId}:${reqId}`;\n else if (msgId !== undefined) dedupKey = `message:${msgId}`;\n\n const ts = parseTs(value[\"timestamp\"]) ?? fallbackTs;\n\n // Merge into an existing duplicate via PER-FIELD MAX.\n if (dedupKey !== undefined) {\n const existing = byKey.get(dedupKey);\n if (existing !== undefined) {\n mergeUsageMax(existing.record.tokens, u);\n if (ts >= existing.record.ts) existing.record.ts = ts;\n // A later duplicate may carry a stronger provider hint; promote it.\n const cand = providerConfidence(msgModel, providerHint);\n if (cand > existing.providerConfidence) {\n existing.providerConfidence = cand;\n existing.record.providerId = resolveProvider(msgModel, providerHint);\n }\n continue;\n }\n }\n\n // First occurrence: model is required to keep the record (matches Rust:\n // a model:None assistant entry is skipped without polluting the dedup map).\n if (msgModel === undefined) continue;\n\n const tokens = emptyTokens();\n tokens.input = toNonNegInt(u.input_tokens);\n tokens.output = toNonNegInt(u.output_tokens);\n tokens.cacheRead = toNonNegInt(u.cache_read_input_tokens);\n tokens.cacheWrite = toNonNegInt(u.cache_creation_input_tokens);\n\n const record = buildRecord({\n rawModel: msgModel,\n providerHint,\n sessionId,\n ts,\n tokens,\n messageCount: 1,\n dedupKey,\n projectKey,\n projectLabel,\n agent: sidechainAgent,\n });\n const accum: Accum = { record, providerConfidence: providerConfidence(msgModel, providerHint) };\n if (dedupKey !== undefined) byKey.set(dedupKey, accum);\n ordered.push(accum);\n continue;\n }\n\n // ── headless streaming lines inside a .jsonl (message_start/_delta/_stop) ─\n const completed = processHeadlessLine(value, rawLine, sessionId, headlessState, fallbackTs);\n if (completed !== undefined) {\n if (projectKey !== undefined) completed.projectKey = projectKey;\n if (projectLabel !== undefined) completed.projectLabel = projectLabel;\n headlessOut.push(completed);\n }\n }\n\n const finalHeadless = finalizeHeadlessState(headlessState, sessionId, fallbackTs);\n if (finalHeadless !== undefined) {\n if (projectKey !== undefined) finalHeadless.projectKey = projectKey;\n if (projectLabel !== undefined) finalHeadless.projectLabel = projectLabel;\n headlessOut.push(finalHeadless);\n }\n\n return [...ordered.map((a) => a.record), ...headlessOut];\n}\n\n/** Per-field MAX merge of a usage block into an existing token breakdown. */\nfunction mergeUsageMax(tokens: TokenBreakdown, u: ClaudeUsage): void {\n tokens.input = Math.max(tokens.input, toNonNegInt(u.input_tokens));\n tokens.output = Math.max(tokens.output, toNonNegInt(u.output_tokens));\n tokens.cacheRead = Math.max(tokens.cacheRead, toNonNegInt(u.cache_read_input_tokens));\n tokens.cacheWrite = Math.max(tokens.cacheWrite, toNonNegInt(u.cache_creation_input_tokens));\n}\n\ninterface BuildRecordArgs {\n rawModel: string | undefined;\n providerHint: string | undefined;\n sessionId: string;\n ts: number;\n tokens: TokenBreakdown;\n messageCount: number;\n dedupKey: string | undefined;\n projectKey: string | undefined;\n projectLabel: string | undefined;\n agent: string | undefined;\n}\n\nfunction buildRecord(a: BuildRecordArgs): UsageRecord {\n const modelId = a.rawModel !== undefined && a.rawModel !== \"\" ? a.rawModel : DEFAULT_MODEL;\n const record: UsageRecord = {\n platformId: PLATFORM_ID,\n modelId,\n providerId: resolveProvider(a.rawModel, a.providerHint),\n sessionId: a.sessionId,\n tokens: a.tokens,\n ts: a.ts,\n messageCount: a.messageCount,\n confidence: \"host-reported\",\n };\n if (a.dedupKey !== undefined) record.dedupKey = a.dedupKey;\n if (a.projectKey !== undefined) record.projectKey = a.projectKey;\n if (a.projectLabel !== undefined) record.projectLabel = a.projectLabel;\n if (a.agent !== undefined) record.agent = a.agent;\n return record;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Model / provider / timestamp extraction off a generic value\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction extractModel(value: Record<string, unknown>): string | undefined {\n const top = asNonEmptyString(value[\"model\"]);\n if (top !== undefined) return top;\n const message = value[\"message\"];\n return isObject(message) ? asNonEmptyString(message[\"model\"]) : undefined;\n}\n\nfunction messageProviderHint(message: Record<string, unknown>): string | undefined {\n return (\n asNonEmptyString(message[\"providerId\"]) ??\n asNonEmptyString(message[\"provider_id\"]) ??\n asNonEmptyString(message[\"provider\"])\n );\n}\n\nfunction extractProviderHint(value: Record<string, unknown>): string | undefined {\n const top =\n asNonEmptyString(value[\"providerId\"]) ??\n asNonEmptyString(value[\"provider_id\"]) ??\n asNonEmptyString(value[\"provider\"]);\n if (top !== undefined) return top;\n const message = value[\"message\"];\n return isObject(message) ? messageProviderHint(message) : undefined;\n}\n\nfunction extractClaudeTimestamp(value: Record<string, unknown>): number | null {\n const direct = parseTs(value[\"timestamp\"]) ?? parseTs(value[\"created_at\"]);\n if (direct !== null) return direct;\n const message = value[\"message\"];\n if (isObject(message)) {\n const m = parseTs(message[\"created_at\"]);\n if (m !== null) return m;\n }\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Headless JSON / streaming-line support (port of *_headless_*)\n// ─────────────────────────────────────────────────────────────────────────\n\ninterface HeadlessState {\n model?: string;\n providerHint?: string;\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n ts: number | null;\n}\n\nfunction newHeadlessState(): HeadlessState {\n return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, ts: null };\n}\n\nfunction resetHeadlessState(s: HeadlessState): void {\n s.model = undefined;\n s.providerHint = undefined;\n s.input = 0;\n s.output = 0;\n s.cacheRead = 0;\n s.cacheWrite = 0;\n s.ts = null;\n}\n\n/** Whole-file headless JSON parse (a single usage object). */\nfunction parseHeadlessJson(\n path: string,\n fallbackTs: number,\n projectKey: string | undefined,\n projectLabel: string | undefined,\n): UsageRecord[] {\n const value = readJsonFile(path);\n if (!isObject(value)) return [];\n const record = extractHeadlessMessage(value, \"unknown-session\", fallbackTs);\n if (record === undefined) return [];\n // session id is the file stem (matches Rust passing session_id from the stem).\n record.sessionId = stripExt(basename(path)) || \"unknown\";\n if (projectKey !== undefined) record.projectKey = projectKey;\n if (projectLabel !== undefined) record.projectLabel = projectLabel;\n return [record];\n}\n\n/** Process one streaming line; returns a completed record on message_start/_stop. */\nfunction processHeadlessLine(\n value: Record<string, unknown>,\n _rawLine: string,\n sessionId: string,\n state: HeadlessState,\n fallbackTs: number,\n): UsageRecord | undefined {\n const eventType = typeof value[\"type\"] === \"string\" ? (value[\"type\"] as string) : \"\";\n\n switch (eventType) {\n case \"message_start\": {\n const completed = finalizeHeadlessState(state, sessionId, fallbackTs);\n state.model = extractModel(value);\n state.providerHint = extractProviderHint(value);\n const ts = extractClaudeTimestamp(value);\n if (ts !== null) state.ts = ts;\n const message = value[\"message\"];\n const usage =\n (isObject(message) && isObject(message[\"usage\"]) ? (message[\"usage\"] as Record<string, unknown>) : undefined) ??\n (isObject(value[\"usage\"]) ? (value[\"usage\"] as Record<string, unknown>) : undefined);\n if (usage !== undefined) updateHeadlessUsage(state, usage);\n return completed;\n }\n case \"message_delta\": {\n const delta = value[\"delta\"];\n const usage =\n (isObject(value[\"usage\"]) ? (value[\"usage\"] as Record<string, unknown>) : undefined) ??\n (isObject(delta) && isObject(delta[\"usage\"]) ? (delta[\"usage\"] as Record<string, unknown>) : undefined);\n if (usage !== undefined) updateHeadlessUsage(state, usage);\n return undefined;\n }\n case \"message_stop\":\n return finalizeHeadlessState(state, sessionId, fallbackTs);\n default:\n return extractHeadlessMessage(value, sessionId, fallbackTs);\n }\n}\n\nfunction updateHeadlessUsage(state: HeadlessState, usage: Record<string, unknown>): void {\n const input = toIntOrNull(usage[\"input_tokens\"]);\n if (input !== null) state.input = Math.max(state.input, input);\n const output = toIntOrNull(usage[\"output_tokens\"]);\n if (output !== null) state.output = Math.max(state.output, output);\n const cacheRead = toIntOrNull(usage[\"cache_read_input_tokens\"]);\n if (cacheRead !== null) state.cacheRead = Math.max(state.cacheRead, cacheRead);\n const cacheWrite = toIntOrNull(usage[\"cache_creation_input_tokens\"]);\n if (cacheWrite !== null) state.cacheWrite = Math.max(state.cacheWrite, cacheWrite);\n}\n\n/** Emit a record from accumulated streaming state, then reset (port of finalize_headless_state). */\nfunction finalizeHeadlessState(\n state: HeadlessState,\n sessionId: string,\n fallbackTs: number,\n): UsageRecord | undefined {\n const rawModel = state.model;\n if (rawModel === undefined) return undefined;\n if (state.input === 0 && state.output === 0 && state.cacheRead === 0 && state.cacheWrite === 0) {\n resetHeadlessState(state);\n return undefined;\n }\n const tokens = emptyTokens();\n tokens.input = Math.max(0, state.input);\n tokens.output = Math.max(0, state.output);\n tokens.cacheRead = Math.max(0, state.cacheRead);\n tokens.cacheWrite = Math.max(0, state.cacheWrite);\n\n const record = buildRecord({\n rawModel,\n providerHint: state.providerHint,\n sessionId,\n ts: state.ts ?? fallbackTs,\n tokens,\n messageCount: 1,\n dedupKey: undefined,\n projectKey: undefined,\n projectLabel: undefined,\n agent: undefined,\n });\n resetHeadlessState(state);\n return record;\n}\n\n/** Extract a flat usage-bearing message (port of extract_claude_headless_message). */\nfunction extractHeadlessMessage(\n value: Record<string, unknown>,\n sessionId: string,\n fallbackTs: number,\n): UsageRecord | undefined {\n const message = value[\"message\"];\n const usage =\n (isObject(value[\"usage\"]) ? (value[\"usage\"] as Record<string, unknown>) : undefined) ??\n (isObject(message) && isObject(message[\"usage\"]) ? (message[\"usage\"] as Record<string, unknown>) : undefined);\n if (usage === undefined) return undefined;\n const rawModel = extractModel(value);\n if (rawModel === undefined) return undefined;\n const providerHint = extractProviderHint(value);\n const ts = extractClaudeTimestamp(value) ?? fallbackTs;\n\n const tokens = emptyTokens();\n tokens.input = Math.max(0, toIntOrNull(usage[\"input_tokens\"]) ?? 0);\n tokens.output = Math.max(0, toIntOrNull(usage[\"output_tokens\"]) ?? 0);\n tokens.cacheRead = Math.max(0, toIntOrNull(usage[\"cache_read_input_tokens\"]) ?? 0);\n tokens.cacheWrite = Math.max(0, toIntOrNull(usage[\"cache_creation_input_tokens\"]) ?? 0);\n\n return buildRecord({\n rawModel,\n providerHint,\n sessionId,\n ts,\n tokens,\n messageCount: 1,\n dedupKey: undefined,\n projectKey: undefined,\n projectLabel: undefined,\n agent: undefined,\n });\n}\n\n// Touch the parity-only helper so a strict no-unused-locals build stays clean\n// without changing behavior: isHumanTurn mirrors the Rust turn-detection used\n// for is_turn_start, which the usage record does not carry.\nvoid isHumanTurn;\n\n// ─────────────────────────────────────────────────────────────────────────\n// Reader\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The Claude Code usage reader singleton. */\nconst claudeCodeReader: UsageReader = {\n platformId: PLATFORM_ID,\n kind: \"local\",\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\n const root = firstExistingRoot(PLATFORM_ID);\n if (root === undefined) return []; // no ~/.claude/projects → fail-open\n\n // ~/.claude/projects/<key>/**.jsonl (+ subagents/agent-*.jsonl) + .json headless\n const files = walkFiles(root, (name) => {\n if (name.endsWith(\".meta.json\")) return false; // sidecar, not a transcript\n return name.endsWith(\".jsonl\") || name.endsWith(\".json\");\n });\n\n const parentCache: ParentSubagentCache = new Map();\n const records: UsageRecord[] = [];\n for (const file of files) {\n let rows: UsageRecord[];\n try {\n rows = parseClaudeFile(file, parentCache);\n } catch {\n continue; // fail-open per file\n }\n for (const row of rows) {\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\n records.push(row);\n }\n }\n return records;\n },\n};\n\nexport default claudeCodeReader;\n"],"mappings":"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","names":["inferred"]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * cli/sdk — the EMBEDDED SDK entry point for a developer-branded CLI.
3
+ *
4
+ * A developer adds agent-connector as a dependency, writes their connector
5
+ * config, and ships their OWN bin. `createConnectorCli` returns a runner that
6
+ * exposes EVERY agent-connector subcommand under the developer's brand, fully
7
+ * delegated and AUTO-SCOPED to the developer's connector:
8
+ *
9
+ * #!/usr/bin/env node
10
+ * import { fileURLToPath } from "node:url";
11
+ * import { createConnectorCli } from "@ken-jo/agent-connector/cli";
12
+ * createConnectorCli({
13
+ * name: "acme-db",
14
+ * connector: fileURLToPath(new URL("./agent-connector.config.mjs", import.meta.url)),
15
+ * }).run();
16
+ * // fileURLToPath, NOT URL.pathname — .pathname yields "/C:/…" on Windows.
17
+ *
18
+ * Then a consumer runs `acme-db install`, `acme-db leaderboard`, `acme-db
19
+ * telemetry`, `acme-db doctor`, etc. — and each one targets the developer's
20
+ * connector WITHOUT the consumer having to pass --connector.
21
+ *
22
+ * This module is PURE ARGUMENT TRANSFORMATION: it injects the right --connector
23
+ * (path) or --connector <id> filter when the user did not supply one, then hands
24
+ * off to {@link main} with the developer's program name. It never duplicates any
25
+ * command logic — every behavior still lives in the command modules.
26
+ */
27
+ /** Options for {@link createConnectorCli}. */
28
+ interface CreateConnectorCliOptions {
29
+ /** The developer's bin/brand name (shown in usage/help, e.g. "acme-db"). */
30
+ name: string;
31
+ /**
32
+ * Absolute path to the developer's connector config (.mjs / .js / .json) that
33
+ * ships inside their package. Injected as `--connector <path>` for the
34
+ * connector-targeted subcommands when the user did not pass one.
35
+ */
36
+ connector: string;
37
+ /**
38
+ * The connector id used for the telemetry/leaderboard FILTER injection. When
39
+ * omitted it is derived lazily by loading the connector config the first time a
40
+ * telemetry/leaderboard subcommand runs.
41
+ */
42
+ connectorId?: string;
43
+ }
44
+ /** The runner returned by {@link createConnectorCli}. */
45
+ interface ConnectorCli {
46
+ /**
47
+ * Run a branded invocation. `argv` defaults to `process.argv.slice(2)`.
48
+ * Resolves to the process exit code (0 success, non-zero failure).
49
+ */
50
+ run(argv?: string[]): Promise<number>;
51
+ }
52
+ declare function createConnectorCli(opts: CreateConnectorCliOptions): ConnectorCli;
53
+
54
+ export { type ConnectorCli, type CreateConnectorCliOptions, createConnectorCli };
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ main
4
+ } from "../chunk-54EMKTTX.js";
5
+ import {
6
+ loadConnectorFromPath
7
+ } from "../chunk-LTT56GI3.js";
8
+ import "../chunk-XNAM7F35.js";
9
+ import "../chunk-TRQKHP7D.js";
10
+
11
+ // src/cli/sdk.ts
12
+ var CONFIG_PATH_COMMANDS = /* @__PURE__ */ new Set([
13
+ "install",
14
+ "uninstall",
15
+ "upgrade",
16
+ // `sync` + `update` are back-compat aliases of `upgrade`; scope them too so a
17
+ // branded `acme-db sync` / `acme-db update` still targets the dev connector.
18
+ "sync",
19
+ "update",
20
+ "doctor",
21
+ "status",
22
+ "package"
23
+ ]);
24
+ var ID_HEAD_COMMANDS = /* @__PURE__ */ new Set(["serve", "hook"]);
25
+ var ID_TAIL_COMMANDS = /* @__PURE__ */ new Set(["telemetry", "leaderboard"]);
26
+ function wantsHelp(argv) {
27
+ if (argv.includes("--help") || argv.includes("-h")) return true;
28
+ if (argv[0] === "telemetry" && argv[1] === void 0) return true;
29
+ return false;
30
+ }
31
+ function hasConnectorFlag(argv) {
32
+ for (const a of argv) {
33
+ if (a === "--connector" || a === "--connector-id") return true;
34
+ if (a.startsWith("--connector=") || a.startsWith("--connector-id=")) return true;
35
+ }
36
+ return false;
37
+ }
38
+ function injectAfterCommand(argv, flag, value) {
39
+ return [argv[0], flag, value, ...argv.slice(1)];
40
+ }
41
+ function appendFlag(argv, flag, value) {
42
+ return [...argv, flag, value];
43
+ }
44
+ function createConnectorCli(opts) {
45
+ if (typeof opts.name !== "string" || opts.name.trim() === "") {
46
+ throw new TypeError("createConnectorCli: `name` must be a non-empty string");
47
+ }
48
+ if (typeof opts.connector !== "string" || opts.connector.trim() === "") {
49
+ throw new TypeError("createConnectorCli: `connector` must be a non-empty path");
50
+ }
51
+ let cachedId = opts.connectorId;
52
+ async function resolveConnectorId() {
53
+ if (cachedId !== void 0 && cachedId !== "") return cachedId;
54
+ const { connector } = await loadConnectorFromPath(opts.connector);
55
+ cachedId = connector.id;
56
+ return cachedId;
57
+ }
58
+ async function run(argv = process.argv.slice(2)) {
59
+ const command = argv[0];
60
+ if (command === void 0 || command.startsWith("-")) {
61
+ return main(argv, { programName: opts.name });
62
+ }
63
+ let scoped = argv;
64
+ if (!hasConnectorFlag(argv)) {
65
+ if (CONFIG_PATH_COMMANDS.has(command)) {
66
+ scoped = injectAfterCommand(argv, "--connector", opts.connector);
67
+ } else if (ID_HEAD_COMMANDS.has(command)) {
68
+ const id = await resolveConnectorId();
69
+ scoped = injectAfterCommand(argv, "--connector", id);
70
+ } else if (ID_TAIL_COMMANDS.has(command) && !wantsHelp(argv)) {
71
+ const id = await resolveConnectorId();
72
+ scoped = appendFlag(argv, "--connector", id);
73
+ }
74
+ }
75
+ return main(scoped, { programName: opts.name });
76
+ }
77
+ return { run };
78
+ }
79
+ export {
80
+ createConnectorCli
81
+ };
82
+ //# sourceMappingURL=sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/sdk.ts"],"sourcesContent":["/**\n * cli/sdk — the EMBEDDED SDK entry point for a developer-branded CLI.\n *\n * A developer adds agent-connector as a dependency, writes their connector\n * config, and ships their OWN bin. `createConnectorCli` returns a runner that\n * exposes EVERY agent-connector subcommand under the developer's brand, fully\n * delegated and AUTO-SCOPED to the developer's connector:\n *\n * #!/usr/bin/env node\n * import { fileURLToPath } from \"node:url\";\n * import { createConnectorCli } from \"@ken-jo/agent-connector/cli\";\n * createConnectorCli({\n * name: \"acme-db\",\n * connector: fileURLToPath(new URL(\"./agent-connector.config.mjs\", import.meta.url)),\n * }).run();\n * // fileURLToPath, NOT URL.pathname — .pathname yields \"/C:/…\" on Windows.\n *\n * Then a consumer runs `acme-db install`, `acme-db leaderboard`, `acme-db\n * telemetry`, `acme-db doctor`, etc. — and each one targets the developer's\n * connector WITHOUT the consumer having to pass --connector.\n *\n * This module is PURE ARGUMENT TRANSFORMATION: it injects the right --connector\n * (path) or --connector <id> filter when the user did not supply one, then hands\n * off to {@link main} with the developer's program name. It never duplicates any\n * command logic — every behavior still lives in the command modules.\n */\n\nimport { loadConnectorFromPath } from \"../core/load-connector.js\";\nimport { main } from \"./app.js\";\n\n/** Options for {@link createConnectorCli}. */\nexport interface CreateConnectorCliOptions {\n /** The developer's bin/brand name (shown in usage/help, e.g. \"acme-db\"). */\n name: string;\n /**\n * Absolute path to the developer's connector config (.mjs / .js / .json) that\n * ships inside their package. Injected as `--connector <path>` for the\n * connector-targeted subcommands when the user did not pass one.\n */\n connector: string;\n /**\n * The connector id used for the telemetry/leaderboard FILTER injection. When\n * omitted it is derived lazily by loading the connector config the first time a\n * telemetry/leaderboard subcommand runs.\n */\n connectorId?: string;\n}\n\n/** The runner returned by {@link createConnectorCli}. */\nexport interface ConnectorCli {\n /**\n * Run a branded invocation. `argv` defaults to `process.argv.slice(2)`.\n * Resolves to the process exit code (0 success, non-zero failure).\n */\n run(argv?: string[]): Promise<number>;\n}\n\n/**\n * Connector-targeted subcommands whose `--connector` is a CONFIG PATH: they load\n * the connector definition from disk. For these we inject the developer's config\n * PATH so their connector is the implicit target.\n *\n * (`uninstall` also accepts `--connector-id`; injecting the path is sufficient —\n * uninstall derives the id from the config when no explicit id is given.)\n */\nconst CONFIG_PATH_COMMANDS: ReadonlySet<string> = new Set([\n \"install\",\n \"uninstall\",\n \"upgrade\",\n // `sync` + `update` are back-compat aliases of `upgrade`; scope them too so a\n // branded `acme-db sync` / `acme-db update` still targets the dev connector.\n \"sync\",\n \"update\",\n \"doctor\",\n \"status\",\n \"package\",\n]);\n\n/**\n * Connector-targeted subcommands whose `--connector` is the registered connector\n * ID, spliced right AFTER the subcommand token: `serve` and `hook` pass it\n * straight to the runtime. (They cannot take a trailing flag — serve has a `--`\n * separator and hook has positionals — so the flag must lead.)\n */\nconst ID_HEAD_COMMANDS: ReadonlySet<string> = new Set([\"serve\", \"hook\"]);\n\n/**\n * View subcommands where `--connector <id>` is a FILTER appended at the END:\n * `leaderboard` (top-level) and `telemetry` (whose own sub — report|export|\n * leaderboard — is the leading positional, so the filter must come after it).\n * Appending keeps that leading positional intact while parseArgs still reads the\n * flag, so the branded tool shows ITS connector's data.\n *\n * Note: `usage` reads host CLI logs which carry no connector attribution, so it\n * is intentionally absent — there is nothing to scope it to.\n */\nconst ID_TAIL_COMMANDS: ReadonlySet<string> = new Set([\"telemetry\", \"leaderboard\"]);\n\n/**\n * Should the tail (telemetry/leaderboard) filter injection be SKIPPED because the\n * invocation is just asking for help / has no actionable sub? Skipping leaves the\n * branded usage text intact instead of turning `acme-db telemetry` into\n * `telemetry --connector <id>` (which the telemetry dispatcher would reject as an\n * unknown sub). True when a help flag appears, or when `telemetry` has no sub.\n */\nfunction wantsHelp(argv: string[]): boolean {\n if (argv.includes(\"--help\") || argv.includes(\"-h\")) return true;\n // `telemetry` with no sub-subcommand → show its help, don't inject a filter.\n if (argv[0] === \"telemetry\" && argv[1] === undefined) return true;\n return false;\n}\n\n/** Did the user already pass a --connector / --connector-id flag (any form)? */\nfunction hasConnectorFlag(argv: string[]): boolean {\n for (const a of argv) {\n if (a === \"--connector\" || a === \"--connector-id\") return true;\n if (a.startsWith(\"--connector=\") || a.startsWith(\"--connector-id=\")) return true;\n }\n return false;\n}\n\n/**\n * Splice `--connector <value>` immediately after the subcommand token (argv[0])\n * so it sits BEFORE any positionals or a `serve` `--` separator. Used for the\n * config-path commands and for serve/hook (whose `--` / positional grammar makes\n * trailing append unsafe). The original argv is never mutated.\n */\nfunction injectAfterCommand(argv: string[], flag: string, value: string): string[] {\n return [argv[0] as string, flag, value, ...argv.slice(1)];\n}\n\n/**\n * Append `--connector <value>` to the END of argv. Used for the view commands\n * (`leaderboard`/`telemetry`) whose leading positional sub must be preserved.\n * The original argv is never mutated.\n */\nfunction appendFlag(argv: string[], flag: string, value: string): string[] {\n return [...argv, flag, value];\n}\n\nexport function createConnectorCli(opts: CreateConnectorCliOptions): ConnectorCli {\n if (typeof opts.name !== \"string\" || opts.name.trim() === \"\") {\n throw new TypeError(\"createConnectorCli: `name` must be a non-empty string\");\n }\n if (typeof opts.connector !== \"string\" || opts.connector.trim() === \"\") {\n throw new TypeError(\"createConnectorCli: `connector` must be a non-empty path\");\n }\n\n // Cache the derived id so the connector module is loaded at most once across\n // repeated `.run()` calls in the same process.\n let cachedId: string | undefined = opts.connectorId;\n\n async function resolveConnectorId(): Promise<string> {\n if (cachedId !== undefined && cachedId !== \"\") return cachedId;\n const { connector } = await loadConnectorFromPath(opts.connector);\n cachedId = connector.id;\n return cachedId;\n }\n\n async function run(argv: string[] = process.argv.slice(2)): Promise<number> {\n const command = argv[0];\n\n // No subcommand, a help/version flag, or an unknown command: nothing to scope\n // — hand off verbatim so usage/version is branded and errors stay accurate.\n if (command === undefined || command.startsWith(\"-\")) {\n return main(argv, { programName: opts.name });\n }\n\n let scoped = argv;\n\n if (!hasConnectorFlag(argv)) {\n if (CONFIG_PATH_COMMANDS.has(command)) {\n // Inject the connector CONFIG PATH as the implicit target.\n scoped = injectAfterCommand(argv, \"--connector\", opts.connector);\n } else if (ID_HEAD_COMMANDS.has(command)) {\n // serve/hook take the connector ID and pass it to the runtime. Splice it\n // right after the subcommand token so a serve `--` separator and hook's\n // positionals are preserved.\n const id = await resolveConnectorId();\n scoped = injectAfterCommand(argv, \"--connector\", id);\n } else if (ID_TAIL_COMMANDS.has(command) && !wantsHelp(argv)) {\n // leaderboard/telemetry use the connector ID as a FILTER. Append it so\n // telemetry's leading positional sub (report|export|leaderboard) stays\n // the first token while parseArgs still reads the flag. We skip injection\n // for a bare/help invocation so the (un-filtered) usage text still shows.\n const id = await resolveConnectorId();\n scoped = appendFlag(argv, \"--connector\", id);\n }\n }\n\n return main(scoped, { programName: opts.name });\n }\n\n return { run };\n}\n"],"mappings":";;;;;;;;;;;AAiEA,IAAM,uBAA4C,oBAAI,IAAI;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQD,IAAM,mBAAwC,oBAAI,IAAI,CAAC,SAAS,MAAM,CAAC;AAYvE,IAAM,mBAAwC,oBAAI,IAAI,CAAC,aAAa,aAAa,CAAC;AASlF,SAAS,UAAU,MAAyB;AAC1C,MAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,EAAG,QAAO;AAE3D,MAAI,KAAK,CAAC,MAAM,eAAe,KAAK,CAAC,MAAM,OAAW,QAAO;AAC7D,SAAO;AACT;AAGA,SAAS,iBAAiB,MAAyB;AACjD,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,iBAAiB,MAAM,iBAAkB,QAAO;AAC1D,QAAI,EAAE,WAAW,cAAc,KAAK,EAAE,WAAW,iBAAiB,EAAG,QAAO;AAAA,EAC9E;AACA,SAAO;AACT;AAQA,SAAS,mBAAmB,MAAgB,MAAc,OAAyB;AACjF,SAAO,CAAC,KAAK,CAAC,GAAa,MAAM,OAAO,GAAG,KAAK,MAAM,CAAC,CAAC;AAC1D;AAOA,SAAS,WAAW,MAAgB,MAAc,OAAyB;AACzE,SAAO,CAAC,GAAG,MAAM,MAAM,KAAK;AAC9B;AAEO,SAAS,mBAAmB,MAA+C;AAChF,MAAI,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,KAAK,MAAM,IAAI;AAC5D,UAAM,IAAI,UAAU,uDAAuD;AAAA,EAC7E;AACA,MAAI,OAAO,KAAK,cAAc,YAAY,KAAK,UAAU,KAAK,MAAM,IAAI;AACtE,UAAM,IAAI,UAAU,0DAA0D;AAAA,EAChF;AAIA,MAAI,WAA+B,KAAK;AAExC,iBAAe,qBAAsC;AACnD,QAAI,aAAa,UAAa,aAAa,GAAI,QAAO;AACtD,UAAM,EAAE,UAAU,IAAI,MAAM,sBAAsB,KAAK,SAAS;AAChE,eAAW,UAAU;AACrB,WAAO;AAAA,EACT;AAEA,iBAAe,IAAI,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAoB;AAC1E,UAAM,UAAU,KAAK,CAAC;AAItB,QAAI,YAAY,UAAa,QAAQ,WAAW,GAAG,GAAG;AACpD,aAAO,KAAK,MAAM,EAAE,aAAa,KAAK,KAAK,CAAC;AAAA,IAC9C;AAEA,QAAI,SAAS;AAEb,QAAI,CAAC,iBAAiB,IAAI,GAAG;AAC3B,UAAI,qBAAqB,IAAI,OAAO,GAAG;AAErC,iBAAS,mBAAmB,MAAM,eAAe,KAAK,SAAS;AAAA,MACjE,WAAW,iBAAiB,IAAI,OAAO,GAAG;AAIxC,cAAM,KAAK,MAAM,mBAAmB;AACpC,iBAAS,mBAAmB,MAAM,eAAe,EAAE;AAAA,MACrD,WAAW,iBAAiB,IAAI,OAAO,KAAK,CAAC,UAAU,IAAI,GAAG;AAK5D,cAAM,KAAK,MAAM,mBAAmB;AACpC,iBAAS,WAAW,MAAM,eAAe,EAAE;AAAA,MAC7C;AAAA,IACF;AAEA,WAAO,KAAK,QAAQ,EAAE,aAAa,KAAK,KAAK,CAAC;AAAA,EAChD;AAEA,SAAO,EAAE,IAAI;AACf;","names":[]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ main
4
+ } from "./chunk-54EMKTTX.js";
5
+
6
+ // src/cli/index.ts
7
+ main(process.argv.slice(2)).then((code) => {
8
+ process.exitCode = code;
9
+ }).catch((err) => {
10
+ const message = err instanceof Error ? err.stack ?? err.message : String(err);
11
+ process.stderr.write(`agent-connector: fatal: ${message}
12
+ `);
13
+ process.exitCode = 1;
14
+ });
15
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/index.ts"],"sourcesContent":["/**\n * cli/index — the agent-connector bin entry.\n *\n * Intentionally tiny: it imports `main` from ./app.js and runs it\n * UNCONDITIONALLY. All helpers + dispatch live in ./app.ts (no side effects), so\n * command modules can import helpers without re-triggering the program, and the\n * auto-run survives bundler entry-splitting (no import.meta.url entry-guard).\n */\n\nimport { main } from \"./app.js\";\n\nmain(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code;\n })\n .catch((err: unknown) => {\n const message = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`agent-connector: fatal: ${message}\\n`);\n process.exitCode = 1;\n });\n"],"mappings":";;;;;;AAWA,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,EACvB,KAAK,CAAC,SAAS;AACd,UAAQ,WAAW;AACrB,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,QAAM,UAAU,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG;AAC9E,UAAQ,OAAO,MAAM,2BAA2B,OAAO;AAAA,CAAI;AAC3D,UAAQ,WAAW;AACrB,CAAC;","names":[]}
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ fileMtimeMs,
4
+ readJsonFile
5
+ } from "./chunk-RK6VBLXP.js";
6
+ import {
7
+ expandHome,
8
+ walkFiles
9
+ } from "./chunk-JWNT5KW7.js";
10
+ import {
11
+ emptyTokens,
12
+ inferProvider,
13
+ workspaceFromPath
14
+ } from "./chunk-XD7MY6HB.js";
15
+
16
+ // src/usage/readers/codebuff.ts
17
+ import { basename, dirname, isAbsolute, join, resolve } from "path";
18
+ import { homedir } from "os";
19
+ var PLATFORM_ID = "codebuff";
20
+ var DEFAULT_MODEL = "codebuff-unknown";
21
+ var DEFAULT_PROVIDER = "unknown";
22
+ var CHANNELS = ["manicode", "manicode-dev", "manicode-staging"];
23
+ function xdgConfigHome() {
24
+ const xdg = process.env.XDG_CONFIG_HOME;
25
+ if (xdg !== void 0 && xdg.trim() !== "") return resolve(expandHome(xdg.trim()));
26
+ return join(homedir(), ".config");
27
+ }
28
+ function envOverride(name) {
29
+ const raw = process.env[name];
30
+ if (raw == null || raw.trim() === "") return void 0;
31
+ const expanded = expandHome(raw.trim());
32
+ return isAbsolute(expanded) ? expanded : resolve(expanded);
33
+ }
34
+ function codebuffProjectRoots() {
35
+ const out = [];
36
+ const override = envOverride("AGENT_CONNECTOR_CODEBUFF_DIR");
37
+ if (override !== void 0) out.push(override);
38
+ const config = xdgConfigHome();
39
+ for (const channel of CHANNELS) {
40
+ out.push(join(config, channel, "projects"));
41
+ }
42
+ return out;
43
+ }
44
+ function asRecord(v) {
45
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
46
+ }
47
+ function asString(v) {
48
+ return typeof v === "string" ? v : void 0;
49
+ }
50
+ function asNumber(v) {
51
+ if (typeof v === "number") return Number.isFinite(v) ? v : null;
52
+ return null;
53
+ }
54
+ function pickNumber(obj, keys) {
55
+ for (const key of keys) {
56
+ const num = asNumber(obj[key]);
57
+ if (num !== null) {
58
+ const n = Math.trunc(num);
59
+ if (n > 0) return n;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ function parseTimestampStr(value) {
65
+ const ms = Date.parse(value);
66
+ if (!Number.isNaN(ms)) return ms;
67
+ if (/^-?\d+$/.test(value)) {
68
+ const numeric = Number(value);
69
+ if (!Number.isFinite(numeric) || numeric <= 0) return null;
70
+ return numeric >= 1e12 ? numeric : numeric * 1e3;
71
+ }
72
+ return null;
73
+ }
74
+ function parseTimestampValue(value) {
75
+ if (typeof value === "string") return parseTimestampStr(value);
76
+ const numeric = asNumber(value);
77
+ if (numeric === null) return null;
78
+ const n = Math.trunc(numeric);
79
+ if (n <= 0) return null;
80
+ return n >= 1e12 ? n : n * 1e3;
81
+ }
82
+ function parseChatIdToMillis(chatId) {
83
+ const tIndex = chatId.indexOf("T");
84
+ if (tIndex < 0) return null;
85
+ const date = chatId.slice(0, tIndex);
86
+ const timeWithSeparator = chatId.slice(tIndex);
87
+ const rebuilt = date + replaceFirst(timeWithSeparator, "-", ":", 2);
88
+ return parseTimestampStr(rebuilt);
89
+ }
90
+ function replaceFirst(s, from, to, count) {
91
+ let result = s;
92
+ let done = 0;
93
+ let idx = result.indexOf(from);
94
+ while (idx >= 0 && done < count) {
95
+ result = result.slice(0, idx) + to + result.slice(idx + from.length);
96
+ done++;
97
+ idx = result.indexOf(from, idx + to.length);
98
+ }
99
+ return result;
100
+ }
101
+ function deriveContextFromPath(path) {
102
+ const chatDir = dirname(path);
103
+ const chatId = basename(chatDir) || "unknown";
104
+ const chatsDir = dirname(chatDir);
105
+ const projectDir = dirname(chatsDir);
106
+ const project = basename(projectDir) || "unknown";
107
+ const projectsDir = dirname(projectDir);
108
+ const channelDir = dirname(projectsDir);
109
+ const channel = basename(channelDir) || "manicode";
110
+ return { channel, project, chatId };
111
+ }
112
+ function isAssistantRole(msg) {
113
+ const variant = asString(msg.variant) ?? asString(msg.role) ?? "";
114
+ return variant === "ai" || variant === "agent" || variant === "assistant";
115
+ }
116
+ function messageTimestamp(msg) {
117
+ for (const key of ["timestamp", "createdAt"]) {
118
+ if (key in msg) {
119
+ const ts = parseTimestampValue(msg[key]);
120
+ if (ts !== null) return ts;
121
+ }
122
+ }
123
+ const meta = asRecord(msg.metadata);
124
+ if (meta !== void 0 && "timestamp" in meta) {
125
+ return parseTimestampValue(meta.timestamp);
126
+ }
127
+ return null;
128
+ }
129
+ function newUsage() {
130
+ return { credits: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
131
+ }
132
+ function hasSignal(u) {
133
+ return u.input > 0 || u.output > 0 || u.cacheRead > 0 || u.cacheWrite > 0 || u.credits > 0;
134
+ }
135
+ function mergeFallback(self, other) {
136
+ if (self.input <= 0) self.input = other.input;
137
+ if (self.output <= 0) self.output = other.output;
138
+ if (self.cacheRead <= 0) self.cacheRead = other.cacheRead;
139
+ if (self.cacheWrite <= 0) self.cacheWrite = other.cacheWrite;
140
+ if (self.model === void 0) self.model = other.model;
141
+ if (self.credits <= 0) self.credits = other.credits;
142
+ }
143
+ function parseUsageObject(value) {
144
+ const usage = newUsage();
145
+ const obj = asRecord(value);
146
+ if (obj === void 0) return usage;
147
+ const input = pickNumber(obj, ["inputTokens", "input_tokens", "promptTokens", "prompt_tokens"]);
148
+ const output = pickNumber(obj, [
149
+ "outputTokens",
150
+ "output_tokens",
151
+ "completionTokens",
152
+ "completion_tokens"
153
+ ]);
154
+ let cacheRead = pickNumber(obj, [
155
+ "cacheReadInputTokens",
156
+ "cache_read_input_tokens",
157
+ "cachedTokensCreated",
158
+ "cached_tokens_created"
159
+ ]);
160
+ if (cacheRead === null) {
161
+ const details = asRecord(obj.promptTokensDetails) ?? asRecord(obj.prompt_tokens_details);
162
+ if (details !== void 0) {
163
+ const cached = asNumber(details.cachedTokens ?? details.cached_tokens);
164
+ if (cached !== null) cacheRead = Math.trunc(cached);
165
+ }
166
+ }
167
+ const cacheWrite = pickNumber(obj, [
168
+ "cacheCreationInputTokens",
169
+ "cache_creation_input_tokens",
170
+ "cacheCreationTokens",
171
+ "cache_creation_tokens"
172
+ ]);
173
+ usage.input = input ?? 0;
174
+ usage.output = output ?? 0;
175
+ usage.cacheRead = cacheRead ?? 0;
176
+ usage.cacheWrite = cacheWrite ?? 0;
177
+ const credits = asNumber(obj.credits);
178
+ if (credits !== null) usage.credits = credits;
179
+ const model = asString(obj.model);
180
+ if (model !== void 0) usage.model = model;
181
+ return usage;
182
+ }
183
+ function extractUsageFromRunState(metadata) {
184
+ const history = asRecord(asRecord(asRecord(asRecord(metadata.runState)?.sessionState)?.mainAgentState));
185
+ const rawHistory = history?.messageHistory;
186
+ if (!Array.isArray(rawHistory)) return void 0;
187
+ const accumulator = newUsage();
188
+ let foundAny = false;
189
+ for (let i = rawHistory.length - 1; i >= 0; i--) {
190
+ const entry = asRecord(rawHistory[i]);
191
+ if (entry === void 0) continue;
192
+ if (asString(entry.role) !== "assistant") continue;
193
+ const providerOptions = asRecord(entry.providerOptions);
194
+ if (providerOptions === void 0) continue;
195
+ const entryUsage = newUsage();
196
+ if ("usage" in providerOptions) {
197
+ mergeFallback(entryUsage, parseUsageObject(providerOptions.usage));
198
+ }
199
+ const codebuff = asRecord(providerOptions.codebuff);
200
+ if (codebuff !== void 0 && "usage" in codebuff) {
201
+ mergeFallback(entryUsage, parseUsageObject(codebuff.usage));
202
+ }
203
+ const cbModel = codebuff !== void 0 ? asString(codebuff.model) : void 0;
204
+ if (cbModel !== void 0) entryUsage.model = cbModel;
205
+ if (hasSignal(entryUsage) || entryUsage.model !== void 0) foundAny = true;
206
+ mergeFallback(accumulator, entryUsage);
207
+ }
208
+ return foundAny ? accumulator : void 0;
209
+ }
210
+ function extractAssistantUsage(msg) {
211
+ const usage = newUsage();
212
+ const metadata = asRecord(msg.metadata);
213
+ if (metadata !== void 0) {
214
+ const model = asString(metadata.model);
215
+ if (model !== void 0) usage.model = model;
216
+ if ("usage" in metadata) {
217
+ mergeFallback(usage, parseUsageObject(metadata.usage));
218
+ }
219
+ const codebuff = asRecord(metadata.codebuff);
220
+ if (codebuff !== void 0 && "usage" in codebuff) {
221
+ mergeFallback(usage, parseUsageObject(codebuff.usage));
222
+ }
223
+ const runStateUsage = extractUsageFromRunState(metadata);
224
+ if (runStateUsage !== void 0) mergeFallback(usage, runStateUsage);
225
+ }
226
+ const credits = asNumber(msg.credits);
227
+ if (credits !== null && credits > 0 && usage.credits <= 0) usage.credits = credits;
228
+ return usage;
229
+ }
230
+ function upstreamMessageId(msg) {
231
+ const id = asString(msg.id);
232
+ return id !== void 0 && id !== "" ? id : void 0;
233
+ }
234
+ function deriveDedupKey(sessionId, ts, model, usage, ordinal) {
235
+ const i = Math.max(0, usage.input);
236
+ const o = Math.max(0, usage.output);
237
+ const cr = Math.max(0, usage.cacheRead);
238
+ const cw = Math.max(0, usage.cacheWrite);
239
+ return `codebuff:${sessionId}:${ts}:${model}:${ordinal}:${i}:${o}:${cr}:${cw}`;
240
+ }
241
+ function parseCodebuffFile(path) {
242
+ const root = readJsonFile(path);
243
+ if (!Array.isArray(root)) return [];
244
+ const { channel, project, chatId } = deriveContextFromPath(path);
245
+ const sessionId = `${channel}/${project}/${chatId}`;
246
+ const { projectKey, projectLabel } = workspaceFromPath(project);
247
+ const chatIdTs = parseChatIdToMillis(chatId);
248
+ const mtime = fileMtimeMs(path);
249
+ const out = [];
250
+ for (let ordinal = 0; ordinal < root.length; ordinal++) {
251
+ const msg = asRecord(root[ordinal]);
252
+ if (msg === void 0) continue;
253
+ if (!isAssistantRole(msg)) continue;
254
+ const usage = extractAssistantUsage(msg);
255
+ if (!hasSignal(usage)) continue;
256
+ const ts = messageTimestamp(msg) ?? (chatIdTs !== null && chatIdTs > 0 ? chatIdTs : mtime);
257
+ const modelId = usage.model ?? DEFAULT_MODEL;
258
+ const providerId = inferProvider(modelId) ?? DEFAULT_PROVIDER;
259
+ const dedupKey = upstreamMessageId(msg) ?? deriveDedupKey(sessionId, ts, modelId, usage, ordinal);
260
+ const tokens = emptyTokens();
261
+ tokens.input = Math.max(0, usage.input);
262
+ tokens.output = Math.max(0, usage.output);
263
+ tokens.cacheRead = Math.max(0, usage.cacheRead);
264
+ tokens.cacheWrite = Math.max(0, usage.cacheWrite);
265
+ const record = {
266
+ platformId: PLATFORM_ID,
267
+ modelId,
268
+ providerId,
269
+ sessionId,
270
+ tokens,
271
+ ts,
272
+ messageCount: 1,
273
+ dedupKey,
274
+ confidence: "host-reported"
275
+ };
276
+ const cost = Math.max(0, usage.credits);
277
+ if (cost > 0) record.cost = cost;
278
+ if (projectKey !== void 0) record.projectKey = projectKey;
279
+ if (projectLabel !== void 0) record.projectLabel = projectLabel;
280
+ out.push(record);
281
+ }
282
+ return out;
283
+ }
284
+ var codebuffReader = {
285
+ platformId: PLATFORM_ID,
286
+ kind: "local",
287
+ async read({ sinceMs }) {
288
+ const records = [];
289
+ for (const root of codebuffProjectRoots()) {
290
+ const files = walkFiles(root, (name, abs) => {
291
+ return name === "chat-messages.json" && /[\\/]chats[\\/][^\\/]+[\\/]chat-messages\.json$/.test(abs);
292
+ });
293
+ for (const file of files) {
294
+ for (const row of parseCodebuffFile(file)) {
295
+ if (sinceMs !== void 0 && row.ts < sinceMs) continue;
296
+ records.push(row);
297
+ }
298
+ }
299
+ }
300
+ return records;
301
+ }
302
+ };
303
+ var codebuff_default = codebuffReader;
304
+ export {
305
+ codebuff_default as default
306
+ };
307
+ //# sourceMappingURL=codebuff-3PMZJJHP.js.map