首页
/ VS Code Copilot Chronicle 技能实战:用 SQL 查询会话历史,实现 Standup、成本分析与会话检索

VS Code Copilot Chronicle 技能实战:用 SQL 查询会话历史,实现 Standup、成本分析与会话检索

2026-09-06 20:03:02作者:傅爽业Veleda

本文围绕 VS Code Copilot 扩展内置的 Chronicle(编年史)技能展开,它是 Copilot Agent 用于分析用户全部 Copilot 会话历史的能力模块。读完本文,你将掌握 Chronicle 的启用配置、copilot_sessionStoreSql 工具的两种动作(query / reindex)、本地 SQLite 与云端 DuckDB 双后端的 Schema 与 SQL 方言差异,以及 Standup 日报、使用技巧(Tips)、成本优化建议(Cost Tips)、指令改进(Improve)、会话检索(Search)、重建索引(Reindex)六大工作流的完整操作规范,并理解该工具在源码层面的安全限制与查询路由实现。

Chronicle 是什么:从技能定位到工具依赖

Chronicle 是一个定义在 SKILL.md 中的 Copilot 技能。它的职责是:基于用户的 Copilot 会话历史完成四类任务——生成站会(standup)报告、提供使用/成本优化建议、按关键词/文件/PR 检索历史会话、以及维护(重建)本地会话索引。其触发场景在技能 frontmatter 中写得很明确:当用户请求 standup、每日总结、使用技巧、工作流建议、想按关键词/文件/PR 查找历史会话、想重建会话索引,或询问删除会话数据时,应使用本技能。

整个技能的技术底座是 copilot_sessionStoreSql 工具。从源码看,该工具的注册名与参数定义位于 sessionStoreSqlTool.tstoolNames.ts(其中 SessionStoreSql = 'copilot_sessionStoreSql'),并且它被归类为核心工具(ToolCategory.Core)。在 Agent 意图中,agentIntent.ts 明确将 ToolName.SessionStoreSql 加入允许工具列表,说明它主要服务于 Agent 模式的对话执行。

会话的存储形态是双重的:

  • 本地:SQLite 数据库,只包含当前设备的会话;
  • 云端(可选):开启 chat.sessionSync.enabled 后同步到云端,供跨设备、跨 Agent 访问。

前置条件:Chronicle 要求 github.copilot.chat.localIndex.enabled 设置为 true。若 copilot_sessionStoreSql 工具不可用,应提示用户在 VS Code 设置中开启该选项。该设置在 package.json 中声明,本地化描述为 “Enable local session tracking. When enabled, session data is tracked locally for /chronicle commands.”

工具动作与调用参数

copilot_sessionStoreSql 工具支持两种动作:

Action 用途 query 参数
query 执行只读 SQL 查询 必填
reindex 重建本地会话索引 + 云同步 不需要

sessionStoreSqlTool.ts 的参数接口可以看到完整的输入面:

export interface SessionStoreSqlParams {
    readonly action?: 'query' | 'reindex';
    readonly query?: string;
    readonly force?: boolean;   // 仅 reindex:强制重新处理已索引的会话
    readonly description: string;
    // 发起该调用的 /chronicle 斜杠命令(standup/tips/cost-tips/search/improve/reindex),仅用于遥测归因
    readonly subcommand?: 'standup' | 'tips' | 'cost-tips' | 'search' | 'improve' | 'reindex';
}

几个对实际使用有影响的源码级细节:

  • 行数上限MAX_ROWS = 100,超过即截断并在结果中标注 [TRUNCATED],这与技能文档中 “Always use LIMIT (max 100)” 的查询规范相互印证;
  • 输出预算:查询结果以 Markdown 表格返回,总字符量受 30,000 的硬预算约束,超出会被整体截断——所以查询应优先聚合(COUNTGROUP BY)而非原始行转储;
  • reindex 的输出:工具会返回 Before / After / Delta 的统计表(Sessions、Turns、Files、Refs 四项),并追加 “N session(s) processed, M skipped”;若云同步开启,还会触发 github.copilot.sessionSync.reindex 命令将新会话上传云端(该阶段失败不影响本地重建结果)。

查询路由:本地 SQLite 还是云端 DuckDB?

