VS Code Copilot Chat
- ID:
copilot-chat - Store: two, and a newer extension writes only the second. VS Code User folder
workspaceStorage/<hash>/chatSessions/<sessionId>.jsonl(flat.jsonon older builds), empty-window chats underglobalStorage/emptyWindowChatSessions/, and the extension's ownworkspaceStorage/<hash>/GitHub.copilot-chat/transcripts/<sessionId>.jsonl. A named profile keeps its own copy of both underprofiles/<id>/, and those are read too. Code, Code Insiders and VSCodium hosts. Not Copilot CLI (copilot). - Read override:
DEJA_COPILOT_CHAT_ROOTS(path list of User folders) - Format: two, one per store. The
chatSessionsfile is a JSONL mutation log (kind0 initial / 1 set / 2 push / 3 delete) or whole-file JSON; full re-parse per pass, because compaction rewrites the file and a byte-offset resume would apply deltas to state it never saw. TheGitHub.copilot-chat/transcriptsfile is atype-discriminated event log —session.start,user.message,assistant.message,assistant.turn_start/turn_end,tool.execution_start/complete— read byinternal/sources/copilot_agent.go.
On VS Code Server 1.137 there is no chatSessions directory at all: the machine in #3637 had 47 transcripts holding eleven weeks of chats and deja found zero files. On desktop 1.136.1 both layouts sit side by side — 28 chatSessions directories and five transcripts — so the second store is where a chat written by the newer extension goes, whatever the host. A transcript with no user.message in it is an agent run and is indexed: 11 of those 47 and all five here have none, and what the agent said and the files it touched are the only record of that work.
globalStorage/github.copilot-chat/session-store.db is a third store and is not read: every id in it also exists as a transcript, so it adds the session's cwd rather than history, and on the reporter's machine it covered the last two days against the transcripts' eleven weeks.
workspace.json beside the storage directory holds {"folder":"file:///…"} (or workspace for a multi-root .code-workspace); the project name is the folder's last two path segments, escapes decoded, as Claude Code names a directory (#4457). A folder on a UNC share keeps its host, so resume opens \\server\share\… (#4462). Empty-window sessions take workingDirectory the same way, or -. inputState is not read (it carries the GitHub account label).
User turns are message as a string or {text}. Assistant speech is bare {value} markdown chunks (and a plain string in old files); thinking and UI chrome (progressMessage, warning, info, systemNotification) are skipped. An inlineReference part is read into the reply as the name VS Code draws for it: the part's name, a symbol's name, or the file's base name (#4589). Agent mode writes an edit as an empty code fence around a codeblockUri and the textEditGroup; deja reads that as the file's name on its own line, where VS Code draws a pill naming it (#4590). Tool paths come from toolInvocationSerialized.resultDetails and inlineReference, and for copilot_readFile, which has no resultDetails, from the uris of its pastTenseMessage or invocationMessage (the other tools' uris there are directories, workspace-wide problem lists or the extension's own files); terminal commands from toolSpecificData.commandLine.
Edits are textEditGroup parts: a uri and a list of lists of {text, range}, where the text is what replaced the range. Only the written side is there — the range says where the old text was, not what it said — so the file and hashes of the written lines are recorded from it. Counted on one machine's store: 655 edit groups across 34 session files, which became 624 written records over 6 sessions and 23,141 hashed lines.
The replaced side is in the edit call itself. Agent mode stores each request's calls under result.metadata.toolCallRounds[].toolCalls[] as {name, arguments, id}, the arguments a JSON string, and each call's result under toolCallResults[id]: replace_string_in_file {filePath, oldString, newString}, multi_replace_string_in_file {replacements: [{filePath, oldString, newString}]}, create_file {filePath, content} and apply_patch {input} in codex's patch format. deja records the replaced span of each, and skips a call whose result opens with ERROR or Applying patch failed. The transcripts carry the same calls under tool.execution_start, with success on tool.execution_complete; there both sides come from the call, since no textEditGroup is written. insert_edit_into_file {explanation, filePath, code} sends only the changed stretches, with ...existing code... for the rest, so in the transcripts it gives the written side with those markers dropped and no replaced span (#595). On the same store: 352 replace_string_in_file, 190 apply_patch, 19 multi_replace_string_in_file and 118 create_file calls, which became 638 replaced spans over 6 sessions (#595).
- MCP:
deja install vscodewrites the usermcp.jsonVS Code reads in agent mode (top-levelservers,type: stdio). Verified on VS Code 1.134.0: Copilot Chat started the server and called thedejatool withmode: recall. - Skill: the same install writes
<User>/prompts/deja.instructions.mdwithapplyTo: "**", VS Code's custom instructions, which are in front of the model on every chat. Verified on 1.134.0: with that file and no mention of deja in the question, Copilot Chat calledrecallon its own. - Command: a prompt file.
deja install vscodewrites
<User>/prompts/deja.prompt.md for every host it finds — the same User
directory this reader walks for workspaceStorage — and the chat box lists it
as /deja. The frontmatter carries the description shown beside it and an
argument hint, and the body tells the model to call the recall tool, with the
CLI as the fallback when the tool is not in that window.
- Auto-recall:
deja install vscode-auto(orcopilot-auto; both write the
same file) puts deja's hooks in ~/.copilot/hooks/deja.json. VS Code 1.140
lists that directory among its hook locations, chat.useHooks is on by
default, and Copilot Chat 0.68 runs SessionStart, UserPromptSubmit,
PreToolUse, PostToolUse and PreCompact. It maps the camelCase events
it shares with Copilot CLI (sessionStart, userPromptSubmitted,
preToolUse, postToolUse) and reads PreCompact under that name, so one
file serves both hosts. The payload carries hook_event_name, session_id
and transcript_path (the GitHub.copilot-chat/transcripts file above), and
deja answers in the nested hookSpecificOutput shape, the only one VS Code
reads for SessionStart, PreToolUse and PostToolUse. VS Code drops
matcher, so the pre-tool hook runs for every tool and speaks for
replace_string_in_file, create_file, insert_edit_into_file and
run_in_terminal; a failed terminal command reaches PostToolUse with
Command exited with code N in the result text. PreCompact output is
ignored by VS Code; deja captures the transcript there and hands the packet
back on the next prompt or tool call. Copilot Chat never runs a
SessionEnd hook (no call site in 0.68), so the live stamp expires on its
own. Checked against the workbench and extension source; not yet run
against a live VS Code.
- Resume: Chat: Show Chats… in the editor, not a command. The list holds only the open workspace’s chats;
deja resume <id>names the folder to open first. - Handoff: exec,
code chat -m agent <prompt>opens VS Code's chat in agent mode with it.
Last verified: 2026-10-07
deja reads this format and 42 others, and turns what it finds into memory your agents can search. See the harness matrix for what is wired where, or install it and search your own history.
Found this useful? Star deja-vu on GitHub.