Skip to content

Latest commit

 

History

History
125 lines (98 loc) · 5.62 KB

File metadata and controls

125 lines (98 loc) · 5.62 KB

Example hooks

A collection of ready-to-use hooks — mostly UserPromptSubmit, plus a Stop hook for per-prompt cost. Each one runs a single command whose stdout becomes an OTLP attribute copilot.<name> (or a body entry when as = "body"). Copy the blocks you want into your copilot-otel-logging.toml.

For the config file locations, the COPILOT_HOOK_* variables available to commands, and how output is routed to attributes vs. the body, see the README.

GitHub username

Records the GitHub login of the authenticated user via the GitHub CLI, so you can attribute activity to a person. Becomes copilot.username.

[[hooks.UserPromptSubmit]]
name = "username"
run = "gh api user -q .login"

Prompt

Captures the text the user submitted. Routed to the log body (as = "body") because prompts are free-form and high-cardinality — better kept out of the attribute indexes.

[[hooks.UserPromptSubmit]]
name = "prompt"
run = "printf '%s' \"$COPILOT_HOOK_PROMPT\""
as = "body"

Model

The model the current session is using, read from Copilot's local session store (~/.copilot/session-store.db, table assistant_usage_events, keyed by session_id). More reliable than parsing settings.json: it reports the model actually used (including the default), works whether Copilot runs standalone or inside VS Code, and is opened read-only so it never interferes with the live agent. Falls back to the most recent model overall on the first prompt, before any usage row exists for the session. Becomes copilot.model. Requires python3 (python on Windows) on PATH.

[[hooks.UserPromptSubmit]]
name = "model"
run = "python3 -c \"import sqlite3,os;h=os.environ.get('COPILOT_HOME') or os.path.expanduser('~/.copilot');db=h+'/session-store.db';s=os.environ.get('COPILOT_HOOK_SESSION_ID','');q=lambda c:(c.execute('select model from assistant_usage_events where session_id=? order by created_at desc limit 1',(s,)).fetchone() or c.execute('select model from assistant_usage_events order by created_at desc limit 1').fetchone());r=q(sqlite3.connect('file:%s?mode=ro'%db,uri=True,timeout=1)) if os.path.exists(db) else None;print(r[0] if r else '')\""

AI Units (AIU) per prompt

The AI Units a prompt consumed, reported when the agent finishes the turn. This uses the Stop event (which fires once per completed prompt) rather than UserPromptSubmit (which fires before the prompt runs, when its cost doesn't exist yet). It reads the session's cumulative total_nano_aiu from the session store and reports the delta since the previous turn — one prompt's cost — tracking the running total in a small per-session marker file under the temp directory, divided by 1e9. Becomes copilot.aiu. Requires python3 (python on Windows) on PATH.

[[hooks.Stop]]
name = "aiu"
run = "python3 -c \"import sqlite3,os,tempfile;h=os.environ.get('COPILOT_HOME') or os.path.expanduser('~/.copilot');db=h+'/session-store.db';s=os.environ.get('COPILOT_HOOK_SESSION_ID','');t=(sqlite3.connect('file:%s?mode=ro'%db,uri=True,timeout=1).execute('select coalesce(sum(total_nano_aiu),0) from assistant_usage_events where session_id=?',(s,)).fetchone()[0] if os.path.exists(db) else 0) or 0;m=os.path.join(tempfile.gettempdir(),'copilot-otel-aiu-'+''.join(c if c.isalnum() or c in '-_' else '_' for c in (s or 'default')));p=int(open(m).read().strip() or '0') if os.path.exists(m) else 0;open(m,'w').write(str(t));print('%.6f'%(max(t-p,0)/1e9))\""

The session store is an internal Copilot SQLite schema, so treat the model and AIU hooks as best-effort — a future CLI update could rename the table or columns. The same assistant_usage_events table also exposes input_tokens, output_tokens, cache_read_tokens, reasoning_tokens, and duration_ms per turn if you want to export those too.

Total AI Units (AIU) per session

The AI Units the whole session consumed, logged once when the session ends via the SessionEnd event — the cumulative sum of total_nano_aiu for the session, divided by 1e9. Use this alongside (or instead of) the per-prompt Stop hook above when you only need one usage figure per session. as = "span" also adds it to the session span's attributes as copilot.aiu-total. Requires python3 (python on Windows) on PATH.

[[hooks.SessionEnd]]
name = "aiu-total"
as = "span"
run = "python3 -c \"import sqlite3,os;h=os.environ.get('COPILOT_HOME') or os.path.expanduser('~/.copilot');db=h+'/session-store.db';s=os.environ.get('COPILOT_HOOK_SESSION_ID','');n=(sqlite3.connect('file:%s?mode=ro'%db,uri=True,timeout=1).execute('select coalesce(sum(total_nano_aiu),0) from assistant_usage_events where session_id=?',(s,)).fetchone()[0] if os.path.exists(db) else 0) or 0;print('%.6f'%(n/1e9))\""

Repository

The Git remote origin URL for the working directory the prompt was submitted from. The || true keeps it quiet (no error) when the prompt is submitted outside a Git repository. Becomes copilot.repo.

[[hooks.UserPromptSubmit]]
name = "repo"
run = "git -C \"$COPILOT_HOOK_CWD\" remote get-url origin 2>/dev/null || true"

Branch

The current Git branch of the working directory. Like the repository hook, it stays quiet outside a Git repository. Becomes copilot.branch.

[[hooks.UserPromptSubmit]]
name = "branch"
run = "git -C \"$COPILOT_HOOK_CWD\" rev-parse --abbrev-ref HEAD 2>/dev/null || true"

Host

The operating system, architecture, and hostname of the machine running Copilot, useful for distinguishing developer environments. Becomes copilot.host.

[[hooks.UserPromptSubmit]]
name = "host"
run = "printf '%s' \"$(uname -sm) $(hostname)\""