技能文档中反复强调 “Always follow the SQL syntax shown in the tool description”,这句话在源码中有精确的对应实现:工具类实现了 alternativeDefinition() 方法——当云同步开启时,工具动态替换自己暴露给模型的描述与输入 Schema 为 DuckDB 方言版本(CLOUD_MODEL_DESCRIPTION 明确写着 “Use now() - INTERVAL '1 day'ILIKE for text search (no FTS5/MATCH)”)。

路由决策由 sessionIndexingPreference.ts 中的 SessionIndexingPreference 类完成,它读取两个设置:

  • chat.localIndex.enabled:启用本地 SQLite 跟踪与 /chronicle 命令;
  • chat.sessionSync.enabled:启用云端上传,并支持 chat.sessionSync.excludeRepositories 按 picomatch 模式排除特定仓库。

由此得到的路由规则与技能文档一致:

  • 云同步开启:查询路由到云端 DuckDB 后端,覆盖所有设备与 Agent(VS Code、CLI、Copilot Coding Agent、PR reviews)的会话;
  • 云同步关闭:查询路由到本地 SQLite,仅含本机会话;
  • 容错降级:云端调用若发生鉴权/网络失败(CloudSessionStoreClient 返回空值),工具自动回落到本地执行(遥测中记录为 local_fallback)。

数据库 Schema:两张后端共有的表 + 云端专属表

本地与云端共有表

关键列 说明
sessions id, cwd, repository, branch, host_type, summary, agent_name, agent_description, created_at, updated_at 会话主表;id 是主键(不是 session_id);云端中 cwd 恒为 NULL
turns session_id, turn_index, user_message, assistant_response, timestamp 对话轮次;assistant_response 只有开头约 1000 字符,可能被截断
checkpoints session_id, checkpoint_number, title, overview, history, work_done, technical_details, important_files, next_steps, created_at 压缩(compaction)检查点,存储会话的摘要状态;云端列更少(无 history / work_done / technical_details)
session_files session_id, file_path, tool_name, turn_index, first_seen_at 会话中触碰的文件与工具
session_refs session_id, ref_type (commit/pr/issue), ref_value, turn_index, created_at PR / Issue / Commit 引用
search_index content, session_id, source_type, source_id 仅本地的 FTS5 虚拟表,用于全文检索

本地 SQLite 的建表 DDL 可以在 sessionStore.ts 中逐列对上号:sessionsid TEXT PRIMARY KEY 为主键,turnsUNIQUE(session_id, turn_index) 约束,session_filessession_refs 均带外键指向 sessions(id)。该文件还展示了 Schema 的演进方式——schema_version 表记录版本,v1→v2 通过 ALTER TABLE 增加 host_type 列,v1→v3 增加 agent_name / agent_description 列,与技能文档中 “checkpoints 云端列更少” 等差异描述一致。search_index 则是受保护的 FTS5 虚拟表(content, session_id UNINDEXED, source_type UNINDEXED, source_id UNINDEXED)。

云端专属表(DuckDB)

  • events:原始事件表(约 90 列)。关键列:session_idtimestamptypeuser_contentassistant_contenttool_start_nametool_complete_successtool_complete_result_contentusage_modelusage_input_tokensusage_output_tokens注意:本地 SQLite 没有 events 表,也不记录逐事件 token 用量——本地后端下无法做真正的 token 级分析;
  • tool_requestssession_idtool_call_idnamearguments_json

SQL 方言速查

需求 本地 SQLite 云端 DuckDB
近 1 天窗口 datetime('now', '-1 day') now() - INTERVAL '1 day'
近 7 天窗口 datetime('now', '-7 days') now() - INTERVAL '7 days'
文本检索 FTS5:WHERE search_index MATCH '<query>' ILIKE '%<query>%'(无 FTS5/MATCH)
时长计算 date_diff('minute', start, end)

工作流一:Standup(站会日报)

当用户请求 standup、每日总结或 “what did I do”(如 /chronicle standup)时,按三步执行:

Step 1:收集近 24 小时的活动

action: "query" 查询 sessions 表中 updated_at 落在最近 24 小时内的行,按 updated_at 降序。近期窗口谓词按后端区分:

  • 本地 SQLite:WHERE updated_at >= datetime('now', '-1 day')
  • 云端 DuckDB:WHERE updated_at >= now() - INTERVAL '1 day'

随后对命中的 session id 从 session_refs 拉取关联引用(PR、issue、commit)。需要某个会话的更多细节时,再深入查询 turns(必要时加 session_files,云端可加 checkpoints)——不要一上来就把所有会话的每一轮对话都拉出来

