@getbourdon/participants 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/LICENSE_FAQ.md +1 -1
- package/dist/base.d.ts +109 -0
- package/dist/base.js +88 -0
- package/dist/index.cjs +12 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +42 -1083
- package/dist/index.js +53 -6590
- package/dist/index.js.map +1 -1
- package/dist/network-base.d.ts +108 -0
- package/dist/network-base.js +248 -0
- package/dist/readers/cascade.d.ts +49 -0
- package/dist/readers/cascade.js +428 -0
- package/dist/readers/claude-code-automations.d.ts +62 -0
- package/dist/readers/claude-code-automations.js +693 -0
- package/dist/readers/claude-code.d.ts +50 -0
- package/dist/readers/claude-code.js +590 -0
- package/dist/readers/claude-desktop-code.d.ts +54 -0
- package/dist/readers/claude-desktop-code.js +526 -0
- package/dist/readers/claude-desktop-cowork.d.ts +47 -0
- package/dist/readers/claude-desktop-cowork.js +659 -0
- package/dist/readers/codex-automations.d.ts +55 -0
- package/dist/readers/codex-automations.js +519 -0
- package/dist/readers/codex-sessions.d.ts +88 -0
- package/dist/readers/codex-sessions.js +194 -0
- package/dist/readers/copilot-automations.d.ts +64 -0
- package/dist/readers/copilot-automations.js +530 -0
- package/dist/readers/copilot-cli.d.ts +45 -0
- package/dist/readers/copilot-cli.js +416 -0
- package/dist/readers/copilot-vscode.d.ts +54 -0
- package/dist/readers/copilot-vscode.js +553 -0
- package/dist/readers/copilot.d.ts +59 -0
- package/dist/readers/copilot.js +413 -0
- package/dist/readers/cursor-automations.d.ts +57 -0
- package/dist/readers/cursor-automations.js +685 -0
- package/dist/readers/cursor.d.ts +50 -0
- package/dist/readers/cursor.js +670 -0
- package/dist/readers/github-copilot.d.ts +34 -0
- package/dist/readers/github-copilot.js +242 -0
- package/dist/readers/hermes.d.ts +37 -0
- package/dist/readers/hermes.js +426 -0
- package/dist/readers/openclaw.d.ts +78 -0
- package/dist/readers/openclaw.js +386 -0
- package/dist/registry.d.ts +39 -0
- package/dist/registry.js +98 -0
- package/dist/sqlite-base.d.ts +65 -0
- package/dist/sqlite-base.js +163 -0
- package/package.json +6 -6
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot CLI participant — normalize the `copilot` terminal agent's
|
|
3
|
+
* SQLite session store into L5. Port of `participants/copilot_cli.py`.
|
|
4
|
+
*
|
|
5
|
+
* Copilot CLI stores session history in a SQLite database at
|
|
6
|
+
* `~/.copilot/session-store.db` (override `$COPILOT_CLI_HOME`). This is distinct
|
|
7
|
+
* from:
|
|
8
|
+
* - the VS Code Copilot Chat extension (`github-copilot` network reader)
|
|
9
|
+
* - the `~/.copilot-bourdon/memory.md` convention-file participant (`copilot`)
|
|
10
|
+
* - GitHub-embedded Copilot (no local state)
|
|
11
|
+
*
|
|
12
|
+
* The schema (schema_version 4) carries `sessions` / `turns` / `checkpoints`
|
|
13
|
+
* plus `session_files` / `session_refs` / `dynamic_context_items`. The reader
|
|
14
|
+
* opens the DB read-only (`mode=ro` via {@link tryOpenReadonly}) so a live
|
|
15
|
+
* writer's lock can never block the export, probes each table defensively (a
|
|
16
|
+
* missing table degrades to "no rows" rather than raising), redacts every native
|
|
17
|
+
* string, applies the visibility filter BEFORE emission, and is deterministic.
|
|
18
|
+
* `healthCheck` never throws.
|
|
19
|
+
*/
|
|
20
|
+
import { statSync } from "node:fs";
|
|
21
|
+
import { homedir, hostname } from "node:os";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
import { Visibility, filterForFederation, makeAgentInfo, makeEntity, makeManifest, makeSession, makeVisibilityPolicy, } from "@getbourdon/l5";
|
|
24
|
+
import { redactText } from "@getbourdon/redaction";
|
|
25
|
+
import { ParticipantDiscoveryError, SPEC_VERSION, } from "../base.js";
|
|
26
|
+
import { tryOpenReadonly } from "../sqlite-base.js";
|
|
27
|
+
const AGENT_ID = "copilot-cli";
|
|
28
|
+
const AGENT_TYPE = "code-assistant";
|
|
29
|
+
const DISPLAY_NAME = "GitHub Copilot CLI";
|
|
30
|
+
const ROLE_NARRATIVE = "Terminal-native Copilot agent with full filesystem + tool access. " +
|
|
31
|
+
"Runs multi-turn sessions with checkpoints, file edits, and git " +
|
|
32
|
+
"operations. The CLI surface has the deepest tool integration of " +
|
|
33
|
+
"all Copilot surfaces — shell, grep, edit, git — and persists " +
|
|
34
|
+
"rich session history locally in SQLite.";
|
|
35
|
+
const DEFAULT_POLICY = makeVisibilityPolicy({
|
|
36
|
+
default: Visibility.TEAM,
|
|
37
|
+
private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
|
|
38
|
+
team_tags: ["copilot-cli", "copilot", "terminal", "workspace"],
|
|
39
|
+
});
|
|
40
|
+
const DB_FILENAME = "session-store.db";
|
|
41
|
+
const COPILOT_DIR_NAME = ".copilot";
|
|
42
|
+
const MAX_SUMMARY_CHARS = 260;
|
|
43
|
+
const MAX_KEY_ACTIONS = 6;
|
|
44
|
+
const MAX_KEY_ACTION_CHARS = 280;
|
|
45
|
+
// -- Path resolution ----------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Conventional `~/.copilot/` directory. Respects the `COPILOT_CLI_HOME`
|
|
48
|
+
* environment-variable override (mirrors `default_copilot_cli_dir`).
|
|
49
|
+
*/
|
|
50
|
+
export function defaultCopilotCliDir() {
|
|
51
|
+
const env = process.env.COPILOT_CLI_HOME;
|
|
52
|
+
if (env)
|
|
53
|
+
return env;
|
|
54
|
+
return join(homedir(), COPILOT_DIR_NAME);
|
|
55
|
+
}
|
|
56
|
+
/** Path to `session-store.db` under the given (or conventional) copilot dir. */
|
|
57
|
+
function defaultCopilotCliDbPath(copilotDir) {
|
|
58
|
+
return join(copilotDir ?? defaultCopilotCliDir(), DB_FILENAME);
|
|
59
|
+
}
|
|
60
|
+
function isFile(p) {
|
|
61
|
+
try {
|
|
62
|
+
return statSync(p).isFile();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function isDir(p) {
|
|
69
|
+
try {
|
|
70
|
+
return statSync(p).isDirectory();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// -- Text helpers -------------------------------------------------------------
|
|
77
|
+
/** Redact secrets + clamp to the native-memory limit (`_safe_native_memory_text`). */
|
|
78
|
+
function safeNativeMemoryText(value, limit = 180) {
|
|
79
|
+
return redactText(value, limit);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Collapse whitespace and clamp with a single-char ellipsis (mirrors the
|
|
83
|
+
* oracle's `_bounded`). NOTE: distinct from {@link redactText}'s `...` clamp —
|
|
84
|
+
* this one uses `…` (U+2026) and does no redaction.
|
|
85
|
+
*/
|
|
86
|
+
function bounded(value, limit) {
|
|
87
|
+
const normalized = value.split(/\s+/).filter(Boolean).join(" ");
|
|
88
|
+
if (normalized.length <= limit)
|
|
89
|
+
return normalized;
|
|
90
|
+
return normalized.slice(0, limit - 1).replace(/\s+$/, "") + "…";
|
|
91
|
+
}
|
|
92
|
+
// -- SQLite (read-only) -------------------------------------------------------
|
|
93
|
+
/** Run a query and return rows as plain objects. Never raises (a missing table
|
|
94
|
+
* or any SQLite error degrades to `[]`, matching the oracle's `_query_db`). */
|
|
95
|
+
function queryDb(db, sql, params = []) {
|
|
96
|
+
try {
|
|
97
|
+
return db.prepare(sql).all(...params);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function extractSessions(db, since) {
|
|
104
|
+
let sql = "SELECT id, cwd, repository, branch, summary, created_at, updated_at FROM sessions";
|
|
105
|
+
const params = [];
|
|
106
|
+
if (since) {
|
|
107
|
+
sql += " WHERE created_at >= ?";
|
|
108
|
+
params.push(since);
|
|
109
|
+
}
|
|
110
|
+
sql += " ORDER BY created_at DESC LIMIT 200";
|
|
111
|
+
return queryDb(db, sql, params);
|
|
112
|
+
}
|
|
113
|
+
function extractSessionFiles(db, sessionId) {
|
|
114
|
+
const rows = queryDb(db, "SELECT file_path FROM session_files WHERE session_id = ? LIMIT 20", [sessionId]);
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const r of rows) {
|
|
117
|
+
const fp = r["file_path"];
|
|
118
|
+
if (fp)
|
|
119
|
+
out.push(String(fp));
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
function extractSessionRefs(db, sessionId) {
|
|
124
|
+
const rows = queryDb(db, "SELECT ref_type, ref_value FROM session_refs WHERE session_id = ?", [sessionId]);
|
|
125
|
+
return rows.map((r) => ({
|
|
126
|
+
ref_type: String(r["ref_type"] ?? ""),
|
|
127
|
+
ref_value: String(r["ref_value"] ?? ""),
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
function extractCheckpoints(db, sessionId) {
|
|
131
|
+
return queryDb(db, "SELECT checkpoint_number, title, overview FROM checkpoints " +
|
|
132
|
+
"WHERE session_id = ? ORDER BY checkpoint_number", [sessionId]);
|
|
133
|
+
}
|
|
134
|
+
function extractDynamicContext(db) {
|
|
135
|
+
return queryDb(db, "SELECT repository, branch, src, name, description, content, read_count, count " +
|
|
136
|
+
"FROM dynamic_context_items ORDER BY count DESC LIMIT 50");
|
|
137
|
+
}
|
|
138
|
+
function extractTurnCount(db, sessionId) {
|
|
139
|
+
const rows = queryDb(db, "SELECT COUNT(*) as cnt FROM turns WHERE session_id = ?", [sessionId]);
|
|
140
|
+
const first = rows[0];
|
|
141
|
+
return first ? Number(first["cnt"] ?? 0) || 0 : 0;
|
|
142
|
+
}
|
|
143
|
+
// -- Conversion to Bourdon types ----------------------------------------------
|
|
144
|
+
function sessionToBourdon(raw, files, refs, checkpoints, turnCount) {
|
|
145
|
+
const created = raw["created_at"] ? String(raw["created_at"]) : "";
|
|
146
|
+
const dateStr = created ? created.slice(0, 10) : "";
|
|
147
|
+
const keyActions = [];
|
|
148
|
+
if (raw["summary"]) {
|
|
149
|
+
keyActions.push(bounded(safeNativeMemoryText(String(raw["summary"])), MAX_KEY_ACTION_CHARS));
|
|
150
|
+
}
|
|
151
|
+
for (const cp of checkpoints.slice(0, 3)) {
|
|
152
|
+
if (cp["title"]) {
|
|
153
|
+
keyActions.push(bounded(`checkpoint: ${safeNativeMemoryText(String(cp["title"]))}`, MAX_KEY_ACTION_CHARS));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (turnCount) {
|
|
157
|
+
keyActions.push(`turns: ${turnCount}`);
|
|
158
|
+
}
|
|
159
|
+
for (const ref of refs.slice(0, 3)) {
|
|
160
|
+
const refLabel = `${ref.ref_type}: ${ref.ref_value}`;
|
|
161
|
+
keyActions.push(bounded(refLabel, MAX_KEY_ACTION_CHARS));
|
|
162
|
+
}
|
|
163
|
+
const projectFocus = [];
|
|
164
|
+
if (raw["repository"])
|
|
165
|
+
projectFocus.push(String(raw["repository"]));
|
|
166
|
+
return makeSession({
|
|
167
|
+
date: dateStr,
|
|
168
|
+
cwd: raw["cwd"] ? String(raw["cwd"]) : undefined,
|
|
169
|
+
project_focus: projectFocus,
|
|
170
|
+
key_actions: keyActions.slice(0, MAX_KEY_ACTIONS),
|
|
171
|
+
files_touched: files.slice(0, 20),
|
|
172
|
+
visibility: Visibility.TEAM,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function entitiesFromSessionsAndContext(sessions, dynamicContext) {
|
|
176
|
+
const entities = new Map();
|
|
177
|
+
// Repositories as project entities.
|
|
178
|
+
for (const raw of sessions) {
|
|
179
|
+
const repo = raw["repository"] ? String(raw["repository"]) : "";
|
|
180
|
+
if (repo && !entities.has(repo)) {
|
|
181
|
+
const created = raw["created_at"] ? String(raw["created_at"]) : "";
|
|
182
|
+
const lastTouched = created.slice(0, 10) || null;
|
|
183
|
+
entities.set(repo, makeEntity({
|
|
184
|
+
name: repo,
|
|
185
|
+
type: "project",
|
|
186
|
+
summary: "Repository worked on via Copilot CLI.",
|
|
187
|
+
last_touched: lastTouched ?? undefined,
|
|
188
|
+
tags: ["copilot-cli", "project"],
|
|
189
|
+
visibility: Visibility.TEAM,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Dynamic context items as knowledge entities.
|
|
194
|
+
for (const ctx of dynamicContext) {
|
|
195
|
+
const name = ctx["name"] ? String(ctx["name"]) : "";
|
|
196
|
+
if (!name || entities.has(name))
|
|
197
|
+
continue;
|
|
198
|
+
const desc = ctx["description"] ? String(ctx["description"]) : "";
|
|
199
|
+
const content = ctx["content"] ? String(ctx["content"]) : "";
|
|
200
|
+
let summaryText = desc || content;
|
|
201
|
+
if (summaryText) {
|
|
202
|
+
summaryText = bounded(safeNativeMemoryText(summaryText), MAX_SUMMARY_CHARS);
|
|
203
|
+
}
|
|
204
|
+
entities.set(name, makeEntity({
|
|
205
|
+
name,
|
|
206
|
+
type: "dynamic-context",
|
|
207
|
+
summary: summaryText || undefined,
|
|
208
|
+
last_touched: undefined,
|
|
209
|
+
tags: ["copilot-cli", "dynamic-context", ctx["src"] ? String(ctx["src"]) : "unknown"],
|
|
210
|
+
visibility: Visibility.TEAM,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
return [...entities.values()];
|
|
214
|
+
}
|
|
215
|
+
/** UTC ISO datetime with `+00:00` offset (matching Python `isoformat`). */
|
|
216
|
+
function toUtcIso(d) {
|
|
217
|
+
return d.toISOString().replace(/\.\d{3}Z$/, "+00:00").replace(/Z$/, "+00:00");
|
|
218
|
+
}
|
|
219
|
+
// -- Participant --------------------------------------------------------------
|
|
220
|
+
/**
|
|
221
|
+
* External participant for GitHub Copilot CLI (`~/.copilot/session-store.db`).
|
|
222
|
+
* Reads the SQLite database read-only (never locks the live db).
|
|
223
|
+
*/
|
|
224
|
+
export class CopilotCliParticipant {
|
|
225
|
+
agentId = AGENT_ID;
|
|
226
|
+
agentType = AGENT_TYPE;
|
|
227
|
+
displayName = DISPLAY_NAME;
|
|
228
|
+
nativePath;
|
|
229
|
+
copilotDir;
|
|
230
|
+
/** Conventional `~/.copilot` dir used by the setup wizard's detection. */
|
|
231
|
+
static defaultNativePath(home) {
|
|
232
|
+
if (home)
|
|
233
|
+
return join(home, COPILOT_DIR_NAME);
|
|
234
|
+
return defaultCopilotCliDir();
|
|
235
|
+
}
|
|
236
|
+
constructor(copilotDir) {
|
|
237
|
+
this.copilotDir = copilotDir ?? null;
|
|
238
|
+
this.nativePath = this.copilotDir ?? defaultCopilotCliDir();
|
|
239
|
+
}
|
|
240
|
+
// -- Protocol surface -------------------------------------------------------
|
|
241
|
+
discover() {
|
|
242
|
+
const dbPath = defaultCopilotCliDbPath(this.copilotDir);
|
|
243
|
+
if (!isFile(dbPath)) {
|
|
244
|
+
throw new ParticipantDiscoveryError(`Copilot CLI session-store.db not found at ${dbPath}. ` +
|
|
245
|
+
"The Copilot CLI agent must be run at least once to create this database.");
|
|
246
|
+
}
|
|
247
|
+
let dbSize = 0;
|
|
248
|
+
try {
|
|
249
|
+
dbSize = statSync(dbPath).size;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
dbSize = 0;
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
path: dirname(dbPath),
|
|
256
|
+
version: "schema-v4",
|
|
257
|
+
metadata: { db_path: dbPath, db_size_bytes: dbSize },
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
exportSessions(since, limit = 100) {
|
|
261
|
+
const dbPath = defaultCopilotCliDbPath(this.copilotDir);
|
|
262
|
+
const db = tryOpenReadonly(dbPath);
|
|
263
|
+
if (!db)
|
|
264
|
+
return [];
|
|
265
|
+
try {
|
|
266
|
+
const sinceIso = since ? toUtcIso(since) : null;
|
|
267
|
+
const rawSessions = extractSessions(db, sinceIso);
|
|
268
|
+
const sessions = [];
|
|
269
|
+
for (const raw of rawSessions.slice(0, limit)) {
|
|
270
|
+
const sid = String(raw["id"]);
|
|
271
|
+
const files = extractSessionFiles(db, sid);
|
|
272
|
+
const refs = extractSessionRefs(db, sid);
|
|
273
|
+
const checkpoints = extractCheckpoints(db, sid);
|
|
274
|
+
const turnCount = extractTurnCount(db, sid);
|
|
275
|
+
sessions.push(sessionToBourdon(raw, files, refs, checkpoints, turnCount));
|
|
276
|
+
}
|
|
277
|
+
return sessions;
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
db.close();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
exportL5(since) {
|
|
284
|
+
const dbPath = defaultCopilotCliDbPath(this.copilotDir);
|
|
285
|
+
const db = tryOpenReadonly(dbPath);
|
|
286
|
+
if (!db)
|
|
287
|
+
return this.emptyManifest();
|
|
288
|
+
try {
|
|
289
|
+
const sinceIso = since ? toUtcIso(since) : null;
|
|
290
|
+
const rawSessions = extractSessions(db, sinceIso);
|
|
291
|
+
const dynamicContext = extractDynamicContext(db);
|
|
292
|
+
const sessions = [];
|
|
293
|
+
for (const raw of rawSessions.slice(0, 100)) {
|
|
294
|
+
const sid = String(raw["id"]);
|
|
295
|
+
const files = extractSessionFiles(db, sid);
|
|
296
|
+
const refs = extractSessionRefs(db, sid);
|
|
297
|
+
const checkpoints = extractCheckpoints(db, sid);
|
|
298
|
+
const turnCount = extractTurnCount(db, sid);
|
|
299
|
+
sessions.push(sessionToBourdon(raw, files, refs, checkpoints, turnCount));
|
|
300
|
+
}
|
|
301
|
+
const entities = entitiesFromSessionsAndContext(rawSessions, dynamicContext);
|
|
302
|
+
const visibleEntities = filterForFederation(entities, DEFAULT_POLICY);
|
|
303
|
+
return makeManifest({
|
|
304
|
+
spec_version: SPEC_VERSION,
|
|
305
|
+
agent: makeAgentInfo({
|
|
306
|
+
id: AGENT_ID,
|
|
307
|
+
type: AGENT_TYPE,
|
|
308
|
+
instance: hostname() || "unknown",
|
|
309
|
+
spec_version_compat: `>=${SPEC_VERSION}`,
|
|
310
|
+
role_narrative: ROLE_NARRATIVE,
|
|
311
|
+
}),
|
|
312
|
+
last_updated: new Date().toISOString(),
|
|
313
|
+
capabilities: [
|
|
314
|
+
"terminal-agent",
|
|
315
|
+
"file-edit",
|
|
316
|
+
"shell-execution",
|
|
317
|
+
"git-operations",
|
|
318
|
+
"multi-turn-sessions",
|
|
319
|
+
"checkpoints",
|
|
320
|
+
"dynamic-context",
|
|
321
|
+
],
|
|
322
|
+
recent_sessions: sessions,
|
|
323
|
+
known_entities: visibleEntities,
|
|
324
|
+
visibility_policy: DEFAULT_POLICY,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
db.close();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
healthCheck() {
|
|
332
|
+
try {
|
|
333
|
+
const copilotDir = this.copilotDir ?? defaultCopilotCliDir();
|
|
334
|
+
if (!isDir(copilotDir)) {
|
|
335
|
+
return {
|
|
336
|
+
status: "blocked",
|
|
337
|
+
reason: `Copilot CLI directory not found at ${copilotDir}.`,
|
|
338
|
+
details: { expected_path: copilotDir },
|
|
339
|
+
proposedFix: "Run the Copilot CLI agent (`copilot` or `gh copilot`) at least once " +
|
|
340
|
+
"to create the session store.",
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const dbPath = defaultCopilotCliDbPath(copilotDir);
|
|
344
|
+
if (!isFile(dbPath)) {
|
|
345
|
+
return {
|
|
346
|
+
status: "blocked",
|
|
347
|
+
reason: `session-store.db not found at ${dbPath}.`,
|
|
348
|
+
details: { expected_db: dbPath },
|
|
349
|
+
proposedFix: "Run the Copilot CLI agent at least once. The session-store.db is " +
|
|
350
|
+
"created on first use.",
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const db = tryOpenReadonly(dbPath);
|
|
354
|
+
if (!db) {
|
|
355
|
+
return {
|
|
356
|
+
status: "degraded",
|
|
357
|
+
reason: "Cannot copy session-store.db for reading.",
|
|
358
|
+
details: { db_path: dbPath },
|
|
359
|
+
proposedFix: "Check file permissions on the session-store.db.",
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
const rawSessions = extractSessions(db);
|
|
364
|
+
const dynamicContext = extractDynamicContext(db);
|
|
365
|
+
const totalTurns = queryDb(db, "SELECT COUNT(*) as cnt FROM turns");
|
|
366
|
+
const first = totalTurns[0];
|
|
367
|
+
let dbSize = 0;
|
|
368
|
+
try {
|
|
369
|
+
dbSize = statSync(dbPath).size;
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
dbSize = 0;
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
status: "ok",
|
|
376
|
+
details: {
|
|
377
|
+
db_path: dbPath,
|
|
378
|
+
db_size_bytes: dbSize,
|
|
379
|
+
session_count: rawSessions.length,
|
|
380
|
+
total_turns: first ? Number(first["cnt"] ?? 0) || 0 : 0,
|
|
381
|
+
dynamic_context_items: dynamicContext.length,
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
db.close();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
// healthCheck must NEVER raise (contract).
|
|
391
|
+
return {
|
|
392
|
+
status: "degraded",
|
|
393
|
+
reason: `unexpected error: ${String(err)}`,
|
|
394
|
+
details: {},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// -- Internal ---------------------------------------------------------------
|
|
399
|
+
emptyManifest() {
|
|
400
|
+
return makeManifest({
|
|
401
|
+
spec_version: SPEC_VERSION,
|
|
402
|
+
agent: makeAgentInfo({
|
|
403
|
+
id: AGENT_ID,
|
|
404
|
+
type: AGENT_TYPE,
|
|
405
|
+
instance: hostname() || "unknown",
|
|
406
|
+
spec_version_compat: `>=${SPEC_VERSION}`,
|
|
407
|
+
role_narrative: ROLE_NARRATIVE,
|
|
408
|
+
}),
|
|
409
|
+
last_updated: new Date().toISOString(),
|
|
410
|
+
capabilities: [],
|
|
411
|
+
recent_sessions: [],
|
|
412
|
+
known_entities: [],
|
|
413
|
+
visibility_policy: DEFAULT_POLICY,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot · VS Code participant — reads the GUI Copilot Chat extension's
|
|
3
|
+
* per-workspace transcripts + repo-scoped memory. Port of
|
|
4
|
+
* `participants/copilot_vscode.py`.
|
|
5
|
+
*
|
|
6
|
+
* VS Code's Copilot Chat extension stores per-workspace data at
|
|
7
|
+
* `<workspaceStorage>/<hash>/GitHub.copilot-chat/`:
|
|
8
|
+
* - `transcripts/<session-uuid>.jsonl` — event stream per chat session
|
|
9
|
+
* - `memory-tool/memories/**\/*.md` — per-repo memory (markdown bullets)
|
|
10
|
+
*
|
|
11
|
+
* This is a FILE/CONVENTION reader (not SQLite, not network): it walks the
|
|
12
|
+
* workspaceStorage tree read-only, parses JSONL transcripts + markdown memory
|
|
13
|
+
* files, aggregates across every workspace hash, redacts every native string,
|
|
14
|
+
* applies the visibility filter BEFORE emission, and is deterministic.
|
|
15
|
+
* `healthCheck` never throws.
|
|
16
|
+
*
|
|
17
|
+
* Distinct from:
|
|
18
|
+
* - `copilot` (convention-file at `~/.copilot-bourdon/memory.md`)
|
|
19
|
+
* - `copilot-cli` (terminal agent, `~/.copilot/session-store.db`, SQLite)
|
|
20
|
+
* - `github-copilot` (network reader — GitHub-embedded Copilot)
|
|
21
|
+
*/
|
|
22
|
+
import { type L5ManifestModel, type SessionModel } from "@getbourdon/l5";
|
|
23
|
+
import { type AgentStore, type BourdonParticipant, type HealthStatus } from "../base.js";
|
|
24
|
+
/**
|
|
25
|
+
* Return the VS Code workspaceStorage path for this platform, or null when it
|
|
26
|
+
* cannot be resolved (Windows without `%APPDATA%`). Respects the
|
|
27
|
+
* `COPILOT_VSCODE_STORAGE` environment-variable override. Mirrors
|
|
28
|
+
* `default_vscode_workspace_storage_dir`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function defaultVscodeWorkspaceStorageDir(): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* External participant for GitHub Copilot in VS Code. Reads transcripts and
|
|
33
|
+
* memory-tool files from VS Code's workspaceStorage read-only, aggregating
|
|
34
|
+
* across all workspace hashes.
|
|
35
|
+
*/
|
|
36
|
+
export declare class CopilotVscodeParticipant implements BourdonParticipant {
|
|
37
|
+
agentId: string;
|
|
38
|
+
agentType: string;
|
|
39
|
+
displayName: string;
|
|
40
|
+
private readonly workspaceStorage;
|
|
41
|
+
/** The workspaceStorage dir the setup wizard probes for presence. */
|
|
42
|
+
static defaultNativePath(home?: string): string;
|
|
43
|
+
constructor(workspaceStorage?: string);
|
|
44
|
+
get nativePath(): string;
|
|
45
|
+
private resolveWs;
|
|
46
|
+
discover(): AgentStore;
|
|
47
|
+
exportSessions(since?: Date, limit?: number): SessionModel[];
|
|
48
|
+
exportL5(since?: Date): L5ManifestModel;
|
|
49
|
+
healthCheck(): HealthStatus;
|
|
50
|
+
/** Collect and parse all transcripts across all workspaces. */
|
|
51
|
+
private allTranscripts;
|
|
52
|
+
/** Collect all memory-tool files across all workspaces. */
|
|
53
|
+
private allMemories;
|
|
54
|
+
}
|