copilot-cli¶
GitHub Copilot CLI (copilot, catalog agent copilot-cli) is GitHub's closed-source terminal
coding agent. It writes one event log per session, events.jsonl, under
~/.copilot/session-state/<session id>/, and keeps copies of the conversation in a cross-session
SQLite database and a prompt history file. The GitHub Copilot Desktop app runs its sessions through
a bundled Copilot CLI and writes them into the same ~/.copilot/session-state/ directory, in this
format, with workspace.yaml client_name github/autopilot where the CLI writes github/cli;
those sessions stay copilot-cli, and the app's own files and store are the copilot-desktop
agent's (copilot-desktop.md). The CLI shares no format with the VS Code
Copilot Chat extension (vscode.md) or the copilot catalog agent. The
copilot-cli parser reads the event logs, the database, the prompt history and the CLI-written
Copilot Desktop sessions.
Source and evidence¶
Closed source: github/copilot-cli holds issues and a changelog only, so there are no commit links.
Evidence is the shipped bundle; each claim names its evidence level:
- Bundle 1.0.91:
npm pack @github/copilot1.0.91 (5 KB loader,buildMetadata.gitCommit 216810c5) →npm pack @github/copilot-linux-x64@1.0.91, a 178 MB Node single-executable (copilot). Its SEA blob (SEA is a Node single executable application: a Node binary with a script and assets appended in a__NODE_SEAblob; magic 0x143da20 at 0x67a01b8) holdssea-loader.js(122 KB,scratchpad/npm/cp-sea-loader.js) and one assetcopilot.tgz(69.7 MB,scratchpad/npm/cp-asset-copilot.tgz), extracted toscratchpad/npm/cp-pkg/package/:app.js(7.6 MB JS),prebuilds/linux-x64/cli-native.node(8 MB) andruntime.node(122 MB, Rust). Citations to it: "app.js" with the surrounding identifiers, "sea-loader" (same code appears in cc-loader), "native"/"runtime" for byte-string hits. - Bundle 1.0.98:
@github/copilot-darwin-arm641.0.98 (loader package@github/copilot1.0.98,buildMetadata.gitCommit45ef16251c8), thecopilotexecutable, sha256b1a76d9eec3c88e8545ee96834189ab08c3d75ef65500a60925187fa6c33fdf1, 167,681,584 bytes, copied from a Homebrew npm install on 2026-10-15. It has the SEA layout of 1.0.91 plus a one-byte field after the blob flags: the blob magic0x0143da20is at file offset0x5888000; flags0x19(no warning, assets, exec arguments);sea-loader.js(124,974 bytes, sha256a9bc83c428713c66388718e554df0be3f9aa92f3e79df17fe47dbadb2e9e7eaf); one assetcopilot.tgzat0x58a6877(70,377,164 bytes, sha25626c6165cb45bce1b37a3a8d6bbcd484e2d938cf513d5970cbdf9ef0da8b92a70); six Node exec arguments (--no-warnings,--expose-gc, ...).app.jsis now a 42 KB bootstrap (sha256c5f37d9040e5d866e01e4266c83d87d08ff3ab0804d6a0a0dc863b4414196a7b) that importscli-main.js(8,193,186 bytes, sha256151c2e2a37ddb2514fc5cdbd5c473f9aeeb1a74d59e5062f3921a46d12a8f1c2), the minified CLI. The state paths, the event store and the session database are in, and written by,prebuilds/darwin-arm64/runtime.node, a Rust Node addon (97,100,816 bytes, sha256e5bb26efbdff6cc21a607d3fee0fb9efa933ebeb3d7fe97332ee1a7f0793a02e). The package also ships the event schema as JSON Schema,schemas/session-events.schema.json(990,556 bytes, sha25681c4a1eb9f0f3bd21a68219f30aaa774fd7c041eafa76c62ff691ac53efae1ed, 159 event types with a description per field) and as TypeScript,copilot-sdk/generated/session-events.d.ts(sha256a2f3897149e0ec1aee24c23dbed3632b44916971d06f5c82768ead98981eaa37). Citations, for collection paths and records alike, arecopilot 1.0.98 darwin-arm64 <file>@<byte offset>with a literal snippet, shortened after this paragraph tocli-main.js@<n>,runtime.node@<n>andschema@<n>(the schema file, at the definition's"title"). Offsets change with every build; grep for the snippet. The binary was read withstrings,grepand Python, never executed. - Docs: GitHub Copilot CLI configuration
directory
(the Copilot CLI configuration directory reference, fetched 2026-10-15) for the directory layout
and
permissions-config.json. - Local: names-only listing of
~/.copilotand~/.cache/copilot.
Observed¶
- Windows, winget, standalone CLI 1.0.98, 2026-10-17: installed with
winget install GitHub.Copilot(winget is the Windows Package Manager;GitHub.Copilotis the package id) on a Windows 11 ARM64 Parallels VM (build 10.0.26300.9550, usersean, homeC:\Users\sean) reverted to a clean snapshot that had never run Copilot Desktop; all times UTC; names, sizes, key names and types only, with a names-only listing diff after every step. The install (listing 21:44Z) wrote the winget package directory, a userPATHentry, the HKCU uninstall key (HKCU isHKEY_CURRENT_USER, the registry hive of the signed-in user) and the installer download (Outside the catalog); no.copilot, noAppData\Local\copilot, no credential file and noDeveloperToolsregistry key yet. The first start,copilotinC:\Users\sean\demoat 21:46:48 (listing 21:48Z), wrote the extracted packageAppData\Local\copilot\pkg\win32-arm64\1.0.98\(181,983,762 bytes),AppData\Local\copilot\marketplaces\.auto-update.lockand.copilot\with the Linux layout (Inside~/.copilot):config.json,command-history-state.json(43 bytes),open-sessions-state.json(188 bytes),sidebar-sessions-state\<sha256 of cwd>.json(125 bytes),logs\process-1792273608752-2868.logand the session directory withworkspace.yaml(212 bytes),checkpoints\index.md(172 bytes) and the process'sinuse.2868pair. The user logged in from inside the session (/login, the device-code flow, in which the user types a one-time code into github.com in a browser), completing at 21:48:02; while the code was pending the process log looped, about 280,000 lines in 74 seconds and four rotations at 10 MB (Process logs). The login rewroteconfig.json(418 bytes), stored the token in Windows Credential Manager (Windows' per-user credential store; Credentials), created the registry device id (Outside the catalog) and wrote inAppData\Local\copilot\copilot-user-cache.json(3,309 bytes),exp-cache.json(10,904 bytes),memory-enablement-cache-config.json(351 bytes),managed-settings.<sha256>.order.json(36 bytes) andmanaged-settings.<sha256>.fence(36 bytes), each with a.lock, and an emptymanaged-settings\directory (Package and cache directory). The first prompt (21:50:55, listing 22:02Z), a task that read web pages, createdevents.jsonl(1,216,194 bytes by 22:01:14),session-store.db(4,096 bytes) with-wal(1,268,992 bytes) and-shm, andrewind-file-snapshots\tracking.json(29 bytes), and grewcommand-history-state.jsonto 114 bytes andworkspace.yamlto 251 bytes (thenamekey added). One tool call denied and one approved with "allow always" (listing 22:06Z; the two steps share a diff) wrotepermissions-config.jsonat 22:06:06 (142 bytes, onewritegrant fordemo;permissions-config.json), grewevents.jsonlby 750 KB, the WAL to 1,590,352 bytes andcommand-history-state.jsonto 190 bytes, and the approvedapply_patchcall wrotedemo\ai-coding-tools-review.md. A/modelswitch to Auto (listing 22:08Z) grewevents.jsonlby 870 bytes andcommand-history-state.jsonby 14 bytes and rewroteexp-cache.jsonat the same size, nothing else. Quitting at 22:08:31 wroterewind-file-snapshots\index.json(1,204 bytes), grewevents.jsonlby 8 KB to 1,973,737 bytes and removed theinuse.2868pair; the log then held 55assistant.message, 109 tool calls, 79 permission requests with their completions, 6user.message, 4session.model_change, 12 of each of the eightmodel.*types and 2 of each of foursubagent.*types (Event types). Resuming at 22:08:50 (copilotagain, the session picked from the resume picker; listing 22:09Z) wrote a new process logprocess-1792274929863-4428.log(36 KB, no loop), rewroteworkspace.yamlfrom 251 to 376 bytes with the sync keysremote_steerable,mc_task_id,mc_session_idandmc_last_event_id(Mission Control ids; Mission Control is GitHub's service that mirrors a session's events for remote viewing and steering;workspace.yaml), rewroteopen-sessions-state.json(373 bytes) and the sidebar file (169 bytes), touchedconfig.jsonat the same size, and addedmanaged-settings\<sha256>.json(252 bytes) to the cache, withmanaged-settings.<sha256>.order.jsonat 101 bytes andmemory-enablement-cache-config.jsonat - At 22:11:04 the user started
copilotagain indemo(a new session whose directory heldworkspace.yamlof 212 bytes and a 1,162-byteevents.jsonlwithsession.start,session.model_changeandsession.shutdownonly, and nosession.dborrewind-file-snapshots) and deleted the first session from it with/session delete <id> --yes --remote, after trying/session,/session delete-all,/session delete <id>and/session delete copilot <id>; the user agreed to delete the synced copy too, and quit at 22:16:32 (listing 22:17Z). What the delete removed and left is in What survives a delete;mcp-config.jsonwas never written. An npm install of the standalone CLI on Windows is not observed (issue 133). - Windows, through Copilot Desktop, bundled CLI 1.0.99-2, 2026-10-17: Copilot CLI 1.0.99-2 as the
bundled CLI of GitHub Copilot Desktop 1.1.30 (
copilot-sdk/cliVersion.d.tsCOPILOT_CLI_VERSION = "1.0.99-2",COPILOT_CLI_USE_NPM_PACKAGE = false), downloaded by the app toAppData\Local\github-copilot-sdk\cli\1.0.99-2\copilot.exe(158,577,952 bytes, sha256b7bc340acf1c82228de2e3504444847f8e18beec1f07f72f260f1bbeb4c3a9b2), on a Windows 11 ARM64 Parallels VM (build 10.0.26300.9550, homeC:\Users\sean); names, sizes, key names and types only; the round is in Source and evidence incopilot-desktop.md. The CLI wrote the Linux layout underC:\Users\sean\.copilot(Per-user storage):config.json(131 bytes, one key; Credentials),session-store.db(4096 bytes,-wal609,792 then 704,552 bytes,-shm),logs/.copilot-log.lockand 17logs/process-<epoch ms>-<pid>.log(1.7 KB to 47 KB),session-state/, emptyextensions/and emptyinstalled-plugins/. Adding three MCP servers (Model Context Protocol servers, programs that offer the agent extra tools) from the app wrotemcp-config.json(725 bytes),.mcp-writer.binding(157 bytes) and.mcp-writer.lock(0 bytes) (Transcript stores; one server'senvheld a token, Credentials); the two servers whose commands were not installed failed to spawn, six and five times (Process logs), and the one that connected left one tool cache per session inAppData\Local\copilot\mcp-tools\. The cache directoryAppData\Local\copilotalso heldcopilot-user-cache.json(3,309 bytes),managed-settings/<sha256>.json(252 bytes),tgrep-serve.lock, the marketplace clone (4,560 files, 263,616,505 bytes) and the packagepkg/win32-arm64/1.0.99-2/(255 files, 191,966,761 bytes) (Package and cache directory). The general chat'sevents.jsonl(115 lines) held 3user.message, 8assistant.message, 9 tool calls (powershellandcreate), 27hook.startandhook.endpairs, 8session.usage_record, 3session.usage_checkpoint, 1system.messageand 1session.shutdown; the project session's (30 lines) one prompt, onepowershellcall, 7 hook pairs and asession.shutdown(Event types). Threecreatecalls failed ("Parent directory does not exist") and the firstpowershellcall exited 1 because Windows PowerShell's execution policy refusednpm.ps1; both are ordinary failed tool results.session-store.dbhad 17 tables atschema_version8:sessions2,turns4 (3 for the chat, 1 for the project session),session_files3 (tool_namecreate),search_index4 (source_typeturn) andassistant_usage_events10 (modelclaude-sonnet-5.5,initiatoruserandagent,api_endpoint/v1/messages,reasoning_effortmedium,finish_reasonstopandtool_calls), withcheckpoints,dynamic_context_items,forge_skill_proposals,forge_trajectory_eventsandsession_refsempty (session-store.db). The app also created seven sessions that were never prompted (Transcript stores). - Linux, npm, 1.0.98, 2026-10-16: installed with
sudo npm install -g @github/copiloton an Ubuntu 26.04.1 arm64 Parallels VM (kernel 7.0.0-38-generic, home/home/sean); names, sizes, key names and types only, read withanalyzer/tools/shape.py. Exercised:copilot login, then two interactive sessions, the first in~/demo(created 21:01:19.518Z, prompts at 21:02:40Z and 21:05:34.923Z, shut down 21:16:01Z, all UTC; 194 lines) and the second in~/secret(created 21:16:26.715Z, one prompt at 21:17:18Z, shut down 21:19:27Z; 35 lines), after which the user removed the project withrm -r secret/. Every action ran before the first listing, so no per-action listing diff exists; the times above and the files' modification times are the only mapping from action to file.copilot loginwrote a process log (105 bytes) and put the token in the GNOME keyring (Credentials); the first session start wrote one file outside the catalog (Outside the catalog); each session wrote itssession-state/<uuid>/directory, a process log (16 KB and 15 KB),session-store.dbrows (in a 1.37 MB WAL beside a 4096-byte main file), asidebar-sessions-statefile and its prompts incommand-history-state.json.~/.copilotheld the macOS file set exceptsettings.json, which was not written, and~/.cache/copilotthe names macOS keeps in~/Library/Caches/copilot, with the package underpkg/linux-arm64/1.0.98/(Per-user storage); there was no~/.config/.copilotor~/.local/state/.copilot. Session 1 calledbash2 times,apply_patch2 (11 files, then 1),view3,glob1,web_fetch18,web_search7 andfetch_copilot_cli_documentation1, with 20 permission requests (kindshell2,url17,write1, each carryingpermissionModemanualandagentModeinteractive): 19approvedand oneapproved-for-locationfor the write, alldecisionSourcehuman_response, and one automatic model resolution. Session 2 had onewriterequest approved and a model switch the user typed:/model, choosing Auto, at 21:19:20Z (Event types). What the project's removal left is in What survives a delete. Not exercised: a denial, a resume, a rewind, a session deleted with the CLI's own command, a subagent, a compaction, plan or autopilot mode, a scheduled task. - macOS, Homebrew npm, 1.0.98, 2026-10-15: the author's workstation; names, sizes, key names and
types only, read with
analyzer/tools/shape.pyand a schema dump. Three interactive sessions inmanualpermission mode (52, 168 and 64 lines, none bad), withbash,view,apply_patch,web_fetch,web_search(an MCP tool),ask_userandfetch_copilot_cli_documentationcalls; 17 permission prompts approved once (approved) or for the project (approved-for-location, a command and a write approval); one automatic model resolution per session; three normal shutdowns; one session whoseturnsrows were re-indexed (Other stores). They wrote the~/.copilotfile set of Per-user storage,settings.jsonandpermissions-config.jsonincluded, the session store's rows in a 1.7 MB-walbeside a 4 KB main file, and~/Library/Caches/copilotwith the packagepkg/darwin-arm64/1.0.98/; there was no~/.config/.copilot,~/.local/state/.copilotor~/.cache/copilot. Not exercised: a denial, a model switch the user typed, a resumed session, a rewind, a deleted session, a subagent, a compaction.
Source reviews¶
- 2026-10-19: microsoft/vscode at
4d22f4f, for the Copilot CLI sessions that VS Code's Copilot Chat extension runs in a git worktree; added the worktree to Scratch directories and thevscode.metadata.jsonfile VS Code writes into the session directory to its subsection under Other stores.
Collection¶
Per-user storage¶
Config home: configDir ?? process.env.COPILOT_HOME ?? join(homedir(), ".copilot") (app.js
function dot(e), dQe; native: "Set the configuration directory (default: ~/.copilot).
Deprecated: use COPILOT_HOME env var instead"). Same on all OSes; docs confirm ~/.copilot and
COPILOT_HOME. On Windows that is %USERPROFILE%\.copilot: the Copilot Desktop-bundled
copilot.exe 1.0.99-2 carries the strings COPILOT_HOME, USERPROFILE and the named pipe prefix
\\.\pipe\copilot-taskbar-, and no keyring strings.
XDG handling: on start the CLI migrates state out of XDG dirs into ~/.copilot (app.js
function CEn(e,t)): from $XDG_STATE_HOME/.copilot/ the entries session-state,
session-store.db, command-history-state, command-history-state.json, installed-plugins; from
$XDG_CONFIG_HOME/.copilot/ the entries config.json, config, mcp-config, lsp-config,
permissions-config, copilot-instructions.md, mcp-oauth-config, hooks. So older installs with
XDG set may still have $XDG_STATE_HOME/.copilot and $XDG_CONFIG_HOME/.copilot (default
~/.local/state/.copilot, ~/.config/.copilot), and symlinks are left behind (symlinkSync in the
same function).
VS Code writes into ~/.copilot without the Copilot CLI. On Ubuntu 26.04.1 arm64 (2026-10-26),
VS Code 1.143.1's first start, before any extension was installed and with no Copilot CLI on the
host, wrote ~/.copilot/logs/.copilot-log.lock and logs/process-<epoch>-<pid>.log, the logs of
the Copilot agent that VS Code 1.143 bundles (Process logs); it was seen during
a Claude Code round on that host (Observed in claude-code.md). The
copilot-cli|.copilot catalog line collects them under this agent, so a ~/.copilot that holds
only logs/, with no session-state/, may be VS Code's rather than evidence that the Copilot CLI
ran.
Scratch directories¶
VS Code's Copilot Chat extension can run a Copilot CLI session in a git worktree it creates at
<repo parent>/<repo>.worktrees/<branch>, unless the git extension's global state holds a
worktreeRoot:<repo> root
(extensions/git/src/repository.ts:1952-1970,
chatSessionWorktreeServiceImpl.ts:113),
and records it in session-state/<id>/vscode.metadata.json
(vscode.metadata.json), at microsoft/vscode
4d22f4f;
not seen on a real install. Such a worktree is a scratch directory of the agent, not the user's
project. It sits beside the user's repository, outside every catalog line, so it is not
collected, and discover_projects and Find-Projects do not read vscode.metadata.json; see
vscode.md.
Inside ~/.copilot¶
The docs table plus the bundle:
session-state/<sessionId>/session history:events.jsonl(runtime: "Computes the absolute path to a session's persisted events.jsonl file"; app.js crash report copiese.sessionFilePathasevents.jsonl),workspace.yaml(app.jsloadSessionFsWorkspace, keysid,created_at,updated_at,name,user_named,summary_count;workspaceLoadPersistedSessionCwdreads cwd from it),plan.md,autopilot-objective.json(app.jscopySessionFsOptionalFile), checkpoints and files subdirs (/session checkpoints|files|plancommands). Observed at 1.0.98 (macOS and Linux):checkpoints/index.md(a Markdown table of compaction checkpoints),files/("Persistent storage for session artifacts",runtime.node@82945561),research/,rewind-file-snapshots/index.jsonandtracking.json(file snapshots for/rewind,runtime.node@82575983rewind-file-snapshotstracking.json),.workspace-fork.lock; beside the session directories.session-operation-locks/<uuid>.lock(runtime.node@82828676). On the Windows winget install (1.0.98) a running process also keptinuse.<pid>.hold(0 bytes) andinuse.<pid>.lock(5 bytes) in the session directory, its in-use markers, removed when it quit;rewind-file-snapshots/tracking.jsonwas written at the first prompt andindex.jsonat the quit. The bundle also namessession-state/<id>/session.db, a per-session SQLite of the agent's todo tables (cli-main.js@149653this.join(this.sessionStatePath,"session.db"); not observed), and the legacy flat transcriptsession-state/<id>.jsonlthat 1.0.98 no longer reads (cli-main.js@1654639Rwr=".jsonl").session-store.dbSQLite "cross-session data such as checkpoint indexing and search" (docs; runtime SQLFROM checkpoints ... session_id, checkpoint_number), in WAL mode with its rows in the-walsidecar on every install, so the sidecars must be collected with it; tables, writers and sizes insession-store.db.command-history-state.json, the Ctrl+R prompt history (command-history-state.json).open-sessions-state.jsonopen-session tracker (runtime.node@79300674open-sessions):{}with no session open, and{schemaVersion, openedAt, refreshedAt, working}with one open (373 bytes after the resume on the Windows winget install,{}again after the delete).sidebar-sessions-state/<sha256 of cwd>.json{schemaVersion, cwd, sessionIds}(real install; the file name is the sha256 ofcwd, verified;runtime.node@87698768sidebar-sessions); on the Windows winget install a resume from the resume picker added a second id that got an operation lock and a sidebar entry but never a directory (the picker appears to start a fresh session before switching to the chosen one).servers/andinstalled-plugins.lock(observed, empty);extensions/(observed empty on Windows through Copilot Desktop's bundled 1.0.99-2).- Docs-only at 1.0.98 (not observed):
mcp-secrets/("Local fallback storage and index for MCP secret placeholders";cli-main.js@5455460huo="mcp-secrets"),plugin-data/(cli-main.js@1688706),providers.json(the BYOK, bring-your-own-key, provider and model registry;cli-main.js@4314272jwr="providers.json", overridable withCOPILOT_PROVIDERS_CONFIG). logs/process logs (native: "Set log file directory (default: ~/.copilot/logs/)"); locallogs/process-<epoch>-<pid>.logandlogs/.copilot-log.lock, rotated at 10 MB; a login left pending on Windows 1.0.98 wrote about 45 MB of them (Process logs). The logs stay collected and are not excluded (Exclusions), because the sign-in lines that name the account are in them.ide/<uuid>.lockIDE discovery lock files (app.jsQBscansGi(e,"state")/idefor.lock; localide/).state/for the session store default path (app.jssessionStoreDefaultPath(Gi(e,"state"))).config.json"automatically managed application state (authentication, installed plugins...)" (docs),settings.jsonuser settings (docs; runtime "Settings migration ... between config.json and settings.json"; written on the macOS install, absent on the Linux one),mcp-config.json(native help text; observed on Windows with.mcp-writer.bindingand.mcp-writer.lockbeside it, the writer's lock and its binding to the file's volume and file index),mcp-oauth-config(app.jsJDn="mcp-oauth-config"),permissions-config.json,lsp-config,command-history-state/,installed-plugins/<marketplace>/<plugin>(docs; runtime "installed-plugins root"),copilot-instructions.md,instructions/*.instructions.md,agents/*.agent.md,skills/<name>/SKILL.md,hooks/(docs; app.js help list$HOME/.copilot/copilot-instructions.md,$HOME/.copilot/instructions/**/*.instructions.md).
Package and cache directory¶
- Package cache for auto-update (sea-loader
Xi()):$COPILOT_PKG_CACHE_HOME/pkg,$COPILOT_CACHE_HOME/pkg,~/Library/Caches/copilot/pkg(macOS),%LOCALAPPDATA%\copilot\pkg(Windows),$XDG_CACHE_HOME/copilot/pkgor~/.cache/copilot/pkg(Linux),$COPILOT_HOME/pkg,~/.copilot/pkg; extraction marker.extraction-complete. Each version directory holds.extraction-complete,cli-main.js,prebuilds/<platform>/runtime.nodeandschemas/(prebuilds/linux-arm64/runtime.nodeinpkg/linux-arm64/1.0.98/on Linux); on Windowspkg/win32-arm64/1.0.98/was 181,983,762 bytes (winget) andpkg/win32-arm64/1.0.99-2/255 files and 191,966,761 bytes (Copilot Desktop's bundled CLI), with theinuse.<pid>.holdand.lockpairs of five processes. Local~/.cache/copilot/copilot-user-cache.json(runtime literalcopilot-user-cache). On macOS 1.0.98~/Library/Caches/copilot, and on Linux 1.0.98~/.cache/copilotwith the same names, also heldexp-cache.json(the ExP experiment-assignment cache; ExP is Microsoft's experimentation service),managed-settings/("cache for enterprise managed settings, safe to delete",runtime.node@79814215; on the Windows winget install alsomanaged-settings.<sha256>.fence, 36 bytes, beside the.order.jsonand.lock, contents not read),memory-enablement-cache-config.jsonandmarketplaces/, each with.lockfiles. - Five of these files are JSONC, not JSON (Linux install):
config.json,copilot-user-cache.json,exp-cache.json,memory-enablement-cache-config.jsonandmanaged-settings/<hex>.jsonstart with one or two//comment lines ("Disposable cache for ..., safe to delete. Managed automatically." and "User settings belong in settings.json.") and then hold plain JSON, which is why the macOS survey could not parse them. Shapes observed on Linux:copilot-user-cache.jsoniscopilotUserCache{<key>: {schemaVersion, generation, retrievedAt, response}}, whereresponseis the Copilot user entitlement response (login,copilot_plan,access_type_sku,organization_login_list,organization_list,enterprise_list,endpoints{api, origin-tracker, proxy, telemetry, exp},quota_snapshots{chat, completions, premium_interactions}with entitlement and remaining counts,quota_reset_date, and feature flags such aschat_enabled,is_mcp_enabled,codex_agent_enabled,cloud_session_storage_enabled,cli_remote_control_enabled; on Windows, 3,309 bytes through Copilot Desktop's bundled 1.0.99-2, alsoanalytics_tracking_id,assigned_date,can_signup_for_limited,copilotignore_enabled,restricted_telemetry,enterprise_usage_telemetry,is_staff,te,token_based_billing,can_upgrade_planandquota_reset_date_utc);exp-cache.jsonisexpAssignmentsCache{"v3:<hex>": {schemaVersion, retrievedAt, response{Features[...], ...}}}(feature names such asanthropic_tool_search_enabled,openai_toolsearch_enabled,gpt-default);managed-settings/<hex>.jsonis{schemaVersion, retrievedAtMs, account, response, ignoredPaths[]}(responsenull, on Windows too, 252 bytes), besidemanaged-settings.<hex>.order.json{lastAppliedOrder, nextOrder};memory-enablement-cache-config.jsonismemoryEnablementCache{"v1:<hex>": {schemaVersion, retrievedAt, enabled}}. marketplaces/.auto-update.lockwas the only file inmarketplaces/on macOS and Linux. On Windows (CLI 1.0.99-2, bundled with Copilot Desktop) the same directory heldfreshness.json(98 bytes),github-awesome-copilot.lock(0 bytes) andgithub-awesome-copilot/, a shallow git clone of the awesome-copilot plugin marketplace (4,533 files and 160 MB outside its.git), 263 MB in all; it is excluded from collector 1.13.0 (Exclusions, issue 131).- Also on Windows (bundled 1.0.99-2):
tgrep-serve.lock, andmcp-tools/<sha256>.json, one file per MCP server that connected in a session (two files of 20,383 and 20,384 bytes, written at 19:59:20Z and 20:04:57Z by the two sessions that connected, both namingplaywright), shape{schemaVersion, serverName, tools: [{name, description, annotations{destructiveHint, openWorldHint, readOnlyHint, title}, inputSchema{...}}], updatedAt}, the cached tool list of a connected MCP server (25 tools); collected.
Outside the catalog¶
- Outside
~/.copilotand the cache, not in the catalog: the first session start on Linux wrote~/.cache/Microsoft/DeveloperTools/deviceid(36 bytes, a UUID), the device id Microsoft's developer tools share (issue 148); on Windows the login of the winget install created the registry valueHKCU\SOFTWARE\Microsoft\DeveloperTools\deviceidinstead (a string value, typeREG_SZ, of 36 characters), as Copilot Desktop's did, and no file; neither wroteAppData\Local\Microsoft\DeveloperToolsor~/.cacheon Windows. The registry is outside the collector's scope. - The winget install puts the executable in
AppData/Local/Microsoft/WinGet/Packages/GitHub.Copilot_Microsoft.Winget.Source_8wekyb3d8bbwe/(copilot.exe, 154,351,392 bytes, file date 2026-10-14, version 1.0.98, and winget's own 16 KB databaseGitHub.Copilot_Microsoft.Winget.Source_8wekyb3d8bbwe.db), outside every catalog entry, like the npm global directory on Linux; the program holds no user state. The install also adds a userPATHentry for that directory, the HKCU uninstall keyGitHub.Copilot_Microsoft.Winget.Source_8wekyb3d8bbwe(DisplayNameCopilot CLI, DisplayVersionv1.0.98, InstallLocation that directory) and leaves the installer download underAppData\Local\Temp\WinGet\GitHub.Copilot.v1.0.98. - System policy:
/etc/github-copilot/managed-settings.json,/etc/github-copilot/policy.d(runtime).
Credentials¶
- GitHub token: OS keyring by default (runtime vendors
keyring-coreandzbus-secret-service-keyring-store; envCOPILOT_DISABLE_KEYTAR), withstoreTokenPlaintextsetting "Store auth token in plaintext (less secure)" (runtime settings schema) which puts it inconfig.json("authentication" per docs). On the Linux installcopilot loginwrote the token to the GNOME keyring (~/.local/share/keyrings/login.keyring, modified at the minute of the login), as the runtime's keyring-store strings predict, andconfig.jsonheld no token: its keys wereloggedInUsers[{host, login, kind}],lastLoggedInUser{host, login},firstLaunchAt,appTipShownand a fifth key of 20 or more characters whose name was not recorded; the Windows winget install named it:reasoningSummariesCleanupDone(418-byteconfig.jsonafter the login, the same five keys). Through Copilot Desktop's bundled 1.0.99-2 on Windowsconfig.jsonwas 131 bytes with the one keyfirstLaunchAt. On the winget install the login wrote two Windows Credential Manager files underAppData/Roaming/Microsoft/Credentials/(634 and 666 bytes, the only ones in the profile besides one of 2026-10-10 underAppData/Local/Microsoft/Credentials/) in the same second, so the token went to Credential Manager as Copilot Desktop's did; the target name was not determined (unreadable from the SSH session), andconfig.jsonheld no token. Keepconfig.jsona secret glob for the plaintext fallback. copilot-user-cache.jsonholds no token (Linux and Windows installs; shape in Package and cache directory): it names the login, plan, organizations and quotas, and stays collected and unflagged, which answers issue 130 for that file. The process log names the login (Process logs).- Env precedence
COPILOT_GITHUB_TOKEN,GH_TOKEN,GITHUB_TOKEN; falls back togh auth token(app.jsLgo, runtime "gh auth tokenstdout collection"), so~/.config/gh/hosts.ymlis a related credential. mcp-oauth-configMCP OAuth store (copilot-mcp-oauth-store-key-v1);mcp-config.jsoncan embed headers/tokens, and on the Windows install it did: thegithubserver'senvheldGITHUB_PERSONAL_ACCESS_TOKEN, a 33-character GitHub personal access token in plain text, written when the user added the server from Copilot Desktop's settings;.copilot/mcp-config.jsonis a secret glob.settings.jsonsandbox.userPolicy.network.proxy.password;.copilot/settings.jsonis flagged.mcp-secrets/, the local fallback store for MCP secret placeholders (Inside~/.copilot), is flagged (.copilot/mcp-secrets/*).- The pre-migration copies under
~/.config/.copilotofconfig.json,mcp-configandmcp-oauth-configare flagged like the~/.copilotones (.config/.copilot/config.json,.config/.copilot/mcp-config*,.config/.copilot/mcp-oauth-config*). - A project's
.github/copilot/settings.json,.github/copilot/settings.local.json,.github/mcp.jsonand.mcp.json(Project-local files) can hold MCP serverenvandheadersvalues and are flagged. - The CLI does not use
~/.config/github-copilot/{hosts,apps}.json(no hit in app.js, native or runtime).
Exclusions¶
.copilot/pkg, .cache/copilot/pkg, Library/Caches/copilot/pkg, AppData/Local/copilot/pkg
(extracted ~180 MB packages per version; 192 MB for win32-arm64/1.0.99-2),
.copilot/installed-plugins (marketplace copies; keep manifests if desired). Keep logs/,
session-state/, session-store.db.
From collector 1.13.0 also .cache/copilot/marketplaces, Library/Caches/copilot/marketplaces and
AppData/Local/copilot/marketplaces: the git clone of each plugin marketplace the CLI reads (263 MB
and 4,560 files on the Windows install;
issue 131). The skipped_excluded row
keeps the directory's size, but which marketplaces were cloned, and when, is inside it and is lost;
--full collects it.
Project-local files¶
.github/copilot-instructions.md, .github/instructions/*.instructions.md,
.github/git-commit-instructions.md, AGENTS.md, CLAUDE.md, GEMINI.md, .claude/rules/**/*.md
(read; app.js help list), .github/copilot/settings.json, .github/copilot/settings.local.json
(app.js LO), .mcp.json, .github/mcp.json (native help), .github/lsp.json, .github/hooks/,
.github/skills/, .github/agents/ (app.js). No per-project database.
Where the project path is recorded¶
session-state/<id>/workspace.yaml cwd, the persisted session cwd (app.js
workspaceLoadPersistedSessionCwd(FP(Wp(e,t),"workspace.yaml")); the key name is not visible in
the JS, which leaves it to the runtime, so it was read from the installs); the file's other keys,
the sync marker among them, are in workspace.yaml.
permissions-config.json is "organized by project location" (docs): its locations keys are
project paths, the git root or cwd (permissions-config.json).
sidebar-sessions-state/*.json cwd maps a project path to its session ids, and outlives the
project: the removed ~/secret kept its file on Linux. events.jsonl session.start
data.context.cwd and session-store.db sessions.cwd carry it too
(Joins and timestamps).
Parsing¶
Transcript stores¶
Relative to the home; ~/.copilot unless COPILOT_HOME moves it.
Installs that predate the XDG migration keep the same names under
.local/state/.copilot (session state, store, history) and
.config/.copilot (config, permissions)
(Per-user storage).
| Path | Content | Parse |
|---|---|---|
.copilot/session-state/<uuid>/events.jsonl |
the session event log | yes, primary |
.copilot/session-state/<uuid>/workspace.yaml |
session metadata: name, cwd, counts | yes |
.copilot/session-state/<uuid>/rewind-file-snapshots/index.json |
per-turn file snapshots for /rewind, with the prompt |
yes, secondary |
.copilot/session-state/<uuid>/rewind-file-snapshots/tracking.json |
{schema, tracking} |
no |
.copilot/session-state/<uuid>/checkpoints/index.md |
Markdown table of compaction checkpoints | no |
.copilot/session-state/<uuid>/session.db |
per-session SQLite of the agent's own todo tables (bundle; not observed) | no |
.copilot/session-state/<uuid>/files/, research/, plan.md |
agent artifacts (empty on every install) | no |
.copilot/session-state/<uuid>.jsonl |
legacy flat transcript, refused by 1.0.98 | no, Format versions |
.copilot/session-store.db (+-wal, -shm) |
cross-session SQLite ("chronicle" store) | yes, secondary |
.copilot/command-history-state.json |
prompt history for Ctrl+R | yes, secondary |
.copilot/permissions-config.json |
standing approvals per project | yes |
.copilot/settings.json |
user settings, JSON with comments (written on macOS, absent on the Linux install) | yes, allowedUrls/deniedUrls |
.copilot/sidebar-sessions-state/<sha256 of cwd>.json |
{schemaVersion, cwd, sessionIds} |
yes, as a join |
.copilot/logs/process-<epoch ms>-<pid>.log |
process log | yes, sign-in line only |
.copilot/open-sessions-state.json |
open-session tracker ({} observed) |
no |
.copilot/mcp-config.json |
MCP server configuration, mcpServers{<name>{type, command, args[], tools[], env{}}} (type local on the Windows install); env can hold a token in plain text (a GitHub personal access token on the Windows install) |
no (MCP) |
.copilot/.mcp-writer.binding, .copilot/.mcp-writer.lock |
the lock of the writer of mcp-config.json: lock{file_index, platform, volume} (platform windows), root{...} (Windows install) |
no |
AppData/Local/copilot/mcp-tools/<sha256>.json |
the cached tool list of a connected MCP server, {schemaVersion, serverName, tools[{name, description, annotations{destructiveHint, openWorldHint, readOnlyHint, title}, inputSchema}], updatedAt}, one per session that connected (Windows install; not observed on macOS or Linux) |
no (MCP) |
session-state/<uuid>/ is created at session start (runtime.node@83194924
Workspace initialized); files/ is "Persistent storage for session
artifacts" (runtime.node@82945561); session.db is the per-session SQL
database (cli-main.js@149653
this.join(this.sessionStatePath,"session.db")).
A session the Copilot Desktop app pre-created and never prompted holds only workspace.yaml
(client_name github/autopilot, user_named false), .workspace-fork.lock, an empty
checkpoints/index.md table and empty files/ and research/, with no events.jsonl or
session.db (seven such sessions on Windows, bundled 1.0.99-2, 2026-10-17).
What survives a delete¶
/session delete <id> (with --remote, the synced copy on the account as well; for a synced
session the CLI asks "has been synced to your account. Would you also like to delete the synced
session data") removes the session directory whole: events.jsonl, workspace.yaml with the
mc_ keys, both rewind-file-snapshots files, checkpoints/, files/ and research/, and the
id from the sidebar file, and leaves open-sessions-state.json {} (Windows winget install,
1.0.98, 2026-10-17; Observed). A session deleted from the GitHub Copilot Desktop
app is a sessions.delete call to the CLI the app bundles, which removes the same
directory; a /rewind prunes the discarded turns' file snapshots.
What remains: the sessions, turns, session_files, search_index
and assistant_usage_events rows of session-store.db, for which the
bundle holds no DELETE (session-store.db); the session's 0-byte
.session-operation-locks/<id>.lock; the slash commands and prompts in
command-history-state.json, newest first, at most 50 by default, with no time, session or
directory (after the winget round's delete, 10 entries in 450 bytes, the seven slash commands among
them); the process logs, which record nothing about the delete; permissions-config.json; and the
files the session wrote. With the remote copy deleted too, no local file says the session was ever
synced, since the mc_ keys lived only in workspace.yaml. What is gone with events.jsonl and
kept nowhere else: the tool calls and their permission decisions, a denial, the session.info
output of a /model switch, the resume, and every event of the prompts the store did not keep.
Removing the project directory (rm -r secret/ on Linux, 1.0.98, 2026-10-16) removes nothing
under ~/.copilot: the session's events.jsonl, its session-store.db rows,
rewind-file-snapshots/index.json (which still names the file under the removed directory),
its command-history-state.json entries and its sidebar file all survive, and together hold the
whole session.
The store's rows that outlived a delete on the real installs:
- Copilot Desktop, Linux install of 2026-10-16: two deleted app sessions kept their
sessions,turns,session_files,assistant_usage_eventsandsearch_indexrows. - Copilot Desktop, Windows install of 2026-10-17: the deleted general chat kept its
sessionsrow, 3turns, 3session_files, 3search_indexand 8assistant_usage_eventsrows, and the WAL holding them was not checkpointed when the app quit (What survives a delete incopilot-desktop.md). - The standalone CLI's own command,
/session delete <id> --yes --remote, locally and from the account (Windows winget install, 1.0.98, 2026-10-17): the session kept itssessionsrow (cwdset,repositorynull, a 32-charactersummary), 3turns(prompts of 63, 18 and 42 characters, replies of 4,346, 1,704 and 30), 1session_filesrow (apply_patch), 3search_indexrows and 55assistant_usage_eventsrows (modelclaude-sonnet-5,api_endpoint/v1/messages; the later session'sgpt-6.1-solrows usews:/responses), withsession_refs,checkpointsandforge_trajectory_eventsempty; the WAL's size and time were unchanged since 22:06, before the delete. Itsevents.jsonlhad held 6user.messagelines, and the store kept only 3 turns, the last three (stamped 22:03:42, 22:04:58 and 22:05:54); why the first three are missing is not determined.
The analyzer reads session-store.db, command-history-state.json and
rewind-file-snapshots/index.json as secondary sources (see
Secondary sources), so a session whose directory
was deleted still yields its store rows; of the process logs it reads the sign-in line only, and
search_index and assistant_usage_events give no rows (Parser plan).
Record schema¶
Envelope¶
Every line is {id, parentId, timestamp, type, data} (observed, 284 of 284 on macOS and every line
on Linux; schema@1133). id is a UUID v4; timestamp is ISO 8601 UTC with milliseconds and Z;
type names the event; data is its payload. Two optional envelope keys are in the schema and were
not observed: agentId, set on events from a subagent ("Absent for events from the root/main
agent", schema@1503), and ephemeral, which marks events that are "not persisted to the session
event log on disk" (schema@1331): 83 of the 159 types are ephemeral (streaming deltas,
session.idle, session.title_changed, assistant.usage, session.snapshot_rewind, the
model.call_* family) and never reach the file. A parser skips unknown types.
parentId is "the chronologically preceding event in the session ... Null
for the first event" (schema@1133). Observed: the previous line on 258
lines, null on session.start and on model.turn_started, and on 3 of
9 model.model_call_started an id absent from the file. The dangling ids
are ephemeral events, which take a place in the chain but are not
written; the model.* events come from the Rust runtime's own publisher
(runtime.node@80033248 Failed to publish native model event) and start
a chain of their own. A rewind truncates the file instead of branching it
(Joins and timestamps), so the chain carries no tree and a
parser ignores it.
Event types¶
Observed types with the keys a parser reads (all named in the schema unless marked):
type |
data keys |
Schema |
|---|---|---|
session.start |
sessionId (= directory name), version (1), producer (copilot-agent), copilotVersion, startTime (session creation, not the line time), selectedModel, context{cwd, gitRoot, repository, hostType, branch, headCommit} (cwd alone on Linux, outside a git repository), alreadyInUse, remoteSteerable, contextTier |
@801015 StartData; @987057 WorkingDirectoryContext |
session.model_change |
previousModel, newModel, cause (initial_resolution observed; rate_limit_auto_switch per the schema), source (automatic observed; model_picker observed on Linux for a typed /model choosing Auto, with previousModel, newModel auto, previousReasoningEffort medium, reasoningEffort and contextTier null; startup observed through Copilot Desktop's bundled 1.0.99-2 on Windows at the first prompt; also model_command, settings_command, config_command, agent, plan_mode, managed_settings, repo_settings, sdk, ...), reasoningEffort, previousReasoningEffort, providerId |
@460110 ModelChangeData, @462830 ModelChangeSource |
session.info |
infoType ("Category of informational message": model, fork, remote, tip, warning, managed_plugins, managed_plugins_complete, relay_attachments_dropped, canvas, hook_progress_transient in cli-main.js, offset not recorded), message ("Human-readable informational message for display in the timeline"), url and tip (optional). Emitted persisted (this.emit("session.info", ...)) and ephemeral (emitEphemeral) in cli-main.js of the 1.0.98 linux-arm64 package (offset not recorded). Observed on Linux right after the model_picker change: infoType model, Model changed from GitHub Copilot • GPT-6.1 Sol (medium) to GitHub Copilot • Auto for this session and Auto routing profile change to Balance requested. |
schema InfoEvent / InfoData of the 1.0.98 linux-arm64 package, offset not recorded ("Informational message for timeline display with categorization"; additionalProperties: false) |
system.message |
content (the full system prompt, 36 KB observed), role (system, developer), interactionId, metadata.variables (gitCommitTrailer{clientName, coauthorEnabled, sessionId} real install and runtime.node@81992124) |
@829319 |
user.message |
content (the prompt as typed), transformedContent (the prompt with injected context), messageId, interactionId, turnId, delivery (idle, steering, queued), agentMode (interactive, plan, autopilot, shell), source (absent or user for typed input; skill-<name>, agent-<id>, command-, schedule-, autopilot- prefixes), isAutopilotContinuation, attachments, responsesReasoning |
@959453 |
assistant.turn_start / assistant.turn_end |
turnId ("typically a stringified turn number"), interactionId, model, parentToolCallId |
@63598 |
assistant.message |
messageId, originatingMessageId (= the prompt's messageId), content (empty when the message only calls tools), model, providerId, turnId, phase (final_answer observed), reasoningText (readable reasoning summary), reasoningOpaque and encryptedContent (opaque provider blobs), reasoningBlocks{provider, blocks[{type, id, summary[{type, text}], encrypted_content}]}, toolRequests[], apiCallId, serverTools, rte, chunkIndex/chunkCount, parentToolCallId |
@29947 |
toolRequests[] |
toolCallId (call_...; custom_... for grammar tools such as apply_patch), name, arguments (object, or a string for type custom), type (function, custom), toolTitle, intentionSummary, mcpServerName, mcpToolName |
@41785 |
tool.execution_start |
toolCallId, toolName, toolTitle, arguments, model, turnId, mcpServerName, mcpToolName, parentToolCallId |
@899827 |
tool.execution_complete |
toolCallId, success, result{content, detailedContent, structuredContent, contents}, error{code, message}, shellExecution{exitCode}, isUserRequested, toolTelemetry, model, turnId, interactionId, rte |
@873844 |
permission.requested |
requestId, permissionRequest{kind, toolCallId, intention, ...}, promptRequest{kind, ...} (the prompt shown), permissionMode (manual, assisted, allow-all), agentMode, ownerSessionId, resolvedByHook |
@565075, @561612 PermissionRequest, @520733 PermissionMode |
permission.completed |
requestId, toolCallId, decisionSource (human_response observed; assisted_approval, host_policy, unattended_fallback, authorization_carry_forward), result{kind, ...} |
@596459 PermissionResult, @504557 |
session.usage_record |
usage{model, inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cost, duration, finishReason, initiator, interactionType, apiEndpoint, transport, provider, isByok, isAuto, accounting{sequence, sourceSessionId, usageId}, apiCallId} |
@81773 AssistantUsageData |
session.usage_checkpoint, session.shutdown |
accountingSnapshot, shutdownType (routine, error), errorReason, sessionStartTime (epoch ms), codeChanges{filesModified, linesAdded, linesRemoved}, modelMetrics, currentModel, eventsFileSizeBytes |
@765167, @771298 |
model.turn_started, model.model_call_started, model.turn_ended |
kind, model, modelInfo{...}, timestampMs, turn |
not in the schema; cli-main.js@1147661 |
model.messages_snapshot |
kind, messages[{role, content}] (the utility prompt) |
not in the schema |
model.captured_assignment_context |
kind, assignmentContext (string) |
not in the schema |
model.message, model.response |
callId, kind, message{role, content, apiCallId, outputTokens, refusal} / response{...}, modelCall{model, api_endpoint, initiator, transport, request_id, ...}, turn |
not in the schema |
model.model_call_success |
callId, modelCall, copilotUsage{token_details, total_nano_aiu}, quotaSnapshots, provider, modelCallDurationMs; on the Windows winget install (1.0.98) also turn, ttftMs, outputTtftMs, interTokenLatencyMs and modelCall{model, api_id, api_endpoint, request_id, client_request_id, service...} |
not in the schema |
subagent.configured, subagent.selected, subagent.started, subagent.completed |
keys not recorded; 2 of each on the Windows winget install (1.0.98), from a session's web-reading task, seen on no other install | subagent.started and .completed are in the schema (list below); the other two not checked |
Glosses: CAPI is the Copilot API, the GitHub endpoint that serves the
models; interactionId is its id for one prompt's run. rte is the
"treatment/eligibility signal returned by the Copilot API in the
X-GitHub-Copilot-Request-TE response header" (schema@29947). A nano AIU
is a billing unit ("nano-AI units cost", ShutdownData).
On the Windows winget install (1.0.98) session.start carried contextTier, context{cwd},
alreadyInUse and remoteSteerable beside the common keys, and system.message
metadata{variables{gitCommitTrailer, sessionId, clientName, coauthorEnabled}}. Under Copilot
Desktop's allow-all mode (bundled 1.0.99-2, Windows) no permission.requested or
permission.completed was written. There the general chat's session.shutdown was written ten
minutes after its last reply (20:13:47); the Linux app sessions wrote none.
permissionRequest.kind values: shell (fullCommandText, commands[{identifier, readOnly}] plus
allowMatchIdentifier (real install and runtime.node@87055448), commandSegments,
possiblePaths, possibleUrls, hasWriteFileRedirection, canOfferSessionApproval, shellType
(real install and runtime.node@87056164)), write (fileName, diff, newFileContents, and
fileEdits and canOfferSessionApproval on the Linux install), read (path), url (url, and
requestSandboxBypass and requestSandboxBypassReason on the Linux install), mcp (serverName,
toolName, args), memory, custom-tool, hook, workflow, extension-management,
extension-permission-access, extension-env-access (schema@583572, @594037, @587312).
promptRequest.kind adds commands (in place of shell) and path. On the Windows winget
install (1.0.98) the 79 requests had four key sets: 72 of the url form (kind, toolCallId,
intention, url, requestSandboxBypass, requestSandboxBypassReason, then promptRequest;
the web fetches), 2 of the shell form (fullCommandText, shellType, intention, commands,
with identifier and readOnly, ...), 2 of the write form (fileName, diff,
newFileContents, canOfferSessionApproval, fileEdits) and 3 with a key beginning redirecte
(the rest of its name was cut in the listing; not determined).
result.kind values (schema@596459): approved;
approved-for-session and approved-for-location (with approval{kind,
commandIdentifiers | serverName, toolName | toolName | operation |
extensionName}, and locationKey, the git root or cwd, for the location
form, @485843); approved-read-only-for-session (directories);
cancelled (reason); denied-by-rules (rules[{kind, argument}]);
denied-no-approval-rule-and-could-not-request-from-user;
denied-interactively-by-user (feedback, forceReject);
denied-by-content-exclusion-policy (path, message);
denied-by-permission-request-hook (message, interrupt). Only the
first and the location form were observed; on Linux the location form
carried approval{kind: write} and the project path as locationKey. The result.kind of the
winget round's denial was not recorded. That round's listing showed permission.completed as
{requestId, toolCallId, result{kind, decisionSource}, agentId}, agentId on some records only,
with decisionSource inside result where the Linux records and the parser have it beside
result; whether that is the shape or the listing's nesting is not determined.
The eight model.* types are the runtime's native model events, listed in
cli-main.js@1147861 (captured_assignment_context:!0,...,turn_started:!0)
and kept out of the CLI's own timeline (cli-main.js@1158330
if(!($M(c)||Ree(c)))return t(c)). Each set is one call to a utility
model, not the session model. Observed: three sets per session, one to
gpt-4o-mini and two to gpt-5.4-nano or gpt-5.6-luna-utility. The
first is the session-name generator: the CLI calls
modelGenerateSessionName with the first prompt, stores the answer as the
session's name and logs it (cli-main.js@7310874
await e.name.setAuto({summary:O.value}),s.info(`Session named: "${O.value}"`);
prompt runtime.node@90556512 Generate a session title for this
message:). The other two are the task-analytics classifier, a telemetry
feature that labels each turn (runtime.node@83390256
judgeModelPreferredgpt-5.6-luna-utility, @83388829 gpt-5.4-nano,
@83390335 copilot_cli_task_analytics). assignmentContext is the ExP
assignment string (the string
names the experiment treatments the client is in, runtime.node@79937841
ExP assignment response AssignmentContext must be a string).
Schema types a parser should handle although none was observed: session.resume (resumeTime,
selectedModel, context), session.error (errorType, message, statusCode), abort
(reason: user_initiated, remote_command, user_abort, autopilot_credit_limit),
session.compaction_complete (summaryContent, checkpointNumber, checkpointPath),
session.context_changed (WorkingDirectoryContext), session.permissions_changed
(previousMode, mode), session.mode_changed (previousMode, newMode),
session.auto_mode_resolved (chosenModel, routingMethod; Auto is the model setting that lets
the service choose), session.task_complete (summary), session.handoff,
session.schedule_created (prompt, cron, at), system.notification (content, kind),
assistant.reasoning (reasoningId, content), tool.user_requested (toolCallId, toolName,
arguments; a tool the user ran), permission.carriedForward, human_response.recorded,
subagent.failed (toolCallId, agentName, model, the keys the schema gives .started and
.completed too, which were observed on Windows), skill.invoked (name, path), hook.start /
hook.end. The hook pairs were observed on Windows through Copilot Desktop's bundled 1.0.99-2, with
hookType sessionStart, userPromptSubmitted, userPromptTransformed, preToolUse,
postToolUse, postToolUseFailure (not seen on Linux), agentStop and sessionEnd.
Joins and timestamps¶
- Session:
session.start.data.sessionId= the directory name =turns,sessions.id,sidebar-sessions-statesessionIds[]andworkspace.yamlid. - Tool call:
toolRequests[].toolCallId=tool.execution_startandtool.execution_completetoolCallId=permissionRequest.toolCallId=permission.completed.toolCallId.requestIdjoins the two permission events. - Prompt to reply:
user.message.messageId=assistant.message.originatingMessageId;interactionIdgroups the prompt, its turns and its tools.turnIdcounts agent-loop turns (22 observed for 7 prompts) and is notturns.turn_index, which counts prompts. - Utility call:
model.*callIdjoins message, response and success. - Rewind: a snapshot's
eventIdis the id of theuser.messageit precedes (real install, equality not checked)./rewindtruncatesevents.jsonlat that event ("Truncates persisted session history to a specific event",runtime.node@83147234; restore and truncation are "Not crash-atomic",@83147760), so discarded turns are gone from the log and survive only in the stores of Other stores. - Project:
session.start.data.context.cwd, updated bysession.context_changedandsession.resume;workspace.yamlcwd;sessions.cwd;sidebar-sessions-statecwd, whose file name is the sha256 of the cwd (real install, verified).
On the Windows winget install (1.0.98) one session held 12 of each
model.* type beside its 55 assistant.message, so that session made 12
utility calls, not the three per session seen on macOS; what each was for
was not determined.
Every timestamp in the log is UTC with Z. session.shutdown.sessionStartTime
is epoch milliseconds; timestampMs on native model events likewise.
Other stores¶
session-store.db¶
session-store.db runs in WAL mode (runtime.node@80402520 PRAGMA journal_mode = WAL); on the
macOS install the data was in the 1.7 MB WAL and the main file was 4 KB (1.37 MB and 4096 bytes on
Linux, 609,792 then 704,552 bytes beside 4096 on Windows through Copilot Desktop),
so the WAL must be read. schema_version 38 observed on macOS and 8 on Linux, both on 1.0.98, and 8
on Windows with the Copilot Desktop app's bundled 1.0.99-2 (Format versions).
The runtime writes it as each turn ends and rebuilds it from events.jsonl with
/chronicle reindex ("chronicle" is the CLI's name for its session-history commands: standup,
reindex, skills, tips; runtime.node@80541016 src/runtime/src/session/store_reindex.rs).
Column meanings come from the runtime's own description for its SQL tool (runtime.node@80212161):
| Table | Written by | Meaning |
|---|---|---|
sessions(id, cwd, repository, host_type, branch, summary, created_at, updated_at) |
@80389716 INSERT INTO sessions ... ON CONFLICT(id) DO UPDATE SET cwd = COALESCE(excluded.cwd, cwd) |
one row per session; summary a 27 to 39 character title (observed) |
turns(session_id, turn_index, user_message, assistant_response, timestamp) |
@80389302 INSERT INTO turns ... ON CONFLICT(session_id, turn_index) DO UPDATE SET user_message = COALESCE(...); next index from SELECT MAX(turn_index) |
one row per prompt: the typed prompt and the final reply |
session_files(session_id, file_path, tool_name, turn_index, first_seen_at) |
@80389148 INSERT OR IGNORE INTO session_files |
files changed, tool_name edit, create or apply_patch |
session_refs(session_id, ref_type, ref_value, turn_index, created_at) |
@80388999 INSERT OR IGNORE INTO session_refs |
ref_type commit, pr or issue (@80212254) |
checkpoints(session_id, checkpoint_number, title, overview, history, work_done, technical_details, important_files, next_steps, created_at) |
@80390542 |
compaction checkpoints |
search_index (FTS5, SQLite's full-text index) |
@80390397 |
source_type turn, checkpoint_* or workspace_artifact; source_id <session>:turn:<n>; content = prompt, newline, reply (observed) |
assistant_usage_events |
@80401153 |
one row per model call: model, tokens, total_nano_aiu, initiator, finish_reason, token_details_json; on Linux 28 rows with model gpt-6.1-sol, copilot_usage_model null, initiator agent or user, api_endpoint ws:/responses or /responses, reasoning_effort medium, finish_reason stop or tool_calls |
forge_trajectory_events(session_id, tool_call_id, turn_index, event_type, command, output, exit_code, event_key, event_value, created_at) |
@80402086 |
tool commands and outputs collected for Forge, the feature that proposes skills from repeated workflows (/chronicle skills create); event_type values not found as literals |
forge_skill_proposals, dynamic_context_items |
skill proposals and per-repository context; no session content (both present and empty on Linux, beside search_index and its five FTS5 shadow tables) |
Observed: turns 9 rows on macOS, user_message equal to a
user.message content and assistant_response equal to an
assistant.message content or null; timestamp 4 to 293 seconds after
the matching user.message line. On one macOS session the first two
prompts appear again at turn_index 2 and 3 after a turn whose response
is null. The Linux round explains the repeat from the timestamps (6
turns rows; prompt lengths 111 and 114 characters in session 1, 90 in
session 2). Session 1 has (0, p1, r1) at 21:05:34.927Z, (1, p2, null)
at 21:08:11.565Z, (2, p1, r1) at 21:08:12.119Z and (3, p2, r2) at
21:16:01.831Z; session 2 has (0, p1, null) at 21:18:16.638Z and (1, p1,
r1) at 21:19:27.956Z. So the complete row of a turn is written when the
next prompt arrives (21:05:34.927Z is 4 ms after prompt 2) or at shutdown
(21:16:01.831Z, 21:19:27.956Z), under MAX(turn_index) + 1; a
prompt-only row with a null response is written mid-turn, 58 to 157
seconds after the prompt, during a model call and aligned with no
transcript event; and the row written on the next prompt repeats the
previous turn under a new index when a prompt-only row sits between
them. No delete, rewind or reindex was involved. The parser's secondary
deduplication drops both kinds of copy (17 rows on the Linux collection).
The Windows install (bundled 1.0.99-2) wrote no prompt-only row: one
complete row per prompt, 3 for the general chat and 1 for the project
session, the chat's last stamped at its shutdown, each with its reply.
The string duplicate_turn sits beside the reindex messages
(runtime.node@80355318); a reindex, which derives turns from the log and
merges with COALESCE, was not run. Other Linux rows: sessions 2
(cwd set; repository, host_type and branch null, since neither
directory was a git repository; summary the generated session name),
session_files 13 (tool_name apply_patch), session_refs,
checkpoints and forge_trajectory_events 0, search_index 6
(source_type turn, source_id <session>:turn:<n>, content the
prompt, a newline and the reply, or the prompt alone when the reply is
null). The bundle holds no DELETE for sessions,
turns, session_files, session_refs or checkpoints (only for
search_index rows it rewrites and dynamic_context_items), so these
rows outlive a rewind and a deleted session directory; what deletes left on the installs is in
What survives a delete.
command-history-state.json¶
command-history-state.json {commandHistory: [...]}: prompts, newest
first, a repeat moved to the front, slash commands included (the
Linux file held 4 entries, /model newest, then the three prompts), at
most commandHistoryMaxSize (50
by default, 1 to 1000) (cli-main.js@1109188
this.commandHistory.unshift(t),this.commandHistory=this.commandHistory.slice(0,this.maxSize);
@1108158 yNt=50,Dze=1e3). No time, session or cwd. The docs name a
command-history-state/ directory instead; 1.0.98 wrote the file.
workspace.yaml¶
workspace.yaml (real install key order): id, cwd, client_name,
name (the generated or /rename name), user_named, summary_count
(compactions), fork_count, created_at, updated_at,
remote_steerable, mc_task_id, mc_session_id, mc_last_event_id;
on Linux client_name was github/cli on both sessions, user_named
false, summary_count and fork_count 0, remote_steerable false. On
the Windows winget install (1.0.98) the file was written at session
start without name and the last four keys (212 bytes), gained name
at the first prompt and remote_steerable and the mc_ keys at a
resume, with updated_at unchanged; the mc_ keys are the sync marker
of Sync, relay and origin.
The struct has 19 fields (runtime.node@82945317 struct WorkspaceMetadata
with 19 elements); repository, host_type, branch,
mc_environment_id and chronicle_sync_dismissed are among the rest
(runtime.node@87588572). The mc_ keys are Mission Control ids
(runtime.node@79041460 enroll published application sessions in Mission Control); every
Copilot Desktop session carries them. They live only in workspace.yaml
(What survives a delete).
rewind-file-snapshots/index.json¶
rewind-file-snapshots/index.json {schema, snapshots[{snapshotId,
eventId, timestamp, userMessage, fileCount, linesAdded, linesRemoved,
backupFiles, files{<hex>: {path, linesAdded, linesRemoved, preimage,
postimage}}}]}, with preimage{kind} and postimage{kind,
contentHash (sha256 hex), encodedPath{data, encoding}, resolvedPath}
(real installs; the removed ~/secret project's snapshot still names
its file under /home/sean/secret; runtime.node@82575983
rewind-file-snapshotstracking.json). A rewind prunes the discarded
turns' snapshots (runtime.node@82545128 Failed to prune a
withdrawn turn's file snapshot).
permissions-config.json¶
permissions-config.json {locations: {<abs path>: {tool_approvals:
[{kind, ...}], allowed_directories: [...]}}}; kind commands
(commandIdentifiers), read, write, mcp (serverName,
toolName), mcp-sampling, memory, custom-tool,
extension-management, workflow, extension-permission-access,
extension-env-access (docs; runtime.node@82849044; runtime.node@82849229
tool_approvalsallowed_directories). Keyed by git root or cwd (docs). Observed two locations with
write entries on macOS, one, ~/demo, with one write entry and no allowed_directories key
on Linux, and on Windows (winget, 1.0.98) one write entry for demo (142 bytes) written by an
"allow always"; the denial in the same step wrote no standing rule.
settings.json¶
settings.json: allowedUrls and deniedUrls, "URLs or domains allowed
without prompting" (runtime.node@79085397); observed four
allowedUrls on macOS; not written on the Linux install. JSON with
comments (docs).
Process logs¶
logs/process-*.log: <ISO ms Z> [LEVEL] <message>. Signed in to
<host> as <login>. (cli-main.js@7556306) names the account; Session
named: "<name>", Starting Copilot CLI: <version>
(cli-main.js@8163060), Workspace initialized: <uuid>. Also seen on
Linux (ids redacted): Node.js version: v<n>, Login status unknown,
Workspace initialized: <uuid> (checkpoints: 0), Registering
foreground session: <uuid> and Unregistering ..., Remote session
access enabled, Session indexing debug: SESSION_INDEXING=true,
repository=undefined, [rust:model_bindings::api_resolver] Using
default model: gpt-6.1-sol, [managedSettings] device MDM: no policy
present on this device (MDM is mobile device management, an
organisation's policy push), [managedSettings] self-fetch starting for
account https://github.com/<login>, [rust:rmcp::service] Service
initialized as client for the github-mcp-server remote MCP server,
Memory enablement check: enabled and Starting graceful shutdown of
CLI server. The login appears twice, in the sign-in line and the
managed-settings line. On the Windows winget install (1.0.98) also
Remote session access disabled (before the login) and enabled (a
resumed process), Unregistering foreground session: <uuid>, Closing
session <uuid>, Closed interrupted sub-agent records on resume
{"closed":0,"dangling":0}, Destroying 0 active sessions and
[rust:rmcp::service] Service initialized as client with no MCP server
configured, and in the resumed process Using default model: gpt-6.1-sol; no line records a
session deleted with /session delete, and the sync a resume made is logged nowhere.
A log rotates at 10 MB to process-<epoch ms>-<pid>.<n>.log. On the Windows winget install
(1.0.98) a /login left pending looped: while the device code waited the CLI wrote about 280,000
lines in 74 seconds, four rotations (process-1792273608752-2868.1.log to .4.log, 10,472,282 to
10,483,404 bytes and 65,093 to 65,615 lines each, seven message kinds) and a live log of 3,068,006
bytes at the login, about 45 MB in all. Each rotated file holds about 9,700 each of [WARNING]
[rust:copilot_runtime::auth::gh] lines saying the CLI failed to run gh auth token
("error":"program path has no file name"; there was no gh on the VM), three
[managedSettings] lines (effective policy resolved: source=none, device MDM: no policy
present on this device, server policy fetch skipped: no authenticated GitHub host available),
[INFO] Remote session access disabled and [rust:lsp::session_service] Resolved LSP telemetry
consent with "telemetry_level":"off","source":"auth_change" (LSP is the Language Server
Protocol), and about 7,500 [INFO] [rust:otel] OTel session policy applied (OTel is
OpenTelemetry, the telemetry framework): a pending login re-resolves the auth state in a tight loop.
After the login the live log grew by 3 KB in fifteen minutes; the Linux and macOS 1.0.98 logs were
105 bytes to 47 KB.
Through Copilot Desktop's bundled 1.0.99-2 on Windows the logs also hold MCP server connection
failed {"server_name":"<name>","raw_error":"failed to spawn MCP server process: program not
found"} for a configured server whose command is not installed, and Best-effort plugin
preparation skipped 1 item.
The parser's pattern (process-\d+-\d+\.log, Parser plan)
does not select the rotated files, so a sign-in line that rotated out
would not be read (issue 152);
on that install every sign-in line was in an unrotated log.
vscode.metadata.json¶
session-state/<id>/vscode.metadata.json is written by VS Code, not by the CLI, for a session
VS Code's Copilot Chat extension runs in a git worktree
(Scratch directories).
Its entries carry workspaceFolder, worktreeProperties and additionalWorkspaces
(chatSessionMetadataStoreImpl.ts:170-186,
microsoft/vscode at
4d22f4f);
not seen on a real install. The VS Code side is in vscode.md.
Format versions¶
session.start.data.version 1 and producer copilot-agent on every
observed log. Several fields are "Absent on historical events"
(permissionMode, ownerSessionId) or absent "on completions recorded
before this field existed" (decisionSource, schema@504557), so older
logs lack them. Earlier builds wrote a flat session-state/<session
id>.jsonl; 1.0.98 refuses to resume one (cli-main.js@1698895 uses a
legacy flat JSONL transcript format that is no longer supported;
cli-main.js@1654639 Rwr=".jsonl") and the reindex skips it. Its
record shape is not in this bundle. The schema allows no extra keys
(additionalProperties: false), yet the install carried shellType and
allowMatchIdentifier, so the file is a superset of the published
schema. session-store.db migrates through schema_version: 38 on
the macOS install and 8 on the Linux install, both 1.0.98 and both with
the tables of Other stores; neither number is
explained (the Windows install's bundled 1.0.99-2 also had 8);
rewind-file-snapshots and sidebar-sessions-state carry
their own schema and schemaVersion numbers.
Secrets¶
system.message.content is the full system prompt with the user's custom
instructions; transformedContent repeats each prompt with injected
context. Tool arguments and results are verbatim (shell output, fetched
pages), and a write permission request carries the diff and
newFileContents of the file. encryptedContent, reasoningOpaque and
reasoningBlocks[].encrypted_content are opaque provider blobs
("Session-bound and stripped on resume", schema@29947); apiCallId (500
characters observed) is the provider's response id, and usageId,
request_id and service_request_id are request ids, none a credential.
model_call_success.quotaSnapshots records the account's plan
entitlements. The log's sign-in line names the GitHub login. No token was
seen in a session file; where the token is kept, and the mcpServers.<name>.env values of
mcp-config.json that can hold one in plain text, are in Credentials. The parser
reads neither config.json nor mcp-config.json.
Parser plan¶
wants() by artifact.rel, with (?:\.copilot|\.local/state/\.copilot|\.config/\.copilot)
written <home>:
^<home>/session-state/[0-9a-f-]{36}/events\.jsonl$
^<home>/session-state/[0-9a-f-]{36}/workspace\.yaml$
^<home>/session-state/[0-9a-f-]{36}/rewind-file-snapshots/index\.json$
^<home>/session-store\.db$
^<home>/command-history-state\.json$
^<home>/permissions-config\.json$
^<home>/settings\.json$
^<home>/sidebar-sessions-state/[0-9a-f]{64}\.json$
^<home>/logs/process-\d+-\d+\.log$
Rows follow the log order, timestamped by the line's timestamp.
| Record | turn_type and text |
|---|---|
session.start |
system, session start: copilot <copilotVersion> producer=<producer> cwd=<context.cwd>; fills session_id, project_path, git_branch (context.branch) |
session.resume |
system, session resumed; updates cwd and branch from context |
session.context_changed |
no row; updates project_path and git_branch |
system.message |
system, system prompt: <content> (role developer: developer message:), never shortened |
user.message, no agentId, source absent or user, not isAutopilotContinuation |
user, content |
user.message, other |
system, <label>: <content>: auto-continuation for isAutopilotContinuation, subagent task with agentId, otherwise the source value |
assistant.message |
assistant with content when non-empty; then thinking with reasoningText (only with include_thinking; else the summaries joined from reasoningBlocks.blocks[].summary[].text); then one tool_use per toolRequests[] |
assistant.reasoning |
thinking, content (only with include_thinking) |
tool.execution_start |
no row when an earlier toolRequests[] had the toolCallId; otherwise tool_use (a user-requested or subagent call) |
tool.user_requested |
tool_use, prefixed user requested: |
tool.execution_complete |
tool_result: the longer of result.detailedContent and result.content, detailedContent on a tie (it is the complete text, but a successful skill invocation puts the concise text there: one observed record had 25 characters in detailedContent beside 8091 in content); prefixed [exit N] when shellExecution.exitCode is non-zero; error.message appended on a new line when success is false |
permission.requested |
system, permission request: <permissionRequest.kind> <fullCommandText, url, fileName, path or serverName/toolName> intention=<intention>, tool_use_id permissionRequest.toolCallId; first permissionMode and agentMode give permission mode: <permissionMode> agent=<agentMode>, a change gives permission mode: <old> -> <new> |
permission.completed |
system, permission decision: <result.kind> source=<decisionSource or ->, then location=<locationKey>, approval=<compact JSON of approval>, directories=..., feedback=<feedback>, rules=<compact JSON>, reason=<reason>, path=<path>, message=<message> when present; tool_use_id toolCallId |
permission.carriedForward |
system, permission decision: carried-forward source=<decisionSource> |
session.permissions_changed / session.mode_changed |
system, permission mode: <previousMode> -> <mode> / agent mode: <previousMode> -> <newMode>; the Copilot Desktop app's form (previousAllowAllPermissions, allowAllPermissions, previousAllowAllPermissionMode, allowAllPermissionMode, no mode and no previousMode; real installs, Linux, bundled CLI 1.0.95-3, and Windows, 1.0.99-2) is permission mode: allow-all <previousAllowAllPermissionMode or -> -> <allowAllPermissionMode or ->, and the tracked mode becomes allow-all <allowAllPermissionMode> |
session.model_change |
system, model: <newModel> at the first, model: <old> -> <newModel> on a change, kept when equal; followed by cause=<cause> source=<source> effort=<reasoningEffort> for the keys present |
session.auto_mode_resolved |
system, model: <old> -> <chosenModel> (auto, <routingMethod>) |
session.info |
system, session.info <infoType>: <message>, then url=<url> and tip=<tip> for the keys present, at the line's timestamp; model the tracked model, tool_name and tool_use_id empty |
model.message |
system, utility model <modelCall.model>: <message.content>, model that model; never changes the session model |
other model.* |
skip (the prompt snapshot, the ExP string and call bookkeeping) |
session.compaction_complete |
system, compaction summary: <summaryContent> |
abort, session.error, session.warning |
system, <type>: <reason or message> |
session.task_complete, system.notification, session.schedule_created, skill.invoked, subagent.*, session.handoff |
system, <type>: and the main text field, else compact JSON |
hook.start / hook.end |
system, hook <hookType> start: <compact JSON of input, or {}> / hook <hookType> end: success=<true or false> output=<compact JSON of output, or {}>, then error=<compact JSON> when error is present; - for a missing hookType or a non-boolean success; tool_use_id parentToolCallId when present, tool_name empty; never shortened |
session.shutdown |
system, session end: <shutdownType> <errorReason> files modified: <codeChanges.filesModified> |
assistant.turn_*, session.usage_record, session.usage_checkpoint, unknown types |
skip |
Other files:
| File | Rows |
|---|---|
session-store.db turns |
user from user_message and assistant from assistant_response when not null; timestamp, session_id, project_path and git_branch from sessions; source_line the rowid |
session-store.db sessions |
system, session name: <summary> at created_at |
session-store.db session_files / session_refs |
system, file changed: <file_path> tool=<tool_name> at first_seen_at / reference: <ref_type> <ref_value> at created_at |
session-store.db checkpoints |
system, checkpoint <n>: <title> and each non-null column on its own line, at created_at |
session-store.db forge_trajectory_events |
tool_use (command) and tool_result (output, [exit N]) joined on tool_call_id (empty table observed; unverified) |
search_index, assistant_usage_events, forge_skill_proposals, dynamic_context_items |
none: concatenated copies, accounting, or no session |
command-history-state.json |
user per entry, empty timestamp_utc, session_id and project_path; source_line the 1-based index (newest first) |
rewind-file-snapshots/index.json |
user from userMessage at timestamp; and system, rewind snapshot: <fileCount> files +<linesAdded> -<linesRemoved>: <path>, ... |
workspace.yaml |
system, session name: <name> and session metadata: user_named=<user_named> forks=<fork_count> compactions=<summary_count> client=<client_name> at created_at (client= empty when the key is absent; github/cli for the CLI, github/autopilot for the Copilot Desktop app); cwd is the session's project fallback; when the session directory holds no events.jsonl and cwd is set (a session the Copilot Desktop app pre-created and never prompted, its pool and probe sessions), one more system row after the metadata row with the same columns, scratch directory: <cwd> (working directory of a session the app pre-created and never prompted: no events.jsonl), so inventory.json lists the cwd under scratch_directories, the working directories that belong to the agent rather than to the user, not under projects (issue 149) |
permissions-config.json |
one system row per tool_approvals and allowed_directories entry: permission setting: locations.<path>.tool_approvals=<compact JSON> (from .copilot/permissions-config.json, file mtime), project_path the location, timestamp the file's mtime, empty session_id |
settings.json |
one system row per allowedUrls and deniedUrls entry, permission setting: allowedUrls=<url> (from .copilot/settings.json, file mtime); strip comments before parsing |
sidebar-sessions-state/*.json |
none; cwd per sessionIds[] fills project_path of database and snapshot rows that lack one |
logs/process-*.log |
system, signed in: <host> as <login> at the line's time; session_id from Workspace initialized: <uuid> when the file names exactly one |
Columns:
| Column | Source |
|---|---|
| timestamp_utc | line timestamp; database columns as stored |
| session_id | the directory name (= session.start.sessionId); session_id columns |
| project_path | latest context.cwd, else workspace.yaml cwd, else sessions.cwd, else sidebar-sessions-state |
| git_branch | latest context.branch, else sessions.branch; empty when neither is set |
| model | as in Model |
| tool_name | toolRequests[].name or toolName; on results and permission rows, the name remembered for the toolCallId |
| tool_use_id | toolCallId |
| text | coverage tables; a tool_use text is the arguments string, else command, url, query, path or input, else compact JSON |
| source_line | 1-based line, or array index, or rowid |
Permission rows¶
Per-call decisions: permission.requested, permission.completed and
permission.carriedForward give the rows of the table above, joined to the call on
tool_use_id, and session.permissions_changed and session.mode_changed give the mode
changes; the record shapes are in Event types. Standing grants: one row per
permissions-config.json entry and per settings.json allowedUrls and deniedUrls entry, at
the file's modification time (permissions-config.json,
settings.json).
Model rows¶
The model column is assistant.message.model or the tool event's model, else the model tracked
from session.model_change. session.model_change and session.auto_mode_resolved give the
model: rows of the table above, and model.message a utility model row that never changes the
session model.
A /model command the user types is not a user.message: the log keeps
only its session.model_change and session.info lines, and the command
itself survives only as an entry of command-history-state.json, which
no other source repeats, so deduplication keeps it as an undated user
row (Linux install).
Past-use rows and secondary sources¶
secondary() is true for session-store.db, command-history-state.json
and rewind-file-snapshots/index.json. secondary_tolerance() is 3600
seconds for the first and the last: turns.timestamp is written at the end
of the turn (4 to 293 seconds after the prompt observed, and a turn with
many tools can run longer), and the snapshot time was not measured against
the prompt's, so both get the one-hour window letta uses.
The sessions row and the workspace.yaml row share the session name:
form so the database copy drops when the two agree; the metadata is a row
of its own so that the name rows can match. The workspace.yaml rows are
not secondary.
What a delete leaves for these sources to rebuild is in What survives a delete.
Sync, relay and origin rows¶
The sync keys of workspace.yaml, remote_steerable and the three mc_ keys,
mark a session synced to the account through Mission Control (Copilot Desktop sessions on Linux
1.1.29 and Windows 1.1.30 carry them too). When mc_session_id is
non-empty the parser yields one system row after the session
metadata: row, session synced: remote session <mc_session_id> task
<mc_task_id or -> last event <mc_last_event_id or ->
remote_steerable=<remote_steerable or -> (from workspace.yaml, file
mtime), timestamped by the file's modification time, a key absent or
empty printed -; the keys can be present as null on a session that
is not synced, and then no row is yielded. The parser sets
records_sync, so the session's sessions.jsonl record says synced
yes when it has that row and no when it has none (Copilot Desktop
sessions included, since they are copilot-cli rows), and
inventory.json counts the synced sessions in synced_sessions, at the
top level and in each homes[].agents[] entry. A session deleted
locally and remotely leaves no marker anywhere on disk
(What survives a delete).
/chronicle reindex also uploads the sessions to the account (docs: "Reindexing also syncs
your session data to your account").
Relay and origin: not stated.
Scratch directory rows¶
A session the Copilot Desktop app pre-created and never prompted gives a scratch directory: row
from its workspace.yaml (the workspace.yaml row of the table above). The VS Code worktree of
Scratch directories sits outside every catalog line, so the analyzer's
catalog rule does not class it as a scratch directory; the parser does not read
vscode.metadata.json.
Deletion rows¶
The process logs record nothing about a delete, so no row marks one; what a delete leaves and what the analyzer rebuilds from it is in What survives a delete.
Bad-line rows¶
events.jsonl is read with iter_jsonl; each bad line is its own
system row, parser: unparseable line (<reason>): <line> with the line
in full (issue 136), and reading continues.
Fixtures¶
Sample fixture
.copilot/session-state/5e55a0a1-0000-4000-8000-000000000001/events.jsonl
(synthetic; start, model, system prompt, prompt, utility call, reasoning
and a bash request, a location approval, a failed run, an apply_patch
request approved once, a final reply, usage, the Linux round's /model
switch to Auto with its two session.info lines, shutdown, then a cut
line):
{"id":"e0000000-0000-4000-8000-000000000001","parentId":null,"timestamp":"2026-10-01T09:00:00.000Z","type":"session.start","data":{"sessionId":"5e55a0a1-0000-4000-8000-000000000001","version":1,"producer":"copilot-agent","copilotVersion":"1.0.98","startTime":"2026-10-01T08:59:59.000Z","context":{"cwd":"/srv/proj","gitRoot":"/srv/proj","branch":"main"},"alreadyInUse":false,"remoteSteerable":false,"contextTier":null}}
{"id":"e0000000-0000-4000-8000-000000000002","parentId":"e0000000-0000-4000-8000-000000000001","timestamp":"2026-10-01T09:00:00.100Z","type":"session.model_change","data":{"newModel":"gpt-6.1-sol","cause":"initial_resolution","source":"automatic","reasoningEffort":"medium","contextTier":null}}
{"id":"e0000000-0000-4000-8000-000000000003","parentId":"e0000000-0000-4000-8000-000000000002","timestamp":"2026-10-01T09:00:01.000Z","type":"system.message","data":{"content":"You are the GitHub Copilot CLI.\nFollow the user's instructions.","role":"system","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","metadata":{"variables":{"gitCommitTrailer":{"clientName":"copilot-cli","coauthorEnabled":true,"sessionId":"5e55a0a1-0000-4000-8000-000000000001"}}}}}
{"id":"e0000000-0000-4000-8000-000000000004","parentId":"e0000000-0000-4000-8000-000000000003","timestamp":"2026-10-01T09:00:01.200Z","type":"user.message","data":{"content":"fix the failing test","transformedContent":"<current_datetime>2026-10-01T09:00:01Z</current_datetime>\nfix the failing test","messageId":"2b2b2b2b-0000-4000-8000-000000000001","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","parentAgentTaskId":"3c3c3c3c-0000-4000-8000-000000000001","turnId":"0","delivery":"idle","responsesReasoning":{"effort":"medium","initialEffort":"medium","model":"gpt-6.1-sol"},"supportedNativeDocumentMimeTypes":[]}}
{"id":"e0000000-0000-4000-8000-000000000005","parentId":"e0000000-0000-4000-8000-000000000004","timestamp":"2026-10-01T09:00:01.300Z","type":"assistant.turn_start","data":{"interactionId":"1a1a1a1a-0000-4000-8000-000000000001","turnId":"0"}}
{"id":"e0000000-0000-4000-8000-000000000006","parentId":null,"timestamp":"2026-10-01T09:00:01.400Z","type":"model.turn_started","data":{"kind":"turn_started","model":"gpt-4o-mini","modelInfo":{"id":"gpt-4o-mini","name":"GPT-4o mini","object":"model","version":"gpt-4o-mini-2024-07-18","provider":{"id":"copilot","kind":"copilot","label":"GitHub Copilot"}},"timestampMs":1790845201400,"turn":0}}
{"id":"e0000000-0000-4000-8000-000000000007","parentId":"e0000000-0000-4000-8000-000000000006","timestamp":"2026-10-01T09:00:01.410Z","type":"model.model_call_started","data":{"kind":"model_call_started","model":"gpt-4o-mini","modelInfo":{"id":"gpt-4o-mini","name":"GPT-4o mini","object":"model","version":"gpt-4o-mini-2024-07-18","provider":{"id":"copilot","kind":"copilot","label":"GitHub Copilot"}},"timestampMs":1790845201410,"turn":0}}
{"id":"e0000000-0000-4000-8000-000000000008","parentId":"e0000000-0000-4000-8000-000000000007","timestamp":"2026-10-01T09:00:01.420Z","type":"model.messages_snapshot","data":{"kind":"messages_snapshot","messages":[{"role":"system","content":"Generate a session title for this message."},{"role":"user","content":"fix the failing test"}]}}
{"id":"e0000000-0000-4000-8000-000000000009","parentId":"e0000000-0000-4000-8000-000000000008","timestamp":"2026-10-01T09:00:01.430Z","type":"model.captured_assignment_context","data":{"kind":"captured_assignment_context","assignmentContext":"exp-a:1;exp-b:0;"}}
{"id":"e0000000-0000-4000-8000-000000000010","parentId":"e0000000-0000-4000-8000-000000000009","timestamp":"2026-10-01T09:00:02.000Z","type":"model.message","data":{"callId":"4d4d4d4d-0000-4000-8000-000000000001","kind":"message","message":{"apiCallId":"chatcmpl-0001","content":"Fix failing test","outputTokens":4,"providerId":"copilot","refusal":null,"role":"assistant"},"modelCall":{"api_endpoint":"/chat/completions","api_id":"chatcmpl-0001","client_request_id":"req-0001","initiator":"agent","model":"gpt-4o-mini","request_id":"5f5f5f5f-0000-4000-8000-000000000001","rte":false,"service_request_id":"6a6a6a6a-0000-4000-8000-000000000001","transport":"http"},"turn":0}}
{"id":"e0000000-0000-4000-8000-000000000011","parentId":"e0000000-0000-4000-8000-000000000010","timestamp":"2026-10-01T09:00:02.010Z","type":"model.response","data":{"callId":"4d4d4d4d-0000-4000-8000-000000000001","kind":"response","modelCall":{"api_id":"chatcmpl-0001","model":"gpt-4o-mini"},"response":{"content":"Fix failing test","providerId":"copilot","refusal":null,"role":"assistant"},"turn":0}}
{"id":"e0000000-0000-4000-8000-000000000012","parentId":"e0000000-0000-4000-8000-000000000011","timestamp":"2026-10-01T09:00:02.020Z","type":"model.model_call_success","data":{"callId":"4d4d4d4d-0000-4000-8000-000000000001","kind":"model_call_success","modelCall":{"api_endpoint":"/chat/completions","api_id":"chatcmpl-0001","client_request_id":"req-0001","initiator":"agent","model":"gpt-4o-mini","request_id":"5f5f5f5f-0000-4000-8000-000000000001","rte":false,"service_request_id":"6a6a6a6a-0000-4000-8000-000000000001","transport":"http"},"modelCallDurationMs":590,"modelDisplayName":"GPT-4o mini","provider":{"id":"copilot","kind":"copilot","label":"GitHub Copilot"}}}
{"id":"e0000000-0000-4000-8000-000000000013","parentId":"e0000000-0000-4000-8000-000000000012","timestamp":"2026-10-01T09:00:02.030Z","type":"model.turn_ended","data":{"kind":"turn_ended","model":"gpt-4o-mini","modelInfo":{"id":"gpt-4o-mini","name":"GPT-4o mini","object":"model","version":"gpt-4o-mini-2024-07-18","provider":{"id":"copilot","kind":"copilot","label":"GitHub Copilot"}},"timestampMs":1790845202030,"turn":0}}
{"id":"e0000000-0000-4000-8000-000000000014","parentId":"e0000000-0000-4000-8000-000000000013","timestamp":"2026-10-01T09:00:03.000Z","type":"assistant.message","data":{"content":"","messageId":"7b7b7b7b-0000-4000-8000-000000000001","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","originatingMessageId":"2b2b2b2b-0000-4000-8000-000000000001","model":"gpt-6.1-sol","providerId":"copilot","turnId":"0","reasoningText":"Run the tests first.","reasoningOpaque":"rs_AAAA","encryptedContent":"gAAAAA","reasoningBlocks":{"provider":"openai-responses","blocks":[{"type":"reasoning","id":"rs_AAAA","summary":[{"type":"summary_text","text":"Run the tests first."}],"encrypted_content":"gAAAAA"}]},"apiCallId":"resp_0001","rte":false,"serverTools":{"provider":"openai-responses"},"toolRequests":[{"type":"function","name":"bash","toolCallId":"call_01","toolTitle":"Run shell command","arguments":{"command":"npm test","description":"Run the test suite"},"intentionSummary":"Run the test suite"}]}}
{"id":"e0000000-0000-4000-8000-000000000015","parentId":"e0000000-0000-4000-8000-000000000014","timestamp":"2026-10-01T09:00:03.100Z","type":"permission.requested","data":{"requestId":"8c8c8c8c-0000-4000-8000-000000000001","ownerSessionId":"5e55a0a1-0000-4000-8000-000000000001","agentMode":"interactive","permissionMode":"manual","permissionRequest":{"kind":"shell","toolCallId":"call_01","intention":"Run the test suite","fullCommandText":"npm test","shellType":"bash","commands":[{"identifier":"npm test","readOnly":false,"allowMatchIdentifier":"npm test"}],"commandSegments":[{"identifier":"npm test","fullCommandText":"npm test"}],"possiblePaths":[],"possibleUrls":[],"hasWriteFileRedirection":false,"canOfferSessionApproval":true},"promptRequest":{"kind":"commands","toolCallId":"call_01","intention":"Run the test suite","fullCommandText":"npm test","commandIdentifiers":["npm test"]}}}
{"id":"e0000000-0000-4000-8000-000000000016","parentId":"e0000000-0000-4000-8000-000000000015","timestamp":"2026-10-01T09:00:06.000Z","type":"permission.completed","data":{"requestId":"8c8c8c8c-0000-4000-8000-000000000001","toolCallId":"call_01","decisionSource":"human_response","result":{"kind":"approved-for-location","locationKey":"/srv/proj","approval":{"kind":"commands","commandIdentifiers":["npm test"]}}}}
{"id":"e0000000-0000-4000-8000-000000000017","parentId":"e0000000-0000-4000-8000-000000000016","timestamp":"2026-10-01T09:00:06.100Z","type":"tool.execution_start","data":{"toolCallId":"call_01","toolName":"bash","toolTitle":"Run shell command","arguments":{"command":"npm test","description":"Run the test suite"},"model":"gpt-6.1-sol","turnId":"0"}}
{"id":"e0000000-0000-4000-8000-000000000018","parentId":"e0000000-0000-4000-8000-000000000017","timestamp":"2026-10-01T09:00:08.000Z","type":"tool.execution_complete","data":{"toolCallId":"call_01","success":false,"result":{"content":"1 failing","detailedContent":"1 failing\n date.test.js: expected 2026"},"error":{"code":"failure","message":"Command exited with code 1"},"shellExecution":{"exitCode":1},"toolTelemetry":{"metrics":{},"properties":{}},"model":"gpt-6.1-sol","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","turnId":"0","rte":false}}
{"id":"e0000000-0000-4000-8000-000000000019","parentId":"e0000000-0000-4000-8000-000000000018","timestamp":"2026-10-01T09:00:08.100Z","type":"assistant.turn_end","data":{"turnId":"0"}}
{"id":"e0000000-0000-4000-8000-000000000020","parentId":"e0000000-0000-4000-8000-000000000019","timestamp":"2026-10-01T09:00:08.200Z","type":"assistant.turn_start","data":{"interactionId":"1a1a1a1a-0000-4000-8000-000000000001","turnId":"1"}}
{"id":"e0000000-0000-4000-8000-000000000021","parentId":"e0000000-0000-4000-8000-000000000020","timestamp":"2026-10-01T09:00:10.000Z","type":"assistant.message","data":{"content":"","messageId":"7b7b7b7b-0000-4000-8000-000000000002","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","originatingMessageId":"2b2b2b2b-0000-4000-8000-000000000001","model":"gpt-6.1-sol","providerId":"copilot","turnId":"1","apiCallId":"resp_0002","rte":false,"toolRequests":[{"type":"custom","name":"apply_patch","toolCallId":"custom_0000000000000000000000000001","toolTitle":"Apply patch","arguments":"*** Begin Patch\n*** Update File: /srv/proj/date.test.js\n-2025\n+2026\n*** End Patch\n","intentionSummary":"Update the fixture year"}]}}
{"id":"e0000000-0000-4000-8000-000000000022","parentId":"e0000000-0000-4000-8000-000000000021","timestamp":"2026-10-01T09:00:10.100Z","type":"permission.requested","data":{"requestId":"8c8c8c8c-0000-4000-8000-000000000002","ownerSessionId":"5e55a0a1-0000-4000-8000-000000000001","agentMode":"interactive","permissionMode":"manual","permissionRequest":{"kind":"write","toolCallId":"custom_0000000000000000000000000001","intention":"Update the fixture year","fileName":"/srv/proj/date.test.js","diff":"-2025\n+2026\n","canOfferSessionApproval":true},"promptRequest":{"kind":"write","toolCallId":"custom_0000000000000000000000000001","intention":"Update the fixture year","fileName":"/srv/proj/date.test.js"}}}
{"id":"e0000000-0000-4000-8000-000000000023","parentId":"e0000000-0000-4000-8000-000000000022","timestamp":"2026-10-01T09:00:12.000Z","type":"permission.completed","data":{"requestId":"8c8c8c8c-0000-4000-8000-000000000002","toolCallId":"custom_0000000000000000000000000001","decisionSource":"human_response","result":{"kind":"approved"}}}
{"id":"e0000000-0000-4000-8000-000000000024","parentId":"e0000000-0000-4000-8000-000000000023","timestamp":"2026-10-01T09:00:12.100Z","type":"tool.execution_start","data":{"toolCallId":"custom_0000000000000000000000000001","toolName":"apply_patch","toolTitle":"Apply patch","arguments":{"input":"*** Begin Patch\n*** Update File: /srv/proj/date.test.js\n-2025\n+2026\n*** End Patch\n"},"model":"gpt-6.1-sol","turnId":"1"}}
{"id":"e0000000-0000-4000-8000-000000000025","parentId":"e0000000-0000-4000-8000-000000000024","timestamp":"2026-10-01T09:00:12.500Z","type":"tool.execution_complete","data":{"toolCallId":"custom_0000000000000000000000000001","success":true,"result":{"content":"Modified 1 file: /srv/proj/date.test.js"},"toolTelemetry":{"metrics":{},"properties":{}},"model":"gpt-6.1-sol","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","turnId":"1","rte":false}}
{"id":"e0000000-0000-4000-8000-000000000026","parentId":"e0000000-0000-4000-8000-000000000025","timestamp":"2026-10-01T09:00:12.600Z","type":"assistant.turn_end","data":{"turnId":"1"}}
{"id":"e0000000-0000-4000-8000-000000000027","parentId":"e0000000-0000-4000-8000-000000000026","timestamp":"2026-10-01T09:00:12.700Z","type":"assistant.turn_start","data":{"interactionId":"1a1a1a1a-0000-4000-8000-000000000001","turnId":"2"}}
{"id":"e0000000-0000-4000-8000-000000000028","parentId":"e0000000-0000-4000-8000-000000000027","timestamp":"2026-10-01T09:00:15.000Z","type":"assistant.message","data":{"content":"The fixture year was stale; it now reads 2026.","messageId":"7b7b7b7b-0000-4000-8000-000000000003","interactionId":"1a1a1a1a-0000-4000-8000-000000000001","originatingMessageId":"2b2b2b2b-0000-4000-8000-000000000001","model":"gpt-6.1-sol","providerId":"copilot","turnId":"2","phase":"final_answer","apiCallId":"resp_0003","rte":false,"toolRequests":[]}}
{"id":"e0000000-0000-4000-8000-000000000029","parentId":"e0000000-0000-4000-8000-000000000028","timestamp":"2026-10-01T09:00:15.100Z","type":"assistant.turn_end","data":{"turnId":"2"}}
{"id":"e0000000-0000-4000-8000-000000000030","parentId":"e0000000-0000-4000-8000-000000000029","timestamp":"2026-10-01T09:00:15.200Z","type":"session.usage_record","data":{"usage":{"model":"gpt-6.1-sol","inputTokens":900,"outputTokens":40,"initiator":"agent","interactionType":"conversation-agent","apiEndpoint":"ws:/responses","transport":"websocket","isByok":false,"accounting":{"sequence":1,"sourceSessionId":"5e55a0a1-0000-4000-8000-000000000001","usageId":"u-0001"}}}}
{"id":"e0000000-0000-4000-8000-000000000031","parentId":"e0000000-0000-4000-8000-000000000030","timestamp":"2026-10-01T09:00:20.000Z","type":"session.model_change","data":{"previousModel":"gpt-6.1-sol","newModel":"auto","previousReasoningEffort":"medium","reasoningEffort":null,"contextTier":null,"source":"model_picker"}}
{"id":"e0000000-0000-4000-8000-000000000032","parentId":"e0000000-0000-4000-8000-000000000031","timestamp":"2026-10-01T09:00:20.395Z","type":"session.info","data":{"message":"Model changed from GitHub Copilot • GPT-6.1 Sol (medium) to GitHub Copilot • Auto for this session","infoType":"model"}}
{"id":"e0000000-0000-4000-8000-000000000033","parentId":"e0000000-0000-4000-8000-000000000032","timestamp":"2026-10-01T09:00:20.396Z","type":"session.info","data":{"message":"Auto routing profile change to Balance requested.","infoType":"model"}}
{"id":"e0000000-0000-4000-8000-000000000034","parentId":"e0000000-0000-4000-8000-000000000033","timestamp":"2026-10-01T09:00:30.000Z","type":"session.shutdown","data":{"shutdownType":"routine","sessionStartTime":1790845199000,"totalApiDurationMs":9000,"eventsFileSizeBytes":20000,"codeChanges":{"filesModified":["/srv/proj/date.test.js"],"linesAdded":1,"linesRemoved":1},"modelMetrics":{},"currentModel":"gpt-6.1-sol"}}
{"id":"e0000000-0000-4000-8000-000000000035","parentId":"e0000000-0000-4000-8000-000000000034","timestamp":"2026-10-01T09:01:00.000Z","type":"user.message","data":{"content":"and run
session-store.db:
INSERT INTO sessions VALUES ('5e55a0a1-0000-4000-8000-000000000001', '/srv/proj', NULL, NULL, NULL, 'Fix failing test', '2026-10-01T09:00:00.000Z', '2026-10-01T09:00:30.000Z');
INSERT INTO turns (session_id, turn_index, user_message, assistant_response, timestamp) VALUES ('5e55a0a1-0000-4000-8000-000000000001', 0, 'fix the failing test', 'The fixture year was stale; it now reads 2026.', '2026-10-01T09:00:15.200Z');
INSERT INTO turns (session_id, turn_index, user_message, assistant_response, timestamp) VALUES ('5e55a0a1-0000-4000-8000-000000000001', 1, 'and run lint', NULL, '2026-10-01T09:01:05.000Z');
INSERT INTO session_files (session_id, file_path, tool_name, turn_index, first_seen_at) VALUES ('5e55a0a1-0000-4000-8000-000000000001', '/srv/proj/date.test.js', 'apply_patch', 0, '2026-10-01T09:00:12.500Z');
.copilot/session-state/5e55a0a1-0000-4000-8000-000000000001/workspace.yaml:
id: 5e55a0a1-0000-4000-8000-000000000001
cwd: /srv/proj
client_name: copilot-cli
name: Fix failing test
user_named: false
summary_count: 0
fork_count: 0
created_at: 2026-10-01T09:00:00.000Z
updated_at: 2026-10-01T09:00:30.000Z
remote_steerable: false
mc_task_id: null
mc_session_id: null
mc_last_event_id: null
.copilot/command-history-state.json:
{"commandHistory":["and run lint","/model","fix the failing test"]}
.copilot/permissions-config.json:
{"locations":{"/srv/proj":{"tool_approvals":[{"kind":"write"},{"kind":"commands","commandIdentifiers":["npm test"]}]}}}
.copilot/settings.json:
{
// user settings
"allowedUrls": ["https://registry.example.org"]
}
.copilot/session-state/5e55a0a1-0000-4000-8000-000000000001/rewind-file-snapshots/index.json
(the snapshot taken before the first prompt, whose eventId is line 4's
id; the file key is a synthetic digest and the images are left out):
{"schema":1,"snapshots":[{"snapshotId":"9d9d9d9d-0000-4000-8000-000000000001","eventId":"e0000000-0000-4000-8000-000000000004","timestamp":"2026-10-01T09:00:01.150Z","userMessage":"fix the failing test","fileCount":1,"linesAdded":1,"linesRemoved":1,"backupFiles":[],"files":{"a1a1a1a1a1a1a1a1":{"path":"/srv/proj/date.test.js","linesAdded":1,"linesRemoved":1,"preimage":null,"postimage":null}}}]}
.copilot/sidebar-sessions-state/<sha256>.json, where <sha256> is
6b9af143446a411aefa3edb76e4693799bd7513e7ed8d919962d6713d3d3b972, the sha256 of /srv/proj:
{"schemaVersion":1,"cwd":"/srv/proj","sessionIds":["5e55a0a1-0000-4000-8000-000000000001"]}
.copilot/logs/process-1790845199000-4242.log:
2026-10-01T08:59:59.100Z [INFO] Starting Copilot CLI: 1.0.98
2026-10-01T08:59:59.400Z [INFO] Signed in to https://github.com as octo-user.
2026-10-01T08:59:59.900Z [INFO] Workspace initialized: 5e55a0a1-0000-4000-8000-000000000001
Noise the parser must not want: .copilot/config.json,
.copilot/session-state/5e55a0a1-0000-4000-8000-000000000001/rewind-file-snapshots/tracking.json
and .copilot/session-state/5e55a0a1-0000-4000-8000-000000000001/checkpoints/index.md.
Expected rows from events.jsonl: system session start (line 1, with
project_path /srv/proj and git_branch main); system model:
gpt-6.1-sol cause=initial_resolution source=automatic effort=medium
(line 2); system system prompt (line 3); user fix the failing test
(line 4); system utility model gpt-4o-mini: Fix failing test (line
10); thinking Run the tests first. with include_thinking (line 14);
tool_use bash call_01 text npm test (line 14); system
permission request: shell npm test intention=Run the test suite and
permission mode: manual agent=interactive (line 15); system
permission decision: approved-for-location source=human_response
location=/srv/proj approval={"commandIdentifiers":["npm test"],"kind":"commands"}
(line 16); tool_result call_01 text [exit 1] 1 failing and the
detail, then Command exited with code 1 (line 18); tool_use
apply_patch (line 21); the write request and permission decision:
approved source=human_response (lines 22, 23); tool_result (line 25);
assistant reply (line 28); system model: gpt-6.1-sol -> auto
source=model_picker (line 31); system session.info model: Model
changed from GitHub Copilot • GPT-6.1 Sol (medium) to GitHub Copilot •
Auto for this session and session.info model: Auto routing profile
change to Balance requested. (lines 32, 33, model auto); system
session end (line 34); the bad-line system row (line 35). Six secondary rows drop: the snapshot's
prompt pairs with line 4 (the nearest in time), and the turns row 0
prompt, which loses that one-to-one match, collapses onto the snapshot's
copy; the row 0 reply pairs with line 28; the sessions row's session
name: Fix failing test pairs with the workspace.yaml row; the history's
fix the failing test pairs with line 4, and its and run lint collapses
onto the turns row 1, the only other copy of a prompt the cut log lost.
Kept: turns row 1 (and run lint), the history's /model (an undated
user row, in no other source), file changed:
/srv/proj/date.test.js tool=apply_patch and rewind snapshot: 1 files +1
-1: /srv/proj/date.test.js.
MCP¶
The configured servers (mcp-config.json), their failed spawns (the process log's
MCP server connection failed lines) and the cached tool lists
(AppData/Local/copilot/mcp-tools/<sha256>.json) are recorded on disk (Windows install) and not yet
in the timeline (issue 150).
Confidence¶
- High, from the docs and the bundle with real installs on macOS and Linux (1.0.98) and Windows
(winget 1.0.98 and Copilot Desktop's bundled 1.0.99-2): the config home and
COPILOT_HOME, the directory names, the XDG migration list, the package cache locations (~/Library/Caches/copilot,~/.cache/copilot,AppData/Local/copilot), the log directory, the keyring and plaintext token model (the GNOME keyring on Linux, Windows Credential Manager on Windows), thesession-state/<id>/file set, theworkspace.yamlkeys (cwd incwd) and the sync keys a resume adds, the fifthconfig.jsonkey, thesession-store.dbschema and its SQL, thepermissions-config.jsonshape and the registry device id on Windows. - High, real install on Windows:
mcp-config.jsonholding a plain-text token under a server'senv, themcp-tools/<sha256>.jsonshape and the marketplace clone's size. - High, schema and real installs agree: the envelope, the observed event types and keys, the
permission result kinds, the
toolCallIdjoin, the history file's order, the WAL; the session-name generator and the task-analytics classifier as the three utility calls per session on macOS; theturnswrite pattern behind the repeated rows (complete row at the next prompt or at shutdown, prompt-only row mid-turn; Linux), thesession.infoshape and themodel_pickerchange; the subagent event types (observed on Windows, keys not recorded). - Medium:
state/ideagainstide/(every install showside/at the top level; the code joins through a "state" resolver);session.db,plan.mdandmcp-secrets/(bundle or docs, not observed); theconfig.jsontoken field name understoreTokenPlaintext(not observed); the unobserved event types, the denial and session-approval shapes,forge_trajectory_eventsand the snapshot's timing (bundle only). - Not determined: why
session-store.dbreportsschema_version8 on Linux and Windows and 38 on macOS; the Credential Manager target name; what themanaged-settings.<sha256>.fencefile holds; what the 12 utility calls of the winget session were for; why the store kept 3 of 6 turns; whetherpermission.completednestsdecisionSourceinresulton Windows; the legacy flat transcript's shape; the layout of an npm-installed CLI on Windows (issue 133).