若近 24 小时没有任何会话,如实告知用户没有近期活动,建议加大时间窗口或执行 /chronicle reindex 后停止。不要编造 standup 内容。

Step 2:纳入无 PR 的工作

每一个近期会话都是候选工作项,即使它没有任何 PR / issue / commit 引用。PR 只是佐证而非事实来源。不能仅因缺少 PR 就省略某个会话或分支——应依据会话摘要与轮次内容判断收录。

Step 3:核对 PR 状态并格式化

对找到的 PR 引用,用 GitHub CLI 或 MCP 工具核对当前状态(open / merged / draft / closed)。每个工作项要么给一行 PR 状态,要么写 “No PR found”——绝不虚构 PR。

按工作流(分支/特性)分组输出,结构固定为:

Standup for <date>:

**✅ Done**

**Feature name** (`branch-name` branch, `repo-name`)
  - 3-7 words describing the status
  - Key files: 2-3 most important files changed
  - Merged: [#123](https://github.com/owner/repo/pull/123) or No PR found
  - Session: `full-session-id`

**🚧 In Progress**

**Feature name** (`branch-name` branch, `repo-name`)
  - 3-7 words describing the current state of work
  - Key files: 2-3 most important files being worked on
  - Draft: [#789](https://github.com/owner/repo/pull/789) or No PR found
  - Session: `full-session-id`

(上例中的 GitHub URL 为文档规定的 Markdown 链接占位格式,实际输出时应替换为对应 PR 的真实链接。)

格式规则:

  • 保持简洁——用户随时可以追问细节;
  • turns 数据(用户消息助手回复)理解 “做了什么”;
  • session_files 中的文件路径判断影响了哪些组件/区域;
  • 同一分支的多个相关会话合并为一条;每个特性/分支只展示最近一个会话;
  • PR 与 issue 用 Markdown 链接语法;
  • 工作看起来已完成则归入 Done,否则归入 In Progress;
  • 没有分支/仓库信息的会话归入 “Other” 小节。

工作流二:Tips(使用技巧与改进建议)

Step 1:调研用户的工作方式

action: "query" 探索近期会话。目标是理解模式:如何写 prompt、用哪些工具、时间花在哪里。直接开始查询,不要先解释要做什么。建议查询:

  • 最近 7 天会话:数量、时长、所在仓库;
  • turns:读取真实用户消息,理解 prompt 模式;
  • session_files:哪些文件/工具使用最频繁;
  • session_refs:PR / issue / commit 活动模式。

Step 2:盘点可用能力

若当前工作区有 .github/ 目录,检查 .github/copilot-instructions.md.github/skills/.github/agents/ 中已有哪些自定义配置。不要查看工作区之外的东西。找出 “已有能力” 与 “用户实际使用” 之间的差距。

Step 3:给出 3-5 条具体可执行的建议

每条建议须:基于真实使用数据(引用观察到的具体模式);非显而易见(跳过普通用户已知的入门功能);聚焦于能通过某个功能、工作流变化或不同方法带来实质性改进的缺口。

可探索的分析维度:

  • Prompt 模式:用户消息是含糊还是具体?是否提供上下文?是否频繁纠正/重定向 Agent?
  • 工具使用:哪些工具用得最多?有哪些被低估的工具本可派上用场?
  • 会话模式:会话多长?是否存在大量短命即弃的会话?
  • 文件模式:代码库哪些区域被关注最多?同一批文件是否被反复编辑?
  • 工作流:用户是否在用 Agent 模式、自定义指令、prompt 文件、skills?

若会话数据很少,如实说明,并基于工作区中找到的配置给出可尝试的功能建议。推荐自定义 skill / agent / instructions 作为建议时,应参考 agent-customization 技能中规范的建文件模式——不能只给 “去创建一个自定义 skill” 这种没有文件结构指导的空泛建议。

工作流三:Cost Tips(Token 成本优化建议)

当用户请求成本建议、减少 token 用量的方法(如 /chronicle cost-tips)时,目标是个性化的、以数据为据的推荐,而不是通用清单。每条 tip 必须指向你在数据中观察到的具体模式。

作用域:聚焦 VS Code 交互式聊天

其他 Agent 面(Copilot CLI、Copilot Coding Agent、Copilot Code Review、自定义 agent/subagent)的成本画像差异很大,混在一起会扭曲分析。默认应把每条查询过滤到交互式 VS Code 聊天;仅当用户明确要求分析 CLI / Coding Agent / 自定义 agent 时才扩大范围,且要为每种 agent 类型单独跑查询,不要混在一次分析里。

存储中的 agent_name 值因后端而异,必须精确匹配(大小写与空格都敏感):

  • 云端(DuckDB):sessions.agent_name = 'VS Code Chat'
  • 本地(SQLite):sessions.agent_name = 'GitHub Copilot Chat'。本地还会把 subagent 调用(如 ExploresummarizeConversationHistory)记录为独立会话行,默认过滤会正确排除它们。

先跑一次 agent 构成检查(例如 SELECT agent_name, COUNT(*) AS n FROM sessions WHERE updated_at > <30-day cutoff> GROUP BY 1 ORDER BY n DESC)确认被排除的是什么。如果交互式聊天只占用户会话的小头,要在总结中说明建议只覆盖了活动的一个切片,并主动提出可以对另一种 agent 类型单独跑一轮分析——点名你看到的候选值(如 “要不要对 Copilot CLICopilot Coding Agent 单独跑一遍?”)。用户要求扩大范围时,把默认 agent_name 过滤换成对应值,只对该切片做分析,并在总结中注明范围、以及当前后端无法分析的内容(例如本地后端下云端专属的 token 列)。

成本相关的 Schema 要点

  • 仅云端 DuckDB 有逐事件计费数据——本地 SQLite 不记录逐事件 token 用量,也没有 events 表。若当前后端是本地,应门控所有 token 查询,并告知用户真正的 token 级分析需要开启 chat.sessionSync.enabled
  • events(云端)type = 'assistant.usage' 的行携带 usage_input_tokensusage_output_tokensusage_model。连接方式为 JOIN events esessions s ON s.id = e.session_id,并过滤 WHERE s.agent_name = 'VS Code Chat' 保持作用域收紧;
  • turns:用 LENGTH(user_message)(云端则用 eventstype = 'user.message' 行的 LENGTH(user_content))定位超大粘贴内容。

Step 1:调研成本与 token 模式

云端(DuckDB)——深挖成本模式events 行按 type = 'assistant.usage' 过滤,JOIN sessions 保持 agent_name = 'VS Code Chat'):

  • Token 大户会话与轮次:按会话、按模型对 eventstype = 'assistant.usage')的 usage_input_tokens / usage_output_tokens 求和。哪些会话烧 token 最多?用的哪些模型?
  • 输入/输出比:输入 token 远大于输出时,说明用户每轮都在为重发臃肿上下文付费——这是 compaction、缩小工作集或开新会话最能帮到的强信号;
  • 模型构成:按 usage_model 拆解花费。昂贵模型是否被用于改名、简单编辑、状态确认等常规工作?
  • 逐轮增长:长会话中 usage_input_tokens 是否逐轮攀升?是未使用 compaction 的强信号;
  • 超大粘贴eventstype = 'user.message')上的 LENGTH(user_content) 找出本应是文件引用的用户消息(在 session_files 中也表现为同一会话内对同一路径的重复读取)。

