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.
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"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"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 '')\""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_eventstable also exposesinput_tokens,output_tokens,cache_read_tokens,reasoning_tokens, andduration_msper turn if you want to export those too.
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))\""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"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"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)\""