@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.
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +348 -0
- package/dist/amp-QSFH2V7Y.js +217 -0
- package/dist/amp-QSFH2V7Y.js.map +1 -0
- package/dist/amp-TULUPXXL.js +237 -0
- package/dist/amp-TULUPXXL.js.map +1 -0
- package/dist/antigravity-CX2YLSY7.js +17 -0
- package/dist/antigravity-CX2YLSY7.js.map +1 -0
- package/dist/antigravity-STHZWMP3.js +171 -0
- package/dist/antigravity-STHZWMP3.js.map +1 -0
- package/dist/antigravity-cli-P6HADOAO.js +121 -0
- package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
- package/dist/antigravity-cli-YQDFVV4U.js +86 -0
- package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
- package/dist/chunk-54EMKTTX.js +191 -0
- package/dist/chunk-54EMKTTX.js.map +1 -0
- package/dist/chunk-7L6ITC4T.js +54 -0
- package/dist/chunk-7L6ITC4T.js.map +1 -0
- package/dist/chunk-7MSDGHJW.js +316 -0
- package/dist/chunk-7MSDGHJW.js.map +1 -0
- package/dist/chunk-BCHWFTV4.js +196 -0
- package/dist/chunk-BCHWFTV4.js.map +1 -0
- package/dist/chunk-DBQMX75U.js +36 -0
- package/dist/chunk-DBQMX75U.js.map +1 -0
- package/dist/chunk-EXLS7BSI.js +29 -0
- package/dist/chunk-EXLS7BSI.js.map +1 -0
- package/dist/chunk-JNM3TPLX.js +593 -0
- package/dist/chunk-JNM3TPLX.js.map +1 -0
- package/dist/chunk-JWNT5KW7.js +197 -0
- package/dist/chunk-JWNT5KW7.js.map +1 -0
- package/dist/chunk-KHO5WNTP.js +30 -0
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/chunk-LTT56GI3.js +155 -0
- package/dist/chunk-LTT56GI3.js.map +1 -0
- package/dist/chunk-MEH7GC7R.js +111 -0
- package/dist/chunk-MEH7GC7R.js.map +1 -0
- package/dist/chunk-MQPNU2CY.js +64 -0
- package/dist/chunk-MQPNU2CY.js.map +1 -0
- package/dist/chunk-OPXAP3AC.js +12 -0
- package/dist/chunk-OPXAP3AC.js.map +1 -0
- package/dist/chunk-RK6VBLXP.js +50 -0
- package/dist/chunk-RK6VBLXP.js.map +1 -0
- package/dist/chunk-TIIP3RG2.js +420 -0
- package/dist/chunk-TIIP3RG2.js.map +1 -0
- package/dist/chunk-TRKWUNO4.js +702 -0
- package/dist/chunk-TRKWUNO4.js.map +1 -0
- package/dist/chunk-TRQKHP7D.js +93 -0
- package/dist/chunk-TRQKHP7D.js.map +1 -0
- package/dist/chunk-WCETQWWK.js +59 -0
- package/dist/chunk-WCETQWWK.js.map +1 -0
- package/dist/chunk-WV63X7IK.js +357 -0
- package/dist/chunk-WV63X7IK.js.map +1 -0
- package/dist/chunk-XD7MY6HB.js +343 -0
- package/dist/chunk-XD7MY6HB.js.map +1 -0
- package/dist/chunk-XNAM7F35.js +276 -0
- package/dist/chunk-XNAM7F35.js.map +1 -0
- package/dist/chunk-XUCWLJY5.js +238 -0
- package/dist/chunk-XUCWLJY5.js.map +1 -0
- package/dist/chunk-YNZU6PAV.js +800 -0
- package/dist/chunk-YNZU6PAV.js.map +1 -0
- package/dist/chunk-YT43MUSJ.js +146 -0
- package/dist/chunk-YT43MUSJ.js.map +1 -0
- package/dist/claude-code-HKDV7T32.js +637 -0
- package/dist/claude-code-HKDV7T32.js.map +1 -0
- package/dist/claude-code-W74MNTAC.js +652 -0
- package/dist/claude-code-W74MNTAC.js.map +1 -0
- package/dist/cli/sdk.d.ts +54 -0
- package/dist/cli/sdk.js +82 -0
- package/dist/cli/sdk.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/codebuff-3PMZJJHP.js +307 -0
- package/dist/codebuff-3PMZJJHP.js.map +1 -0
- package/dist/codebuff-QX57FJDN.js +220 -0
- package/dist/codebuff-QX57FJDN.js.map +1 -0
- package/dist/codex-JFQTS6LU.js +593 -0
- package/dist/codex-JFQTS6LU.js.map +1 -0
- package/dist/codex-Q3ATRRX3.js +364 -0
- package/dist/codex-Q3ATRRX3.js.map +1 -0
- package/dist/copilot-cli-MTCGHZ2T.js +622 -0
- package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
- package/dist/copilot-cli-SJGIXVSD.js +369 -0
- package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
- package/dist/crush-I7PK3FGI.js +360 -0
- package/dist/crush-I7PK3FGI.js.map +1 -0
- package/dist/crush-LN5QFFR2.js +183 -0
- package/dist/crush-LN5QFFR2.js.map +1 -0
- package/dist/cursor-4TMXKJOZ.js +603 -0
- package/dist/cursor-4TMXKJOZ.js.map +1 -0
- package/dist/cursor-QZ6MTO74.js +194 -0
- package/dist/cursor-QZ6MTO74.js.map +1 -0
- package/dist/detect-WIAGVYS4.js +68 -0
- package/dist/detect-WIAGVYS4.js.map +1 -0
- package/dist/doctor-EZW2K4NI.js +372 -0
- package/dist/doctor-EZW2K4NI.js.map +1 -0
- package/dist/droid-KN6VRMMT.js +424 -0
- package/dist/droid-KN6VRMMT.js.map +1 -0
- package/dist/droid-W5WF5XEX.js +224 -0
- package/dist/droid-W5WF5XEX.js.map +1 -0
- package/dist/gemini-cli-CIRW37TU.js +369 -0
- package/dist/gemini-cli-CIRW37TU.js.map +1 -0
- package/dist/gemini-cli-Y35FFK4M.js +705 -0
- package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
- package/dist/goose-A3M5GODH.js +212 -0
- package/dist/goose-A3M5GODH.js.map +1 -0
- package/dist/goose-ZTGTSUFJ.js +480 -0
- package/dist/goose-ZTGTSUFJ.js.map +1 -0
- package/dist/hermes-H2KV44GK.js +470 -0
- package/dist/hermes-H2KV44GK.js.map +1 -0
- package/dist/hermes-ZYLMOFLZ.js +196 -0
- package/dist/hermes-ZYLMOFLZ.js.map +1 -0
- package/dist/hook-YVDILAMZ.js +75 -0
- package/dist/hook-YVDILAMZ.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/install-HQRYZOQ2.js +60 -0
- package/dist/install-HQRYZOQ2.js.map +1 -0
- package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
- package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
- package/dist/kilo-NSWAVP6P.js +235 -0
- package/dist/kilo-NSWAVP6P.js.map +1 -0
- package/dist/kilo-QXJRCSLT.js +384 -0
- package/dist/kilo-QXJRCSLT.js.map +1 -0
- package/dist/kilo-cli-EAQTHXUW.js +587 -0
- package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
- package/dist/kilo-cli-EVBKUODA.js +160 -0
- package/dist/kilo-cli-EVBKUODA.js.map +1 -0
- package/dist/kimi-EI3UM3ZV.js +155 -0
- package/dist/kimi-EI3UM3ZV.js.map +1 -0
- package/dist/kimi-GQ7VXNYT.js +423 -0
- package/dist/kimi-GQ7VXNYT.js.map +1 -0
- package/dist/kiro-UCLSLBW2.js +217 -0
- package/dist/kiro-UCLSLBW2.js.map +1 -0
- package/dist/kiro-VQTT6LO2.js +451 -0
- package/dist/kiro-VQTT6LO2.js.map +1 -0
- package/dist/leaderboard-K6ON3SWW.js +137 -0
- package/dist/leaderboard-K6ON3SWW.js.map +1 -0
- package/dist/mux-T2TV4CIQ.js +211 -0
- package/dist/mux-T2TV4CIQ.js.map +1 -0
- package/dist/mux-ZMLVAXOO.js +96 -0
- package/dist/mux-ZMLVAXOO.js.map +1 -0
- package/dist/omp-PB4VDKS6.js +586 -0
- package/dist/omp-PB4VDKS6.js.map +1 -0
- package/dist/openclaw-ETN2M63O.js +751 -0
- package/dist/openclaw-ETN2M63O.js.map +1 -0
- package/dist/openclaw-IYA7ASL6.js +150 -0
- package/dist/openclaw-IYA7ASL6.js.map +1 -0
- package/dist/opencode-FZ33JKUZ.js +734 -0
- package/dist/opencode-FZ33JKUZ.js.map +1 -0
- package/dist/opencode-HWT3RSMN.js +216 -0
- package/dist/opencode-HWT3RSMN.js.map +1 -0
- package/dist/package-DLOVE2E4.js +1132 -0
- package/dist/package-DLOVE2E4.js.map +1 -0
- package/dist/pi-CIU2QHT4.js +208 -0
- package/dist/pi-CIU2QHT4.js.map +1 -0
- package/dist/pi-UFX2OYFV.js +139 -0
- package/dist/pi-UFX2OYFV.js.map +1 -0
- package/dist/qwen-code-5POCNU6P.js +573 -0
- package/dist/qwen-code-5POCNU6P.js.map +1 -0
- package/dist/qwen-code-W7NUCDEN.js +134 -0
- package/dist/qwen-code-W7NUCDEN.js.map +1 -0
- package/dist/roo-code-IOIUXRDE.js +242 -0
- package/dist/roo-code-IOIUXRDE.js.map +1 -0
- package/dist/roo-code-ZLKH4A7N.js +229 -0
- package/dist/roo-code-ZLKH4A7N.js.map +1 -0
- package/dist/runtime/index.d.ts +157 -0
- package/dist/runtime/index.js +20 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/serve-HVWWY4B6.js +65 -0
- package/dist/serve-HVWWY4B6.js.map +1 -0
- package/dist/status-FE7TECZD.js +128 -0
- package/dist/status-FE7TECZD.js.map +1 -0
- package/dist/synthetic-UKR6SMGS.js +178 -0
- package/dist/synthetic-UKR6SMGS.js.map +1 -0
- package/dist/telemetry-36GESKQQ.js +349 -0
- package/dist/telemetry-36GESKQQ.js.map +1 -0
- package/dist/trae-JSNDZFKL.js +204 -0
- package/dist/trae-JSNDZFKL.js.map +1 -0
- package/dist/trae-PA26DPBW.js +157 -0
- package/dist/trae-PA26DPBW.js.map +1 -0
- package/dist/types-CYUB1OTr.d.ts +427 -0
- package/dist/uninstall-IGCKNOSF.js +72 -0
- package/dist/uninstall-IGCKNOSF.js.map +1 -0
- package/dist/upgrade-JV2VK242.js +104 -0
- package/dist/upgrade-JV2VK242.js.map +1 -0
- package/dist/usage-6ER6EMEY.js +324 -0
- package/dist/usage-6ER6EMEY.js.map +1 -0
- package/dist/usage-event-JKBI6XCF.js +48 -0
- package/dist/usage-event-JKBI6XCF.js.map +1 -0
- package/dist/vscode-copilot-CK3NO54M.js +715 -0
- package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
- package/dist/warp-JNQA7PBU.js +126 -0
- package/dist/warp-JNQA7PBU.js.map +1 -0
- package/dist/warp-QNVDXOG4.js +199 -0
- package/dist/warp-QNVDXOG4.js.map +1 -0
- package/dist/zed-NNNX5MNT.js +272 -0
- package/dist/zed-NNNX5MNT.js.map +1 -0
- package/dist/zed-VRJKWYUR.js +209 -0
- package/dist/zed-VRJKWYUR.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/copilot-cli/index.ts"],"sourcesContent":["/**\n * adapters/copilot-cli — GitHub Copilot CLI platform adapter for agent-connector.\n *\n * GitHub Copilot CLI is a json-stdio host: the host pipes a JSON payload to a\n * hook command on stdin and reads JSON/exit-code back. Its hook event names and\n * reply shape are Claude-compatible (PascalCase events, `hookSpecificOutput`\n * reply wrapper), so the normalized HookEventName values map 1:1 — no event\n * rename table is needed.\n *\n * Native config (user/global only — Copilot CLI has no project-scoped config):\n * - MCP servers: ~/.copilot/mcp-config.json, root key \"mcpServers\". An stdio\n * server is written with type \"local\" (the host also accepts \"stdio\") plus\n * `tools: [\"*\"]`. Remote servers use type \"http\".\n * - Hooks: a Claude-compatible hooks file shaped `{ version: 1, hooks: { … } }`\n * discovered from ~/.copilot/hooks/*.json. We write a single dedicated file,\n * ~/.copilot/hooks/agent-connector.json, so we never disturb a user's own\n * hook files and removal is a clean, scoped operation. Each event maps to an\n * array of flat command entries `{ matcher?, hooks: [{ type:\"command\", command }] }`\n * — the Claude shape, which Copilot CLI reads.\n * - Reply: a `hookSpecificOutput` object keyed by `hookEventName` carrying\n * `permissionDecision` (allow|deny|ask) + `permissionDecisionReason`,\n * `additionalContext`, and (PreToolUse) `updatedInput`. PreToolUse is\n * fail-closed on the host side; exit 0 + JSON refines the decision.\n *\n * Env handling: the host is not documented to support `${env:VAR}` interpolation\n * inside mcp-config.json, so env/header/url refs are resolved to literals at\n * install time via resolveEnvRefsDeep (the safe default for a no-native-interp\n * host, matching the Codex adapter's approach).\n *\n * Content surfaces (surfaces-design §4-5): Copilot CLI exposes skills and\n * subagents but NO prompt-file command surface, so commands inherit the\n * BaseAdapter skip/warn default (supportsCommands stays false).\n * - skills: folder-per-skill `<dir>/skills/<name>/SKILL.md` (+ resource files).\n * user scope → ~/.copilot/skills; project scope → <projectDir>/.github/skills.\n * - subagents: user scope → ~/.copilot/agents/<name>.agent.md; project scope →\n * <projectDir>/.github/agents/<name>.agent.md (md + frontmatter:\n * name, description, tools, model).\n * The .github/ tree is shared with the vscode-copilot and jetbrains-copilot\n * connectors; we write identical, idempotent content and on uninstall remove\n * only the files this connector wrote.\n *\n * Grounded in docs/research/understand-report.md §2 (Platform Integration\n * Matrix, \"GitHub Copilot CLI\" row).\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\nimport type {\n ChangeRecord,\n DetectedPlatform,\n HealthCheck,\n HookEventName,\n HookParadigm,\n HookResponse,\n NotificationEvent,\n PlatformCapabilities,\n PlatformId,\n PostToolUseEvent,\n PreCompactEvent,\n PreToolUseEvent,\n ServerDef,\n SessionEndEvent,\n SessionStartEvent,\n SkillDef,\n StopEvent,\n SubagentDef,\n Transport,\n UserPromptSubmitEvent,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport {\n buildHomeBinHookCommand,\n buildServeWrapperCommand,\n isHomeBinHookCommand,\n shouldWrapForTelemetry,\n} from \"../../core/spawn.js\";\n\nconst HOST: PlatformId = \"copilot-cli\";\nconst MCP_ROOT_KEY = \"mcpServers\";\n\n/** Native hooks-file version Copilot CLI expects (`{ version: 1, hooks: {…} }`). */\nconst COPILOT_HOOKS_VERSION = 1;\n\n/**\n * Copilot CLI accepts both camelCase and PascalCase hook event names; PascalCase\n * is the portable, Claude/VS Code-compatible form, so we emit it directly. Our\n * normalized HookEventName values are already PascalCase and match 1:1, hence no\n * rename table — every declared event has a native equivalent.\n */\ntype CopilotHookEvent = HookEventName;\n\n/** A single Copilot CLI hook registration entry (Claude-shaped). */\ninterface CopilotHookEntry {\n matcher: string;\n hooks: Array<{ type: \"command\"; command: string }>;\n}\n\n/** The shape of the Copilot CLI hooks file we own. */\ninterface CopilotHooksFile {\n version?: number;\n hooks?: Record<string, CopilotHookEntry[]>;\n}\n\n/** Native MCP server entry shapes Copilot CLI accepts under `mcpServers`. */\ninterface CopilotLocalServer {\n /** stdio transport is registered as type \"local\" (host also accepts \"stdio\"). */\n type: \"local\";\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n tools: string[];\n}\ninterface CopilotHttpServer {\n type: \"http\";\n url: string;\n headers?: Record<string, string>;\n tools: string[];\n}\n\n/** Raw Copilot CLI hook stdin payload (Claude-style: PascalCase event, snake_case fields). */\ninterface CopilotWireInput {\n connector?: unknown;\n session_id?: string;\n transcript_path?: string;\n cwd?: string;\n hook_event_name?: string;\n\n // tool events\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n tool_response?: unknown;\n\n // SessionStart\n source?: string;\n // SessionEnd\n reason?: string;\n // UserPromptSubmit\n prompt?: string;\n // PreCompact\n trigger?: string;\n // Stop\n stop_hook_active?: boolean;\n // Notification\n message?: string;\n}\n\nexport class CopilotCliAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"GitHub Copilot CLI\";\n readonly paradigm: HookParadigm = \"json-stdio\";\n\n readonly capabilities: PlatformCapabilities = {\n // Copilot CLI delivers the full Claude-compatible lifecycle event set.\n preToolUse: true,\n postToolUse: true,\n preCompact: true,\n sessionStart: true,\n sessionEnd: true,\n userPromptSubmit: true,\n stop: true,\n notification: true,\n // PreToolUse is fail-closed and can rewrite tool input (updatedInput); a\n // PostToolUse hook cannot rewrite already-emitted tool output.\n canModifyArgs: true,\n canModifyOutput: false,\n canInjectSessionContext: true,\n transports: [\"stdio\", \"http\"],\n // Content surfaces: Copilot CLI exposes skills + subagents, but has no\n // prompt-file command surface, so commands stay false (inherits BaseAdapter\n // skip/warn).\n supportsCommands: false,\n supportsSkills: true,\n supportsSubagents: true,\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(_projectDir: string): DetectedPlatform {\n const userDir = join(homedir(), \".copilot\");\n const mcpConfig = join(userDir, \"mcp-config.json\");\n const hooksDir = join(userDir, \"hooks\");\n const installed =\n existsSync(userDir) || existsSync(mcpConfig) || existsSync(hooksDir);\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath: mcpConfig,\n scope: \"user\",\n reason: installed\n ? `found GitHub Copilot CLI config under ${userDir}`\n : `no GitHub Copilot CLI config at ${userDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n /** Copilot CLI is user/global only — scope is ignored. */\n getConfigDir(_ctx: InstallContext): string {\n return join(homedir(), \".copilot\");\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"mcp-config.json\");\n }\n\n /**\n * Copilot CLI discovers hooks from any `~/.copilot/hooks/*.json`. We write a\n * single dedicated file so we never disturb the user's own hook files and\n * uninstall is a clean, scoped operation.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"hooks\", \"agent-connector.json\");\n }\n\n // ── MCP server install / uninstall ───────────────────────────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n const override = connector.platforms[HOST]?.server;\n if (!connector.server || override === false) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: connector.server\n ? \"server registration disabled for copilot-cli\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n // Shallow-merge any per-platform server override into the base ServerDef.\n const server: ServerDef =\n override && typeof override === \"object\"\n ? { ...connector.server, ...override }\n : connector.server;\n\n const serverPath = this.getServerConfigPath(ctx);\n const entry = this.renderServerEntry(ctx, server);\n\n return [\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\n ];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\n ];\n }\n\n /** Render a normalized ServerDef into Copilot CLI's native mcpServers entry. */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): CopilotLocalServer | CopilotHttpServer {\n const transport: Transport = server.transport;\n const tools = this.renderTools(server);\n\n if (transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\n const wrapped = buildServeWrapperCommand(\n ctx.homeBinPath,\n ctx.connector.id,\n command,\n args,\n ctx.scope,\n this.id,\n );\n command = wrapped.command;\n args = wrapped.args;\n }\n\n const entry: CopilotLocalServer = {\n type: \"local\",\n command: resolveEnvRefsDeep(command),\n tools,\n };\n if (args.length > 0) entry.args = args.map((a) => resolveEnvRefsDeep(a));\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n if (server.cwd) entry.cwd = resolveEnvRefsDeep(server.cwd);\n return entry;\n }\n\n // http (and any other remote transport we surface) — Copilot registers a URL.\n const entry: CopilotHttpServer = {\n type: \"http\",\n url: resolveEnvRefsDeep(server.url ?? \"\"),\n tools,\n };\n const headers = this.renderEnv(server.headers);\n if (headers) entry.headers = headers;\n return entry;\n }\n\n /**\n * Render env/header values. Copilot CLI is not documented to support native\n * `${env:VAR}` interpolation in mcp-config.json, so refs resolve to literals\n * at install time (the safe default for a no-native-interp host).\n */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n return resolveEnvRefsDeep({ ...env });\n }\n\n /** Render the tool allow-list. Copilot CLI expects `tools` on every entry; default [\"*\"]. */\n private renderTools(server: ServerDef): string[] {\n const include = server.tools?.include;\n return include && include.length > 0 ? [...include] : [\"*\"];\n }\n\n // ── Hook install / uninstall ─────────────────────────────────────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.hooks === false) {\n return [\n { platform: this.id, action: \"skip\", detail: \"hooks disabled for copilot-cli\" },\n ];\n }\n if (connector.hookEvents.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\n }\n\n const hooksPath = this.getHookConfigPath(ctx);\n const file = this.readJson<CopilotHooksFile>(hooksPath) ?? {};\n const hooks = (file.hooks ??= {});\n\n const changes: ChangeRecord[] = [];\n let mutated = false;\n\n for (const event of connector.hookEvents) {\n // PascalCase events map 1:1 to Copilot CLI's native event names.\n const copilotEvent: CopilotHookEvent = event;\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);\n const matcher = connector.hooks[event]?.matcher ?? \"\";\n const entry: CopilotHookEntry = {\n matcher,\n hooks: [{ type: \"command\", command }],\n };\n\n const bucket = (hooks[copilotEvent] ??= []);\n const existingIdx = bucket.findIndex((e) => this.entryHasOurCommand(e, ctx));\n\n if (existingIdx >= 0) {\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: `hooks.${copilotEvent} already registered`,\n });\n continue;\n }\n bucket[existingIdx] = entry;\n changes.push({\n platform: this.id,\n action: \"update\",\n path: hooksPath,\n detail: `hooks.${copilotEvent}`,\n });\n } else {\n bucket.push(entry);\n changes.push({\n platform: this.id,\n action: \"create\",\n path: hooksPath,\n detail: `hooks.${copilotEvent}`,\n });\n }\n mutated = true;\n }\n\n if (mutated) {\n file.version = COPILOT_HOOKS_VERSION;\n this.writeJson(hooksPath, file, ctx.dryRun);\n }\n return changes;\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n const hooksPath = this.getHookConfigPath(ctx);\n const file = this.readJson<CopilotHooksFile>(hooksPath);\n const hooks = file?.hooks;\n if (!file || !hooks) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: \"no hooks section present\",\n },\n ];\n }\n\n const changes: ChangeRecord[] = [];\n let mutated = false;\n\n for (const copilotEvent of Object.keys(hooks)) {\n const bucket = hooks[copilotEvent];\n if (!Array.isArray(bucket)) continue;\n\n // Strip our hook command from each entry; drop entries left empty so we\n // never remove another connector's (or the user's own) hook commands.\n const next: CopilotHookEntry[] = [];\n let removed = 0;\n for (const e of bucket) {\n const innerBefore = e.hooks?.length ?? 0;\n const inner = (e.hooks ?? []).filter((h) => !this.isOurCommand(h.command, ctx));\n removed += innerBefore - inner.length;\n if (inner.length > 0) next.push({ matcher: e.matcher ?? \"\", hooks: inner });\n }\n\n if (removed > 0) {\n if (next.length > 0) hooks[copilotEvent] = next;\n else delete hooks[copilotEvent];\n changes.push({\n platform: this.id,\n action: \"remove\",\n path: hooksPath,\n detail: `hooks.${copilotEvent} (${removed})`,\n });\n mutated = true;\n }\n }\n\n if (mutated) this.writeJson(hooksPath, file, ctx.dryRun);\n if (changes.length === 0) {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: \"no matching hook entries\",\n });\n }\n return changes;\n }\n\n private entryHasOurCommand(entry: CopilotHookEntry, ctx: InstallContext): boolean {\n return (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx));\n }\n\n /** True when a hook command references our home binary AND this connector id\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\n }\n\n // ── Content surfaces: skills / subagents ─────────────────────────────────\n // CONTENT-ONLY: pure native-file writers. No runtime dispatch, no home-bin\n // pointer, no telemetry wrap. Each method is idempotent (byte-identical →\n // skip) via BaseAdapter.writeContentFile and reversible via removeContentFile.\n // Honors platforms[\"copilot-cli\"] per-surface false to skip. Commands are\n // unsupported here — they inherit the BaseAdapter skip/warn default.\n //\n // Path scoping: user scope lives under ~/.copilot (getConfigDir); project\n // scope lives under the shared <projectDir>/.github tree (the same files\n // vscode-copilot / jetbrains-copilot would write). We write identical content\n // and on uninstall remove only the files this connector wrote.\n\n /** Root dir for content surfaces: ~/.copilot (user) or <projectDir>/.github (project). */\n private contentDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".github\")\n : this.getConfigDir(ctx);\n }\n\n private skillsDir(ctx: InstallContext): string {\n return join(this.contentDir(ctx), \"skills\");\n }\n private agentsDir(ctx: InstallContext): string {\n return join(this.contentDir(ctx), \"agents\");\n }\n\n /** Native skill dir: <contentDir>/skills/<name>. */\n private skillDir(ctx: InstallContext, name: string): string {\n return join(this.skillsDir(ctx), name);\n }\n /** Native subagent file path: <contentDir>/agents/<name>.agent.md. */\n private subagentPath(ctx: InstallContext, name: string): string {\n return join(this.agentsDir(ctx), `${name}.agent.md`);\n }\n\n // ── Skills ────────────────────────────────────────────────────────────────\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for copilot-cli\" }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\n // (config-time validation already rejects these, but never trust input).\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n /**\n * Render a skill's SKILL.md: frontmatter (name, description + optional model,\n * allowed-tools, disable-model-invocation) + body. Uniform Agent Skills shape\n * shared across every skill-supporting connector.\n */\n private renderSkill(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\", \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return this.renderFrontmatterMd(frontmatter, skill.body);\n }\n\n // ── Subagents ───────────────────────────────────────────────────────────────\n\n override installSubagents(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.subagents === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"subagents disabled for copilot-cli\" }];\n }\n if (connector.subagents.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\n }\n return connector.subagents.map((agent) =>\n this.writeContentFile(\n this.subagentPath(ctx, agent.name),\n this.renderSubagent(agent),\n ctx.dryRun,\n ),\n );\n }\n\n override uninstallSubagents(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.subagents.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\n }\n return connector.subagents.map((agent) =>\n this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun),\n );\n }\n\n /** Render a subagent to md+frontmatter (name, description, tools, model) + prompt body. */\n private renderSubagent(agent: SubagentDef): string {\n const frontmatter: Record<string, unknown> = {\n name: agent.name,\n description: agent.description,\n };\n const allow = agent.tools?.allow;\n if (allow && allow.length > 0) frontmatter.tools = allow.join(\", \");\n if (agent.model !== undefined) frontmatter.model = agent.model;\n if (agent.extra) Object.assign(frontmatter, agent.extra);\n return this.renderFrontmatterMd(frontmatter, agent.prompt);\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const mcpPath = this.getServerConfigPath(ctx);\n const hooksPath = this.getHookConfigPath(ctx);\n const connectorId = ctx.connector.id;\n const homeBin = ctx.homeBinPath;\n const hookEvents = ctx.connector.hookEvents;\n const checks: HealthCheck[] = [\n {\n name: `${this.name}: mcp-config.json present`,\n check: () =>\n existsSync(mcpPath)\n ? { status: \"OK\", detail: mcpPath }\n : { status: \"FAIL\", detail: `not found: ${mcpPath}` },\n },\n {\n name: `${this.name}: hook command registered`,\n check: () => {\n if (hookEvents.length === 0) {\n return { status: \"OK\", detail: \"no hooks declared\" };\n }\n const file = this.readJson<CopilotHooksFile>(hooksPath);\n if (!file) return { status: \"FAIL\", detail: `cannot read ${hooksPath}` };\n const hooks = file.hooks ?? {};\n const registered = Object.values(hooks).some((entries) =>\n (entries ?? []).some((e) =>\n (e.hooks ?? []).some((h) =>\n isHomeBinHookCommand(h.command, homeBin, connectorId),\n ),\n ),\n );\n return registered\n ? { status: \"OK\", detail: \"hook command present\" }\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${hooksPath}` };\n },\n },\n ];\n\n // Content-surface checks: only assert presence of the surfaces this\n // connector declares (skip silently for surfaces it never asked for).\n // Commands are unsupported on Copilot CLI, so no command check.\n for (const skill of ctx.connector.skills) {\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\n checks.push({\n name: `${this.name}: skill ${skill.name} present`,\n check: () =>\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n for (const agent of ctx.connector.subagents) {\n const p = this.subagentPath(ctx, agent.name);\n checks.push({\n name: `${this.name}: subagent ${agent.name} present`,\n check: () =>\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n return checks;\n }\n\n // ── Runtime: parse Copilot CLI stdin JSON → normalized event ─────────────\n\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as CopilotWireInput;\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\n const sessionId = extractSessionId(input);\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\n\n const base = {\n hostPlatform: HOST,\n connectorId,\n sessionId,\n raw,\n ...(projectDir !== undefined ? { projectDir } : {}),\n } as const;\n\n switch (event) {\n case \"PreToolUse\": {\n const ev: PreToolUseEvent = {\n ...base,\n toolName: input.tool_name ?? \"\",\n toolInput: input.tool_input ?? {},\n };\n return ev;\n }\n case \"PostToolUse\": {\n const toolOutput = toolResponseToString(input.tool_response);\n const ev: PostToolUseEvent = {\n ...base,\n toolName: input.tool_name ?? \"\",\n toolInput: input.tool_input ?? {},\n ...(toolOutput !== undefined ? { toolOutput } : {}),\n };\n return ev;\n }\n case \"SessionStart\": {\n const ev: SessionStartEvent = {\n ...base,\n source: normalizeSessionSource(input.source),\n };\n return ev;\n }\n case \"SessionEnd\": {\n const ev: SessionEndEvent = {\n ...base,\n ...(typeof input.reason === \"string\" ? { reason: input.reason } : {}),\n };\n return ev;\n }\n case \"UserPromptSubmit\": {\n const ev: UserPromptSubmitEvent = {\n ...base,\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\n };\n return ev;\n }\n case \"PreCompact\": {\n const ev: PreCompactEvent = {\n ...base,\n ...(input.trigger === \"auto\" || input.trigger === \"manual\"\n ? { trigger: input.trigger }\n : {}),\n };\n return ev;\n }\n case \"Stop\": {\n const ev: StopEvent = {\n ...base,\n ...(typeof input.stop_hook_active === \"boolean\"\n ? { stopHookActive: input.stop_hook_active }\n : {}),\n };\n return ev;\n }\n case \"Notification\": {\n const ev: NotificationEvent = {\n ...base,\n message: typeof input.message === \"string\" ? input.message : \"\",\n };\n return ev;\n }\n default: {\n // Exhaustive guard — every HookEventName is handled above.\n const _never: never = event;\n throw new Error(`unsupported copilot-cli hook event: ${String(_never)}`);\n }\n }\n }\n\n // ── Runtime: normalized response → Copilot CLI native hook reply ─────────\n\n formatReply(event: HookEventName, response: HookResponse): HookReply {\n const hookEventName = event;\n const decision = response.decision ?? \"allow\";\n\n // deny → block the action with a reason (exit 0; JSON carries the decision).\n if (decision === \"deny\") {\n return this.stdout({\n hookSpecificOutput: {\n hookEventName,\n permissionDecision: \"deny\",\n permissionDecisionReason: response.reason ?? \"Blocked by hook\",\n },\n });\n }\n\n // ask → prompt the user to confirm.\n if (decision === \"ask\") {\n return this.stdout({\n hookSpecificOutput: {\n hookEventName,\n permissionDecision: \"ask\",\n permissionDecisionReason: response.reason ?? \"Confirmation required by hook\",\n },\n });\n }\n\n // modify → rewrite PreToolUse input (only where Copilot CLI supports it).\n if (decision === \"modify\") {\n if (event === \"PreToolUse\" && response.updatedInput) {\n return this.stdout({\n hookSpecificOutput: { hookEventName, updatedInput: response.updatedInput },\n });\n }\n // Output rewrite is unsupported on Copilot CLI; fall through to allow.\n }\n\n // context → inject soft guidance (also the SessionStart context path).\n if (decision === \"context\" && response.additionalContext) {\n return this.stdout({\n hookSpecificOutput: { hookEventName, additionalContext: response.additionalContext },\n });\n }\n\n // allow / void / unsupported-degradation → pass through with exit 0.\n return { exitCode: 0 };\n }\n\n private stdout(payload: unknown): HookReply {\n return { exitCode: 0, stdout: JSON.stringify(payload) };\n }\n}\n\n/**\n * Extract a stable session id from a Copilot CLI wire payload.\n * Priority mirrors the Claude wire protocol: transcript UUID > session_id > \"\".\n */\nfunction extractSessionId(input: CopilotWireInput): string {\n if (typeof input.transcript_path === \"string\") {\n const m = input.transcript_path.match(/([a-f0-9-]{36})\\.jsonl$/);\n if (m && m[1]) return m[1];\n }\n if (typeof input.session_id === \"string\" && input.session_id !== \"\") {\n return input.session_id;\n }\n return \"\";\n}\n\n/** Coerce a Copilot PostToolUse `tool_response` into a string for the normalized event. */\nfunction toolResponseToString(value: unknown): string | undefined {\n if (value == null) return undefined;\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction normalizeSessionSource(source: string | undefined): SessionStartEvent[\"source\"] {\n switch (source) {\n case \"compact\":\n return \"compact\";\n case \"resume\":\n return \"resume\";\n case \"clear\":\n return \"clear\";\n default:\n return \"startup\";\n }\n}\n\nexport const adapter = new CopilotCliAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
fileMtimeMs,
|
|
4
|
+
readJsonlLines
|
|
5
|
+
} from "./chunk-RK6VBLXP.js";
|
|
6
|
+
import {
|
|
7
|
+
firstExistingRoot,
|
|
8
|
+
walkFiles
|
|
9
|
+
} from "./chunk-JWNT5KW7.js";
|
|
10
|
+
import {
|
|
11
|
+
emptyTokens,
|
|
12
|
+
inferProvider
|
|
13
|
+
} from "./chunk-XD7MY6HB.js";
|
|
14
|
+
|
|
15
|
+
// src/usage/readers/copilot-cli.ts
|
|
16
|
+
var PLATFORM_ID = "copilot-cli";
|
|
17
|
+
var DEFAULT_MODEL = "unknown";
|
|
18
|
+
var DEFAULT_PROVIDER = "github-copilot";
|
|
19
|
+
var DEFAULT_SESSION = "unknown-session";
|
|
20
|
+
var MODEL_ATTRS = ["gen_ai.response.model", "gen_ai.request.model"];
|
|
21
|
+
var SESSION_ATTRS = [
|
|
22
|
+
["gen_ai.conversation.id", 3 /* Session */],
|
|
23
|
+
["copilot_chat.session_id", 3 /* Session */],
|
|
24
|
+
["copilot_chat.chat_session_id", 3 /* Session */],
|
|
25
|
+
["session.id", 3 /* Session */],
|
|
26
|
+
["github.copilot.interaction_id", 2 /* Interaction */],
|
|
27
|
+
["gen_ai.response.id", 1 /* Response */]
|
|
28
|
+
];
|
|
29
|
+
function isObject(v) {
|
|
30
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
31
|
+
}
|
|
32
|
+
function asString(v) {
|
|
33
|
+
return typeof v === "string" ? v : void 0;
|
|
34
|
+
}
|
|
35
|
+
function valueAsI64(v) {
|
|
36
|
+
if (typeof v === "number") {
|
|
37
|
+
if (!Number.isFinite(v)) return void 0;
|
|
38
|
+
return Math.trunc(v);
|
|
39
|
+
}
|
|
40
|
+
if (typeof v === "string") {
|
|
41
|
+
const trimmed = v.trim();
|
|
42
|
+
if (!/^[+-]?\d+$/.test(trimmed)) return void 0;
|
|
43
|
+
const n = Number(trimmed);
|
|
44
|
+
return Number.isFinite(n) ? Math.trunc(n) : void 0;
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
function attrI64(attrs, key) {
|
|
49
|
+
const raw = valueAsI64(attrs[key]);
|
|
50
|
+
if (raw === void 0) return 0;
|
|
51
|
+
return Math.max(0, raw);
|
|
52
|
+
}
|
|
53
|
+
function attrI64First(attrs, keys) {
|
|
54
|
+
for (const key of keys) {
|
|
55
|
+
const v = attrI64(attrs, key);
|
|
56
|
+
if (v > 0) return v;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
function attrStr(attrs, key) {
|
|
61
|
+
return asString(attrs[key]);
|
|
62
|
+
}
|
|
63
|
+
function firstNonEmptyAttr(attrs, keys) {
|
|
64
|
+
for (const key of keys) {
|
|
65
|
+
const s = asString(attrs[key]);
|
|
66
|
+
if (s !== void 0 && s.trim() !== "") return s;
|
|
67
|
+
}
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
function bestSessionAttr(attrs) {
|
|
71
|
+
let best;
|
|
72
|
+
for (const [key, priority] of SESSION_ATTRS) {
|
|
73
|
+
const value = asString(attrs[key]);
|
|
74
|
+
if (value === void 0 || value.trim() === "") continue;
|
|
75
|
+
if (best === void 0 || priority >= best.priority) {
|
|
76
|
+
best = { id: value, priority };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return best;
|
|
80
|
+
}
|
|
81
|
+
function recordBody(value) {
|
|
82
|
+
return asString(value["body"]) ?? asString(value["_body"]);
|
|
83
|
+
}
|
|
84
|
+
function getObject(value, key) {
|
|
85
|
+
const v = value[key];
|
|
86
|
+
return isObject(v) ? v : void 0;
|
|
87
|
+
}
|
|
88
|
+
function traceIdFromRecord(value) {
|
|
89
|
+
const top = asString(value["traceId"]);
|
|
90
|
+
if (top !== void 0) return top;
|
|
91
|
+
const ctx = getObject(value, "spanContext");
|
|
92
|
+
return ctx === void 0 ? void 0 : asString(ctx["traceId"]);
|
|
93
|
+
}
|
|
94
|
+
function spanIdFromRecord(value) {
|
|
95
|
+
const top = asString(value["spanId"]);
|
|
96
|
+
if (top !== void 0) return top;
|
|
97
|
+
const ctx = getObject(value, "spanContext");
|
|
98
|
+
return ctx === void 0 ? void 0 : asString(ctx["spanId"]);
|
|
99
|
+
}
|
|
100
|
+
function timestampMsFromValue(value) {
|
|
101
|
+
if (!Array.isArray(value)) return void 0;
|
|
102
|
+
const seconds = valueAsI64(value[0]);
|
|
103
|
+
if (seconds === void 0) return void 0;
|
|
104
|
+
const nanos = valueAsI64(value[1]);
|
|
105
|
+
if (nanos === void 0) return void 0;
|
|
106
|
+
return seconds * 1e3 + Math.trunc(nanos / 1e6);
|
|
107
|
+
}
|
|
108
|
+
function timestampMsFromScalar(value) {
|
|
109
|
+
const raw = valueAsI64(value);
|
|
110
|
+
if (raw === void 0) return void 0;
|
|
111
|
+
const abs = Math.abs(raw);
|
|
112
|
+
if (abs >= 1e17) return Math.trunc(raw / 1e6);
|
|
113
|
+
if (abs >= 1e14) return Math.trunc(raw / 1e3);
|
|
114
|
+
if (abs >= 1e11) return raw;
|
|
115
|
+
return raw * 1e3;
|
|
116
|
+
}
|
|
117
|
+
function timestampMsFromUnixNanos(value) {
|
|
118
|
+
const raw = valueAsI64(value);
|
|
119
|
+
if (raw === void 0 || raw <= 0) return void 0;
|
|
120
|
+
return Math.trunc(raw / 1e6);
|
|
121
|
+
}
|
|
122
|
+
function timestampMsFromRecord(value) {
|
|
123
|
+
return timestampMsFromValue(value["endTime"]) ?? timestampMsFromValue(value["startTime"]) ?? timestampMsFromValue(value["hrTime"]) ?? timestampMsFromValue(value["_hrTime"]) ?? timestampMsFromValue(value["time"]) ?? timestampMsFromScalar(value["timestamp"]) ?? timestampMsFromScalar(value["observedTimestamp"]) ?? timestampMsFromUnixNanos(value["timeUnixNano"]);
|
|
124
|
+
}
|
|
125
|
+
function normalizeInputTokens(input, output, cacheRead, cacheWrite, reasoning) {
|
|
126
|
+
const cacheReadForInput = Math.min(Math.max(cacheRead, 0), Math.max(input, 0));
|
|
127
|
+
const tokens = emptyTokens();
|
|
128
|
+
tokens.input = Math.max(0, Math.max(input, 0) - cacheReadForInput);
|
|
129
|
+
tokens.output = Math.max(output, 0);
|
|
130
|
+
tokens.cacheRead = Math.max(cacheRead, 0);
|
|
131
|
+
tokens.cacheWrite = Math.max(cacheWrite, 0);
|
|
132
|
+
tokens.reasoning = Math.max(reasoning, 0);
|
|
133
|
+
return tokens;
|
|
134
|
+
}
|
|
135
|
+
function tokenTotal(t) {
|
|
136
|
+
return t.input + t.output + t.cacheRead + t.cacheWrite + t.reasoning;
|
|
137
|
+
}
|
|
138
|
+
function isSpanRecord(value) {
|
|
139
|
+
const type = asString(value["type"]);
|
|
140
|
+
if (type === "span") return true;
|
|
141
|
+
if (type !== void 0) return false;
|
|
142
|
+
const hasName = asString(value["name"]) !== void 0;
|
|
143
|
+
const hasSpanIdentity = asString(value["spanId"]) !== void 0 || asString(value["traceId"]) !== void 0;
|
|
144
|
+
const hasSpanTiming = value["startTime"] !== void 0 || value["endTime"] !== void 0 || value["duration"] !== void 0;
|
|
145
|
+
return hasName && (hasSpanIdentity || hasSpanTiming || value["kind"] !== void 0);
|
|
146
|
+
}
|
|
147
|
+
function isChatSpanRecord(value, attrs) {
|
|
148
|
+
if (!isSpanRecord(value)) return false;
|
|
149
|
+
if (attrStr(attrs, "gen_ai.operation.name") === "chat") return true;
|
|
150
|
+
const name = asString(value["name"]);
|
|
151
|
+
return name !== void 0 && name.startsWith("chat ");
|
|
152
|
+
}
|
|
153
|
+
function isAgentSummarySpanRecord(value, attrs) {
|
|
154
|
+
if (!isSpanRecord(value)) return false;
|
|
155
|
+
if (attrStr(attrs, "gen_ai.operation.name") === "invoke_agent") return true;
|
|
156
|
+
const name = asString(value["name"]);
|
|
157
|
+
return name !== void 0 && name.startsWith("invoke_agent ");
|
|
158
|
+
}
|
|
159
|
+
function isInferenceLogRecord(value, attrs) {
|
|
160
|
+
if (isSpanRecord(value)) return false;
|
|
161
|
+
if (attrStr(attrs, "event.name") === "gen_ai.client.inference.operation.details") return true;
|
|
162
|
+
const body = recordBody(value);
|
|
163
|
+
return body !== void 0 && body.startsWith("GenAI inference:");
|
|
164
|
+
}
|
|
165
|
+
function isAgentTurnLogRecord(value, attrs) {
|
|
166
|
+
if (isSpanRecord(value)) return false;
|
|
167
|
+
if (attrStr(attrs, "event.name") === "copilot_chat.agent.turn") return true;
|
|
168
|
+
const body = recordBody(value);
|
|
169
|
+
return body !== void 0 && body.startsWith("copilot_chat.agent.turn");
|
|
170
|
+
}
|
|
171
|
+
function collectTraceContexts(records) {
|
|
172
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
173
|
+
for (const record of records) {
|
|
174
|
+
const traceId = traceIdFromRecord(record);
|
|
175
|
+
if (traceId === void 0) continue;
|
|
176
|
+
const attrs = getObject(record, "attributes");
|
|
177
|
+
if (attrs === void 0) continue;
|
|
178
|
+
let context = contexts.get(traceId);
|
|
179
|
+
if (context === void 0) {
|
|
180
|
+
context = { sessionIdPriority: 0 /* Missing */ };
|
|
181
|
+
contexts.set(traceId, context);
|
|
182
|
+
}
|
|
183
|
+
if (context.model === void 0) {
|
|
184
|
+
context.model = firstNonEmptyAttr(attrs, MODEL_ATTRS);
|
|
185
|
+
}
|
|
186
|
+
const best = bestSessionAttr(attrs);
|
|
187
|
+
if (best !== void 0 && best.priority > context.sessionIdPriority) {
|
|
188
|
+
context.sessionId = best.id;
|
|
189
|
+
context.sessionIdPriority = best.priority;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return contexts;
|
|
193
|
+
}
|
|
194
|
+
function dedupKeyForRecord(source, record, attrs, traceId, sessionId, ts, index) {
|
|
195
|
+
const spanId = spanIdFromRecord(record);
|
|
196
|
+
switch (source) {
|
|
197
|
+
case 0 /* ChatSpan */:
|
|
198
|
+
case 3 /* AgentSummarySpan */:
|
|
199
|
+
if (traceId !== void 0 && spanId !== void 0) return `${traceId}:${spanId}`;
|
|
200
|
+
return `span:${sessionId}:${ts}:${index}`;
|
|
201
|
+
case 1 /* InferenceLog */:
|
|
202
|
+
if (traceId !== void 0 && spanId !== void 0) return `log:${traceId}:${spanId}`;
|
|
203
|
+
return `log:${sessionId}:${ts}:${index}`;
|
|
204
|
+
case 2 /* AgentTurnLog */: {
|
|
205
|
+
let turnPart;
|
|
206
|
+
for (const key of ["turn.index", "copilot_chat.turn.index"]) {
|
|
207
|
+
const v = valueAsI64(attrs[key]);
|
|
208
|
+
if (v !== void 0) {
|
|
209
|
+
turnPart = String(v);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const part = turnPart ?? `idx-${index}`;
|
|
214
|
+
if (traceId !== void 0) return `agent-turn:${traceId}:${part}`;
|
|
215
|
+
return `agent-turn:${sessionId}:${part}:${index}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function classify(value, attrs) {
|
|
220
|
+
if (isChatSpanRecord(value, attrs)) return 0 /* ChatSpan */;
|
|
221
|
+
if (isInferenceLogRecord(value, attrs)) return 1 /* InferenceLog */;
|
|
222
|
+
if (isAgentTurnLogRecord(value, attrs)) return 2 /* AgentTurnLog */;
|
|
223
|
+
if (isAgentSummarySpanRecord(value, attrs)) return 3 /* AgentSummarySpan */;
|
|
224
|
+
return void 0;
|
|
225
|
+
}
|
|
226
|
+
function candidateFromRecord(record, index, fallbackTimestamp, traceContexts) {
|
|
227
|
+
const attrs = getObject(record, "attributes");
|
|
228
|
+
if (attrs === void 0) return void 0;
|
|
229
|
+
const source = classify(record, attrs);
|
|
230
|
+
if (source === void 0) return void 0;
|
|
231
|
+
const traceId = traceIdFromRecord(record);
|
|
232
|
+
const traceContext = traceId === void 0 ? void 0 : traceContexts.get(traceId);
|
|
233
|
+
const input = attrI64First(attrs, ["gen_ai.usage.input_tokens"]);
|
|
234
|
+
const output = attrI64First(attrs, ["gen_ai.usage.output_tokens"]);
|
|
235
|
+
const cacheRead = attrI64First(attrs, ["gen_ai.usage.cache_read.input_tokens"]);
|
|
236
|
+
const cacheWrite = attrI64First(attrs, [
|
|
237
|
+
"gen_ai.usage.cache_write.input_tokens",
|
|
238
|
+
"gen_ai.usage.cache_creation.input_tokens"
|
|
239
|
+
]);
|
|
240
|
+
const reasoning = attrI64First(attrs, [
|
|
241
|
+
"gen_ai.usage.reasoning.output_tokens",
|
|
242
|
+
"gen_ai.usage.reasoning_tokens"
|
|
243
|
+
]);
|
|
244
|
+
const tokens = normalizeInputTokens(input, output, cacheRead, cacheWrite, reasoning);
|
|
245
|
+
if (tokenTotal(tokens) === 0) return void 0;
|
|
246
|
+
const responseIdRaw = asString(attrs["gen_ai.response.id"]);
|
|
247
|
+
const responseId = responseIdRaw !== void 0 && responseIdRaw.trim() !== "" ? responseIdRaw.trim() : void 0;
|
|
248
|
+
const model = firstNonEmptyAttr(attrs, MODEL_ATTRS) ?? traceContext?.model ?? DEFAULT_MODEL;
|
|
249
|
+
const providerId = inferProvider(model) ?? DEFAULT_PROVIDER;
|
|
250
|
+
const sessionId = bestSessionAttr(attrs)?.id ?? traceContext?.sessionId ?? traceId ?? DEFAULT_SESSION;
|
|
251
|
+
const ts = timestampMsFromRecord(record) ?? fallbackTimestamp;
|
|
252
|
+
const dedupKey = dedupKeyForRecord(source, record, attrs, traceId, sessionId, ts, index);
|
|
253
|
+
const candidate = {
|
|
254
|
+
source,
|
|
255
|
+
model,
|
|
256
|
+
providerId,
|
|
257
|
+
sessionId,
|
|
258
|
+
ts,
|
|
259
|
+
tokens,
|
|
260
|
+
dedupKey
|
|
261
|
+
};
|
|
262
|
+
if (traceId !== void 0) candidate.traceId = traceId;
|
|
263
|
+
if (responseId !== void 0) candidate.responseId = responseId;
|
|
264
|
+
return candidate;
|
|
265
|
+
}
|
|
266
|
+
function traceIdsForSource(candidates, source) {
|
|
267
|
+
const set = /* @__PURE__ */ new Set();
|
|
268
|
+
for (const c of candidates) {
|
|
269
|
+
if (c.source === source && c.traceId !== void 0) set.add(c.traceId);
|
|
270
|
+
}
|
|
271
|
+
return set;
|
|
272
|
+
}
|
|
273
|
+
function responseIdsForSource(candidates, source) {
|
|
274
|
+
const set = /* @__PURE__ */ new Set();
|
|
275
|
+
for (const c of candidates) {
|
|
276
|
+
if (c.source === source && c.responseId !== void 0) set.add(c.responseId);
|
|
277
|
+
}
|
|
278
|
+
return set;
|
|
279
|
+
}
|
|
280
|
+
function shouldEmit(candidate, chatTraces, inferenceTraces, agentTurnTraces, chatResponseIds, inferenceResponseIds, agentTurnResponseIds) {
|
|
281
|
+
const { traceId, responseId } = candidate;
|
|
282
|
+
const traceMatch = (traces) => traceId !== void 0 && traces.has(traceId);
|
|
283
|
+
const responseMatch = (ids) => responseId !== void 0 && ids.has(responseId);
|
|
284
|
+
switch (candidate.source) {
|
|
285
|
+
case 0 /* ChatSpan */:
|
|
286
|
+
return true;
|
|
287
|
+
case 1 /* InferenceLog */:
|
|
288
|
+
return !traceMatch(chatTraces) && !responseMatch(chatResponseIds);
|
|
289
|
+
case 2 /* AgentTurnLog */:
|
|
290
|
+
return !traceMatch(chatTraces) && !traceMatch(inferenceTraces) && !responseMatch(chatResponseIds) && !responseMatch(inferenceResponseIds);
|
|
291
|
+
case 3 /* AgentSummarySpan */:
|
|
292
|
+
return !traceMatch(chatTraces) && !traceMatch(inferenceTraces) && !traceMatch(agentTurnTraces) && !responseMatch(chatResponseIds) && !responseMatch(inferenceResponseIds) && !responseMatch(agentTurnResponseIds);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function parseCopilotFile(path) {
|
|
296
|
+
const lines = readJsonlLines(path);
|
|
297
|
+
if (lines.length === 0) return [];
|
|
298
|
+
const fallbackTimestamp = fileMtimeMs(path);
|
|
299
|
+
const records = [];
|
|
300
|
+
for (const raw of lines) {
|
|
301
|
+
if (isObject(raw)) records.push(raw);
|
|
302
|
+
}
|
|
303
|
+
const traceContexts = collectTraceContexts(records);
|
|
304
|
+
const candidates = [];
|
|
305
|
+
records.forEach((record, index) => {
|
|
306
|
+
const candidate = candidateFromRecord(record, index, fallbackTimestamp, traceContexts);
|
|
307
|
+
if (candidate !== void 0) candidates.push(candidate);
|
|
308
|
+
});
|
|
309
|
+
const chatTraces = traceIdsForSource(candidates, 0 /* ChatSpan */);
|
|
310
|
+
const inferenceTraces = traceIdsForSource(candidates, 1 /* InferenceLog */);
|
|
311
|
+
const agentTurnTraces = traceIdsForSource(candidates, 2 /* AgentTurnLog */);
|
|
312
|
+
const chatResponseIds = responseIdsForSource(candidates, 0 /* ChatSpan */);
|
|
313
|
+
const inferenceResponseIds = responseIdsForSource(candidates, 1 /* InferenceLog */);
|
|
314
|
+
const agentTurnResponseIds = responseIdsForSource(candidates, 2 /* AgentTurnLog */);
|
|
315
|
+
const out = [];
|
|
316
|
+
for (const candidate of candidates) {
|
|
317
|
+
if (!shouldEmit(
|
|
318
|
+
candidate,
|
|
319
|
+
chatTraces,
|
|
320
|
+
inferenceTraces,
|
|
321
|
+
agentTurnTraces,
|
|
322
|
+
chatResponseIds,
|
|
323
|
+
inferenceResponseIds,
|
|
324
|
+
agentTurnResponseIds
|
|
325
|
+
)) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
out.push({
|
|
329
|
+
platformId: PLATFORM_ID,
|
|
330
|
+
modelId: candidate.model,
|
|
331
|
+
providerId: candidate.providerId,
|
|
332
|
+
sessionId: candidate.sessionId,
|
|
333
|
+
tokens: candidate.tokens,
|
|
334
|
+
ts: candidate.ts,
|
|
335
|
+
messageCount: 1,
|
|
336
|
+
dedupKey: candidate.dedupKey,
|
|
337
|
+
confidence: "host-reported"
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
var copilotReader = {
|
|
343
|
+
platformId: PLATFORM_ID,
|
|
344
|
+
kind: "local",
|
|
345
|
+
async read({ sinceMs }) {
|
|
346
|
+
const root = firstExistingRoot(PLATFORM_ID);
|
|
347
|
+
if (root === void 0) return [];
|
|
348
|
+
const files = walkFiles(root, (name) => name.endsWith(".jsonl"));
|
|
349
|
+
const records = [];
|
|
350
|
+
for (const file of files) {
|
|
351
|
+
let rows;
|
|
352
|
+
try {
|
|
353
|
+
rows = parseCopilotFile(file);
|
|
354
|
+
} catch {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
for (const row of rows) {
|
|
358
|
+
if (sinceMs !== void 0 && row.ts < sinceMs) continue;
|
|
359
|
+
records.push(row);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return records;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
var copilot_cli_default = copilotReader;
|
|
366
|
+
export {
|
|
367
|
+
copilot_cli_default as default
|
|
368
|
+
};
|
|
369
|
+
//# sourceMappingURL=copilot-cli-SJGIXVSD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/usage/readers/copilot-cli.ts"],"sourcesContent":["/**\n * usage/readers/copilot-cli — GitHub Copilot CLI / VS Code Copilot Chat OTEL reader.\n *\n * Faithful port of tokscale sessions/copilot.rs. Parses file-exported\n * OpenTelemetry JSONL emitted by Copilot CLI and VS Code Copilot Chat monitoring:\n * ~/.local/share/Copilot/telemetry/*.jsonl (XDG data home / Copilot/telemetry)\n * ~/.config/copilot/telemetry/*.jsonl (XDG config home / copilot/telemetry)\n * (host roots resolved by paths.ts hostRoots(\"copilot-cli\"), env-overridable).\n *\n * Each JSONL line is one OTel record. Four record shapes carry token usage, in\n * descending trust order:\n * 1. ChatSpan — a span with gen_ai.operation.name == \"chat\" (or name \"chat …\")\n * 2. InferenceLog — a log with event.name == \"gen_ai.client.inference.operation.details\"\n * (or body \"GenAI inference:\")\n * 3. AgentTurnLog — a log with event.name == \"copilot_chat.agent.turn\"\n * 4. AgentSummarySpan — a span with gen_ai.operation.name == \"invoke_agent\" (aggregate, fallback only)\n *\n * Tokens (per record `attributes`):\n * gen_ai.usage.input_tokens → input (incl. cache reads)\n * gen_ai.usage.output_tokens → output\n * gen_ai.usage.cache_read.input_tokens → cacheRead\n * gen_ai.usage.cache_write.input_tokens | cache_creation.… → cacheWrite\n * gen_ai.usage.reasoning.output_tokens | reasoning_tokens → reasoning\n * OTEL reports input_tokens inclusive of cache reads, so net input =\n * input - min(cacheRead, input), while the reported cache buckets are kept intact.\n *\n * DEDUP / DOUBLE-COUNT GUARD (the load-bearing bit): chat span, inference log and\n * agent-turn log can all describe the SAME response. Cross-source suppression keys\n * off two stable per-event identifiers — the OTel `trace_id` and `gen_ai.response.id`\n * — and drops a lower-priority lane whenever EITHER matches a higher-priority lane.\n * Coarse session attributes (e.g. gen_ai.conversation.id) span many turns and are\n * intentionally NOT used for suppression. The surviving records then carry a stable\n * per-record `dedupKey`:\n * ChatSpan / AgentSummarySpan : \"trace_id:span_id\" (fallback \"span:session:ts:index\")\n * InferenceLog : \"log:trace_id:span_id\" (fallback \"log:session:ts:index\")\n * AgentTurnLog : \"agent-turn:trace_id:turn.index\" (fallback variants)\n *\n * model/provider: model from gen_ai.response.model | gen_ai.request.model (else the\n * trace context, else \"unknown\"); provider inferred from the model, defaulting to\n * \"github-copilot\". session: best session attr, else trace context, else trace_id,\n * else \"unknown-session\". timestamp: endTime/startTime/hrTime/… (epoch ms), else file\n * mtime. No project attribution (Copilot does not log cwd). Confidence \"host-reported\".\n *\n * Fail-open: no root → []; unreadable/malformed file or line → skipped (never throws).\n */\n\nimport type { UsageReader, UsageRecord, TokenBreakdown } from \"../types.js\";\nimport { emptyTokens } from \"../aggregate.js\";\nimport { fileMtimeMs, readJsonlLines } from \"../jsonl.js\";\nimport { inferProvider } from \"../normalize.js\";\nimport { firstExistingRoot, walkFiles } from \"../paths.js\";\n\nconst PLATFORM_ID = \"copilot-cli\" as const;\nconst DEFAULT_MODEL = \"unknown\";\nconst DEFAULT_PROVIDER = \"github-copilot\";\nconst DEFAULT_SESSION = \"unknown-session\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Record source / priority model (port of CopilotUsageSource + SessionIdPriority)\n// ─────────────────────────────────────────────────────────────────────────\n\nconst enum Source {\n ChatSpan = 0,\n InferenceLog = 1,\n AgentTurnLog = 2,\n AgentSummarySpan = 3,\n}\n\nconst enum SessionPriority {\n Missing = 0,\n Response = 1,\n Interaction = 2,\n Session = 3,\n}\n\n/** Model attribute keys, in preference order. */\nconst MODEL_ATTRS = [\"gen_ai.response.model\", \"gen_ai.request.model\"] as const;\n\n/** Session attribute keys with their priority (port of SESSION_ATTRS). */\nconst SESSION_ATTRS: ReadonlyArray<readonly [string, SessionPriority]> = [\n [\"gen_ai.conversation.id\", SessionPriority.Session],\n [\"copilot_chat.session_id\", SessionPriority.Session],\n [\"copilot_chat.chat_session_id\", SessionPriority.Session],\n [\"session.id\", SessionPriority.Session],\n [\"github.copilot.interaction_id\", SessionPriority.Interaction],\n [\"gen_ai.response.id\", SessionPriority.Response],\n];\n\ntype JsonObject = Record<string, unknown>;\n\ninterface TraceContext {\n model?: string;\n sessionId?: string;\n sessionIdPriority: SessionPriority;\n}\n\ninterface Candidate {\n source: Source;\n traceId?: string;\n responseId?: string;\n model: string;\n providerId: string;\n sessionId: string;\n ts: number;\n tokens: TokenBreakdown;\n dedupKey: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Value coercion helpers (port of value_as_i64 / attr_* / timestamp helpers)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction isObject(v: unknown): v is JsonObject {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction asString(v: unknown): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Port of value_as_i64: number / numeric-string → integer, else undefined. */\nfunction valueAsI64(v: unknown): number | undefined {\n if (typeof v === \"number\") {\n if (!Number.isFinite(v)) return undefined;\n return Math.trunc(v);\n }\n if (typeof v === \"string\") {\n // Rust parses as i64 (integer-only) for value_as_i64's str branch.\n const trimmed = v.trim();\n if (!/^[+-]?\\d+$/.test(trimmed)) return undefined;\n const n = Number(trimmed);\n return Number.isFinite(n) ? Math.trunc(n) : undefined;\n }\n return undefined;\n}\n\n/** attr_i64: non-negative integer at `key` (0 on absence/garbage), clamped ≥ 0. */\nfunction attrI64(attrs: JsonObject, key: string): number {\n const raw = valueAsI64(attrs[key]);\n if (raw === undefined) return 0;\n return Math.max(0, raw);\n}\n\n/** attr_i64_first: first key whose attr_i64 value is > 0, else 0. */\nfunction attrI64First(attrs: JsonObject, keys: readonly string[]): number {\n for (const key of keys) {\n const v = attrI64(attrs, key);\n if (v > 0) return v;\n }\n return 0;\n}\n\nfunction attrStr(attrs: JsonObject, key: string): string | undefined {\n return asString(attrs[key]);\n}\n\n/** first_non_empty_attr: first key whose string value is non-empty (trimmed). */\nfunction firstNonEmptyAttr(attrs: JsonObject, keys: readonly string[]): string | undefined {\n for (const key of keys) {\n const s = asString(attrs[key]);\n if (s !== undefined && s.trim() !== \"\") return s;\n }\n return undefined;\n}\n\n/** best_session_attr: the session attr with the highest priority (LAST wins on ties). */\nfunction bestSessionAttr(attrs: JsonObject): { id: string; priority: SessionPriority } | undefined {\n let best: { id: string; priority: SessionPriority } | undefined;\n for (const [key, priority] of SESSION_ATTRS) {\n const value = asString(attrs[key]);\n if (value === undefined || value.trim() === \"\") continue;\n // Rust's max_by_key keeps the LAST element among equal maxima; SESSION_ATTRS\n // is NOT priority-descending (four Session-priority keys lead the list), so a\n // later equal-priority key must win. Use >= so e.g. `session.id` beats an\n // earlier `gen_ai.conversation.id` when both are present (matches copilot.rs).\n if (best === undefined || priority >= best.priority) {\n best = { id: value, priority };\n }\n }\n return best;\n}\n\nfunction recordBody(value: JsonObject): string | undefined {\n return asString(value[\"body\"]) ?? asString(value[\"_body\"]);\n}\n\nfunction getObject(value: JsonObject, key: string): JsonObject | undefined {\n const v = value[key];\n return isObject(v) ? v : undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Trace / span identity (port of trace_id_from_record / span_id_from_record)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction traceIdFromRecord(value: JsonObject): string | undefined {\n const top = asString(value[\"traceId\"]);\n if (top !== undefined) return top;\n const ctx = getObject(value, \"spanContext\");\n return ctx === undefined ? undefined : asString(ctx[\"traceId\"]);\n}\n\nfunction spanIdFromRecord(value: JsonObject): string | undefined {\n const top = asString(value[\"spanId\"]);\n if (top !== undefined) return top;\n const ctx = getObject(value, \"spanContext\");\n return ctx === undefined ? undefined : asString(ctx[\"spanId\"]);\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Timestamp + duration parsing (port of timestamp_ms_from_* / duration helpers)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** OTel hrTime/startTime/endTime: [seconds, nanos] array → epoch ms. */\nfunction timestampMsFromValue(value: unknown): number | undefined {\n if (!Array.isArray(value)) return undefined;\n const seconds = valueAsI64(value[0]);\n if (seconds === undefined) return undefined;\n const nanos = valueAsI64(value[1]);\n if (nanos === undefined) return undefined;\n return seconds * 1000 + Math.trunc(nanos / 1_000_000);\n}\n\n/** Scalar epoch with magnitude auto-detection (ns/us/ms/s). */\nfunction timestampMsFromScalar(value: unknown): number | undefined {\n const raw = valueAsI64(value);\n if (raw === undefined) return undefined;\n const abs = Math.abs(raw);\n if (abs >= 100_000_000_000_000_000) return Math.trunc(raw / 1_000_000); // ns\n if (abs >= 100_000_000_000_000) return Math.trunc(raw / 1_000); // us\n if (abs >= 100_000_000_000) return raw; // ms\n return raw * 1000; // s\n}\n\n/** OTel timeUnixNano is unsigned; refuse ≤ 0 so a malformed value falls through. */\nfunction timestampMsFromUnixNanos(value: unknown): number | undefined {\n const raw = valueAsI64(value);\n if (raw === undefined || raw <= 0) return undefined;\n return Math.trunc(raw / 1_000_000);\n}\n\nfunction timestampMsFromRecord(value: JsonObject): number | undefined {\n return (\n timestampMsFromValue(value[\"endTime\"]) ??\n timestampMsFromValue(value[\"startTime\"]) ??\n timestampMsFromValue(value[\"hrTime\"]) ??\n timestampMsFromValue(value[\"_hrTime\"]) ??\n timestampMsFromValue(value[\"time\"]) ??\n timestampMsFromScalar(value[\"timestamp\"]) ??\n timestampMsFromScalar(value[\"observedTimestamp\"]) ??\n timestampMsFromUnixNanos(value[\"timeUnixNano\"])\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Token normalization (port of normalize_input_tokens)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction normalizeInputTokens(\n input: number,\n output: number,\n cacheRead: number,\n cacheWrite: number,\n reasoning: number,\n): TokenBreakdown {\n // input_tokens is reported inclusive of cache reads; subtract only the cached\n // portion out of input, keeping the reported cache buckets intact.\n const cacheReadForInput = Math.min(Math.max(cacheRead, 0), Math.max(input, 0));\n const tokens = emptyTokens();\n tokens.input = Math.max(0, Math.max(input, 0) - cacheReadForInput);\n tokens.output = Math.max(output, 0);\n tokens.cacheRead = Math.max(cacheRead, 0);\n tokens.cacheWrite = Math.max(cacheWrite, 0);\n tokens.reasoning = Math.max(reasoning, 0);\n return tokens;\n}\n\nfunction tokenTotal(t: TokenBreakdown): number {\n return t.input + t.output + t.cacheRead + t.cacheWrite + t.reasoning;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Record classification (port of is_*_record)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction isSpanRecord(value: JsonObject): boolean {\n // VS Code Copilot Chat exports omit `type:\"span\"`; infer span-ness from a\n // top-level `name` plus span identity / timing / kind. Inference- and\n // agent-turn-log records carry NO top-level `name`, which disambiguates them.\n const type = asString(value[\"type\"]);\n if (type === \"span\") return true;\n if (type !== undefined) return false;\n\n const hasName = asString(value[\"name\"]) !== undefined;\n const hasSpanIdentity =\n asString(value[\"spanId\"]) !== undefined || asString(value[\"traceId\"]) !== undefined;\n const hasSpanTiming =\n value[\"startTime\"] !== undefined ||\n value[\"endTime\"] !== undefined ||\n value[\"duration\"] !== undefined;\n\n return hasName && (hasSpanIdentity || hasSpanTiming || value[\"kind\"] !== undefined);\n}\n\nfunction isChatSpanRecord(value: JsonObject, attrs: JsonObject): boolean {\n if (!isSpanRecord(value)) return false;\n if (attrStr(attrs, \"gen_ai.operation.name\") === \"chat\") return true;\n const name = asString(value[\"name\"]);\n return name !== undefined && name.startsWith(\"chat \");\n}\n\nfunction isAgentSummarySpanRecord(value: JsonObject, attrs: JsonObject): boolean {\n if (!isSpanRecord(value)) return false;\n if (attrStr(attrs, \"gen_ai.operation.name\") === \"invoke_agent\") return true;\n const name = asString(value[\"name\"]);\n return name !== undefined && name.startsWith(\"invoke_agent \");\n}\n\nfunction isInferenceLogRecord(value: JsonObject, attrs: JsonObject): boolean {\n if (isSpanRecord(value)) return false;\n if (attrStr(attrs, \"event.name\") === \"gen_ai.client.inference.operation.details\") return true;\n const body = recordBody(value);\n return body !== undefined && body.startsWith(\"GenAI inference:\");\n}\n\nfunction isAgentTurnLogRecord(value: JsonObject, attrs: JsonObject): boolean {\n if (isSpanRecord(value)) return false;\n if (attrStr(attrs, \"event.name\") === \"copilot_chat.agent.turn\") return true;\n const body = recordBody(value);\n return body !== undefined && body.startsWith(\"copilot_chat.agent.turn\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Trace context collection (port of collect_trace_contexts)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction collectTraceContexts(records: JsonObject[]): Map<string, TraceContext> {\n const contexts = new Map<string, TraceContext>();\n\n for (const record of records) {\n const traceId = traceIdFromRecord(record);\n if (traceId === undefined) continue;\n const attrs = getObject(record, \"attributes\");\n if (attrs === undefined) continue;\n\n let context = contexts.get(traceId);\n if (context === undefined) {\n context = { sessionIdPriority: SessionPriority.Missing };\n contexts.set(traceId, context);\n }\n\n if (context.model === undefined) {\n context.model = firstNonEmptyAttr(attrs, MODEL_ATTRS);\n }\n\n const best = bestSessionAttr(attrs);\n if (best !== undefined && best.priority > context.sessionIdPriority) {\n context.sessionId = best.id;\n context.sessionIdPriority = best.priority;\n }\n }\n\n return contexts;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Dedup key (port of dedup_key_for_record)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction dedupKeyForRecord(\n source: Source,\n record: JsonObject,\n attrs: JsonObject,\n traceId: string | undefined,\n sessionId: string,\n ts: number,\n index: number,\n): string {\n const spanId = spanIdFromRecord(record);\n\n switch (source) {\n case Source.ChatSpan:\n case Source.AgentSummarySpan:\n if (traceId !== undefined && spanId !== undefined) return `${traceId}:${spanId}`;\n return `span:${sessionId}:${ts}:${index}`;\n case Source.InferenceLog:\n if (traceId !== undefined && spanId !== undefined) return `log:${traceId}:${spanId}`;\n return `log:${sessionId}:${ts}:${index}`;\n case Source.AgentTurnLog: {\n // Use a real turn.index when present (stable across re-runs); otherwise fall\n // back to the line index so two turn-less records in the same trace differ.\n let turnPart: string | undefined;\n for (const key of [\"turn.index\", \"copilot_chat.turn.index\"]) {\n const v = valueAsI64(attrs[key]);\n if (v !== undefined) {\n turnPart = String(v);\n break;\n }\n }\n const part = turnPart ?? `idx-${index}`;\n if (traceId !== undefined) return `agent-turn:${traceId}:${part}`;\n return `agent-turn:${sessionId}:${part}:${index}`;\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Candidate construction (port of usage_candidate_from_record / candidate_from_attributes)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction classify(value: JsonObject, attrs: JsonObject): Source | undefined {\n if (isChatSpanRecord(value, attrs)) return Source.ChatSpan;\n if (isInferenceLogRecord(value, attrs)) return Source.InferenceLog;\n if (isAgentTurnLogRecord(value, attrs)) return Source.AgentTurnLog;\n if (isAgentSummarySpanRecord(value, attrs)) return Source.AgentSummarySpan;\n return undefined;\n}\n\nfunction candidateFromRecord(\n record: JsonObject,\n index: number,\n fallbackTimestamp: number,\n traceContexts: Map<string, TraceContext>,\n): Candidate | undefined {\n const attrs = getObject(record, \"attributes\");\n if (attrs === undefined) return undefined;\n\n const source = classify(record, attrs);\n if (source === undefined) return undefined;\n\n const traceId = traceIdFromRecord(record);\n const traceContext = traceId === undefined ? undefined : traceContexts.get(traceId);\n\n const input = attrI64First(attrs, [\"gen_ai.usage.input_tokens\"]);\n const output = attrI64First(attrs, [\"gen_ai.usage.output_tokens\"]);\n const cacheRead = attrI64First(attrs, [\"gen_ai.usage.cache_read.input_tokens\"]);\n const cacheWrite = attrI64First(attrs, [\n \"gen_ai.usage.cache_write.input_tokens\",\n \"gen_ai.usage.cache_creation.input_tokens\",\n ]);\n const reasoning = attrI64First(attrs, [\n \"gen_ai.usage.reasoning.output_tokens\",\n \"gen_ai.usage.reasoning_tokens\",\n ]);\n\n const tokens = normalizeInputTokens(input, output, cacheRead, cacheWrite, reasoning);\n if (tokenTotal(tokens) === 0) return undefined;\n\n const responseIdRaw = asString(attrs[\"gen_ai.response.id\"]);\n const responseId =\n responseIdRaw !== undefined && responseIdRaw.trim() !== \"\" ? responseIdRaw.trim() : undefined;\n\n const model =\n firstNonEmptyAttr(attrs, MODEL_ATTRS) ?? traceContext?.model ?? DEFAULT_MODEL;\n const providerId = inferProvider(model) ?? DEFAULT_PROVIDER;\n\n const sessionId =\n bestSessionAttr(attrs)?.id ?? traceContext?.sessionId ?? traceId ?? DEFAULT_SESSION;\n\n const ts = timestampMsFromRecord(record) ?? fallbackTimestamp;\n const dedupKey = dedupKeyForRecord(source, record, attrs, traceId, sessionId, ts, index);\n\n const candidate: Candidate = {\n source,\n model,\n providerId,\n sessionId,\n ts,\n tokens,\n dedupKey,\n };\n if (traceId !== undefined) candidate.traceId = traceId;\n if (responseId !== undefined) candidate.responseId = responseId;\n return candidate;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Cross-source suppression (port of should_emit_candidate)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction traceIdsForSource(candidates: Candidate[], source: Source): Set<string> {\n const set = new Set<string>();\n for (const c of candidates) {\n if (c.source === source && c.traceId !== undefined) set.add(c.traceId);\n }\n return set;\n}\n\nfunction responseIdsForSource(candidates: Candidate[], source: Source): Set<string> {\n const set = new Set<string>();\n for (const c of candidates) {\n if (c.source === source && c.responseId !== undefined) set.add(c.responseId);\n }\n return set;\n}\n\nfunction shouldEmit(\n candidate: Candidate,\n chatTraces: Set<string>,\n inferenceTraces: Set<string>,\n agentTurnTraces: Set<string>,\n chatResponseIds: Set<string>,\n inferenceResponseIds: Set<string>,\n agentTurnResponseIds: Set<string>,\n): boolean {\n // Suppression keys off the per-event trace_id and gen_ai.response.id — either\n // match is sufficient to drop a lower-priority lane. Coarse session attributes\n // (gen_ai.conversation.id, …) span multiple turns and are intentionally unused.\n const { traceId, responseId } = candidate;\n const traceMatch = (traces: Set<string>): boolean =>\n traceId !== undefined && traces.has(traceId);\n const responseMatch = (ids: Set<string>): boolean =>\n responseId !== undefined && ids.has(responseId);\n\n switch (candidate.source) {\n case Source.ChatSpan:\n return true;\n case Source.InferenceLog:\n return !traceMatch(chatTraces) && !responseMatch(chatResponseIds);\n case Source.AgentTurnLog:\n return (\n !traceMatch(chatTraces) &&\n !traceMatch(inferenceTraces) &&\n !responseMatch(chatResponseIds) &&\n !responseMatch(inferenceResponseIds)\n );\n case Source.AgentSummarySpan:\n return (\n !traceMatch(chatTraces) &&\n !traceMatch(inferenceTraces) &&\n !traceMatch(agentTurnTraces) &&\n !responseMatch(chatResponseIds) &&\n !responseMatch(inferenceResponseIds) &&\n !responseMatch(agentTurnResponseIds)\n );\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// File parse (port of parse_copilot_file)\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction parseCopilotFile(path: string): UsageRecord[] {\n const lines = readJsonlLines(path);\n if (lines.length === 0) return [];\n\n const fallbackTimestamp = fileMtimeMs(path);\n const records: JsonObject[] = [];\n for (const raw of lines) {\n if (isObject(raw)) records.push(raw);\n }\n\n const traceContexts = collectTraceContexts(records);\n\n const candidates: Candidate[] = [];\n records.forEach((record, index) => {\n const candidate = candidateFromRecord(record, index, fallbackTimestamp, traceContexts);\n if (candidate !== undefined) candidates.push(candidate);\n });\n\n const chatTraces = traceIdsForSource(candidates, Source.ChatSpan);\n const inferenceTraces = traceIdsForSource(candidates, Source.InferenceLog);\n const agentTurnTraces = traceIdsForSource(candidates, Source.AgentTurnLog);\n const chatResponseIds = responseIdsForSource(candidates, Source.ChatSpan);\n const inferenceResponseIds = responseIdsForSource(candidates, Source.InferenceLog);\n const agentTurnResponseIds = responseIdsForSource(candidates, Source.AgentTurnLog);\n\n const out: UsageRecord[] = [];\n for (const candidate of candidates) {\n if (\n !shouldEmit(\n candidate,\n chatTraces,\n inferenceTraces,\n agentTurnTraces,\n chatResponseIds,\n inferenceResponseIds,\n agentTurnResponseIds,\n )\n ) {\n continue;\n }\n\n out.push({\n platformId: PLATFORM_ID,\n modelId: candidate.model,\n providerId: candidate.providerId,\n sessionId: candidate.sessionId,\n tokens: candidate.tokens,\n ts: candidate.ts,\n messageCount: 1,\n dedupKey: candidate.dedupKey,\n confidence: \"host-reported\",\n });\n }\n\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Reader singleton\n// ─────────────────────────────────────────────────────────────────────────\n\nconst copilotReader: 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 telemetry root → fail-open\n\n // <root>/*.jsonl (and any nested .jsonl the export produces).\n const files = walkFiles(root, (name) => name.endsWith(\".jsonl\"));\n\n const records: UsageRecord[] = [];\n for (const file of files) {\n let rows: UsageRecord[];\n try {\n rows = parseCopilotFile(file);\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 copilotReader;\n"],"mappings":"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","names":[]}
|