本地(SQLite)——无 token 数据,用代理指标(每条查询都过滤 sessions.agent_name = 'GitHub Copilot Chat'):

  • 无 compaction 的长会话:轮次很多且 checkpoints 中没有行的会话(每个 checkpoint 行代表一次成功的压缩)。LEFT JOIN checkpoints c ON c.session_id = s.id WHERE c.session_id IS NULL 加轮次阈值即可筛出候选;
  • 压缩过晚:对有 checkpoint 的会话,对比 checkpoints.checkpoint_numbercreated_at 和会话总轮次。80 轮会话在第 60 轮才首次压缩,远不如第 25 轮压缩有效;
  • 重复读取大文件:在 session_files 中找同一会话内(或跨会话)被反复读取的同一文件;
  • 工具调用抖动:轮次很多且工具调用重复的会话,往往意味着 Agent 多次重新发现同样的上下文;
  • 超大粘贴turns 上的 LENGTH(user_message) 找出本应是文件引用的超长用户消息。

两种后端通用

  • 长跑会话:轮次多或跨越多小时的会话,会把不断增长的上下文窗口拖过每一轮;
  • 重复劳动:同一文件/主题出现在很多会话中,或同一类 Agent 绊脚石反复出现——提示用自定义 skill、agent 或 copilot-instructions.md 条目让模型一次做完;
  • subagent 使用:重量级调查是否在主会话里进行(其 token 计入主上下文),本可以委托给只返回摘要的 subagent?

