@getbourdon/participants 0.2.0 → 0.2.1

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/dist/index.cjs CHANGED
@@ -23,9 +23,16 @@ __export(index_exports, {
23
23
  CONTRACT_VERSION: () => CONTRACT_VERSION,
24
24
  CacheEntry: () => CacheEntry,
25
25
  CascadeParticipant: () => CascadeParticipant,
26
+ ClaudeCodeAutomationsParticipant: () => ClaudeCodeAutomationsParticipant,
26
27
  ClaudeCodeParticipant: () => ClaudeCodeParticipant,
28
+ ClaudeDesktopCodeParticipant: () => ClaudeDesktopCodeParticipant,
29
+ ClaudeDesktopCoworkParticipant: () => ClaudeDesktopCoworkParticipant,
30
+ CodexAutomationsParticipant: () => CodexAutomationsParticipant,
31
+ CopilotAutomationsParticipant: () => CopilotAutomationsParticipant,
27
32
  CopilotCliParticipant: () => CopilotCliParticipant,
28
33
  CopilotParticipant: () => CopilotParticipant,
34
+ CopilotVscodeParticipant: () => CopilotVscodeParticipant,
35
+ CursorAutomationsParticipant: () => CursorAutomationsParticipant,
29
36
  CursorParticipant: () => CursorParticipant,
30
37
  DEFAULT_CACHE_TTL_SECONDS: () => DEFAULT_CACHE_TTL_SECONDS,
31
38
  FIRST_PARTY: () => FIRST_PARTY,
@@ -44,14 +51,14 @@ __export(index_exports, {
44
51
  ParticipantVersionMismatchError: () => ParticipantVersionMismatchError,
45
52
  PayloadCache: () => PayloadCache,
46
53
  SPEC_VERSION: () => SPEC_VERSION,
47
- Visibility: () => import_l59.Visibility,
48
- applyVisibility: () => import_l59.applyVisibility,
54
+ Visibility: () => import_l516.Visibility,
55
+ applyVisibility: () => import_l516.applyVisibility,
49
56
  defaultCacheRoot: () => defaultCacheRoot,
50
57
  discoverParticipants: () => discoverParticipants,
51
58
  envAuthProvider: () => envAuthProvider,
52
59
  epochToIsoDate: () => epochToIsoDate,
53
60
  epochToIsoDatetime: () => epochToIsoDatetime,
54
- filterForFederation: () => import_l59.filterForFederation,
61
+ filterForFederation: () => import_l516.filterForFederation,
55
62
  friendlyLabel: () => friendlyLabel,
56
63
  ghTokenProvider: () => ghTokenProvider,
57
64
  isParticipant: () => isParticipant,
@@ -61,9 +68,9 @@ __export(index_exports, {
61
68
  sqliteAvailable: () => sqliteAvailable,
62
69
  tableColumns: () => tableColumns,
63
70
  tableExists: () => tableExists,
64
- toDict: () => import_l59.toDict,
71
+ toDict: () => import_l516.toDict,
65
72
  tryOpenReadonly: () => tryOpenReadonly,
66
- validateManifest: () => import_l59.validateManifest,
73
+ validateManifest: () => import_l516.validateManifest,
67
74
  verifyInstance: () => verifyInstance
68
75
  });
69
76
  module.exports = __toCommonJS(index_exports);
@@ -506,8 +513,8 @@ function collectMemoryEntries(home) {
506
513
  if (isFile(path)) {
507
514
  const entries = parseMemoryFile(path);
508
515
  if (entries.length) {
509
- const stem = fname.replace(/\.md$/, "");
510
- result.push([stem, entries]);
516
+ const stem2 = fname.replace(/\.md$/, "");
517
+ result.push([stem2, entries]);
511
518
  }
512
519
  }
513
520
  }
@@ -517,18 +524,18 @@ function buildSession(record, toolNames) {
517
524
  const key = projectKey(record.cwd);
518
525
  const focus = [];
519
526
  if (key) focus.push(key);
520
- const keyActions = [];
521
- if (record.title) keyActions.push(bounded(record.title, 160));
527
+ const keyActions3 = [];
528
+ if (record.title) keyActions3.push(bounded(record.title, 160));
522
529
  if (toolNames.length) {
523
- keyActions.push("Tools: " + toolNames.slice(0, 8).join(", "));
530
+ keyActions3.push("Tools: " + toolNames.slice(0, 8).join(", "));
524
531
  } else if (record.toolCallCount) {
525
- keyActions.push(`${record.toolCallCount} tool call(s)`);
532
+ keyActions3.push(`${record.toolCallCount} tool call(s)`);
526
533
  }
527
534
  return (0, import_l5.makeSession)({
528
535
  date: record.date,
529
536
  cwd: record.cwd || void 0,
530
537
  project_focus: focus,
531
- key_actions: keyActions,
538
+ key_actions: keyActions3,
532
539
  visibility: import_l5.Visibility.TEAM
533
540
  });
534
541
  }
@@ -641,7 +648,7 @@ var HermesParticipant = class {
641
648
  exportL5(since) {
642
649
  const store = this.discover();
643
650
  const sources = store.metadata["sources"] ?? {};
644
- const capabilities = Object.entries(sources).filter(([k, v]) => v && k !== "hermes_home").map(([k]) => k).sort();
651
+ const capabilities3 = Object.entries(sources).filter(([k, v]) => v && k !== "hermes_home").map(([k]) => k).sort();
645
652
  const home = this.home;
646
653
  const records = home ? collectSessionRows((0, import_node_path2.join)(home, "state.db"), DEFAULT_SESSION_LIMIT) : [];
647
654
  const entities = projectEntities(records);
@@ -674,7 +681,7 @@ var HermesParticipant = class {
674
681
  spec_version_compat: CONTRACT_VERSION
675
682
  }),
676
683
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
677
- capabilities,
684
+ capabilities: capabilities3,
678
685
  recent_sessions: sessions,
679
686
  known_entities: visible,
680
687
  visibility_policy: DEFAULT_POLICY
@@ -919,7 +926,7 @@ var LOG_FILENAME_RE = /^(\d{4}-\d{2}-\d{2})(?:-(\w+?))?(?:-.*)?\.md$/;
919
926
  function parseLogFile(logPath) {
920
927
  const m = LOG_FILENAME_RE.exec((0, import_node_path3.basename)(logPath));
921
928
  if (!m) return null;
922
- const sessionDate = m[1] ?? "";
929
+ const sessionDate3 = m[1] ?? "";
923
930
  let text;
924
931
  try {
925
932
  text = (0, import_node_fs4.readFileSync)(logPath, "utf8");
@@ -929,7 +936,7 @@ function parseLogFile(logPath) {
929
936
  const { body } = parseFrontmatter(text);
930
937
  const headline = extractFirstParagraph(body, 250);
931
938
  return (0, import_l52.makeSession)({
932
- date: sessionDate,
939
+ date: sessionDate3,
933
940
  key_actions: headline ? [headline] : [],
934
941
  files_touched: [`LOG/${(0, import_node_path3.basename)(logPath)}`]
935
942
  });
@@ -1128,7 +1135,7 @@ var ClaudeCodeParticipant = class {
1128
1135
  exportL5(since) {
1129
1136
  const store = this.discover();
1130
1137
  const sources = store.metadata["sources"];
1131
- const capabilities = Object.entries(sources).filter(([, v]) => v).map(([k]) => k).sort();
1138
+ const capabilities3 = Object.entries(sources).filter(([, v]) => v).map(([k]) => k).sort();
1132
1139
  const autoMemoryEntities = this.autoMemoryPath ? parseAutoMemory(this.autoMemoryPath) : [];
1133
1140
  const graphEntities = this.knowledgeGraphPath ? parseKnowledgeGraph(this.knowledgeGraphPath) : [];
1134
1141
  const projectEntities2 = this.brainPath ? parseProjectsDir(this.brainPath) : [];
@@ -1145,7 +1152,7 @@ var ClaudeCodeParticipant = class {
1145
1152
  role_narrative: ROLE_NARRATIVE2
1146
1153
  }),
1147
1154
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
1148
- capabilities,
1155
+ capabilities: capabilities3,
1149
1156
  recent_sessions: sessions,
1150
1157
  known_entities: visibleEntities,
1151
1158
  visibility_policy: DEFAULT_POLICY2
@@ -1799,8 +1806,8 @@ function shortIndexToEntities(workspaceRoot, _cursorDir) {
1799
1806
  paths.push((0, import_node_path4.join)(workspaceRoot, ".cursor", "memory", "short-index.json"));
1800
1807
  }
1801
1808
  const cursorHome = process.env.CURSOR_DIR ? process.env.CURSOR_DIR : null;
1802
- const homeBase = cursorHome ?? (0, import_node_path4.join)(homeDir2(), ".cursor");
1803
- paths.push((0, import_node_path4.join)(homeBase, "memory", "short-index.json"));
1809
+ const homeBase2 = cursorHome ?? (0, import_node_path4.join)(homeDir2(), ".cursor");
1810
+ paths.push((0, import_node_path4.join)(homeBase2, "memory", "short-index.json"));
1804
1811
  const merged = /* @__PURE__ */ new Map();
1805
1812
  for (const path of paths) {
1806
1813
  for (const entry of readShortIndex(path)) {
@@ -2370,23 +2377,23 @@ function extractTurnCount(db, sessionId) {
2370
2377
  function sessionToBourdon(raw, files, refs, checkpoints, turnCount) {
2371
2378
  const created = raw["created_at"] ? String(raw["created_at"]) : "";
2372
2379
  const dateStr = created ? created.slice(0, 10) : "";
2373
- const keyActions = [];
2380
+ const keyActions3 = [];
2374
2381
  if (raw["summary"]) {
2375
- keyActions.push(bounded2(safeNativeMemoryText(String(raw["summary"])), MAX_KEY_ACTION_CHARS));
2382
+ keyActions3.push(bounded2(safeNativeMemoryText(String(raw["summary"])), MAX_KEY_ACTION_CHARS));
2376
2383
  }
2377
2384
  for (const cp of checkpoints.slice(0, 3)) {
2378
2385
  if (cp["title"]) {
2379
- keyActions.push(
2386
+ keyActions3.push(
2380
2387
  bounded2(`checkpoint: ${safeNativeMemoryText(String(cp["title"]))}`, MAX_KEY_ACTION_CHARS)
2381
2388
  );
2382
2389
  }
2383
2390
  }
2384
2391
  if (turnCount) {
2385
- keyActions.push(`turns: ${turnCount}`);
2392
+ keyActions3.push(`turns: ${turnCount}`);
2386
2393
  }
2387
2394
  for (const ref of refs.slice(0, 3)) {
2388
2395
  const refLabel = `${ref.ref_type}: ${ref.ref_value}`;
2389
- keyActions.push(bounded2(refLabel, MAX_KEY_ACTION_CHARS));
2396
+ keyActions3.push(bounded2(refLabel, MAX_KEY_ACTION_CHARS));
2390
2397
  }
2391
2398
  const projectFocus = [];
2392
2399
  if (raw["repository"]) projectFocus.push(String(raw["repository"]));
@@ -2394,7 +2401,7 @@ function sessionToBourdon(raw, files, refs, checkpoints, turnCount) {
2394
2401
  date: dateStr,
2395
2402
  cwd: raw["cwd"] ? String(raw["cwd"]) : void 0,
2396
2403
  project_focus: projectFocus,
2397
- key_actions: keyActions.slice(0, MAX_KEY_ACTIONS),
2404
+ key_actions: keyActions3.slice(0, MAX_KEY_ACTIONS),
2398
2405
  files_touched: files.slice(0, 20),
2399
2406
  visibility: import_l56.Visibility.TEAM
2400
2407
  });
@@ -3199,18 +3206,3169 @@ var OpenClawParticipant = class {
3199
3206
  }
3200
3207
  };
3201
3208
 
3209
+ // src/readers/claude-code-automations.ts
3210
+ var import_node_fs9 = require("fs");
3211
+ var import_node_os9 = require("os");
3212
+ var import_node_path9 = require("path");
3213
+ var import_l59 = require("@getbourdon/l5");
3214
+ var import_redaction9 = require("@getbourdon/redaction");
3215
+ var import_smol_toml = require("smol-toml");
3216
+ var AGENT_ID9 = "claude-code-automations";
3217
+ var AGENT_TYPE9 = "other";
3218
+ var ROLE_NARRATIVE9 = "Publishes read-only Claude Code automation run memory into Bourdon so scheduled /loop continuations, CronCreate jobs, GitHub Action runs of claude-code-action, and /schedule remote-routine summaries become visible alongside interactive Claude Code sessions.";
3219
+ var DEFAULT_POLICY8 = (0, import_l59.makeVisibilityPolicy)({
3220
+ default: import_l59.Visibility.TEAM,
3221
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
3222
+ team_tags: ["claude-code-automation", "automation", "workspace"]
3223
+ });
3224
+ var AUTOMATIONS_DIR_NAME = "automations";
3225
+ var AUTOMATION_TOML = "automation.toml";
3226
+ var MEMORY_MD = "memory.md";
3227
+ var MAX_MEMORY_CHARS = 16e4;
3228
+ var MAX_KEY_ACTIONS_PER_RUN = 6;
3229
+ var MAX_KEY_ACTION_CHARS2 = 280;
3230
+ var RUN_HEADER_RE = /^(\d{4}-\d{2}-\d{2})(?:\b|$)(.*)$/;
3231
+ var HEADER_SUFFIX_STRIP = " -:\u2014";
3232
+ var PROJECT_HINTS = [
3233
+ "ShipStable",
3234
+ "ILTT",
3235
+ "Prun",
3236
+ "PRUN",
3237
+ "OMNIvour",
3238
+ "Castmore",
3239
+ "Bourdon",
3240
+ "RADLAB",
3241
+ "CHIP",
3242
+ "Claude Brain",
3243
+ "Cursor",
3244
+ "Copilot",
3245
+ "Codex",
3246
+ "Cascade"
3247
+ ];
3248
+ var SIGNAL_PATTERNS = [
3249
+ ["human-dashboard-action", /\b(human|ryan|dashboard|manual)\b/i],
3250
+ ["release-gate", /\b(release|store|app review|play console|testflight)\b/i],
3251
+ ["billing-drift", /\b(billing|stripe|revenuecat|iap|subscription)\b/i],
3252
+ ["memory-coverage-gap", /\b(memory|l5|manifest|federated|bourdon)\b/i],
3253
+ ["launch-decision", /\b(launch|go-live|pricing|prod|production)\b/i],
3254
+ ["ci-signal", /\b(github action|workflow run|ci failure|gh action)\b/i]
3255
+ ];
3256
+ var logger3 = {
3257
+ warn(msg) {
3258
+ console.warn(`[bourdon.participants] ${msg}`);
3259
+ }
3260
+ };
3261
+ function homeDir5() {
3262
+ try {
3263
+ return (0, import_node_os9.homedir)();
3264
+ } catch {
3265
+ return process.env.HOME || process.env.USERPROFILE || ".";
3266
+ }
3267
+ }
3268
+ function isDir7(p) {
3269
+ try {
3270
+ return (0, import_node_fs9.statSync)(p).isDirectory();
3271
+ } catch {
3272
+ return false;
3273
+ }
3274
+ }
3275
+ function isFile7(p) {
3276
+ try {
3277
+ return (0, import_node_fs9.statSync)(p).isFile();
3278
+ } catch {
3279
+ return false;
3280
+ }
3281
+ }
3282
+ function truthy(v) {
3283
+ if (v === void 0 || v === null || v === false || v === 0 || v === "") return false;
3284
+ if (Array.isArray(v)) return v.length > 0;
3285
+ if (typeof v === "object") return Object.keys(v).length > 0;
3286
+ return true;
3287
+ }
3288
+ function pyStr(v) {
3289
+ if (v === true) return "True";
3290
+ if (v === false) return "False";
3291
+ if (v === null || v === void 0) return "None";
3292
+ return String(v);
3293
+ }
3294
+ function strOr(a, b) {
3295
+ return pyStr(truthy(a) ? a : b);
3296
+ }
3297
+ function rstrip(s) {
3298
+ return s.replace(/\s+$/, "");
3299
+ }
3300
+ function stripChars2(s, chars) {
3301
+ let start = 0;
3302
+ let end = s.length;
3303
+ while (start < end && chars.includes(s[start])) start++;
3304
+ while (end > start && chars.includes(s[end - 1])) end--;
3305
+ return s.slice(start, end);
3306
+ }
3307
+ function splitLines(s) {
3308
+ const parts = s.split(/\r\n|\r|\n/);
3309
+ if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
3310
+ return parts;
3311
+ }
3312
+ function defaultClaudeCodeAutomationsDir(claudeHome) {
3313
+ if (claudeHome !== void 0) return (0, import_node_path9.join)(claudeHome, AUTOMATIONS_DIR_NAME);
3314
+ const env = process.env.CLAUDE_HOME;
3315
+ if (env) return (0, import_node_path9.join)(env, AUTOMATIONS_DIR_NAME);
3316
+ return (0, import_node_path9.join)(homeDir5(), ".claude", AUTOMATIONS_DIR_NAME);
3317
+ }
3318
+ function readAutomationToml(path) {
3319
+ let raw;
3320
+ try {
3321
+ raw = (0, import_smol_toml.parse)((0, import_node_fs9.readFileSync)(path, "utf8"));
3322
+ } catch (exc) {
3323
+ logger3.warn(
3324
+ `ClaudeCodeAutomationsParticipant: cannot parse ${path}: ${String(exc)}`
3325
+ );
3326
+ return {};
3327
+ }
3328
+ return typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
3329
+ }
3330
+ function buildConfig(tomlPath) {
3331
+ const raw = readAutomationToml(tomlPath);
3332
+ const parent = (0, import_node_path9.dirname)(tomlPath);
3333
+ const parentName = basenameOf2(parent);
3334
+ const automationId = strOr(raw["id"], parentName).trim();
3335
+ if (!automationId) return null;
3336
+ const name = strOr(raw["name"], automationId).trim();
3337
+ const status = strOr(raw["status"], "UNKNOWN").trim().toUpperCase();
3338
+ const rrule = strOr(raw["rrule"], "").trim();
3339
+ const kind = strOr(raw["kind"], "").trim();
3340
+ const cwdsRaw = truthy(raw["cwds"]) ? raw["cwds"] : [];
3341
+ const cwds = [];
3342
+ if (Array.isArray(cwdsRaw)) {
3343
+ for (const cwd of cwdsRaw) {
3344
+ if (typeof cwd === "string" && cwd.trim()) cwds.push(cwd);
3345
+ }
3346
+ } else if (typeof cwdsRaw === "string") {
3347
+ for (const ch of cwdsRaw) {
3348
+ if (ch.trim()) cwds.push(ch);
3349
+ }
3350
+ }
3351
+ const memoryPath = (0, import_node_path9.join)(parent, MEMORY_MD);
3352
+ return {
3353
+ automationId,
3354
+ name,
3355
+ status,
3356
+ rrule,
3357
+ kind,
3358
+ cwds,
3359
+ path: tomlPath,
3360
+ memoryPath: isFile7(memoryPath) ? memoryPath : null
3361
+ };
3362
+ }
3363
+ function basenameOf2(p) {
3364
+ const cleaned = p.replace(/[/\\]+$/, "");
3365
+ const idx = Math.max(cleaned.lastIndexOf("/"), cleaned.lastIndexOf("\\"));
3366
+ return idx === -1 ? cleaned : cleaned.slice(idx + 1);
3367
+ }
3368
+ function iterConfigs(automationsDir) {
3369
+ const configs = [];
3370
+ if (!isDir7(automationsDir)) return configs;
3371
+ const tomlPaths = [];
3372
+ let entries;
3373
+ try {
3374
+ entries = (0, import_node_fs9.readdirSync)(automationsDir);
3375
+ } catch {
3376
+ return configs;
3377
+ }
3378
+ for (const entry of entries) {
3379
+ const sub = (0, import_node_path9.join)(automationsDir, entry);
3380
+ if (!isDir7(sub)) continue;
3381
+ const tomlPath = (0, import_node_path9.join)(sub, AUTOMATION_TOML);
3382
+ if (isFile7(tomlPath)) tomlPaths.push(tomlPath);
3383
+ }
3384
+ tomlPaths.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
3385
+ for (const tomlPath of tomlPaths) {
3386
+ const config = buildConfig(tomlPath);
3387
+ if (config !== null) configs.push(config);
3388
+ }
3389
+ return configs;
3390
+ }
3391
+ function readMemoryText(path) {
3392
+ if (path === null) return "";
3393
+ let text;
3394
+ try {
3395
+ text = (0, import_node_fs9.readFileSync)(path, "utf8");
3396
+ } catch (exc) {
3397
+ logger3.warn(
3398
+ `ClaudeCodeAutomationsParticipant: cannot read ${path}: ${String(exc)}`
3399
+ );
3400
+ return "";
3401
+ }
3402
+ return text.slice(-MAX_MEMORY_CHARS);
3403
+ }
3404
+ function extractMemoryRuns(config) {
3405
+ const text = readMemoryText(config.memoryPath);
3406
+ if (!text) return [];
3407
+ const chunks = [];
3408
+ let currentDate = "";
3409
+ let currentLines = [];
3410
+ for (const rawLine of splitLines(text)) {
3411
+ const line = rstrip(rawLine);
3412
+ const match = RUN_HEADER_RE.exec(line.trim());
3413
+ if (match) {
3414
+ if (currentDate) chunks.push([currentDate, currentLines]);
3415
+ currentDate = match[1];
3416
+ const suffix = stripChars2(match[2] ?? "", HEADER_SUFFIX_STRIP);
3417
+ currentLines = suffix ? [suffix] : [];
3418
+ continue;
3419
+ }
3420
+ if (currentDate) currentLines.push(line);
3421
+ }
3422
+ if (currentDate) chunks.push([currentDate, currentLines]);
3423
+ const runs = [];
3424
+ for (const [runDate, lines] of chunks) {
3425
+ const actions = actionsFromLines(lines);
3426
+ if (actions.length === 0) continue;
3427
+ const body = actions.join(" ");
3428
+ const title = titleFromActions(config, actions);
3429
+ runs.push({
3430
+ automation: config,
3431
+ date: runDate,
3432
+ title,
3433
+ keyActions: actions,
3434
+ projects: inferProjects(body),
3435
+ signals: inferSignals(body)
3436
+ });
3437
+ }
3438
+ return runs;
3439
+ }
3440
+ function actionsFromLines(lines) {
3441
+ const actions = [];
3442
+ for (const line of lines) {
3443
+ let cleaned = line.trim();
3444
+ if (!cleaned) continue;
3445
+ cleaned = removePrefix(cleaned, "- ").trim();
3446
+ const lowered = cleaned.toLowerCase();
3447
+ if (!cleaned || lowered.startsWith("runtime")) continue;
3448
+ if (lowered === "first run" || lowered === "follow-up") continue;
3449
+ if (lowered.startsWith("run:")) cleaned = cleaned.slice(4).trim();
3450
+ const safe = bounded3(safeNativeMemoryText3(cleaned), MAX_KEY_ACTION_CHARS2);
3451
+ if (safe && !actions.includes(safe)) actions.push(safe);
3452
+ if (actions.length >= MAX_KEY_ACTIONS_PER_RUN) break;
3453
+ }
3454
+ return actions;
3455
+ }
3456
+ function removePrefix(s, prefix) {
3457
+ return s.startsWith(prefix) ? s.slice(prefix.length) : s;
3458
+ }
3459
+ function safeNativeMemoryText3(value) {
3460
+ return (0, import_redaction9.redactText)(value, 180);
3461
+ }
3462
+ function titleFromActions(config, actions) {
3463
+ if (actions.length === 0) return config.name;
3464
+ const first = actions[0];
3465
+ const prefix = `${config.name}: `;
3466
+ if (first.startsWith(prefix)) return bounded3(first, 120);
3467
+ return bounded3(prefix + first, 120);
3468
+ }
3469
+ function inferProjects(text) {
3470
+ const projects = [];
3471
+ const lowered = text.toLowerCase();
3472
+ const seen = /* @__PURE__ */ new Set();
3473
+ for (const project of PROJECT_HINTS) {
3474
+ const key = project.toLowerCase();
3475
+ if (lowered.includes(key) && !seen.has(key)) {
3476
+ projects.push(project);
3477
+ seen.add(key);
3478
+ }
3479
+ }
3480
+ return projects;
3481
+ }
3482
+ function inferSignals(text) {
3483
+ const signals = [];
3484
+ for (const [name, pattern] of SIGNAL_PATTERNS) {
3485
+ if (pattern.test(text)) signals.push(name);
3486
+ }
3487
+ return signals;
3488
+ }
3489
+ function bounded3(value, limit) {
3490
+ const normalized = value.trim().replace(/\s+/g, " ");
3491
+ if (normalized.length <= limit) return normalized;
3492
+ return rstrip(normalized.slice(0, limit - 1)) + "...";
3493
+ }
3494
+ function sessionFromRun(run) {
3495
+ const config = run.automation;
3496
+ const keyActions3 = [
3497
+ `automation_id: ${config.automationId}`,
3498
+ `run: ${run.title}`,
3499
+ ...run.keyActions
3500
+ ];
3501
+ const filesTouched = [config.path];
3502
+ if (config.memoryPath !== null) filesTouched.push(config.memoryPath);
3503
+ return (0, import_l59.makeSession)({
3504
+ date: run.date,
3505
+ cwd: config.cwds.length > 0 ? config.cwds[0] : (0, import_node_path9.dirname)(config.path),
3506
+ project_focus: [...run.projects],
3507
+ key_actions: keyActions3.slice(0, MAX_KEY_ACTIONS_PER_RUN + 2),
3508
+ files_touched: filesTouched,
3509
+ visibility: import_l59.Visibility.TEAM
3510
+ });
3511
+ }
3512
+ function entitiesFromConfigsAndRuns(configs, runs) {
3513
+ const byName = /* @__PURE__ */ new Map();
3514
+ for (const config of configs) {
3515
+ byName.set(
3516
+ config.automationId,
3517
+ (0, import_l59.makeEntity)({
3518
+ name: config.automationId,
3519
+ type: "automation",
3520
+ summary: bounded3(
3521
+ `Claude Code automation '${config.name}' (${config.status}). Schedule: ${config.rrule || "unspecified"}.`,
3522
+ 260
3523
+ ),
3524
+ last_touched: void 0,
3525
+ tags: ["claude-code-automation", "automation", config.status.toLowerCase()],
3526
+ visibility: import_l59.Visibility.TEAM
3527
+ })
3528
+ );
3529
+ }
3530
+ for (const run of runs) {
3531
+ for (const project of run.projects) {
3532
+ if (!byName.has(project)) {
3533
+ byName.set(
3534
+ project,
3535
+ (0, import_l59.makeEntity)({
3536
+ name: project,
3537
+ type: "project",
3538
+ summary: "Project mentioned by Claude Code automation run memory.",
3539
+ last_touched: run.date,
3540
+ tags: ["claude-code-automation", "automation-evidence"],
3541
+ visibility: import_l59.Visibility.TEAM
3542
+ })
3543
+ );
3544
+ }
3545
+ }
3546
+ for (const signal of run.signals) {
3547
+ if (!byName.has(signal)) {
3548
+ byName.set(
3549
+ signal,
3550
+ (0, import_l59.makeEntity)({
3551
+ name: signal,
3552
+ type: "automation-signal",
3553
+ summary: "Signal class inferred from Claude Code automation run memory.",
3554
+ last_touched: run.date,
3555
+ tags: ["claude-code-automation", "automation-signal"],
3556
+ visibility: import_l59.Visibility.TEAM
3557
+ })
3558
+ );
3559
+ }
3560
+ }
3561
+ }
3562
+ return [...byName.values()];
3563
+ }
3564
+ var ClaudeCodeAutomationsParticipant = class {
3565
+ agentId = AGENT_ID9;
3566
+ agentType = AGENT_TYPE9;
3567
+ nativePath;
3568
+ automationsDir;
3569
+ policy;
3570
+ /**
3571
+ * Conventional Claude Code automations dir (`~/.claude/automations`). Provided
3572
+ * for protocol uniformity — the setup wizard wires the parent `claude-code`
3573
+ * participant and skips `-automations` sub-surfaces.
3574
+ */
3575
+ static defaultNativePath(home) {
3576
+ return defaultClaudeCodeAutomationsDir(home);
3577
+ }
3578
+ constructor(automationsDir, claudeHome) {
3579
+ this.automationsDir = automationsDir ?? defaultClaudeCodeAutomationsDir(claudeHome);
3580
+ this.nativePath = this.automationsDir;
3581
+ this.policy = DEFAULT_POLICY8;
3582
+ }
3583
+ discover() {
3584
+ if (!isDir7(this.automationsDir)) {
3585
+ throw new ParticipantDiscoveryError(
3586
+ `Claude Code automations directory not found at ${this.automationsDir}.`
3587
+ );
3588
+ }
3589
+ const configs = iterConfigs(this.automationsDir);
3590
+ return {
3591
+ path: this.automationsDir,
3592
+ version: "unknown",
3593
+ metadata: {
3594
+ automations: configs.length,
3595
+ with_memory: configs.filter((c) => c.memoryPath !== null).length
3596
+ }
3597
+ };
3598
+ }
3599
+ exportSessions(since, limit = 100) {
3600
+ const sessions = this.runs(void 0, since).map(sessionFromRun);
3601
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
3602
+ return sessions.slice(0, limit);
3603
+ }
3604
+ exportL5(since) {
3605
+ if (!isDir7(this.automationsDir)) {
3606
+ throw new ParticipantDiscoveryError(
3607
+ `Claude Code automations directory not found at ${this.automationsDir}.`
3608
+ );
3609
+ }
3610
+ const configs = iterConfigs(this.automationsDir);
3611
+ const runs = this.runs(configs, since);
3612
+ const sessions = runs.map(sessionFromRun);
3613
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
3614
+ const entities = entitiesFromConfigsAndRuns(configs, runs);
3615
+ const visibleEntities = (0, import_l59.filterForFederation)(entities, this.policy);
3616
+ return (0, import_l59.makeManifest)({
3617
+ spec_version: SPEC_VERSION,
3618
+ agent: (0, import_l59.makeAgentInfo)({
3619
+ id: AGENT_ID9,
3620
+ type: AGENT_TYPE9,
3621
+ instance: (0, import_node_os9.hostname)(),
3622
+ spec_version_compat: SPEC_VERSION,
3623
+ role_narrative: ROLE_NARRATIVE9
3624
+ }),
3625
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
3626
+ capabilities: ["claude-code-automation-memory", "run-summary-publication"],
3627
+ recent_sessions: sessions,
3628
+ known_entities: visibleEntities,
3629
+ visibility_policy: this.policy
3630
+ });
3631
+ }
3632
+ healthCheck() {
3633
+ if (!isDir7(this.automationsDir)) {
3634
+ return {
3635
+ status: "blocked",
3636
+ reason: `Claude Code automations directory not found at ${this.automationsDir}.`,
3637
+ details: { automations_dir: this.automationsDir },
3638
+ proposedFix: "Create the automations directory and an automation.toml: mkdir -p ~/.claude/automations/<id> && $HOME/.claude/hooks/automation-memory-append.sh <id> '...'"
3639
+ };
3640
+ }
3641
+ const configs = iterConfigs(this.automationsDir);
3642
+ const runs = this.runs(configs);
3643
+ const hasConfigs = configs.length > 0;
3644
+ const status = hasConfigs ? "ok" : "degraded";
3645
+ const details = {
3646
+ automations_dir: this.automationsDir,
3647
+ automation_count: configs.length,
3648
+ memory_files: configs.filter((c) => c.memoryPath !== null).length,
3649
+ runs_extracted: runs.length,
3650
+ active_automations: configs.filter((c) => c.status === "ACTIVE").length
3651
+ };
3652
+ if (hasConfigs) {
3653
+ return { status, details };
3654
+ }
3655
+ return {
3656
+ status,
3657
+ reason: "No automation.toml files found.",
3658
+ details,
3659
+ proposedFix: "Add Claude Code automation.toml files."
3660
+ };
3661
+ }
3662
+ runs(configs, since) {
3663
+ const runCutoff = since ? since.toISOString().slice(0, 10) : null;
3664
+ const runs = [];
3665
+ for (const config of configs ?? iterConfigs(this.automationsDir)) {
3666
+ for (const run of extractMemoryRuns(config)) {
3667
+ if (runCutoff && run.date < runCutoff) continue;
3668
+ runs.push(run);
3669
+ }
3670
+ }
3671
+ runs.sort((a, b) => {
3672
+ if (a.date !== b.date) return a.date < b.date ? 1 : -1;
3673
+ const ai = a.automation.automationId;
3674
+ const bi = b.automation.automationId;
3675
+ if (ai !== bi) return ai < bi ? 1 : -1;
3676
+ return 0;
3677
+ });
3678
+ return runs;
3679
+ }
3680
+ };
3681
+
3682
+ // src/readers/codex-automations.ts
3683
+ var import_node_fs10 = require("fs");
3684
+ var import_node_os10 = require("os");
3685
+ var import_node_path10 = require("path");
3686
+ var import_smol_toml2 = require("smol-toml");
3687
+ var import_l510 = require("@getbourdon/l5");
3688
+ var import_redaction10 = require("@getbourdon/redaction");
3689
+ var AGENT_ID10 = "codex-automations";
3690
+ var AGENT_TYPE10 = "other";
3691
+ var ROLE_NARRATIVE10 = "Publishes read-only Codex automation run memory into Bourdon so background monitors, digests, and recurring checks are visible alongside interactive agent sessions.";
3692
+ var DEFAULT_POLICY9 = (0, import_l510.makeVisibilityPolicy)({
3693
+ default: import_l510.Visibility.TEAM,
3694
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
3695
+ team_tags: ["codex-automation", "automation", "workspace"]
3696
+ });
3697
+ var AUTOMATIONS_DIR_NAME2 = "automations";
3698
+ var AUTOMATION_TOML2 = "automation.toml";
3699
+ var MEMORY_MD2 = "memory.md";
3700
+ var MAX_MEMORY_CHARS2 = 16e4;
3701
+ var MAX_KEY_ACTIONS_PER_RUN2 = 6;
3702
+ var MAX_KEY_ACTION_CHARS3 = 280;
3703
+ var RUN_HEADER_RE2 = /^(\d{4}-\d{2}-\d{2})(?:\b|$)(.*)$/;
3704
+ var PROJECT_HINTS2 = [
3705
+ "ShipStable",
3706
+ "ILTT",
3707
+ "Prun",
3708
+ "PRUN",
3709
+ "OMNIvour",
3710
+ "Castmore",
3711
+ "Bourdon",
3712
+ "RADLAB",
3713
+ "CHIP",
3714
+ "Claude Brain",
3715
+ "Cursor",
3716
+ "Copilot"
3717
+ ];
3718
+ var SIGNAL_PATTERNS2 = [
3719
+ ["human-dashboard-action", /\b(human|ryan|dashboard|manual)\b/i],
3720
+ ["release-gate", /\b(release|store|app review|play console|testflight)\b/i],
3721
+ ["billing-drift", /\b(billing|stripe|revenuecat|iap|subscription)\b/i],
3722
+ ["memory-coverage-gap", /\b(memory|l5|manifest|federated|bourdon)\b/i],
3723
+ ["launch-decision", /\b(launch|go-live|pricing|prod|production)\b/i]
3724
+ ];
3725
+ var logger4 = {
3726
+ warn(msg) {
3727
+ console.warn(`[bourdon.participants] ${msg}`);
3728
+ }
3729
+ };
3730
+ function homeDir6() {
3731
+ try {
3732
+ return (0, import_node_os10.homedir)();
3733
+ } catch {
3734
+ return process.env.HOME || process.env.USERPROFILE || ".";
3735
+ }
3736
+ }
3737
+ function isDir8(p) {
3738
+ try {
3739
+ return (0, import_node_fs10.statSync)(p).isDirectory();
3740
+ } catch {
3741
+ return false;
3742
+ }
3743
+ }
3744
+ function isFile8(p) {
3745
+ try {
3746
+ return (0, import_node_fs10.statSync)(p).isFile();
3747
+ } catch {
3748
+ return false;
3749
+ }
3750
+ }
3751
+ function defaultCodexAutomationsDir(codexHome) {
3752
+ if (codexHome !== void 0 && codexHome !== null) {
3753
+ return (0, import_node_path10.join)(codexHome, AUTOMATIONS_DIR_NAME2);
3754
+ }
3755
+ const env = process.env.CODEX_HOME;
3756
+ if (env) return (0, import_node_path10.join)(env, AUTOMATIONS_DIR_NAME2);
3757
+ return (0, import_node_path10.join)(homeDir6(), ".codex", AUTOMATIONS_DIR_NAME2);
3758
+ }
3759
+ function safeNativeMemoryText4(value) {
3760
+ return (0, import_redaction10.redactText)(value, 180);
3761
+ }
3762
+ function pyStrOr(a, b) {
3763
+ return String(a ? a : b);
3764
+ }
3765
+ function readAutomationToml2(path) {
3766
+ let text;
3767
+ try {
3768
+ text = (0, import_node_fs10.readFileSync)(path, "utf8");
3769
+ } catch (exc) {
3770
+ logger4.warn(`CodexAutomationsParticipant: cannot parse ${path}: ${String(exc)}`);
3771
+ return {};
3772
+ }
3773
+ try {
3774
+ const parsed = (0, import_smol_toml2.parse)(text);
3775
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
3776
+ } catch (exc) {
3777
+ logger4.warn(`CodexAutomationsParticipant: cannot parse ${path}: ${String(exc)}`);
3778
+ return {};
3779
+ }
3780
+ }
3781
+ function buildConfig2(tomlPath) {
3782
+ const raw = readAutomationToml2(tomlPath);
3783
+ const parentName = (0, import_node_path10.basename)((0, import_node_path10.dirname)(tomlPath));
3784
+ const automationId = pyStrOr(raw["id"], parentName).trim();
3785
+ if (!automationId) return null;
3786
+ const name = pyStrOr(raw["name"], automationId).trim();
3787
+ const status = pyStrOr(raw["status"], "UNKNOWN").trim().toUpperCase();
3788
+ const rrule = pyStrOr(raw["rrule"], "").trim();
3789
+ const kind = pyStrOr(raw["kind"], "").trim();
3790
+ const cwdsRaw = raw["cwds"];
3791
+ const cwds = Array.isArray(cwdsRaw) ? cwdsRaw.filter((cwd) => typeof cwd === "string" && cwd.trim() !== "") : [];
3792
+ const memoryPath = (0, import_node_path10.join)((0, import_node_path10.dirname)(tomlPath), MEMORY_MD2);
3793
+ return {
3794
+ automationId,
3795
+ name,
3796
+ status,
3797
+ rrule,
3798
+ kind,
3799
+ cwds,
3800
+ path: tomlPath,
3801
+ memoryPath: isFile8(memoryPath) ? memoryPath : null
3802
+ };
3803
+ }
3804
+ function iterConfigs2(automationsDir) {
3805
+ if (!isDir8(automationsDir)) return [];
3806
+ let entries;
3807
+ try {
3808
+ entries = (0, import_node_fs10.readdirSync)(automationsDir);
3809
+ } catch {
3810
+ return [];
3811
+ }
3812
+ const tomlPaths = [];
3813
+ for (const entry of entries) {
3814
+ const tomlPath = (0, import_node_path10.join)(automationsDir, entry, AUTOMATION_TOML2);
3815
+ if (isFile8(tomlPath)) tomlPaths.push(tomlPath);
3816
+ }
3817
+ tomlPaths.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
3818
+ const configs = [];
3819
+ for (const tomlPath of tomlPaths) {
3820
+ const config = buildConfig2(tomlPath);
3821
+ if (config !== null) configs.push(config);
3822
+ }
3823
+ return configs;
3824
+ }
3825
+ function readMemoryText2(path) {
3826
+ if (path === null) return "";
3827
+ let text;
3828
+ try {
3829
+ text = (0, import_node_fs10.readFileSync)(path, "utf8");
3830
+ } catch (exc) {
3831
+ logger4.warn(`CodexAutomationsParticipant: cannot read ${path}: ${String(exc)}`);
3832
+ return "";
3833
+ }
3834
+ return text.length > MAX_MEMORY_CHARS2 ? text.slice(-MAX_MEMORY_CHARS2) : text;
3835
+ }
3836
+ function splitLines2(text) {
3837
+ return text.split(/\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]/);
3838
+ }
3839
+ function rstrip2(s) {
3840
+ return s.replace(/\s+$/, "");
3841
+ }
3842
+ function collapseWhitespace(value) {
3843
+ return value.split(/\s+/).filter((p) => p.length > 0).join(" ");
3844
+ }
3845
+ function bounded4(value, limit) {
3846
+ const normalized = collapseWhitespace(value);
3847
+ if (normalized.length <= limit) return normalized;
3848
+ return rstrip2(normalized.slice(0, limit - 1)) + "...";
3849
+ }
3850
+ function actionsFromLines2(lines) {
3851
+ const actions = [];
3852
+ for (const line of lines) {
3853
+ let cleaned = line.trim();
3854
+ if (!cleaned) continue;
3855
+ cleaned = (cleaned.startsWith("- ") ? cleaned.slice(2) : cleaned).trim();
3856
+ const lowered = cleaned.toLowerCase();
3857
+ if (!cleaned || lowered.startsWith("runtime")) continue;
3858
+ if (lowered === "first run" || lowered === "follow-up") continue;
3859
+ if (lowered.startsWith("run:")) cleaned = cleaned.slice(4).trim();
3860
+ const safe = bounded4(safeNativeMemoryText4(cleaned), MAX_KEY_ACTION_CHARS3);
3861
+ if (safe && !actions.includes(safe)) actions.push(safe);
3862
+ if (actions.length >= MAX_KEY_ACTIONS_PER_RUN2) break;
3863
+ }
3864
+ return actions;
3865
+ }
3866
+ function titleFromActions2(config, actions) {
3867
+ if (actions.length === 0) return config.name;
3868
+ const first = actions[0];
3869
+ const prefix = `${config.name}: `;
3870
+ if (first.startsWith(prefix)) return bounded4(first, 120);
3871
+ return bounded4(prefix + first, 120);
3872
+ }
3873
+ function inferProjects2(text) {
3874
+ const projects = [];
3875
+ const lowered = text.toLowerCase();
3876
+ const seen = /* @__PURE__ */ new Set();
3877
+ for (const project of PROJECT_HINTS2) {
3878
+ const key = project.toLowerCase();
3879
+ if (lowered.includes(key) && !seen.has(key)) {
3880
+ projects.push(project);
3881
+ seen.add(key);
3882
+ }
3883
+ }
3884
+ return projects;
3885
+ }
3886
+ function inferSignals2(text) {
3887
+ const signals = [];
3888
+ for (const [name, pattern] of SIGNAL_PATTERNS2) {
3889
+ if (pattern.test(text)) signals.push(name);
3890
+ }
3891
+ return signals;
3892
+ }
3893
+ function extractMemoryRuns2(config) {
3894
+ const text = readMemoryText2(config.memoryPath);
3895
+ if (!text) return [];
3896
+ const chunks = [];
3897
+ let currentDate = "";
3898
+ let currentLines = [];
3899
+ for (const rawLine of splitLines2(text)) {
3900
+ const line = rstrip2(rawLine);
3901
+ const match = RUN_HEADER_RE2.exec(line.trim());
3902
+ if (match) {
3903
+ if (currentDate) chunks.push([currentDate, currentLines]);
3904
+ currentDate = match[1];
3905
+ const suffix = stripChars3(match[2].trim(), " -:\u2014");
3906
+ currentLines = suffix ? [suffix] : [];
3907
+ continue;
3908
+ }
3909
+ if (currentDate) currentLines.push(line);
3910
+ }
3911
+ if (currentDate) chunks.push([currentDate, currentLines]);
3912
+ const runs = [];
3913
+ for (const [runDate, lines] of chunks) {
3914
+ const actions = actionsFromLines2(lines);
3915
+ if (actions.length === 0) continue;
3916
+ const body = actions.join(" ");
3917
+ const title = titleFromActions2(config, actions);
3918
+ runs.push({
3919
+ automation: config,
3920
+ date: runDate,
3921
+ title,
3922
+ keyActions: actions,
3923
+ projects: inferProjects2(body),
3924
+ signals: inferSignals2(body)
3925
+ });
3926
+ }
3927
+ return runs;
3928
+ }
3929
+ function stripChars3(s, chars) {
3930
+ let start = 0;
3931
+ let end = s.length;
3932
+ while (start < end && chars.includes(s[start])) start++;
3933
+ while (end > start && chars.includes(s[end - 1])) end--;
3934
+ return s.slice(start, end);
3935
+ }
3936
+ function sessionFromRun2(run) {
3937
+ const config = run.automation;
3938
+ const keyActions3 = [
3939
+ `automation_id: ${config.automationId}`,
3940
+ `run: ${run.title}`,
3941
+ ...run.keyActions
3942
+ ];
3943
+ const filesTouched = [config.path];
3944
+ if (config.memoryPath !== null) filesTouched.push(config.memoryPath);
3945
+ return (0, import_l510.makeSession)({
3946
+ date: run.date,
3947
+ cwd: config.cwds.length > 0 ? config.cwds[0] : (0, import_node_path10.dirname)(config.path),
3948
+ project_focus: [...run.projects],
3949
+ key_actions: keyActions3.slice(0, MAX_KEY_ACTIONS_PER_RUN2 + 2),
3950
+ files_touched: filesTouched,
3951
+ visibility: import_l510.Visibility.TEAM
3952
+ });
3953
+ }
3954
+ function entitiesFromConfigsAndRuns2(configs, runs) {
3955
+ const byName = /* @__PURE__ */ new Map();
3956
+ for (const config of configs) {
3957
+ byName.set(
3958
+ config.automationId,
3959
+ (0, import_l510.makeEntity)({
3960
+ name: config.automationId,
3961
+ type: "automation",
3962
+ summary: bounded4(
3963
+ `Codex automation '${config.name}' (${config.status}). Schedule: ${config.rrule || "unspecified"}.`,
3964
+ 260
3965
+ ),
3966
+ last_touched: void 0,
3967
+ tags: ["codex-automation", "automation", config.status.toLowerCase()],
3968
+ visibility: import_l510.Visibility.TEAM
3969
+ })
3970
+ );
3971
+ }
3972
+ for (const run of runs) {
3973
+ for (const project of run.projects) {
3974
+ if (!byName.has(project)) {
3975
+ byName.set(
3976
+ project,
3977
+ (0, import_l510.makeEntity)({
3978
+ name: project,
3979
+ type: "project",
3980
+ summary: "Project mentioned by Codex automation run memory.",
3981
+ last_touched: run.date,
3982
+ tags: ["codex-automation", "automation-evidence"],
3983
+ visibility: import_l510.Visibility.TEAM
3984
+ })
3985
+ );
3986
+ }
3987
+ }
3988
+ for (const signal of run.signals) {
3989
+ if (!byName.has(signal)) {
3990
+ byName.set(
3991
+ signal,
3992
+ (0, import_l510.makeEntity)({
3993
+ name: signal,
3994
+ type: "automation-signal",
3995
+ summary: "Signal class inferred from Codex automation run memory.",
3996
+ last_touched: run.date,
3997
+ tags: ["codex-automation", "automation-signal"],
3998
+ visibility: import_l510.Visibility.TEAM
3999
+ })
4000
+ );
4001
+ }
4002
+ }
4003
+ }
4004
+ return [...byName.values()];
4005
+ }
4006
+ var CodexAutomationsParticipant = class {
4007
+ agentId = AGENT_ID10;
4008
+ agentType = AGENT_TYPE10;
4009
+ automationsDir;
4010
+ policy;
4011
+ /**
4012
+ * Conventional Codex automations dir (`~/.codex/automations`). Provided for
4013
+ * protocol uniformity — the setup wizard wires the parent `codex` participant
4014
+ * and skips `-automations` sub-surfaces rather than detecting this one.
4015
+ */
4016
+ static defaultNativePath(home) {
4017
+ return defaultCodexAutomationsDir(home);
4018
+ }
4019
+ constructor(automationsDir, codexHome) {
4020
+ this.automationsDir = automationsDir ?? defaultCodexAutomationsDir(codexHome);
4021
+ this.policy = DEFAULT_POLICY9;
4022
+ }
4023
+ get nativePath() {
4024
+ return this.automationsDir;
4025
+ }
4026
+ discover() {
4027
+ if (!isDir8(this.automationsDir)) {
4028
+ throw new ParticipantDiscoveryError(
4029
+ `Codex automations directory not found at ${this.automationsDir}.`
4030
+ );
4031
+ }
4032
+ const configs = iterConfigs2(this.automationsDir);
4033
+ return {
4034
+ path: this.automationsDir,
4035
+ version: "unknown",
4036
+ metadata: {
4037
+ automations: configs.length,
4038
+ with_memory: configs.filter((c) => c.memoryPath !== null).length
4039
+ }
4040
+ };
4041
+ }
4042
+ exportSessions(since, limit = 100) {
4043
+ const sessions = this.runs(void 0, since).map(sessionFromRun2);
4044
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
4045
+ return sessions.slice(0, limit);
4046
+ }
4047
+ exportL5(since) {
4048
+ const configs = iterConfigs2(this.automationsDir);
4049
+ const runs = this.runs(configs, since);
4050
+ const sessions = runs.map(sessionFromRun2);
4051
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
4052
+ const entities = entitiesFromConfigsAndRuns2(configs, runs);
4053
+ const visibleEntities = (0, import_l510.filterForFederation)(entities, this.policy);
4054
+ return (0, import_l510.makeManifest)({
4055
+ spec_version: SPEC_VERSION,
4056
+ agent: (0, import_l510.makeAgentInfo)({
4057
+ id: AGENT_ID10,
4058
+ type: AGENT_TYPE10,
4059
+ instance: (0, import_node_os10.hostname)(),
4060
+ spec_version_compat: SPEC_VERSION,
4061
+ role_narrative: ROLE_NARRATIVE10
4062
+ }),
4063
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
4064
+ capabilities: ["codex-automation-memory", "run-summary-publication"],
4065
+ recent_sessions: sessions,
4066
+ known_entities: visibleEntities,
4067
+ visibility_policy: this.policy
4068
+ });
4069
+ }
4070
+ healthCheck() {
4071
+ if (!isDir8(this.automationsDir)) {
4072
+ return {
4073
+ status: "blocked",
4074
+ reason: `Codex automations directory not found at ${this.automationsDir}.`,
4075
+ details: { automations_dir: this.automationsDir },
4076
+ proposedFix: "Create Codex automations or pass --automations-dir."
4077
+ };
4078
+ }
4079
+ const configs = iterConfigs2(this.automationsDir);
4080
+ const runs = this.runs(configs);
4081
+ const hasConfigs = configs.length > 0;
4082
+ return {
4083
+ status: hasConfigs ? "ok" : "degraded",
4084
+ reason: hasConfigs ? void 0 : "No automation.toml files found.",
4085
+ details: {
4086
+ automations_dir: this.automationsDir,
4087
+ automation_count: configs.length,
4088
+ memory_files: configs.filter((c) => c.memoryPath !== null).length,
4089
+ runs_extracted: runs.length,
4090
+ active_automations: configs.filter((c) => c.status === "ACTIVE").length
4091
+ },
4092
+ proposedFix: hasConfigs ? void 0 : "Add Codex automation.toml files."
4093
+ };
4094
+ }
4095
+ // -- Internal ---------------------------------------------------------------
4096
+ runs(configs, since) {
4097
+ const runCutoff = since ? since.toISOString().slice(0, 10) : null;
4098
+ const runs = [];
4099
+ for (const config of configs ?? iterConfigs2(this.automationsDir)) {
4100
+ for (const run of extractMemoryRuns2(config)) {
4101
+ if (runCutoff && run.date < runCutoff) continue;
4102
+ runs.push(run);
4103
+ }
4104
+ }
4105
+ runs.sort((a, b) => {
4106
+ if (a.date !== b.date) return a.date < b.date ? 1 : -1;
4107
+ const ai = a.automation.automationId;
4108
+ const bi = b.automation.automationId;
4109
+ if (ai !== bi) return ai < bi ? 1 : -1;
4110
+ return 0;
4111
+ });
4112
+ return runs;
4113
+ }
4114
+ };
4115
+
4116
+ // src/readers/copilot-automations.ts
4117
+ var import_node_fs11 = require("fs");
4118
+ var import_node_os11 = require("os");
4119
+ var import_node_path11 = require("path");
4120
+ var import_smol_toml3 = require("smol-toml");
4121
+ var import_l511 = require("@getbourdon/l5");
4122
+ var import_redaction11 = require("@getbourdon/redaction");
4123
+ var AGENT_ID11 = "copilot-automations";
4124
+ var AGENT_TYPE11 = "other";
4125
+ var DISPLAY_NAME7 = "GitHub Copilot \xB7 Automations";
4126
+ var ROLE_NARRATIVE11 = "Publishes read-only Copilot automation run memory into Bourdon so GitHub Actions-triggered Copilot tasks, scheduled PR reviews, issue triage, and other event-driven Copilot work are visible alongside interactive agent sessions.";
4127
+ var DEFAULT_POLICY10 = (0, import_l511.makeVisibilityPolicy)({
4128
+ default: import_l511.Visibility.TEAM,
4129
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
4130
+ team_tags: ["copilot-automation", "automation", "github-actions", "workspace"]
4131
+ });
4132
+ var CONVENTION_DIR_NAME3 = ".copilot-bourdon";
4133
+ var AUTOMATIONS_DIR = "automations";
4134
+ var AUTOMATION_TOML3 = "automation.toml";
4135
+ var MEMORY_MD3 = "memory.md";
4136
+ var MAX_MEMORY_CHARS3 = 16e4;
4137
+ var MAX_KEY_ACTIONS_PER_RUN3 = 6;
4138
+ var MAX_KEY_ACTION_CHARS4 = 280;
4139
+ var RUN_HEADER_RE3 = /^(\d{4}-\d{2}-\d{2})(?:\b|$)(.*)$/;
4140
+ var logger5 = {
4141
+ warn(msg) {
4142
+ console.warn(`[bourdon.participants] ${msg}`);
4143
+ }
4144
+ };
4145
+ function homeDir7() {
4146
+ try {
4147
+ return (0, import_node_os11.homedir)();
4148
+ } catch {
4149
+ return process.env.HOME || process.env.USERPROFILE || ".";
4150
+ }
4151
+ }
4152
+ function isDir9(p) {
4153
+ try {
4154
+ return (0, import_node_fs11.statSync)(p).isDirectory();
4155
+ } catch {
4156
+ return false;
4157
+ }
4158
+ }
4159
+ function isFile9(p) {
4160
+ try {
4161
+ return (0, import_node_fs11.statSync)(p).isFile();
4162
+ } catch {
4163
+ return false;
4164
+ }
4165
+ }
4166
+ function isRecord5(v) {
4167
+ return typeof v === "object" && v !== null && !Array.isArray(v);
4168
+ }
4169
+ function pyTruthy(v) {
4170
+ if (v === null || v === void 0 || v === false) return false;
4171
+ if (typeof v === "string") return v.length > 0;
4172
+ if (typeof v === "number") return v !== 0;
4173
+ if (Array.isArray(v)) return v.length > 0;
4174
+ return true;
4175
+ }
4176
+ function pyStr2(v) {
4177
+ if (typeof v === "string") return v;
4178
+ if (typeof v === "boolean") return v ? "True" : "False";
4179
+ if (v === null || v === void 0) return "None";
4180
+ return String(v);
4181
+ }
4182
+ function defaultCopilotAutomationsDir(copilotBourdonDir) {
4183
+ const env = process.env.COPILOT_AUTOMATIONS_HOME;
4184
+ if (env) return env;
4185
+ const base = copilotBourdonDir || (0, import_node_path11.join)(homeDir7(), CONVENTION_DIR_NAME3);
4186
+ return (0, import_node_path11.join)(base, AUTOMATIONS_DIR);
4187
+ }
4188
+ function readAutomationToml3(path) {
4189
+ let text;
4190
+ try {
4191
+ text = (0, import_node_fs11.readFileSync)(path, "utf8");
4192
+ } catch (exc) {
4193
+ logger5.warn(`CopilotAutomationsParticipant: cannot parse ${path}: ${String(exc)}`);
4194
+ return {};
4195
+ }
4196
+ try {
4197
+ const parsed = (0, import_smol_toml3.parse)(text);
4198
+ return isRecord5(parsed) ? parsed : {};
4199
+ } catch (exc) {
4200
+ logger5.warn(`CopilotAutomationsParticipant: cannot parse ${path}: ${String(exc)}`);
4201
+ return {};
4202
+ }
4203
+ }
4204
+ function buildConfig3(tomlPath) {
4205
+ const raw = readAutomationToml3(tomlPath);
4206
+ const parentDir = (0, import_node_path11.dirname)(tomlPath);
4207
+ const parentName = parentDir.split(/[\\/]/).filter(Boolean).pop() ?? "";
4208
+ const automationId = pyStr2(pyTruthy(raw["id"]) ? raw["id"] : parentName).trim();
4209
+ if (!automationId) return null;
4210
+ const name = pyStr2(pyTruthy(raw["name"]) ? raw["name"] : automationId).trim();
4211
+ const status = pyStr2(pyTruthy(raw["status"]) ? raw["status"] : "UNKNOWN").trim().toUpperCase();
4212
+ const trigger = pyStr2(pyTruthy(raw["trigger"]) ? raw["trigger"] : "").trim();
4213
+ const kind = pyStr2(pyTruthy(raw["kind"]) ? raw["kind"] : "").trim();
4214
+ const reposRaw = pyTruthy(raw["repos"]) ? raw["repos"] : [];
4215
+ const reposIter = Array.isArray(reposRaw) ? reposRaw : typeof reposRaw === "string" ? Array.from(reposRaw) : [];
4216
+ const repos = reposIter.filter(
4217
+ (r) => typeof r === "string" && r.trim() !== ""
4218
+ );
4219
+ const memoryPath = (0, import_node_path11.join)(parentDir, MEMORY_MD3);
4220
+ return {
4221
+ automationId,
4222
+ name,
4223
+ status,
4224
+ trigger,
4225
+ kind,
4226
+ repos,
4227
+ path: tomlPath,
4228
+ memoryPath: isFile9(memoryPath) ? memoryPath : null
4229
+ };
4230
+ }
4231
+ function iterConfigs3(automationsDir) {
4232
+ const configs = [];
4233
+ if (!isDir9(automationsDir)) return configs;
4234
+ let entries;
4235
+ try {
4236
+ entries = (0, import_node_fs11.readdirSync)(automationsDir);
4237
+ } catch {
4238
+ return configs;
4239
+ }
4240
+ const tomlPaths = [];
4241
+ for (const entry of entries) {
4242
+ const candidate = (0, import_node_path11.join)(automationsDir, entry, AUTOMATION_TOML3);
4243
+ if (isFile9(candidate)) tomlPaths.push(candidate);
4244
+ }
4245
+ tomlPaths.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
4246
+ for (const tomlPath of tomlPaths) {
4247
+ const config = buildConfig3(tomlPath);
4248
+ if (config !== null) configs.push(config);
4249
+ }
4250
+ return configs;
4251
+ }
4252
+ function splitLines3(text) {
4253
+ if (text.length === 0) return [];
4254
+ return text.split(/\r\n|\r|\n|\v|\f|\x1c|\x1d|\x1e|\x85|\u2028|\u2029/);
4255
+ }
4256
+ function rstrip3(s) {
4257
+ return s.replace(/\s+$/u, "");
4258
+ }
4259
+ function stripChars4(s, chars) {
4260
+ let start = 0;
4261
+ let end = s.length;
4262
+ while (start < end && chars.includes(s[start])) start++;
4263
+ while (end > start && chars.includes(s[end - 1])) end--;
4264
+ return s.slice(start, end);
4265
+ }
4266
+ function extractMemoryRuns3(config) {
4267
+ if (config.memoryPath === null) return [];
4268
+ let text;
4269
+ try {
4270
+ text = (0, import_node_fs11.readFileSync)(config.memoryPath, "utf8");
4271
+ } catch (exc) {
4272
+ logger5.warn(
4273
+ `CopilotAutomationsParticipant: cannot read ${config.memoryPath}: ${String(exc)}`
4274
+ );
4275
+ return [];
4276
+ }
4277
+ text = text.slice(-MAX_MEMORY_CHARS3);
4278
+ const chunks = [];
4279
+ let currentDate = "";
4280
+ let currentLines = [];
4281
+ for (const rawLine of splitLines3(text)) {
4282
+ const line = rstrip3(rawLine);
4283
+ const match = RUN_HEADER_RE3.exec(line.trim());
4284
+ if (match) {
4285
+ if (currentDate) chunks.push([currentDate, currentLines]);
4286
+ currentDate = match[1];
4287
+ const suffix = stripChars4((match[2] ?? "").trim(), " -:\u2014");
4288
+ currentLines = suffix ? [suffix] : [];
4289
+ continue;
4290
+ }
4291
+ if (currentDate) currentLines.push(line);
4292
+ }
4293
+ if (currentDate) chunks.push([currentDate, currentLines]);
4294
+ const runs = [];
4295
+ for (const [runDate, lines] of chunks) {
4296
+ const actions = actionsFromLines3(lines);
4297
+ if (actions.length === 0) continue;
4298
+ const title = titleFromActions3(config, actions);
4299
+ runs.push({
4300
+ automation: config,
4301
+ date: runDate,
4302
+ title,
4303
+ keyActions: actions,
4304
+ repos: config.repos
4305
+ });
4306
+ }
4307
+ return runs;
4308
+ }
4309
+ function actionsFromLines3(lines) {
4310
+ const actions = [];
4311
+ for (const line of lines) {
4312
+ let cleaned = line.trim();
4313
+ if (!cleaned) continue;
4314
+ cleaned = (cleaned.startsWith("- ") ? cleaned.slice(2) : cleaned).trim();
4315
+ if (!cleaned || cleaned.toLowerCase().startsWith("runtime")) continue;
4316
+ const safe = bounded5(safeNativeMemoryText5(cleaned), MAX_KEY_ACTION_CHARS4);
4317
+ if (safe && !actions.includes(safe)) actions.push(safe);
4318
+ if (actions.length >= MAX_KEY_ACTIONS_PER_RUN3) break;
4319
+ }
4320
+ return actions;
4321
+ }
4322
+ function titleFromActions3(config, actions) {
4323
+ if (actions.length === 0) return config.name;
4324
+ const first = actions[0];
4325
+ const prefix = `${config.name}: `;
4326
+ if (first.startsWith(prefix)) return bounded5(first, 120);
4327
+ return bounded5(prefix + first, 120);
4328
+ }
4329
+ function bounded5(value, limit) {
4330
+ const normalized = value.split(/\s+/u).filter((x) => x.length > 0).join(" ");
4331
+ if (normalized.length <= limit) return normalized;
4332
+ return rstrip3(normalized.slice(0, limit - 1)) + "\u2026";
4333
+ }
4334
+ function safeNativeMemoryText5(value) {
4335
+ return (0, import_redaction11.redactText)(value, 180);
4336
+ }
4337
+ function sessionFromRun3(run) {
4338
+ const config = run.automation;
4339
+ const keyActions3 = [
4340
+ `automation: ${config.automationId}`,
4341
+ config.trigger ? `trigger: ${config.trigger}` : `kind: ${config.kind}`,
4342
+ `run: ${run.title}`,
4343
+ ...run.keyActions
4344
+ ];
4345
+ return (0, import_l511.makeSession)({
4346
+ date: run.date,
4347
+ cwd: (0, import_node_path11.dirname)(config.path),
4348
+ project_focus: [...run.repos],
4349
+ key_actions: keyActions3.slice(0, MAX_KEY_ACTIONS_PER_RUN3 + 3),
4350
+ files_touched: [config.path],
4351
+ visibility: import_l511.Visibility.TEAM
4352
+ });
4353
+ }
4354
+ function entitiesFromConfigsAndRuns3(configs, runs) {
4355
+ const entities = /* @__PURE__ */ new Map();
4356
+ for (const config of configs) {
4357
+ entities.set(
4358
+ config.automationId,
4359
+ (0, import_l511.makeEntity)({
4360
+ name: config.automationId,
4361
+ type: "automation",
4362
+ summary: bounded5(
4363
+ `Copilot automation '${config.name}' (${config.status}). Trigger: ${config.trigger || "unspecified"}. Kind: ${config.kind || "unspecified"}.`,
4364
+ 260
4365
+ ),
4366
+ last_touched: void 0,
4367
+ tags: ["copilot-automation", "automation", config.status.toLowerCase()],
4368
+ visibility: import_l511.Visibility.TEAM
4369
+ })
4370
+ );
4371
+ }
4372
+ for (const run of runs) {
4373
+ for (const repo of run.repos) {
4374
+ if (!entities.has(repo)) {
4375
+ entities.set(
4376
+ repo,
4377
+ (0, import_l511.makeEntity)({
4378
+ name: repo,
4379
+ type: "project",
4380
+ summary: "Repository targeted by Copilot automation.",
4381
+ last_touched: run.date,
4382
+ tags: ["copilot-automation", "project"],
4383
+ visibility: import_l511.Visibility.TEAM
4384
+ })
4385
+ );
4386
+ }
4387
+ }
4388
+ }
4389
+ return [...entities.values()];
4390
+ }
4391
+ var CopilotAutomationsParticipant = class {
4392
+ agentId = AGENT_ID11;
4393
+ agentType = AGENT_TYPE11;
4394
+ displayName = DISPLAY_NAME7;
4395
+ automationsDir;
4396
+ policy;
4397
+ /** Conventional automations dir (`~/.copilot-bourdon/automations`). Does NOT
4398
+ * consult `COPILOT_AUTOMATIONS_HOME` (matches Python `default_native_path`). */
4399
+ static defaultNativePath(home) {
4400
+ const base = (0, import_node_path11.join)(home ?? homeDir7(), CONVENTION_DIR_NAME3);
4401
+ return (0, import_node_path11.join)(base, AUTOMATIONS_DIR);
4402
+ }
4403
+ constructor(automationsDir) {
4404
+ this.automationsDir = automationsDir ?? defaultCopilotAutomationsDir();
4405
+ this.policy = DEFAULT_POLICY10;
4406
+ }
4407
+ get nativePath() {
4408
+ return this.automationsDir;
4409
+ }
4410
+ // -- Protocol surface -------------------------------------------------------
4411
+ discover() {
4412
+ if (!isDir9(this.automationsDir)) {
4413
+ throw new ParticipantDiscoveryError(
4414
+ `Copilot automations directory not found at ${this.automationsDir}. Run \`bourdon copilot-automations init <name>\` to create one.`
4415
+ );
4416
+ }
4417
+ const configs = iterConfigs3(this.automationsDir);
4418
+ return {
4419
+ path: this.automationsDir,
4420
+ version: "convention-v1",
4421
+ metadata: {
4422
+ automations: configs.length,
4423
+ with_memory: configs.filter((c) => c.memoryPath !== null).length
4424
+ }
4425
+ };
4426
+ }
4427
+ exportSessions(since, limit = 100) {
4428
+ const sessions = this.runs(void 0, since).map(sessionFromRun3);
4429
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
4430
+ return sessions.slice(0, limit);
4431
+ }
4432
+ exportL5(since) {
4433
+ const configs = iterConfigs3(this.automationsDir);
4434
+ const runs = this.runs(configs, since);
4435
+ const sessions = runs.map(sessionFromRun3);
4436
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
4437
+ const entities = entitiesFromConfigsAndRuns3(configs, runs);
4438
+ const visibleEntities = (0, import_l511.filterForFederation)(entities, this.policy);
4439
+ return (0, import_l511.makeManifest)({
4440
+ spec_version: SPEC_VERSION,
4441
+ agent: (0, import_l511.makeAgentInfo)({
4442
+ id: AGENT_ID11,
4443
+ type: AGENT_TYPE11,
4444
+ instance: (0, import_node_os11.hostname)() || "unknown",
4445
+ spec_version_compat: `>=${SPEC_VERSION}`,
4446
+ role_narrative: ROLE_NARRATIVE11
4447
+ }),
4448
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
4449
+ capabilities: ["copilot-automation-memory", "run-summary-publication"],
4450
+ recent_sessions: sessions,
4451
+ known_entities: visibleEntities,
4452
+ visibility_policy: this.policy
4453
+ });
4454
+ }
4455
+ healthCheck() {
4456
+ if (!isDir9(this.automationsDir)) {
4457
+ return {
4458
+ status: "blocked",
4459
+ reason: `Copilot automations directory not found at ${this.automationsDir}.`,
4460
+ details: { automations_dir: this.automationsDir },
4461
+ proposedFix: "Run `bourdon copilot-automations init <name>` to create an automation."
4462
+ };
4463
+ }
4464
+ const configs = iterConfigs3(this.automationsDir);
4465
+ const runs = this.runs(configs);
4466
+ const hasConfigs = configs.length > 0;
4467
+ return {
4468
+ status: hasConfigs ? "ok" : "degraded",
4469
+ reason: hasConfigs ? void 0 : "No automation.toml files found.",
4470
+ details: {
4471
+ automations_dir: this.automationsDir,
4472
+ automation_count: configs.length,
4473
+ memory_files: configs.filter((c) => c.memoryPath !== null).length,
4474
+ runs_extracted: runs.length,
4475
+ active_automations: configs.filter((c) => c.status === "ACTIVE").length
4476
+ },
4477
+ proposedFix: hasConfigs ? void 0 : "Run `bourdon copilot-automations init <name>` to create an automation, or add automation.toml files manually."
4478
+ };
4479
+ }
4480
+ // -- Internal ---------------------------------------------------------------
4481
+ runs(configs, since) {
4482
+ const runCutoff = since ? since.toISOString().slice(0, 10) : null;
4483
+ const out = [];
4484
+ for (const config of configs ?? iterConfigs3(this.automationsDir)) {
4485
+ for (const run of extractMemoryRuns3(config)) {
4486
+ if (runCutoff && run.date < runCutoff) continue;
4487
+ out.push(run);
4488
+ }
4489
+ }
4490
+ out.sort((a, b) => {
4491
+ if (a.date !== b.date) return a.date < b.date ? 1 : -1;
4492
+ const ai = a.automation.automationId;
4493
+ const bi = b.automation.automationId;
4494
+ if (ai !== bi) return ai < bi ? 1 : -1;
4495
+ return 0;
4496
+ });
4497
+ return out;
4498
+ }
4499
+ };
4500
+
4501
+ // src/readers/cursor-automations.ts
4502
+ var import_node_fs12 = require("fs");
4503
+ var import_node_os12 = require("os");
4504
+ var import_node_path12 = require("path");
4505
+ var import_smol_toml4 = require("smol-toml");
4506
+ var import_l512 = require("@getbourdon/l5");
4507
+ var import_redaction12 = require("@getbourdon/redaction");
4508
+ var AGENT_ID12 = "cursor-automations";
4509
+ var AGENT_TYPE12 = "other";
4510
+ var ROLE_NARRATIVE12 = "Publishes read-only Cursor Cloud Agent automation run memory into Bourdon so background PR reviews, code generation tasks, and scheduled agent runs become visible alongside interactive Cursor IDE sessions.";
4511
+ var DEFAULT_POLICY11 = (0, import_l512.makeVisibilityPolicy)({
4512
+ default: import_l512.Visibility.TEAM,
4513
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
4514
+ team_tags: ["cursor-automation", "automation", "workspace"]
4515
+ });
4516
+ var AUTOMATIONS_DIR_NAME3 = "automations";
4517
+ var AUTOMATION_TOML4 = "automation.toml";
4518
+ var MEMORY_MD4 = "memory.md";
4519
+ var MAX_MEMORY_CHARS4 = 16e4;
4520
+ var MAX_KEY_ACTIONS_PER_RUN4 = 6;
4521
+ var MAX_KEY_ACTION_CHARS5 = 280;
4522
+ var RUN_HEADER_RE4 = /^(\d{4}-\d{2}-\d{2})(?:\b|$)(.*)$/;
4523
+ var HEADER_SUFFIX_STRIP2 = " -:\u2014";
4524
+ var PROJECT_HINTS3 = [
4525
+ "ShipStable",
4526
+ "ILTT",
4527
+ "Prun",
4528
+ "PRUN",
4529
+ "OMNIvour",
4530
+ "Castmore",
4531
+ "Bourdon",
4532
+ "RADLAB",
4533
+ "CHIP",
4534
+ "Claude Brain",
4535
+ "Cursor",
4536
+ "Copilot",
4537
+ "Codex",
4538
+ "Cascade"
4539
+ ];
4540
+ var SIGNAL_PATTERNS3 = [
4541
+ ["human-dashboard-action", /\b(human|ryan|dashboard|manual)\b/i],
4542
+ ["release-gate", /\b(release|store|app review|play console|testflight)\b/i],
4543
+ ["billing-drift", /\b(billing|stripe|revenuecat|iap|subscription)\b/i],
4544
+ ["memory-coverage-gap", /\b(memory|l5|manifest|federated|bourdon)\b/i],
4545
+ ["launch-decision", /\b(launch|go-live|pricing|prod|production)\b/i],
4546
+ ["ci-signal", /\b(github action|workflow run|ci|cd|deploy|pipeline)\b/i]
4547
+ ];
4548
+ var logger6 = {
4549
+ warn(msg) {
4550
+ console.warn(`[bourdon.participants] ${msg}`);
4551
+ }
4552
+ };
4553
+ function homeDir8() {
4554
+ try {
4555
+ return (0, import_node_os12.homedir)();
4556
+ } catch {
4557
+ return process.env.HOME || process.env.USERPROFILE || ".";
4558
+ }
4559
+ }
4560
+ function isDir10(p) {
4561
+ try {
4562
+ return (0, import_node_fs12.statSync)(p).isDirectory();
4563
+ } catch {
4564
+ return false;
4565
+ }
4566
+ }
4567
+ function isFile10(p) {
4568
+ try {
4569
+ return (0, import_node_fs12.statSync)(p).isFile();
4570
+ } catch {
4571
+ return false;
4572
+ }
4573
+ }
4574
+ function pyFalsy(v) {
4575
+ return v === void 0 || v === null || v === "" || v === 0 || v === false;
4576
+ }
4577
+ function strOr2(v, fallback) {
4578
+ return String(pyFalsy(v) ? fallback : v);
4579
+ }
4580
+ function stripChars5(s, chars) {
4581
+ let start = 0;
4582
+ let end = s.length;
4583
+ while (start < end && chars.includes(s[start])) start++;
4584
+ while (end > start && chars.includes(s[end - 1])) end--;
4585
+ return s.slice(start, end);
4586
+ }
4587
+ function rstrip4(s) {
4588
+ return s.replace(/\s+$/, "");
4589
+ }
4590
+ function bounded6(value, limit) {
4591
+ const trimmed = value.trim();
4592
+ const normalized = trimmed === "" ? "" : trimmed.split(/\s+/).join(" ");
4593
+ if (normalized.length <= limit) return normalized;
4594
+ return rstrip4(normalized.slice(0, limit - 1)) + "...";
4595
+ }
4596
+ function safeNativeMemoryText6(value) {
4597
+ return (0, import_redaction12.redactText)(value, 180);
4598
+ }
4599
+ function defaultCursorAutomationsDir(cursorHome) {
4600
+ if (cursorHome !== void 0 && cursorHome !== null) {
4601
+ return (0, import_node_path12.join)(cursorHome, AUTOMATIONS_DIR_NAME3);
4602
+ }
4603
+ const env = process.env.CURSOR_DIR || process.env.CURSOR_HOME;
4604
+ if (env) return (0, import_node_path12.join)(env, AUTOMATIONS_DIR_NAME3);
4605
+ return (0, import_node_path12.join)(homeDir8(), ".cursor", AUTOMATIONS_DIR_NAME3);
4606
+ }
4607
+ function readAutomationToml4(path) {
4608
+ let text;
4609
+ try {
4610
+ text = (0, import_node_fs12.readFileSync)(path, "utf8");
4611
+ } catch (exc) {
4612
+ logger6.warn(`CursorAutomationsParticipant: cannot parse ${path}: ${String(exc)}`);
4613
+ return {};
4614
+ }
4615
+ try {
4616
+ const parsed = (0, import_smol_toml4.parse)(text);
4617
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
4618
+ } catch (exc) {
4619
+ logger6.warn(`CursorAutomationsParticipant: cannot parse ${path}: ${String(exc)}`);
4620
+ return {};
4621
+ }
4622
+ }
4623
+ function buildConfig4(tomlPath) {
4624
+ const raw = readAutomationToml4(tomlPath);
4625
+ const parentName = basenameOf3((0, import_node_path12.dirname)(tomlPath));
4626
+ const automationId = strOr2(raw["id"], parentName).trim();
4627
+ if (!automationId) return null;
4628
+ const name = strOr2(raw["name"], automationId).trim();
4629
+ const status = strOr2(raw["status"], "UNKNOWN").trim().toUpperCase();
4630
+ const rrule = strOr2(raw["rrule"], "").trim();
4631
+ const kind = strOr2(raw["kind"], "").trim();
4632
+ const cwdsRaw = raw["cwds"];
4633
+ const cwds = [];
4634
+ if (Array.isArray(cwdsRaw)) {
4635
+ for (const cwd of cwdsRaw) {
4636
+ if (typeof cwd === "string" && cwd.trim()) cwds.push(cwd);
4637
+ }
4638
+ } else if (typeof cwdsRaw === "string") {
4639
+ for (const ch of cwdsRaw) {
4640
+ if (ch.trim()) cwds.push(ch);
4641
+ }
4642
+ }
4643
+ const memoryPath = (0, import_node_path12.join)((0, import_node_path12.dirname)(tomlPath), MEMORY_MD4);
4644
+ return {
4645
+ automationId,
4646
+ name,
4647
+ status,
4648
+ rrule,
4649
+ kind,
4650
+ cwds,
4651
+ path: tomlPath,
4652
+ memoryPath: isFile10(memoryPath) ? memoryPath : null
4653
+ };
4654
+ }
4655
+ function basenameOf3(p) {
4656
+ const parts = p.split(/[\\/]+/).filter((x) => x !== "");
4657
+ return parts.length ? parts[parts.length - 1] : "";
4658
+ }
4659
+ function iterConfigs4(automationsDir) {
4660
+ const configs = [];
4661
+ if (!isDir10(automationsDir)) return configs;
4662
+ let names;
4663
+ try {
4664
+ names = (0, import_node_fs12.readdirSync)(automationsDir);
4665
+ } catch {
4666
+ return configs;
4667
+ }
4668
+ const tomlPaths = [];
4669
+ for (const name of names) {
4670
+ const tomlPath = (0, import_node_path12.join)(automationsDir, name, AUTOMATION_TOML4);
4671
+ if (isFile10(tomlPath)) tomlPaths.push(tomlPath);
4672
+ }
4673
+ tomlPaths.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
4674
+ for (const tomlPath of tomlPaths) {
4675
+ const config = buildConfig4(tomlPath);
4676
+ if (config !== null) configs.push(config);
4677
+ }
4678
+ return configs;
4679
+ }
4680
+ function readMemoryText3(path) {
4681
+ if (path === null) return "";
4682
+ let text;
4683
+ try {
4684
+ text = (0, import_node_fs12.readFileSync)(path, "utf8");
4685
+ } catch (exc) {
4686
+ logger6.warn(`CursorAutomationsParticipant: cannot read ${path}: ${String(exc)}`);
4687
+ return "";
4688
+ }
4689
+ return text.slice(-MAX_MEMORY_CHARS4);
4690
+ }
4691
+ function extractMemoryRuns4(config) {
4692
+ const text = readMemoryText3(config.memoryPath);
4693
+ if (!text) return [];
4694
+ const chunks = [];
4695
+ let currentDate = "";
4696
+ let currentLines = [];
4697
+ for (const rawLine of text.split(/\r?\n/)) {
4698
+ const line = rstrip4(rawLine);
4699
+ const match = RUN_HEADER_RE4.exec(line.trim());
4700
+ if (match) {
4701
+ if (currentDate) chunks.push([currentDate, currentLines]);
4702
+ currentDate = match[1];
4703
+ const suffix = stripChars5(match[2].trim(), HEADER_SUFFIX_STRIP2);
4704
+ currentLines = suffix ? [suffix] : [];
4705
+ continue;
4706
+ }
4707
+ if (currentDate) currentLines.push(line);
4708
+ }
4709
+ if (currentDate) chunks.push([currentDate, currentLines]);
4710
+ const runs = [];
4711
+ for (const [runDate, lines] of chunks) {
4712
+ const actions = actionsFromLines4(lines);
4713
+ if (actions.length === 0) continue;
4714
+ const body = actions.join(" ");
4715
+ const title = titleFromActions4(config, actions);
4716
+ runs.push({
4717
+ automation: config,
4718
+ date: runDate,
4719
+ title,
4720
+ keyActions: actions,
4721
+ projects: inferProjects3(body),
4722
+ signals: inferSignals3(body)
4723
+ });
4724
+ }
4725
+ return runs;
4726
+ }
4727
+ function actionsFromLines4(lines) {
4728
+ const actions = [];
4729
+ for (const line of lines) {
4730
+ let cleaned = line.trim();
4731
+ if (!cleaned) continue;
4732
+ cleaned = (cleaned.startsWith("- ") ? cleaned.slice(2) : cleaned).trim();
4733
+ const lowered = cleaned.toLowerCase();
4734
+ if (!cleaned || lowered.startsWith("runtime")) continue;
4735
+ if (lowered === "first run" || lowered === "follow-up") continue;
4736
+ if (lowered.startsWith("run:")) cleaned = cleaned.slice(4).trim();
4737
+ const safe = bounded6(safeNativeMemoryText6(cleaned), MAX_KEY_ACTION_CHARS5);
4738
+ if (safe && !actions.includes(safe)) actions.push(safe);
4739
+ if (actions.length >= MAX_KEY_ACTIONS_PER_RUN4) break;
4740
+ }
4741
+ return actions;
4742
+ }
4743
+ function titleFromActions4(config, actions) {
4744
+ if (actions.length === 0) return config.name;
4745
+ const first = actions[0];
4746
+ const prefix = `${config.name}: `;
4747
+ if (first.startsWith(prefix)) return bounded6(first, 120);
4748
+ return bounded6(prefix + first, 120);
4749
+ }
4750
+ function inferProjects3(text) {
4751
+ const projects = [];
4752
+ const lowered = text.toLowerCase();
4753
+ const seen = /* @__PURE__ */ new Set();
4754
+ for (const project of PROJECT_HINTS3) {
4755
+ const key = project.toLowerCase();
4756
+ if (lowered.includes(key) && !seen.has(key)) {
4757
+ projects.push(project);
4758
+ seen.add(key);
4759
+ }
4760
+ }
4761
+ return projects;
4762
+ }
4763
+ function inferSignals3(text) {
4764
+ const signals = [];
4765
+ for (const [name, pattern] of SIGNAL_PATTERNS3) {
4766
+ if (pattern.test(text)) signals.push(name);
4767
+ }
4768
+ return signals;
4769
+ }
4770
+ function sessionFromRun4(run) {
4771
+ const config = run.automation;
4772
+ const keyActions3 = [
4773
+ `automation_id: ${config.automationId}`,
4774
+ `run: ${run.title}`,
4775
+ ...run.keyActions
4776
+ ];
4777
+ const filesTouched = [config.path];
4778
+ if (config.memoryPath !== null) filesTouched.push(config.memoryPath);
4779
+ return (0, import_l512.makeSession)({
4780
+ date: run.date,
4781
+ cwd: config.cwds.length ? config.cwds[0] : (0, import_node_path12.dirname)(config.path),
4782
+ project_focus: [...run.projects],
4783
+ key_actions: keyActions3.slice(0, MAX_KEY_ACTIONS_PER_RUN4 + 2),
4784
+ files_touched: filesTouched,
4785
+ visibility: import_l512.Visibility.TEAM
4786
+ });
4787
+ }
4788
+ function entitiesFromConfigsAndRuns4(configs, runs) {
4789
+ const entitiesByName = /* @__PURE__ */ new Map();
4790
+ for (const config of configs) {
4791
+ entitiesByName.set(
4792
+ config.automationId,
4793
+ (0, import_l512.makeEntity)({
4794
+ name: config.automationId,
4795
+ type: "automation",
4796
+ summary: bounded6(
4797
+ `Cursor automation '${config.name}' (${config.status}). Schedule: ${config.rrule || "unspecified"}.`,
4798
+ 260
4799
+ ),
4800
+ last_touched: null,
4801
+ tags: ["cursor-automation", "automation", config.status.toLowerCase()],
4802
+ visibility: import_l512.Visibility.TEAM
4803
+ })
4804
+ );
4805
+ }
4806
+ for (const run of runs) {
4807
+ for (const project of run.projects) {
4808
+ if (!entitiesByName.has(project)) {
4809
+ entitiesByName.set(
4810
+ project,
4811
+ (0, import_l512.makeEntity)({
4812
+ name: project,
4813
+ type: "project",
4814
+ summary: "Project mentioned by Cursor automation run memory.",
4815
+ last_touched: run.date,
4816
+ tags: ["cursor-automation", "automation-evidence"],
4817
+ visibility: import_l512.Visibility.TEAM
4818
+ })
4819
+ );
4820
+ }
4821
+ }
4822
+ for (const signal of run.signals) {
4823
+ if (!entitiesByName.has(signal)) {
4824
+ entitiesByName.set(
4825
+ signal,
4826
+ (0, import_l512.makeEntity)({
4827
+ name: signal,
4828
+ type: "automation-signal",
4829
+ summary: "Signal class inferred from Cursor automation run memory.",
4830
+ last_touched: run.date,
4831
+ tags: ["cursor-automation", "automation-signal"],
4832
+ visibility: import_l512.Visibility.TEAM
4833
+ })
4834
+ );
4835
+ }
4836
+ }
4837
+ }
4838
+ return [...entitiesByName.values()];
4839
+ }
4840
+ function nowIsoSeconds3() {
4841
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "+00:00");
4842
+ }
4843
+ var CursorAutomationsParticipant = class {
4844
+ agentId = AGENT_ID12;
4845
+ agentType = AGENT_TYPE12;
4846
+ automationsDir;
4847
+ policy;
4848
+ constructor(automationsDir, cursorHome) {
4849
+ this.automationsDir = automationsDir || defaultCursorAutomationsDir(cursorHome ?? void 0);
4850
+ this.policy = DEFAULT_POLICY11;
4851
+ }
4852
+ get nativePath() {
4853
+ return this.automationsDir;
4854
+ }
4855
+ discover() {
4856
+ if (!isDir10(this.automationsDir)) {
4857
+ throw new ParticipantDiscoveryError(
4858
+ `Cursor automations directory not found at ${this.automationsDir}.`
4859
+ );
4860
+ }
4861
+ const configs = iterConfigs4(this.automationsDir);
4862
+ return {
4863
+ path: this.automationsDir,
4864
+ version: "unknown",
4865
+ metadata: {
4866
+ automations: configs.length,
4867
+ with_memory: configs.filter((c) => c.memoryPath !== null).length
4868
+ }
4869
+ };
4870
+ }
4871
+ exportSessions(since, limit = 100) {
4872
+ const sessions = this.runs(void 0, since).map(sessionFromRun4);
4873
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
4874
+ return sessions.slice(0, limit);
4875
+ }
4876
+ exportL5(since) {
4877
+ const configs = iterConfigs4(this.automationsDir);
4878
+ const runs = this.runs(configs, since);
4879
+ const sessions = runs.map(sessionFromRun4);
4880
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
4881
+ const entities = entitiesFromConfigsAndRuns4(configs, runs);
4882
+ const visibleEntities = (0, import_l512.filterForFederation)(entities, this.policy);
4883
+ return (0, import_l512.makeManifest)({
4884
+ spec_version: SPEC_VERSION,
4885
+ agent: (0, import_l512.makeAgentInfo)({
4886
+ id: AGENT_ID12,
4887
+ type: AGENT_TYPE12,
4888
+ instance: (0, import_node_os12.hostname)(),
4889
+ spec_version_compat: SPEC_VERSION,
4890
+ role_narrative: ROLE_NARRATIVE12
4891
+ }),
4892
+ last_updated: nowIsoSeconds3(),
4893
+ capabilities: ["cursor-automation-memory", "run-summary-publication"],
4894
+ recent_sessions: sessions,
4895
+ known_entities: visibleEntities,
4896
+ visibility_policy: this.policy
4897
+ });
4898
+ }
4899
+ healthCheck() {
4900
+ if (!isDir10(this.automationsDir)) {
4901
+ return {
4902
+ status: "blocked",
4903
+ reason: `Cursor automations directory not found at ${this.automationsDir}.`,
4904
+ details: { automations_dir: this.automationsDir },
4905
+ proposedFix: "Create Cursor automations or pass --automations-dir."
4906
+ };
4907
+ }
4908
+ const configs = iterConfigs4(this.automationsDir);
4909
+ const runs = this.runs(configs);
4910
+ const hasConfigs = configs.length > 0;
4911
+ const details = {
4912
+ automations_dir: this.automationsDir,
4913
+ automation_count: configs.length,
4914
+ memory_files: configs.filter((c) => c.memoryPath !== null).length,
4915
+ runs_extracted: runs.length,
4916
+ active_automations: configs.filter((c) => c.status === "ACTIVE").length
4917
+ };
4918
+ if (hasConfigs) {
4919
+ return { status: "ok", details };
4920
+ }
4921
+ return {
4922
+ status: "degraded",
4923
+ reason: "No automation.toml files found.",
4924
+ details,
4925
+ proposedFix: "Add Cursor automation.toml files."
4926
+ };
4927
+ }
4928
+ runs(configs, since) {
4929
+ const runCutoff = since ? since.toISOString().slice(0, 10) : null;
4930
+ const runs = [];
4931
+ for (const config of configs ?? iterConfigs4(this.automationsDir)) {
4932
+ for (const run of extractMemoryRuns4(config)) {
4933
+ if (runCutoff && run.date < runCutoff) continue;
4934
+ runs.push(run);
4935
+ }
4936
+ }
4937
+ runs.sort((a, b) => {
4938
+ if (a.date !== b.date) return a.date > b.date ? -1 : 1;
4939
+ const ai = a.automation.automationId;
4940
+ const bi = b.automation.automationId;
4941
+ return ai > bi ? -1 : ai < bi ? 1 : 0;
4942
+ });
4943
+ return runs;
4944
+ }
4945
+ };
4946
+
4947
+ // src/readers/claude-desktop-code.ts
4948
+ var import_node_fs13 = require("fs");
4949
+ var import_node_os13 = require("os");
4950
+ var import_node_path13 = require("path");
4951
+ var import_l513 = require("@getbourdon/l5");
4952
+ var import_redaction13 = require("@getbourdon/redaction");
4953
+ var AGENT_ID13 = "claude-desktop-code";
4954
+ var AGENT_TYPE13 = "code-assistant";
4955
+ var DISPLAY_NAME8 = "Claude Desktop \xB7 Code";
4956
+ var SURFACE_ENTITY_NAME = "Claude Desktop Code";
4957
+ var ROLE_NARRATIVE13 = "Claude desktop app, GUI Claude Code. Bourdon reads the metadata-only per-conversation local state to surface recognition metadata -- title, project, model, effort, capability counts -- never conversation content -- so desktop Claude Code work is visible to other agents.";
4958
+ var DESKTOP_DIR_ENV = "BOURDON_CLAUDE_DESKTOP_DIR";
4959
+ var CODE_STORE = "claude-code-sessions";
4960
+ var STATE_PREFIX = "local_";
4961
+ var STATE_FILE_RE = /^local_.*\.json$/;
4962
+ var MAX_KEY_ACTION_CHARS6 = 280;
4963
+ var MAX_PROJECTS = 6;
4964
+ var MAX_KEY_ACTIONS2 = 6;
4965
+ var PROJECT_HINTS4 = [
4966
+ "ShipStable",
4967
+ "ILTT",
4968
+ "Prun",
4969
+ "PRUN",
4970
+ "OMNIvour",
4971
+ "Castmore",
4972
+ "Bourdon",
4973
+ "RADLAB",
4974
+ "CHIP",
4975
+ "Claude Brain",
4976
+ "Cursor",
4977
+ "Copilot",
4978
+ "Codex",
4979
+ "Cascade"
4980
+ ];
4981
+ var DEFAULT_POLICY12 = (0, import_l513.makeVisibilityPolicy)({
4982
+ default: import_l513.Visibility.TEAM,
4983
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
4984
+ team_tags: ["claude-desktop", "claude-desktop-code", "agent-surface", "workspace"]
4985
+ });
4986
+ function isDir11(p) {
4987
+ try {
4988
+ return (0, import_node_fs13.statSync)(p).isDirectory();
4989
+ } catch {
4990
+ return false;
4991
+ }
4992
+ }
4993
+ function homeBase(home) {
4994
+ if (home !== void 0) return home;
4995
+ try {
4996
+ return (0, import_node_os13.homedir)();
4997
+ } catch {
4998
+ return process.env.HOME || process.env.USERPROFILE || ".";
4999
+ }
5000
+ }
5001
+ function defaultClaudeDesktopDir(home) {
5002
+ const env = process.env[DESKTOP_DIR_ENV];
5003
+ if (env) return env;
5004
+ const base = homeBase(home);
5005
+ if (process.platform === "darwin") {
5006
+ return (0, import_node_path13.join)(base, "Library", "Application Support", "Claude");
5007
+ }
5008
+ if (process.platform === "win32") {
5009
+ if (home === void 0) {
5010
+ const appdata = process.env.APPDATA;
5011
+ if (appdata) return (0, import_node_path13.join)(appdata, "Claude");
5012
+ }
5013
+ return (0, import_node_path13.join)(base, "AppData", "Roaming", "Claude");
5014
+ }
5015
+ if (process.platform === "linux") {
5016
+ return (0, import_node_path13.join)(base, ".config", "Claude");
5017
+ }
5018
+ return null;
5019
+ }
5020
+ function iterStateFiles(storeDir) {
5021
+ if (!isDir11(storeDir)) return [];
5022
+ const out = [];
5023
+ let accts;
5024
+ try {
5025
+ accts = (0, import_node_fs13.readdirSync)(storeDir);
5026
+ } catch {
5027
+ return out;
5028
+ }
5029
+ for (const acct of accts) {
5030
+ const acctDir = (0, import_node_path13.join)(storeDir, acct);
5031
+ if (!isDir11(acctDir)) continue;
5032
+ let orgs;
5033
+ try {
5034
+ orgs = (0, import_node_fs13.readdirSync)(acctDir);
5035
+ } catch {
5036
+ continue;
5037
+ }
5038
+ for (const org of orgs) {
5039
+ const orgDir = (0, import_node_path13.join)(acctDir, org);
5040
+ if (!isDir11(orgDir)) continue;
5041
+ let names;
5042
+ try {
5043
+ names = (0, import_node_fs13.readdirSync)(orgDir);
5044
+ } catch {
5045
+ continue;
5046
+ }
5047
+ for (const name of names) {
5048
+ if (name.startsWith(STATE_PREFIX) && STATE_FILE_RE.test(name)) {
5049
+ out.push((0, import_node_path13.join)(orgDir, name));
5050
+ }
5051
+ }
5052
+ }
5053
+ }
5054
+ out.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
5055
+ return out;
5056
+ }
5057
+ function loadStateJson(path) {
5058
+ let raw;
5059
+ try {
5060
+ raw = (0, import_node_fs13.readFileSync)(path, "utf8");
5061
+ } catch {
5062
+ return null;
5063
+ }
5064
+ let data;
5065
+ try {
5066
+ data = JSON.parse(raw);
5067
+ } catch {
5068
+ return null;
5069
+ }
5070
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
5071
+ return data;
5072
+ }
5073
+ function epochToDate(value) {
5074
+ if (typeof value === "boolean") return "";
5075
+ let num = null;
5076
+ if (typeof value === "number") {
5077
+ num = value;
5078
+ } else if (typeof value === "string" && value.trim()) {
5079
+ const parsed = Number(value.trim());
5080
+ num = Number.isNaN(parsed) ? null : parsed;
5081
+ }
5082
+ if (num === null || !Number.isFinite(num)) return "";
5083
+ const seconds = num > 1e12 ? num / 1e3 : num;
5084
+ try {
5085
+ const d = new Date(seconds * 1e3);
5086
+ const iso = d.toISOString();
5087
+ return iso.slice(0, 10);
5088
+ } catch {
5089
+ return "";
5090
+ }
5091
+ }
5092
+ function sessionDate(state) {
5093
+ for (const key of ["createdAt", "lastActivityAt"]) {
5094
+ const parsed = epochToDate(state[key]);
5095
+ if (parsed) return parsed;
5096
+ }
5097
+ return "";
5098
+ }
5099
+ function bounded7(value, limit = MAX_KEY_ACTION_CHARS6) {
5100
+ return (0, import_redaction13.redactText)(value, limit);
5101
+ }
5102
+ function safeLabel(value, limit = MAX_KEY_ACTION_CHARS6) {
5103
+ if (value === null || value === void 0) return "";
5104
+ const text = String(value).trim();
5105
+ if (!text) return "";
5106
+ return bounded7(text, limit);
5107
+ }
5108
+ function countEnabledMcpTools(enabled) {
5109
+ if (typeof enabled !== "object" || enabled === null || Array.isArray(enabled)) return 0;
5110
+ let count = 0;
5111
+ for (const v of Object.values(enabled)) {
5112
+ if (v === true) count += 1;
5113
+ }
5114
+ return count;
5115
+ }
5116
+ function baseName(pathValue) {
5117
+ if (typeof pathValue !== "string" || !pathValue.trim()) return "";
5118
+ const name = (0, import_node_path13.basename)(pathValue.trim()).trim();
5119
+ return name && name !== "." && name !== "/" && name !== "\\" ? name : "";
5120
+ }
5121
+ function inferProjects4(state) {
5122
+ const projects = [];
5123
+ const seen = /* @__PURE__ */ new Set();
5124
+ const add = (name) => {
5125
+ const cleaned = name.trim();
5126
+ if (cleaned && !seen.has(cleaned.toLowerCase())) {
5127
+ projects.push(cleaned);
5128
+ seen.add(cleaned.toLowerCase());
5129
+ }
5130
+ };
5131
+ const pathStrings = [];
5132
+ const cwd = state["cwd"];
5133
+ if (typeof cwd === "string" && cwd.trim()) pathStrings.push(cwd);
5134
+ const folders = state["userSelectedFolders"];
5135
+ if (Array.isArray(folders)) {
5136
+ for (const f of folders) {
5137
+ if (typeof f === "string" && f.trim()) pathStrings.push(f);
5138
+ }
5139
+ }
5140
+ for (const rawPath of pathStrings) {
5141
+ const base = baseName(rawPath);
5142
+ if (base) add(base);
5143
+ }
5144
+ const haystack = pathStrings.join(" ").toLowerCase();
5145
+ for (const hint of PROJECT_HINTS4) {
5146
+ if (haystack.includes(hint.toLowerCase())) add(hint);
5147
+ }
5148
+ return projects.slice(0, MAX_PROJECTS);
5149
+ }
5150
+ function conversationFromState(statePath, state) {
5151
+ const sessionId = state["sessionId"];
5152
+ const stem2 = (0, import_node_path13.parse)(statePath).name;
5153
+ return {
5154
+ convId: String(sessionId || stem2),
5155
+ date: sessionDate(state),
5156
+ cwd: safeLabel(state["cwd"], 300),
5157
+ title: safeLabel(state["title"], 160) || "(untitled conversation)",
5158
+ model: safeLabel(state["model"], 80),
5159
+ effort: safeLabel(state["effort"], 40),
5160
+ permissionMode: safeLabel(state["permissionMode"], 40),
5161
+ isArchived: Boolean(state["isArchived"]),
5162
+ mcpToolCount: countEnabledMcpTools(state["enabledMcpTools"]),
5163
+ projects: inferProjects4(state)
5164
+ };
5165
+ }
5166
+ function keyActions(conv) {
5167
+ const actions = [bounded7(conv.title, 160)];
5168
+ if (conv.model) actions.push(bounded7(`model: ${conv.model}`, 120));
5169
+ if (conv.effort) actions.push(bounded7(`effort: ${conv.effort}`, 60));
5170
+ if (conv.permissionMode) actions.push(bounded7(`permission: ${conv.permissionMode}`, 80));
5171
+ if (conv.mcpToolCount) actions.push(`mcp-tools: ${conv.mcpToolCount}`);
5172
+ return actions.slice(0, MAX_KEY_ACTIONS2);
5173
+ }
5174
+ function sessionFromConversation(conv) {
5175
+ return (0, import_l513.makeSession)({
5176
+ date: conv.date,
5177
+ cwd: conv.cwd || void 0,
5178
+ project_focus: [...conv.projects],
5179
+ key_actions: keyActions(conv),
5180
+ files_touched: [],
5181
+ // never list user files -- privacy
5182
+ visibility: import_l513.Visibility.TEAM
5183
+ });
5184
+ }
5185
+ function capabilities(convs) {
5186
+ let maxMcp = 0;
5187
+ for (const conv of convs) if (conv.mcpToolCount > maxMcp) maxMcp = conv.mcpToolCount;
5188
+ return [AGENT_ID13, `mcp-tools:${maxMcp}`];
5189
+ }
5190
+ function entitiesFromConversations(convs) {
5191
+ let lastSeen;
5192
+ for (const conv of convs) {
5193
+ if (lastSeen === void 0 || conv.date > lastSeen) lastSeen = conv.date;
5194
+ }
5195
+ const entities = /* @__PURE__ */ new Map();
5196
+ entities.set(
5197
+ SURFACE_ENTITY_NAME,
5198
+ (0, import_l513.makeEntity)({
5199
+ name: SURFACE_ENTITY_NAME,
5200
+ type: "agent-surface",
5201
+ summary: bounded7(
5202
+ "Claude desktop app GUI Claude Code surface (metadata-only federation).",
5203
+ 260
5204
+ ),
5205
+ last_touched: lastSeen,
5206
+ tags: ["claude-desktop", "claude-desktop-code", "agent-surface"],
5207
+ visibility: import_l513.Visibility.TEAM
5208
+ })
5209
+ );
5210
+ for (const conv of convs) {
5211
+ for (const project of conv.projects) {
5212
+ if (!entities.has(project)) {
5213
+ entities.set(
5214
+ project,
5215
+ (0, import_l513.makeEntity)({
5216
+ name: project,
5217
+ type: "project",
5218
+ summary: "Project inferred from a Claude Desktop Code conversation cwd.",
5219
+ last_touched: conv.date || void 0,
5220
+ tags: ["claude-desktop", "claude-desktop-code", "project"],
5221
+ visibility: import_l513.Visibility.TEAM
5222
+ })
5223
+ );
5224
+ }
5225
+ }
5226
+ }
5227
+ return [...entities.values()];
5228
+ }
5229
+ var ClaudeDesktopCodeParticipant = class _ClaudeDesktopCodeParticipant {
5230
+ agentId = AGENT_ID13;
5231
+ agentType = AGENT_TYPE13;
5232
+ displayName = DISPLAY_NAME8;
5233
+ storeDir;
5234
+ policy;
5235
+ /**
5236
+ * The Claude Code sub-store dir the setup wizard probes for presence. Resolves
5237
+ * to `<desktop>/claude-code-sessions`. Falls back to a non-existent sentinel
5238
+ * under `home` on an unrecognized platform so the wizard reports "not found".
5239
+ */
5240
+ static defaultNativePath(home) {
5241
+ const desktop = defaultClaudeDesktopDir(home);
5242
+ if (desktop === null) return (0, import_node_path13.join)(homeBase(home), "Claude", CODE_STORE);
5243
+ return (0, import_node_path13.join)(desktop, CODE_STORE);
5244
+ }
5245
+ constructor(storeDir, home) {
5246
+ this.storeDir = storeDir ?? _ClaudeDesktopCodeParticipant.defaultNativePath(home);
5247
+ this.policy = DEFAULT_POLICY12;
5248
+ }
5249
+ get nativePath() {
5250
+ return this.storeDir;
5251
+ }
5252
+ discover() {
5253
+ if (!isDir11(this.storeDir)) {
5254
+ throw new ParticipantDiscoveryError(
5255
+ `Claude Desktop Code store not found at ${this.storeDir}.`
5256
+ );
5257
+ }
5258
+ const stateFiles = iterStateFiles(this.storeDir);
5259
+ return {
5260
+ path: this.storeDir,
5261
+ version: "unknown",
5262
+ metadata: { conversations: stateFiles.length }
5263
+ };
5264
+ }
5265
+ exportSessions(since, limit = 100) {
5266
+ const convs = this.conversations(since);
5267
+ const sessions = convs.map(sessionFromConversation);
5268
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
5269
+ return sessions.slice(0, limit);
5270
+ }
5271
+ exportL5(since) {
5272
+ if (!isDir11(this.storeDir)) {
5273
+ throw new ParticipantDiscoveryError(
5274
+ `Claude Desktop Code store not found at ${this.storeDir}.`
5275
+ );
5276
+ }
5277
+ const convs = this.conversations(since);
5278
+ const sessions = convs.map(sessionFromConversation);
5279
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
5280
+ const entities = entitiesFromConversations(convs);
5281
+ const visibleEntities = (0, import_l513.filterForFederation)(entities, this.policy);
5282
+ return (0, import_l513.makeManifest)({
5283
+ spec_version: SPEC_VERSION,
5284
+ agent: (0, import_l513.makeAgentInfo)({
5285
+ id: AGENT_ID13,
5286
+ type: AGENT_TYPE13,
5287
+ instance: (0, import_node_os13.hostname)(),
5288
+ spec_version_compat: SPEC_VERSION,
5289
+ role_narrative: ROLE_NARRATIVE13
5290
+ }),
5291
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
5292
+ capabilities: capabilities(convs),
5293
+ recent_sessions: sessions,
5294
+ known_entities: visibleEntities,
5295
+ visibility_policy: this.policy
5296
+ });
5297
+ }
5298
+ healthCheck() {
5299
+ if (!isDir11(this.storeDir)) {
5300
+ return {
5301
+ status: "blocked",
5302
+ reason: `Claude Desktop Code store not found at ${this.storeDir}.`,
5303
+ details: { store_dir: this.storeDir },
5304
+ proposedFix: "Install the Claude desktop app and open a Claude Code conversation once. Set BOURDON_CLAUDE_DESKTOP_DIR if the app stores state in a non-standard location."
5305
+ };
5306
+ }
5307
+ let stateFiles;
5308
+ let convs;
5309
+ let malformed;
5310
+ try {
5311
+ stateFiles = iterStateFiles(this.storeDir);
5312
+ const collected = this.collectConversations();
5313
+ convs = collected.convs;
5314
+ malformed = collected.malformed;
5315
+ } catch (exc) {
5316
+ return {
5317
+ status: "degraded",
5318
+ reason: "Code store present but extraction failed.",
5319
+ details: { error: String(exc) },
5320
+ proposedFix: "Close the Claude desktop app (its state files may be locked) and re-run `bourdon claude-desktop-code export`."
5321
+ };
5322
+ }
5323
+ if (stateFiles.length === 0) {
5324
+ return {
5325
+ status: "degraded",
5326
+ reason: "No Claude Code conversations found under the store directory.",
5327
+ details: { store_dir: this.storeDir },
5328
+ proposedFix: "Open a Claude Code conversation in the Claude desktop app, then re-run `bourdon claude-desktop-code export`."
5329
+ };
5330
+ }
5331
+ return {
5332
+ status: "ok",
5333
+ details: {
5334
+ store_dir: this.storeDir,
5335
+ conversation_count: stateFiles.length,
5336
+ conversations_extracted: convs.length,
5337
+ malformed_records: malformed
5338
+ }
5339
+ };
5340
+ }
5341
+ // -- Internal ---------------------------------------------------------------
5342
+ collectConversations() {
5343
+ const convs = [];
5344
+ let malformed = 0;
5345
+ for (const statePath of iterStateFiles(this.storeDir)) {
5346
+ const state = loadStateJson(statePath);
5347
+ if (state === null) {
5348
+ malformed += 1;
5349
+ continue;
5350
+ }
5351
+ convs.push(conversationFromState(statePath, state));
5352
+ }
5353
+ return { convs, malformed };
5354
+ }
5355
+ conversations(since) {
5356
+ let { convs } = this.collectConversations();
5357
+ if (since !== void 0) {
5358
+ const cutoff = since.toISOString().slice(0, 10);
5359
+ convs = convs.filter((conv) => !conv.date || conv.date >= cutoff);
5360
+ }
5361
+ convs.sort((a, b) => {
5362
+ if (a.date !== b.date) return a.date < b.date ? 1 : -1;
5363
+ if (a.convId !== b.convId) return a.convId < b.convId ? 1 : -1;
5364
+ return 0;
5365
+ });
5366
+ return convs;
5367
+ }
5368
+ };
5369
+
5370
+ // src/readers/claude-desktop-cowork.ts
5371
+ var import_node_fs14 = require("fs");
5372
+ var import_node_os14 = require("os");
5373
+ var import_node_path14 = require("path");
5374
+ var import_l514 = require("@getbourdon/l5");
5375
+ var import_redaction14 = require("@getbourdon/redaction");
5376
+ var AGENT_ID14 = "claude-desktop-cowork";
5377
+ var AGENT_TYPE14 = "code-assistant";
5378
+ var DISPLAY_NAME9 = "Claude Desktop \xB7 Co-Work";
5379
+ var SURFACE_ENTITY_NAME2 = "Claude Desktop Co-Work";
5380
+ var ROLE_NARRATIVE14 = "Claude desktop app, Co-Work / local-agent mode. Bourdon reads the per-run local state and audit transcript to surface recognition metadata -- title, project, model, turn/cost scalars, capability counts -- never conversation content -- so Co-Work runs are visible to other agents.";
5381
+ var DEFAULT_POLICY13 = (0, import_l514.makeVisibilityPolicy)({
5382
+ default: import_l514.Visibility.TEAM,
5383
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
5384
+ team_tags: ["claude-desktop", "claude-desktop-cowork", "agent-surface", "workspace"]
5385
+ });
5386
+ var DESKTOP_DIR_ENV2 = "BOURDON_CLAUDE_DESKTOP_DIR";
5387
+ var COWORK_STORE = "local-agent-mode-sessions";
5388
+ var STATE_PREFIX2 = "local_";
5389
+ var MAX_PROJECTS2 = 6;
5390
+ var MAX_KEY_ACTIONS3 = 8;
5391
+ var MAX_AUDIT_LINES = 5e4;
5392
+ var PROJECT_HINTS5 = [
5393
+ "ShipStable",
5394
+ "ILTT",
5395
+ "Prun",
5396
+ "PRUN",
5397
+ "OMNIvour",
5398
+ "Castmore",
5399
+ "Bourdon",
5400
+ "RADLAB",
5401
+ "CHIP",
5402
+ "Claude Brain",
5403
+ "Cursor",
5404
+ "Copilot",
5405
+ "Codex",
5406
+ "Cascade"
5407
+ ];
5408
+ var logger7 = {
5409
+ warn(msg) {
5410
+ console.warn(`[bourdon.participants] ${msg}`);
5411
+ }
5412
+ };
5413
+ function isDir12(p) {
5414
+ try {
5415
+ return (0, import_node_fs14.statSync)(p).isDirectory();
5416
+ } catch {
5417
+ return false;
5418
+ }
5419
+ }
5420
+ function isFile11(p) {
5421
+ try {
5422
+ return (0, import_node_fs14.statSync)(p).isFile();
5423
+ } catch {
5424
+ return false;
5425
+ }
5426
+ }
5427
+ function homeDir9() {
5428
+ return process.env.HOME || process.env.USERPROFILE || ".";
5429
+ }
5430
+ function isRecord6(v) {
5431
+ return typeof v === "object" && v !== null && !Array.isArray(v);
5432
+ }
5433
+ function isoSeconds() {
5434
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00");
5435
+ }
5436
+ function defaultClaudeDesktopDir2(home) {
5437
+ const env = process.env[DESKTOP_DIR_ENV2];
5438
+ if (env) return env;
5439
+ const base = home || homeDir9();
5440
+ const plat = process.platform;
5441
+ if (plat === "darwin") {
5442
+ return (0, import_node_path14.join)(base, "Library", "Application Support", "Claude");
5443
+ }
5444
+ if (plat.startsWith("win")) {
5445
+ if (home === void 0) {
5446
+ const appdata = process.env.APPDATA;
5447
+ if (appdata) return (0, import_node_path14.join)(appdata, "Claude");
5448
+ }
5449
+ return (0, import_node_path14.join)(base, "AppData", "Roaming", "Claude");
5450
+ }
5451
+ if (plat.startsWith("linux")) {
5452
+ return (0, import_node_path14.join)(base, ".config", "Claude");
5453
+ }
5454
+ return null;
5455
+ }
5456
+ function iterStateFiles2(storeDir) {
5457
+ if (!isDir12(storeDir)) return [];
5458
+ const out = [];
5459
+ let accounts;
5460
+ try {
5461
+ accounts = (0, import_node_fs14.readdirSync)(storeDir);
5462
+ } catch {
5463
+ return [];
5464
+ }
5465
+ for (const acct of accounts) {
5466
+ const acctDir = (0, import_node_path14.join)(storeDir, acct);
5467
+ if (!isDir12(acctDir)) continue;
5468
+ let orgs;
5469
+ try {
5470
+ orgs = (0, import_node_fs14.readdirSync)(acctDir);
5471
+ } catch {
5472
+ continue;
5473
+ }
5474
+ for (const org of orgs) {
5475
+ const orgDir = (0, import_node_path14.join)(acctDir, org);
5476
+ if (!isDir12(orgDir)) continue;
5477
+ let entries;
5478
+ try {
5479
+ entries = (0, import_node_fs14.readdirSync)(orgDir);
5480
+ } catch {
5481
+ continue;
5482
+ }
5483
+ for (const name of entries) {
5484
+ if (name.startsWith(STATE_PREFIX2) && name.endsWith(".json")) {
5485
+ out.push((0, import_node_path14.join)(orgDir, name));
5486
+ }
5487
+ }
5488
+ }
5489
+ }
5490
+ out.sort();
5491
+ return out;
5492
+ }
5493
+ function loadStateJson2(path) {
5494
+ let raw;
5495
+ try {
5496
+ raw = (0, import_node_fs14.readFileSync)(path, "utf8");
5497
+ } catch (exc) {
5498
+ logger7.warn(`claude-desktop: cannot read ${path}: ${String(exc)}`);
5499
+ return null;
5500
+ }
5501
+ let data;
5502
+ try {
5503
+ data = JSON.parse(raw);
5504
+ } catch (exc) {
5505
+ logger7.warn(`claude-desktop: cannot parse ${path}: ${String(exc)}`);
5506
+ return null;
5507
+ }
5508
+ return isRecord6(data) ? data : null;
5509
+ }
5510
+ function epochToDate2(value) {
5511
+ if (typeof value === "boolean") return "";
5512
+ let num = null;
5513
+ if (typeof value === "number") {
5514
+ num = value;
5515
+ } else if (typeof value === "string" && value.trim()) {
5516
+ const n = Number(value.trim());
5517
+ num = Number.isNaN(n) ? null : n;
5518
+ }
5519
+ if (num === null || Number.isNaN(num)) return "";
5520
+ const seconds = num > 1e12 ? num / 1e3 : num;
5521
+ try {
5522
+ const d = new Date(seconds * 1e3);
5523
+ if (Number.isNaN(d.getTime())) return "";
5524
+ return d.toISOString().slice(0, 10);
5525
+ } catch {
5526
+ return "";
5527
+ }
5528
+ }
5529
+ function sessionDate2(state) {
5530
+ for (const key of ["createdAt", "lastActivityAt"]) {
5531
+ const parsed = epochToDate2(state[key]);
5532
+ if (parsed) return parsed;
5533
+ }
5534
+ return "";
5535
+ }
5536
+ function bounded8(value, limit = 280) {
5537
+ return (0, import_redaction14.redactText)(value, limit);
5538
+ }
5539
+ function safeLabel2(value, limit = 280) {
5540
+ if (value === null || value === void 0) return "";
5541
+ const text = String(value).trim();
5542
+ if (!text) return "";
5543
+ return bounded8(text, limit);
5544
+ }
5545
+ function countEnabledMcpTools2(enabled) {
5546
+ if (!isRecord6(enabled)) return 0;
5547
+ let n = 0;
5548
+ for (const v of Object.values(enabled)) {
5549
+ if (v === true) n += 1;
5550
+ }
5551
+ return n;
5552
+ }
5553
+ function basenameOf4(pathValue) {
5554
+ if (typeof pathValue !== "string" || !pathValue.trim()) return "";
5555
+ const name = (0, import_node_path14.basename)(pathValue.trim()).trim();
5556
+ return name && name !== "." && name !== "/" && name !== "\\" ? name : "";
5557
+ }
5558
+ function inferProjects5(state) {
5559
+ const projects = [];
5560
+ const seen = /* @__PURE__ */ new Set();
5561
+ const add = (name) => {
5562
+ const cleaned = name.trim();
5563
+ if (cleaned && !seen.has(cleaned.toLowerCase())) {
5564
+ projects.push(cleaned);
5565
+ seen.add(cleaned.toLowerCase());
5566
+ }
5567
+ };
5568
+ const pathStrings = [];
5569
+ const cwd = state["cwd"];
5570
+ if (typeof cwd === "string" && cwd.trim()) pathStrings.push(cwd);
5571
+ const folders = state["userSelectedFolders"];
5572
+ if (Array.isArray(folders)) {
5573
+ for (const f of folders) {
5574
+ if (typeof f === "string" && f.trim()) pathStrings.push(f);
5575
+ }
5576
+ }
5577
+ for (const raw of pathStrings) {
5578
+ const base = basenameOf4(raw);
5579
+ if (base) add(base);
5580
+ }
5581
+ const haystack = pathStrings.join(" ").toLowerCase();
5582
+ for (const hint of PROJECT_HINTS5) {
5583
+ if (haystack.includes(hint.toLowerCase())) add(hint);
5584
+ }
5585
+ return projects.slice(0, MAX_PROJECTS2);
5586
+ }
5587
+ function runDirFor(statePath) {
5588
+ return (0, import_node_path14.join)((0, import_node_path14.dirname)(statePath), (0, import_node_path14.parse)(statePath).name);
5589
+ }
5590
+ function absorbAuditRecord(record, out) {
5591
+ const recType = record["type"];
5592
+ const subtype = record["subtype"];
5593
+ if (recType === "system" && subtype === "init") {
5594
+ const pairs = [
5595
+ ["tools", "init_tools"],
5596
+ ["mcp_servers", "init_mcp_servers"],
5597
+ ["skills", "init_skills"],
5598
+ ["plugins", "init_plugins"],
5599
+ ["slash_commands", "init_slash_commands"]
5600
+ ];
5601
+ for (const [srcKey, dstKey] of pairs) {
5602
+ const value = record[srcKey];
5603
+ if (Array.isArray(value)) out[dstKey] = value.length;
5604
+ }
5605
+ const version = record["claude_code_version"];
5606
+ if (version !== null && version !== void 0) {
5607
+ out["claude_code_version"] = safeLabel2(version, 40);
5608
+ }
5609
+ return;
5610
+ }
5611
+ if (recType === "result") {
5612
+ for (const key of ["total_cost_usd", "num_turns", "is_error", "duration_ms"]) {
5613
+ if (key in record) out[key] = record[key];
5614
+ }
5615
+ const stopReason = record["stop_reason"];
5616
+ if (stopReason !== null && stopReason !== void 0) {
5617
+ out["stop_reason"] = safeLabel2(stopReason, 40);
5618
+ }
5619
+ }
5620
+ }
5621
+ function readAuditScalars(runDir) {
5622
+ const auditPath = (0, import_node_path14.join)(runDir, "audit.jsonl");
5623
+ if (!isFile11(auditPath)) return {};
5624
+ const out = {};
5625
+ let raw;
5626
+ try {
5627
+ raw = (0, import_node_fs14.readFileSync)(auditPath, "utf8");
5628
+ } catch (exc) {
5629
+ logger7.warn(`claude-desktop: cannot read ${auditPath}: ${String(exc)}`);
5630
+ return out;
5631
+ }
5632
+ let lineNo = 0;
5633
+ for (const rawLine of raw.split(/\r?\n/)) {
5634
+ if (lineNo >= MAX_AUDIT_LINES) break;
5635
+ lineNo += 1;
5636
+ const line = rawLine.trim();
5637
+ if (!line || line[0] !== "{") continue;
5638
+ let record;
5639
+ try {
5640
+ record = JSON.parse(line);
5641
+ } catch {
5642
+ continue;
5643
+ }
5644
+ if (!isRecord6(record)) continue;
5645
+ absorbAuditRecord(record, out);
5646
+ }
5647
+ return out;
5648
+ }
5649
+ function runFromState(statePath, state) {
5650
+ const audit = readAuditScalars(runDirFor(statePath));
5651
+ const title = safeLabel2(state["title"], 160) || "(untitled run)";
5652
+ const model = safeLabel2(state["model"], 80);
5653
+ const permissionMode = safeLabel2(state["permissionMode"], 40);
5654
+ const numTurnsRaw = audit["num_turns"];
5655
+ const costRaw = audit["total_cost_usd"];
5656
+ const isErrorRaw = audit["is_error"];
5657
+ const initCounts = {};
5658
+ for (const [k, v] of Object.entries(audit)) {
5659
+ if (k.startsWith("init_") && typeof v === "number" && Number.isInteger(v)) {
5660
+ initCounts[k] = v;
5661
+ }
5662
+ }
5663
+ const stem2 = (0, import_node_path14.parse)(statePath).name;
5664
+ const sid = state["sessionId"];
5665
+ return {
5666
+ runId: String(sid || stem2),
5667
+ date: sessionDate2(state),
5668
+ cwd: safeLabel2(state["cwd"], 300),
5669
+ title,
5670
+ model,
5671
+ permissionMode,
5672
+ isArchived: Boolean(state["isArchived"]),
5673
+ mcpToolCount: countEnabledMcpTools2(state["enabledMcpTools"]),
5674
+ projects: inferProjects5(state),
5675
+ numTurns: typeof numTurnsRaw === "number" && Number.isInteger(numTurnsRaw) ? numTurnsRaw : null,
5676
+ totalCostUsd: typeof costRaw === "number" ? costRaw : null,
5677
+ isError: typeof isErrorRaw === "boolean" ? isErrorRaw : null,
5678
+ initCounts
5679
+ };
5680
+ }
5681
+ function keyActions2(run) {
5682
+ const actions = [bounded8(run.title, 160)];
5683
+ if (run.model) actions.push(bounded8(`model: ${run.model}`, 120));
5684
+ if (run.permissionMode) actions.push(bounded8(`permission: ${run.permissionMode}`, 80));
5685
+ if (run.numTurns !== null) actions.push(`${run.numTurns} turns`);
5686
+ if (run.totalCostUsd !== null) actions.push(`$${run.totalCostUsd.toFixed(2)}`);
5687
+ if (run.isError) actions.push("error");
5688
+ if (run.mcpToolCount) actions.push(`mcp-tools: ${run.mcpToolCount}`);
5689
+ return actions.slice(0, MAX_KEY_ACTIONS3);
5690
+ }
5691
+ function sessionFromRun5(run) {
5692
+ return (0, import_l514.makeSession)({
5693
+ date: run.date,
5694
+ cwd: run.cwd || void 0,
5695
+ // run.cwd or None -- privacy: never empty string
5696
+ project_focus: [...run.projects],
5697
+ key_actions: keyActions2(run),
5698
+ files_touched: [],
5699
+ // never list user files -- privacy
5700
+ visibility: import_l514.Visibility.TEAM
5701
+ });
5702
+ }
5703
+ function capabilities2(runs) {
5704
+ const caps = [AGENT_ID14];
5705
+ const maxMcp = runs.length ? Math.max(...runs.map((r) => r.mcpToolCount)) : 0;
5706
+ caps.push(`mcp-tools:${maxMcp}`);
5707
+ const bestTools = runs.length ? Math.max(...runs.map((r) => r.initCounts["init_tools"] ?? 0)) : 0;
5708
+ if (bestTools) caps.push(`tools:${bestTools}`);
5709
+ const bestSkills = runs.length ? Math.max(...runs.map((r) => r.initCounts["init_skills"] ?? 0)) : 0;
5710
+ if (bestSkills) caps.push(`skills:${bestSkills}`);
5711
+ return caps;
5712
+ }
5713
+ function entitiesFromRuns(runs) {
5714
+ const lastSeen = runs.length ? runs.map((r) => r.date).reduce((a, b) => a > b ? a : b) : null;
5715
+ const entities = /* @__PURE__ */ new Map();
5716
+ entities.set(
5717
+ SURFACE_ENTITY_NAME2,
5718
+ (0, import_l514.makeEntity)({
5719
+ name: SURFACE_ENTITY_NAME2,
5720
+ type: "agent-surface",
5721
+ summary: bounded8(
5722
+ "Claude desktop app Co-Work / local-agent mode surface (metadata-only federation).",
5723
+ 260
5724
+ ),
5725
+ last_touched: lastSeen,
5726
+ tags: ["claude-desktop", "claude-desktop-cowork", "agent-surface"],
5727
+ visibility: import_l514.Visibility.TEAM
5728
+ })
5729
+ );
5730
+ for (const run of runs) {
5731
+ for (const project of run.projects) {
5732
+ if (!entities.has(project)) {
5733
+ entities.set(
5734
+ project,
5735
+ (0, import_l514.makeEntity)({
5736
+ name: project,
5737
+ type: "project",
5738
+ summary: "Project inferred from a Claude Desktop Co-Work run cwd.",
5739
+ last_touched: run.date || void 0,
5740
+ // run.date or None
5741
+ tags: ["claude-desktop", "claude-desktop-cowork", "project"],
5742
+ visibility: import_l514.Visibility.TEAM
5743
+ })
5744
+ );
5745
+ }
5746
+ }
5747
+ }
5748
+ return [...entities.values()];
5749
+ }
5750
+ var ClaudeDesktopCoworkParticipant = class _ClaudeDesktopCoworkParticipant {
5751
+ agentId = AGENT_ID14;
5752
+ agentType = AGENT_TYPE14;
5753
+ displayName = DISPLAY_NAME9;
5754
+ storeDir;
5755
+ policy;
5756
+ /**
5757
+ * The Co-Work sub-store dir the setup wizard probes for presence. Resolves to
5758
+ * `<desktop>/local-agent-mode-sessions`. When the desktop dir cannot be
5759
+ * resolved on this platform, falls back to a non-existent sentinel under
5760
+ * `home` so the wizard reports "not found" rather than crashing.
5761
+ */
5762
+ static defaultNativePath(home) {
5763
+ const desktop = defaultClaudeDesktopDir2(home);
5764
+ if (desktop === null) return (0, import_node_path14.join)(home || homeDir9(), "Claude", COWORK_STORE);
5765
+ return (0, import_node_path14.join)(desktop, COWORK_STORE);
5766
+ }
5767
+ constructor(storeDir, home) {
5768
+ this.storeDir = storeDir || _ClaudeDesktopCoworkParticipant.defaultNativePath(home);
5769
+ this.policy = DEFAULT_POLICY13;
5770
+ }
5771
+ get nativePath() {
5772
+ return this.storeDir;
5773
+ }
5774
+ // -- Protocol surface --------------------------------------------------------
5775
+ discover() {
5776
+ if (!isDir12(this.storeDir)) {
5777
+ throw new ParticipantDiscoveryError(
5778
+ `Claude Desktop Co-Work store not found at ${this.storeDir}.`
5779
+ );
5780
+ }
5781
+ const stateFiles = iterStateFiles2(this.storeDir);
5782
+ return {
5783
+ path: this.storeDir,
5784
+ version: "unknown",
5785
+ metadata: { runs: stateFiles.length }
5786
+ };
5787
+ }
5788
+ exportSessions(since, limit = 100) {
5789
+ const runs = this.runs(since);
5790
+ const sessions = runs.map(sessionFromRun5);
5791
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
5792
+ return sessions.slice(0, limit);
5793
+ }
5794
+ exportL5(since) {
5795
+ if (!isDir12(this.storeDir)) {
5796
+ throw new ParticipantDiscoveryError(
5797
+ `Claude Desktop Co-Work store not found at ${this.storeDir}.`
5798
+ );
5799
+ }
5800
+ const runs = this.runs(since);
5801
+ const sessions = runs.map(sessionFromRun5);
5802
+ sessions.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
5803
+ const entities = entitiesFromRuns(runs);
5804
+ const visibleEntities = (0, import_l514.filterForFederation)(entities, this.policy);
5805
+ return (0, import_l514.makeManifest)({
5806
+ spec_version: SPEC_VERSION,
5807
+ agent: (0, import_l514.makeAgentInfo)({
5808
+ id: AGENT_ID14,
5809
+ type: AGENT_TYPE14,
5810
+ instance: (0, import_node_os14.hostname)(),
5811
+ spec_version_compat: SPEC_VERSION,
5812
+ role_narrative: ROLE_NARRATIVE14
5813
+ }),
5814
+ last_updated: isoSeconds(),
5815
+ capabilities: capabilities2(runs),
5816
+ recent_sessions: sessions,
5817
+ known_entities: visibleEntities,
5818
+ visibility_policy: this.policy
5819
+ });
5820
+ }
5821
+ healthCheck() {
5822
+ if (!isDir12(this.storeDir)) {
5823
+ return {
5824
+ status: "blocked",
5825
+ reason: `Claude Desktop Co-Work store not found at ${this.storeDir}.`,
5826
+ details: { store_dir: this.storeDir },
5827
+ proposedFix: "Install the Claude desktop app and run a Co-Work / local-agent session once. Set BOURDON_CLAUDE_DESKTOP_DIR if the app stores state in a non-standard location."
5828
+ };
5829
+ }
5830
+ let stateFiles;
5831
+ let runs;
5832
+ let malformed;
5833
+ try {
5834
+ stateFiles = iterStateFiles2(this.storeDir);
5835
+ [runs, malformed] = this.collectRuns();
5836
+ } catch (exc) {
5837
+ logger7.warn(`ClaudeDesktopCoworkParticipant health_check failed: ${String(exc)}`);
5838
+ return {
5839
+ status: "degraded",
5840
+ reason: "Co-Work store present but extraction failed.",
5841
+ details: { error: String(exc) },
5842
+ proposedFix: "Close the Claude desktop app (its state files may be locked) and re-run `bourdon claude-desktop-cowork export`."
5843
+ };
5844
+ }
5845
+ if (stateFiles.length === 0) {
5846
+ return {
5847
+ status: "degraded",
5848
+ reason: "No Co-Work runs found under the store directory.",
5849
+ details: { store_dir: this.storeDir },
5850
+ proposedFix: "Run a Co-Work / local-agent session in the Claude desktop app, then re-run `bourdon claude-desktop-cowork export`."
5851
+ };
5852
+ }
5853
+ return {
5854
+ status: "ok",
5855
+ details: {
5856
+ store_dir: this.storeDir,
5857
+ run_count: stateFiles.length,
5858
+ runs_extracted: runs.length,
5859
+ malformed_records: malformed,
5860
+ runs_with_scalars: runs.filter((r) => r.numTurns !== null).length
5861
+ }
5862
+ };
5863
+ }
5864
+ // -- Internal ----------------------------------------------------------------
5865
+ collectRuns() {
5866
+ const runs = [];
5867
+ let malformed = 0;
5868
+ for (const statePath of iterStateFiles2(this.storeDir)) {
5869
+ const state = loadStateJson2(statePath);
5870
+ if (state === null) {
5871
+ malformed += 1;
5872
+ continue;
5873
+ }
5874
+ runs.push(runFromState(statePath, state));
5875
+ }
5876
+ return [runs, malformed];
5877
+ }
5878
+ runs(since) {
5879
+ const [runs] = this.collectRuns();
5880
+ let filtered = runs;
5881
+ if (since !== void 0) {
5882
+ const cutoff = new Date(since.getTime()).toISOString().slice(0, 10);
5883
+ filtered = runs.filter((run) => !run.date || run.date >= cutoff);
5884
+ }
5885
+ filtered.sort((a, b) => {
5886
+ if (a.date !== b.date) return a.date < b.date ? 1 : -1;
5887
+ if (a.runId !== b.runId) return a.runId < b.runId ? 1 : -1;
5888
+ return 0;
5889
+ });
5890
+ return filtered;
5891
+ }
5892
+ };
5893
+
5894
+ // src/readers/copilot-vscode.ts
5895
+ var import_node_fs15 = require("fs");
5896
+ var import_node_os15 = require("os");
5897
+ var import_node_path15 = require("path");
5898
+ var import_l515 = require("@getbourdon/l5");
5899
+ var import_redaction15 = require("@getbourdon/redaction");
5900
+ var AGENT_ID15 = "copilot-vscode";
5901
+ var AGENT_TYPE15 = "code-assistant";
5902
+ var DISPLAY_NAME10 = "GitHub Copilot \xB7 VS Code";
5903
+ var ROLE_NARRATIVE15 = "VS Code integrated Copilot Chat \u2014 the GUI surface for inline completion, chat, plan mode, ask mode, and explore mode. Persists per-workspace transcripts and repo-scoped memory locally. The most widely-used Copilot surface by session count.";
5904
+ var DEFAULT_POLICY14 = (0, import_l515.makeVisibilityPolicy)({
5905
+ default: import_l515.Visibility.TEAM,
5906
+ private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
5907
+ team_tags: ["copilot-vscode", "copilot", "vscode", "workspace"]
5908
+ });
5909
+ var COPILOT_CHAT_EXT = "GitHub.copilot-chat";
5910
+ var TRANSCRIPTS_DIR = "transcripts";
5911
+ var MEMORY_TOOL_DIR = "memory-tool";
5912
+ var MAX_KEY_ACTIONS4 = 6;
5913
+ var MAX_KEY_ACTION_CHARS7 = 280;
5914
+ var MAX_SUMMARY_CHARS3 = 260;
5915
+ var MAX_TRANSCRIPT_EVENTS = 500;
5916
+ function homeDir10() {
5917
+ try {
5918
+ return (0, import_node_os15.homedir)();
5919
+ } catch {
5920
+ return process.env.HOME || process.env.USERPROFILE || ".";
5921
+ }
5922
+ }
5923
+ function isDir13(p) {
5924
+ try {
5925
+ return (0, import_node_fs15.statSync)(p).isDirectory();
5926
+ } catch {
5927
+ return false;
5928
+ }
5929
+ }
5930
+ function safeNativeMemoryText7(value, limit = 180) {
5931
+ return (0, import_redaction15.redactText)(value, limit);
5932
+ }
5933
+ function bounded9(value, limit) {
5934
+ const normalized = value.split(/\s+/).filter(Boolean).join(" ");
5935
+ if (normalized.length <= limit) return normalized;
5936
+ return normalized.slice(0, limit - 1).replace(/\s+$/, "") + "\u2026";
5937
+ }
5938
+ function splitLines4(text) {
5939
+ return text.split(/\r\n|\r|\n/);
5940
+ }
5941
+ function defaultVscodeWorkspaceStorageDir() {
5942
+ const env = process.env.COPILOT_VSCODE_STORAGE;
5943
+ if (env) return env;
5944
+ const system = (0, import_node_os15.platform)();
5945
+ if (system === "win32") {
5946
+ const appdata = process.env.APPDATA;
5947
+ if (appdata) return (0, import_node_path15.join)(appdata, "Code", "User", "workspaceStorage");
5948
+ return null;
5949
+ }
5950
+ if (system === "darwin") {
5951
+ return (0, import_node_path15.join)(
5952
+ homeDir10(),
5953
+ "Library",
5954
+ "Application Support",
5955
+ "Code",
5956
+ "User",
5957
+ "workspaceStorage"
5958
+ );
5959
+ }
5960
+ const wslPath = "/mnt/c/Users";
5961
+ if (isDir13(wslPath)) {
5962
+ try {
5963
+ for (const candidate of (0, import_node_fs15.readdirSync)(wslPath)) {
5964
+ try {
5965
+ const ws = (0, import_node_path15.join)(
5966
+ wslPath,
5967
+ candidate,
5968
+ "AppData",
5969
+ "Roaming",
5970
+ "Code",
5971
+ "User",
5972
+ "workspaceStorage"
5973
+ );
5974
+ if (isDir13(ws)) return ws;
5975
+ } catch {
5976
+ continue;
5977
+ }
5978
+ }
5979
+ } catch {
5980
+ }
5981
+ }
5982
+ const config = process.env.XDG_CONFIG_HOME || (0, import_node_path15.join)(homeDir10(), ".config");
5983
+ return (0, import_node_path15.join)(config, "Code", "User", "workspaceStorage");
5984
+ }
5985
+ function findCopilotChatDirs(workspaceStorage) {
5986
+ if (!isDir13(workspaceStorage)) return [];
5987
+ const dirs = [];
5988
+ try {
5989
+ for (const wsHash of (0, import_node_fs15.readdirSync)(workspaceStorage)) {
5990
+ const chatDir = (0, import_node_path15.join)(workspaceStorage, wsHash, COPILOT_CHAT_EXT);
5991
+ if (isDir13(chatDir)) dirs.push(chatDir);
5992
+ }
5993
+ } catch (exc) {
5994
+ logger8.warn(`CopilotVscodeParticipant: cannot scan workspace storage: ${String(exc)}`);
5995
+ }
5996
+ return dirs;
5997
+ }
5998
+ var logger8 = {
5999
+ warn(msg) {
6000
+ console.warn(`[bourdon.participants] ${msg}`);
6001
+ }
6002
+ };
6003
+ function parseTranscript(path) {
6004
+ let text;
6005
+ try {
6006
+ text = (0, import_node_fs15.readFileSync)(path, "utf8");
6007
+ } catch {
6008
+ return null;
6009
+ }
6010
+ const lines = text.trim() ? splitLines4(text.trim()) : [];
6011
+ if (lines.length === 0) return null;
6012
+ const info = {
6013
+ transcript_path: path,
6014
+ session_id: stem(path),
6015
+ start_time: null,
6016
+ producer: null,
6017
+ copilot_version: null,
6018
+ vscode_version: null,
6019
+ user_messages: [],
6020
+ turn_count: 0
6021
+ };
6022
+ let turnCount = 0;
6023
+ const userMsgs = [];
6024
+ for (const line of lines.slice(0, MAX_TRANSCRIPT_EVENTS)) {
6025
+ let event;
6026
+ try {
6027
+ event = JSON.parse(line);
6028
+ } catch {
6029
+ continue;
6030
+ }
6031
+ if (typeof event !== "object" || event === null) continue;
6032
+ const ev = event;
6033
+ const eventType = ev["type"];
6034
+ const dataRaw = ev["data"];
6035
+ const data = typeof dataRaw === "object" && dataRaw !== null ? dataRaw : {};
6036
+ if (eventType === "session.start") {
6037
+ info.start_time = data["startTime"] == null ? null : String(data["startTime"]);
6038
+ info.producer = data["producer"] ?? null;
6039
+ info.copilot_version = data["copilotVersion"] ?? null;
6040
+ info.vscode_version = data["vscodeVersion"] ?? null;
6041
+ } else if (eventType === "user.message") {
6042
+ const content = data["content"] ? String(data["content"]) : "";
6043
+ if (content) {
6044
+ userMsgs.push(content.slice(0, 200));
6045
+ }
6046
+ } else if (eventType === "assistant.turn_end") {
6047
+ turnCount += 1;
6048
+ }
6049
+ }
6050
+ info.user_messages = userMsgs.slice(0, 20);
6051
+ info.turn_count = turnCount;
6052
+ return info;
6053
+ }
6054
+ function stem(p) {
6055
+ const base = p.replace(/[\\/]+$/, "").split(/[\\/]/).pop() ?? "";
6056
+ return base.replace(/\.[^.]+$/, "");
6057
+ }
6058
+ function walkMarkdown(dir) {
6059
+ const out = [];
6060
+ let entries;
6061
+ try {
6062
+ entries = (0, import_node_fs15.readdirSync)(dir).sort();
6063
+ } catch {
6064
+ return out;
6065
+ }
6066
+ for (const name of entries) {
6067
+ const full = (0, import_node_path15.join)(dir, name);
6068
+ if (isDir13(full)) {
6069
+ out.push(...walkMarkdown(full));
6070
+ } else if (name.endsWith(".md")) {
6071
+ out.push(full);
6072
+ }
6073
+ }
6074
+ return out;
6075
+ }
6076
+ function parseMemoryFiles(chatDir) {
6077
+ const memoryDir = (0, import_node_path15.join)(chatDir, MEMORY_TOOL_DIR, "memories");
6078
+ if (!isDir13(memoryDir)) return [];
6079
+ const memories = [];
6080
+ for (const mdFile of walkMarkdown(memoryDir)) {
6081
+ let text;
6082
+ try {
6083
+ text = (0, import_node_fs15.readFileSync)(mdFile, "utf8");
6084
+ } catch {
6085
+ continue;
6086
+ }
6087
+ if (!text.trim()) continue;
6088
+ const rel = (0, import_node_path15.relative)(memoryDir, mdFile);
6089
+ const name = rel.replace(/\.[^./\\]*$/, "").replace(/\\/g, "/");
6090
+ const bulletCount = text.split("\n- ").length - 1 + (text.startsWith("- ") ? 1 : 0);
6091
+ memories.push({ name, content: text, path: mdFile, bullet_count: bulletCount });
6092
+ }
6093
+ return memories;
6094
+ }
6095
+ function sessionFromTranscript(info) {
6096
+ const start = info.start_time ?? "";
6097
+ const dateStr = start ? start.slice(0, 10) : "";
6098
+ const keyActions3 = [];
6099
+ const msgs = info.user_messages ?? [];
6100
+ if (msgs.length > 0) {
6101
+ keyActions3.push(bounded9(safeNativeMemoryText7(String(msgs[0])), MAX_KEY_ACTION_CHARS7));
6102
+ }
6103
+ if (info.turn_count) keyActions3.push(`turns: ${info.turn_count}`);
6104
+ if (info.copilot_version) keyActions3.push(`copilot: ${String(info.copilot_version)}`);
6105
+ if (info.producer) keyActions3.push(`mode: ${String(info.producer)}`);
6106
+ return (0, import_l515.makeSession)({
6107
+ date: dateStr,
6108
+ cwd: void 0,
6109
+ project_focus: [],
6110
+ key_actions: keyActions3.slice(0, MAX_KEY_ACTIONS4),
6111
+ files_touched: [],
6112
+ visibility: import_l515.Visibility.TEAM
6113
+ });
6114
+ }
6115
+ function entitiesFromMemories(memories) {
6116
+ const entities = [];
6117
+ for (const mem of memories) {
6118
+ const bullets = [];
6119
+ for (const line of splitLines4(mem.content)) {
6120
+ const stripped = line.trim();
6121
+ if (stripped.startsWith("- ")) {
6122
+ bullets.push(stripped.slice(2).trim());
6123
+ if (bullets.length === 3) break;
6124
+ }
6125
+ }
6126
+ let summary = bullets.length > 0 ? bullets.join("; ") : void 0;
6127
+ if (summary) summary = bounded9(safeNativeMemoryText7(summary), MAX_SUMMARY_CHARS3);
6128
+ entities.push(
6129
+ (0, import_l515.makeEntity)({
6130
+ name: mem.name,
6131
+ type: "vscode-memory",
6132
+ summary,
6133
+ last_touched: void 0,
6134
+ tags: ["copilot-vscode", "memory-tool", "repo-memory"],
6135
+ visibility: import_l515.Visibility.TEAM
6136
+ })
6137
+ );
6138
+ }
6139
+ return entities;
6140
+ }
6141
+ function entitiesFromTranscripts(transcripts) {
6142
+ const topicCounts = /* @__PURE__ */ new Map();
6143
+ for (const info of transcripts) {
6144
+ const msgs = info.user_messages ?? [];
6145
+ if (msgs.length > 0) {
6146
+ const first = String(msgs[0]).slice(0, 100);
6147
+ const slashMatch = /^\/(\S+)/.exec(first);
6148
+ if (slashMatch) {
6149
+ const topic = `copilot-command:${slashMatch[1]}`;
6150
+ topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + 1);
6151
+ }
6152
+ }
6153
+ }
6154
+ const ordered = [...topicCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
6155
+ const entities = [];
6156
+ for (const [topic, count] of ordered) {
6157
+ entities.push(
6158
+ (0, import_l515.makeEntity)({
6159
+ name: topic,
6160
+ type: "vscode-capability",
6161
+ summary: `VS Code Copilot command used ${count} time(s).`,
6162
+ tags: ["copilot-vscode", "capability"],
6163
+ visibility: import_l515.Visibility.TEAM
6164
+ })
6165
+ );
6166
+ }
6167
+ return entities;
6168
+ }
6169
+ var CopilotVscodeParticipant = class {
6170
+ agentId = AGENT_ID15;
6171
+ agentType = AGENT_TYPE15;
6172
+ displayName = DISPLAY_NAME10;
6173
+ workspaceStorage;
6174
+ /** The workspaceStorage dir the setup wizard probes for presence. */
6175
+ static defaultNativePath(home) {
6176
+ if (home !== void 0) {
6177
+ return (0, import_node_path15.join)(home, ".config", "Code", "User", "workspaceStorage");
6178
+ }
6179
+ let ws;
6180
+ try {
6181
+ ws = defaultVscodeWorkspaceStorageDir();
6182
+ } catch {
6183
+ ws = null;
6184
+ }
6185
+ if (ws !== null) return ws;
6186
+ return (0, import_node_path15.join)(homeDir10(), ".config", "Code", "User", "workspaceStorage");
6187
+ }
6188
+ constructor(workspaceStorage) {
6189
+ this.workspaceStorage = workspaceStorage ?? null;
6190
+ }
6191
+ get nativePath() {
6192
+ return this.workspaceStorage ?? defaultVscodeWorkspaceStorageDir() ?? "";
6193
+ }
6194
+ resolveWs() {
6195
+ return this.workspaceStorage ?? defaultVscodeWorkspaceStorageDir();
6196
+ }
6197
+ // -- Protocol surface -------------------------------------------------------
6198
+ discover() {
6199
+ const ws = this.resolveWs();
6200
+ if (ws === null || !isDir13(ws)) {
6201
+ throw new ParticipantDiscoveryError(
6202
+ `VS Code workspaceStorage not found at ${ws ?? "None"}. Install VS Code with the GitHub Copilot Chat extension.`
6203
+ );
6204
+ }
6205
+ const chatDirs = findCopilotChatDirs(ws);
6206
+ if (chatDirs.length === 0) {
6207
+ throw new ParticipantDiscoveryError(
6208
+ `No GitHub.copilot-chat data found under ${ws}. Open VS Code and use Copilot Chat at least once.`
6209
+ );
6210
+ }
6211
+ return {
6212
+ path: ws,
6213
+ version: "transcript-v1",
6214
+ metadata: {
6215
+ workspace_count: chatDirs.length,
6216
+ workspace_storage_path: ws
6217
+ }
6218
+ };
6219
+ }
6220
+ exportSessions(since, limit = 100) {
6221
+ const sinceIso = since ? since.toISOString().slice(0, 10) : null;
6222
+ const transcripts = this.allTranscripts();
6223
+ const sessions = [];
6224
+ for (const info of transcripts) {
6225
+ const session = sessionFromTranscript(info);
6226
+ if (sinceIso && session.date && session.date < sinceIso) continue;
6227
+ sessions.push(session);
6228
+ if (sessions.length >= limit) break;
6229
+ }
6230
+ return sessions;
6231
+ }
6232
+ exportL5(since) {
6233
+ const transcripts = this.allTranscripts();
6234
+ const memories = this.allMemories();
6235
+ const sinceIso = since ? since.toISOString().slice(0, 10) : null;
6236
+ const sessions = [];
6237
+ for (const info of transcripts) {
6238
+ const session = sessionFromTranscript(info);
6239
+ if (sinceIso && session.date && session.date < sinceIso) continue;
6240
+ sessions.push(session);
6241
+ }
6242
+ const entities = [
6243
+ ...entitiesFromMemories(memories),
6244
+ ...entitiesFromTranscripts(transcripts)
6245
+ ];
6246
+ const visibleEntities = (0, import_l515.filterForFederation)(entities, DEFAULT_POLICY14);
6247
+ return (0, import_l515.makeManifest)({
6248
+ spec_version: SPEC_VERSION,
6249
+ agent: (0, import_l515.makeAgentInfo)({
6250
+ id: AGENT_ID15,
6251
+ type: AGENT_TYPE15,
6252
+ instance: (0, import_node_os15.hostname)() || "unknown",
6253
+ spec_version_compat: `>=${SPEC_VERSION}`,
6254
+ role_narrative: ROLE_NARRATIVE15
6255
+ }),
6256
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
6257
+ capabilities: [
6258
+ "inline-completion",
6259
+ "chat",
6260
+ "plan-mode",
6261
+ "ask-mode",
6262
+ "explore-mode",
6263
+ "memory-tool",
6264
+ "workspace-scoped"
6265
+ ],
6266
+ recent_sessions: sessions,
6267
+ known_entities: visibleEntities,
6268
+ visibility_policy: DEFAULT_POLICY14
6269
+ });
6270
+ }
6271
+ healthCheck() {
6272
+ try {
6273
+ const ws = this.resolveWs();
6274
+ if (ws === null || !isDir13(ws)) {
6275
+ return {
6276
+ status: "blocked",
6277
+ reason: `VS Code workspaceStorage not found at ${ws ?? "None"}.`,
6278
+ details: { expected_path: ws ?? "None" },
6279
+ proposedFix: "Install VS Code and the GitHub Copilot Chat extension. Set COPILOT_VSCODE_STORAGE if VS Code stores data elsewhere."
6280
+ };
6281
+ }
6282
+ const chatDirs = findCopilotChatDirs(ws);
6283
+ if (chatDirs.length === 0) {
6284
+ return {
6285
+ status: "degraded",
6286
+ reason: "No Copilot Chat data found in any workspace.",
6287
+ details: { workspace_storage: ws },
6288
+ proposedFix: "Open VS Code and start a Copilot Chat conversation, then re-run `bourdon copilot-vscode export`."
6289
+ };
6290
+ }
6291
+ const transcripts = this.allTranscripts();
6292
+ const memories = this.allMemories();
6293
+ return {
6294
+ status: "ok",
6295
+ details: {
6296
+ workspace_storage: ws,
6297
+ workspace_count: chatDirs.length,
6298
+ transcript_count: transcripts.length,
6299
+ memory_file_count: memories.length,
6300
+ total_turns: transcripts.reduce((acc, t) => acc + (t.turn_count || 0), 0)
6301
+ }
6302
+ };
6303
+ } catch (err) {
6304
+ return {
6305
+ status: "degraded",
6306
+ reason: `unexpected error: ${String(err)}`,
6307
+ details: {}
6308
+ };
6309
+ }
6310
+ }
6311
+ // -- Internal ---------------------------------------------------------------
6312
+ /** Collect and parse all transcripts across all workspaces. */
6313
+ allTranscripts() {
6314
+ const ws = this.resolveWs();
6315
+ if (ws === null) return [];
6316
+ const chatDirs = findCopilotChatDirs(ws);
6317
+ const transcripts = [];
6318
+ for (const chatDir of chatDirs) {
6319
+ const transcriptDir = (0, import_node_path15.join)(chatDir, TRANSCRIPTS_DIR);
6320
+ if (!isDir13(transcriptDir)) continue;
6321
+ let files;
6322
+ try {
6323
+ files = (0, import_node_fs15.readdirSync)(transcriptDir).sort();
6324
+ } catch {
6325
+ continue;
6326
+ }
6327
+ for (const fname of files) {
6328
+ if (!fname.endsWith(".jsonl")) continue;
6329
+ const info = parseTranscript((0, import_node_path15.join)(transcriptDir, fname));
6330
+ if (info !== null) transcripts.push(info);
6331
+ }
6332
+ }
6333
+ transcripts.sort((a, b) => {
6334
+ const av = a.start_time ?? "";
6335
+ const bv = b.start_time ?? "";
6336
+ return av < bv ? 1 : av > bv ? -1 : 0;
6337
+ });
6338
+ return transcripts;
6339
+ }
6340
+ /** Collect all memory-tool files across all workspaces. */
6341
+ allMemories() {
6342
+ const ws = this.resolveWs();
6343
+ if (ws === null) return [];
6344
+ const chatDirs = findCopilotChatDirs(ws);
6345
+ const memories = [];
6346
+ for (const chatDir of chatDirs) {
6347
+ memories.push(...parseMemoryFiles(chatDir));
6348
+ }
6349
+ return memories;
6350
+ }
6351
+ };
6352
+
3202
6353
  // src/registry.ts
3203
6354
  var FIRST_PARTY = [
3204
6355
  CascadeParticipant,
3205
6356
  ClaudeCodeParticipant,
6357
+ ClaudeCodeAutomationsParticipant,
6358
+ ClaudeDesktopCodeParticipant,
6359
+ ClaudeDesktopCoworkParticipant,
6360
+ CodexAutomationsParticipant,
3206
6361
  CopilotParticipant,
6362
+ CopilotAutomationsParticipant,
3207
6363
  CopilotCliParticipant,
6364
+ CopilotVscodeParticipant,
3208
6365
  CursorParticipant,
6366
+ CursorAutomationsParticipant,
3209
6367
  GitHubCopilotParticipant,
3210
6368
  HermesParticipant,
3211
6369
  OpenClawParticipant
3212
6370
  ];
3213
- var logger3 = {
6371
+ var logger9 = {
3214
6372
  warn(msg) {
3215
6373
  console.warn(`[bourdon.participants] ${msg}`);
3216
6374
  }
@@ -3222,7 +6380,7 @@ function discoverParticipants(ctors = FIRST_PARTY) {
3222
6380
  try {
3223
6381
  instance = new Ctor();
3224
6382
  } catch (err) {
3225
- logger3.warn(
6383
+ logger9.warn(
3226
6384
  `Skipping participant ${Ctor.name || "<anonymous>"}: construction failed: ${String(err)}`
3227
6385
  );
3228
6386
  continue;
@@ -3240,15 +6398,22 @@ function resolvePluginParticipants() {
3240
6398
  }
3241
6399
 
3242
6400
  // src/index.ts
3243
- var import_l59 = require("@getbourdon/l5");
6401
+ var import_l516 = require("@getbourdon/l5");
3244
6402
  // Annotate the CommonJS export names for ESM import in node:
3245
6403
  0 && (module.exports = {
3246
6404
  CONTRACT_VERSION,
3247
6405
  CacheEntry,
3248
6406
  CascadeParticipant,
6407
+ ClaudeCodeAutomationsParticipant,
3249
6408
  ClaudeCodeParticipant,
6409
+ ClaudeDesktopCodeParticipant,
6410
+ ClaudeDesktopCoworkParticipant,
6411
+ CodexAutomationsParticipant,
6412
+ CopilotAutomationsParticipant,
3250
6413
  CopilotCliParticipant,
3251
6414
  CopilotParticipant,
6415
+ CopilotVscodeParticipant,
6416
+ CursorAutomationsParticipant,
3252
6417
  CursorParticipant,
3253
6418
  DEFAULT_CACHE_TTL_SECONDS,
3254
6419
  FIRST_PARTY,