对最贵的几个会话深挖,读真实对话轮次理解为什么贵——不要只报聚合数字,要解释成因。

Step 2:把发现映射到功能与习惯

若工作区有 .github/,检查 .github/copilot-instructions.md.github/skills/.github/agents/ 中已有什么。不要看工作区之外。与成本相关的能力清单:

  • 会话中 compaction(如 /compact)缩小上下文窗口;对从不压缩的用户,这往往是最单一的最大收益;
  • 模型选择器——常规工作换更便宜的模型;
  • 开新聊天而非续接臃肿会话;
  • subagent/委托,把重研究移到 token 不会累积进主会话的子上下文;
  • 自定义 skills(.github/skills/)与自定义 agents(.github/agents/),避免重复工作流每次重新推导上下文;
  • .github/copilot-instructions.md,把模型否则每次都要被告知的项目约定固化下来;
  • 云同步用户可查 Copilot 用量视图检查 premium 请求花费。

Step 3:给出建议

给 3-5 条具体可执行的 tip,每条要求:

  • 扎根数据——引用观察到的具体会话、文件、模型或模式(有数字就给近似值:轮次数、token 总量、文件读取次数);
  • 非显而易见——假设用户知道 compaction 和新聊天存在,帮他意识到自己没在该用的地方用;
  • 能量化就量化——“在那个 80 轮会话的第 30 轮附近 compact,能砍掉之后每轮约 X 个输入 token” 远好于 “考虑 compact 一下”;
  • 具体——点名工作流变化、命令或配置文件修改;若是建议自定义 skill/agent,勾勒它应覆盖什么;
  • 留在 VS Code 聊天范围内——除非用户明确扩大范围,不要提 CLI / Coding Agent 专属的改动。

数据很少时(如云端存储为空,或本地只有寥寥几个交互式聊天会话),直说,并给出 2-3 条锚定在可用功能上的、非显而易见的省钱习惯,而不是编造发现。用户仅本地存储时,结尾注明开启 chat.sessionSync.enabled 能解锁逐事件 token 分析,让后续建议更锋利。

工作流四:Improve(基于历史改进 Agent 指令)

当用户请求基于会话历史改进 Agent 指令(如 /chronicle improve):

Step 1:读取当前指令文件

读取项目使用的指令文件(.github/copilot-instructions.mdAGENTS.md)。若文件不存在,则需要创建它——此时先分析代码库(参考 init 技能的代码库探索方法),再把该分析与 Step 2 的会话历史发现结合,产出一份全面的指令文件。

Step 2:调研会话历史

copilot_sessionStoreSql 探索,所有查询限定到当前仓库/工作目录的会话。先拿到本仓库近期会话总览,再深入。你在找的是摩擦——Agent 理解错误或用户被迫纠偏的信号:

  • 纠正/重定向的用户消息:读可疑会话的真实轮次,找用户受挫的区域;
  • 开发循环挣扎:Agent 是否在测试、lint、构建、类型检查上栽跟头?找反复失败的命令、测试重试、需要多次尝试的构建错误;
  • 跨会话模式:同类错误是否反复出现?

自己判断该跑什么查询,看到有趣的东西就钻进去逐轮读对话。

Step 3:呈现建议

呈现前先参考 agent-customization 技能中的文件约定、内容原则(链接而非内嵌、最小化、简洁)与反模式——这决定了建议的写法。基于发现简洁地呈现 3-5 条建议,每条既说明发现的问题,也说明自定义指令如何化解。聚焦项目特有模式而非泛泛而谈,只建议数据中发生过不止一次的真实问题对应的指令。

呈现完所有建议后,询问用户想应用哪些,然后只对现有那一个指令文件做被批准的编辑(若不存在则创建 AGENTS.md)。

工作流五:Search(会话检索)

当用户要求按关键词搜索/查找历史会话(如 /chronicle search <query>):

检索策略

  1. 跨会话摘要、对话轮次(用户消息助手回复)、以及其他索引内容(checkpoints、文件路径、PR/issue/commit 引用)搜索。用户查询可能命中主题、文件路径或 PR/issue 编号——三类都要覆盖;
  2. 对每个命中会话,收集足够的元数据用于标注:s.ids.repositorys.branchs.summarys.updated_at,外加一个展示为何命中的短片段(如云端的 substr(user_message, 1, 160),或命中的 file_path / ref_value);
  3. 按仓库分组呈现,按最近更新时间降序。

写查询

调用 copilot_sessionStoreSqlaction: "query"description: "Search sessions for <query>",遵循工具描述中展示的方言(SQLite vs DuckDB)。写查询前必须重读 Schema 章节,要点:

  • sessions 表主键是 id不是 session_id);其他所有表用 session_id 外键指回 sessions.id。始终从 sessions 投影 s.id
  • 不要臆造列名:没有 started_at(用 created_at/updated_at)、没有 workspace(本地用 cwd,云端没有)、没有 title(用 summary)、没有 content/messages(用 turns.user_message/turns.assistant_response,云端用 events.user_content/events.assistant_content);
  • 本地 SQLite:正文内容优先用 FTS5 的 search_index 表(WHERE search_index MATCH '<query>')。search_index 自带 session_id 列,直接选择即可(SELECT session_id, content FROM search_index WHERE search_index MATCH ...)。不要search_index.rowid JOIN 到 turns.rowid——两者无关,会拉进不相干的会话。文件路径和引用不在 FTS 索引里——配合 session_files.file_pathsession_refs.ref_value 上的 LIKE 一起查。这一点与 sessionStore.ts 中 FTS5 建表语句(content, session_id UNINDEXED, source_type UNINDEXED, source_id UNINDEXED)完全一致;
  • 云端 DuckDB:无 FTS5——对 sessionsturnscheckpointssession_filessession_refs 的文本列用 ILIKE '%<query>%'

单引号用双写转义(it'sit''s);FTS5 多词查询整体加引号:MATCH '"apply patch"'

性能——避免云端超时

turns 上的 ILIKE '%X%' 是全表扫描,云端查询跑得过长会返回 context deadline exceeded。为守住预算:

  • 两步模式(推荐):先在 CTE 中用窄时间窗口从 turns 收集命中的 session_idGROUP BY session_id,再用 WHERE id IN (...)sessions 补全元数据。避免在大扫描结果上昂贵 JOIN 导致超时;
  • GROUP BY session_idany_value() / MIN() / array_agg() 聚合每会话命中信息,而非标量子查询或相关子查询;
  • 云端默认给重表(turnsWHERE timestamp >= now() - INTERVAL '7 days'checkpointscreated_at 同理)加 7 天窗口。查不到就渐进放宽:7 天 → 30 天 → 90 天。在总结行中注明窗口,让用户知道范围是有界的;
  • 最终 SELECT 保持 LIMIT 50
  • 查询超时时,收窄窗口(而非放宽)或去掉最重的那张表(通常是 turns),并告知用户你裁掉了什么。不要用相同窗口重试——永远先缩小范围。

输出格式

每个会话用 summary 构建单行标签(无 summary 时用返回的 snippet,截断至约 80 字符)。绝不输出 (no summary)(no metadata) 或裸 session-id 列表。若使用了时间窗口(如云端默认 7 天),把它写进标题;否则省略范围短语或写 “all time”:

**Search results for "<query>"** (<n> sessions, <scope: e.g. "last 7 days" / "all time">)

_owner/repo_
- `session-id` — **<summary or snippet>**
  `branch` · updated <relative time> · matched in <match_kind>
- `session-id` — **<summary or snippet>**
  `branch` · updated <relative time> · matched in <match_kind>

_other-owner/other-repo_
- ...

规则:

  • 每行一个会话——绝不把多个 session id 用逗号串在一起;
  • 标签优先 summary(非空时),否则用 snippet,否则用命中的文件路径等可用兜底;这些都为空就跳过该会话,而不是显示 “no summary”;
  • 能给出 · matched in <match_kind>(turn / file / ref / checkpoint / meta)就给出——帮助用户看到每个会话为何命中;
  • 按仓库分组;repository 为 NULL 的会话归入 Other 标题,格式相同;
  • 每个仓库可见结果上限约 10 条;更多则追加 …and N more (refine your query)

无结果时

告知用户没有命中,并建议:换不同关键词或更宽的检索词(单个词、子串而非整句);放宽时间窗口(“搜全部时间”、“包含更早会话”);尚未建索引就跑 /chronicle reindex;或跑 /chronicle standup 看近期活动。

工作流六:Reindex 与 Delete Sessions(维护操作)

Reindex:用户要求重建/刷新会话存储时:

  1. 调用 copilot_sessionStoreSqlaction: "reindex"description: "Reindex sessions"
  2. 工具从 debug log 重建本地会话存储,若云同步开启则把新会话上传云端;
  3. 把重建前后的统计与云同步结果呈现给用户。

源码上对应 sessionStoreSqlTool.ts_invokeReindex:先取 getStats() 作为 Before 快照,调用 reindexSessions(实现位于 sessionReindexer.ts),再取 After 快照生成 Before / After / Delta 表格,最后尝试执行 github.copilot.sessionSync.reindex 完成云端补传。若用户说 “force reindex” 或想重新处理已索引会话,给调用加 force: true——默认情况下已索引的会话会被跳过以提速。

Delete Sessions:用户要求删除会话数据或清空历史时:

  • 引导其在命令面板中执行 Delete Session Sync Data 命令(github.copilot.sessionSync.deleteSessions);
  • 该命令让用户选择要删除的会话,同时作用于本地与云端;
  • 工具本身不支持删除——这是有意设计,防止误删数据。

查询规范与工具安全边界

使用 action: "query" 时的规范(技能文档 + 源码实现双重印证):

  • 每次调用只能一条查询——不要用分号串联多条语句;
  • 只允许 SELECTWITH(CTE)。DESCRIBESHOWPRAGMA 及一切变更语句都被拦截——不要试图自省数据库,重读本文 Schema 章节即可;
  • 始终带 LIMIT(最大 100),优先聚合(COUNTGROUP BY)而非原始行转储;
  • 查对话内容用 turns 表——它对 “发生了什么” 的洞察最丰富;
  • 查文件路径与工具使用模式用 session_files;查 PR/issue/commit 链接用 session_refs;用 session_id JOIN 各表做完整分析;
  • 时间范围始终过滤 updated_at(而非 created_at);
  • 分析会话内容时始终 JOIN sessionsturns——不要只依赖 sessions.summary

这些限制不是纸面约定,而是写死在工具里的。sessionStoreSqlTool.ts 中定义了 BLOCKED_PATTERNS 黑名单(拦截 INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/TRUNCATE/REPLACEATTACH/DETACHPRAGMAVACUUMREINDEXANALYZELOAD_EXTENSION,以及事务控制语句),随后还做白名单校验:剥离前导注释后,SQL 必须以 SELECTWITH 开头,且整句不允许出现分号。被拦截的查询会以明确错误信息返回并计入遥测。本地执行最终走 sessionStore.executeReadOnly(sql),在引擎层再通过 authorizer 做只读强制。

另外值得注意的实现细节:模型常常在查询末尾追加分号,工具会先 trim 并去掉尾部分号再进入校验;结果表格的单元格长度按 “总预算 30,000 字符 ÷ 总单元格数” 自适应分配,防止单条超长文本撑爆上下文窗口。

小结

Chronicle 把 “Copilot 用过什么、改过什么、花了多少 token” 从黑盒变成可查询的 SQL 问题:本地 SQLite 提供 FTS5 全文检索与轻量分析,云端 DuckDB 提供跨设备会话与逐事件 token 计费数据,二者通过 chat.sessionSync.enabled 与工具的动态描述切换无缝切换。六大工作流(standup / tips / cost-tips / improve / search / reindex)各有明确的输入、查询策略与输出格式约束,而 copilot_sessionStoreSql 工具的只读校验、行数上限与输出预算保证了模型可以安全地自由写 SQL。要开始使用,先确认 github.copilot.chat.localIndex.enabled 已开启,然后直接在聊天中请求 /chronicle standup 或 “搜索我上周改认证逻辑的会话” 即可。

工具行为的更多细节可参考测试文件 sessionStoreSqlTool.spec.ts,它覆盖了工具注册、modelDescription 动态切换与查询执行的断言;技能原文位于 chronicle/SKILL.md,同目录下的 agent-customizationinit 技能是 Tips / Improve / Cost Tips 工作流的配套依赖。

登录后查看全文
热门项目推荐
相关项目推荐