@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 +3194 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +327 -5
- package/dist/index.d.ts +970 -33
- package/dist/index.js +6509 -41
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
- package/dist/base.d.ts +0 -88
- package/dist/base.js +0 -52
- package/dist/network-base.d.ts +0 -108
- package/dist/network-base.js +0 -248
- package/dist/readers/cascade.d.ts +0 -49
- package/dist/readers/cascade.js +0 -428
- package/dist/readers/claude-code.d.ts +0 -33
- package/dist/readers/claude-code.js +0 -552
- package/dist/readers/copilot-cli.d.ts +0 -45
- package/dist/readers/copilot-cli.js +0 -416
- package/dist/readers/copilot.d.ts +0 -59
- package/dist/readers/copilot.js +0 -413
- package/dist/readers/cursor.d.ts +0 -50
- package/dist/readers/cursor.js +0 -670
- package/dist/readers/github-copilot.d.ts +0 -34
- package/dist/readers/github-copilot.js +0 -242
- package/dist/readers/hermes.d.ts +0 -37
- package/dist/readers/hermes.js +0 -426
- package/dist/readers/openclaw.d.ts +0 -78
- package/dist/readers/openclaw.js +0 -386
- package/dist/registry.d.ts +0 -40
- package/dist/registry.js +0 -85
- package/dist/sqlite-base.d.ts +0 -65
- package/dist/sqlite-base.js +0 -163
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: () =>
|
|
48
|
-
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: () =>
|
|
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: () =>
|
|
71
|
+
toDict: () => import_l516.toDict,
|
|
65
72
|
tryOpenReadonly: () => tryOpenReadonly,
|
|
66
|
-
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
|
|
510
|
-
result.push([
|
|
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
|
|
521
|
-
if (record.title)
|
|
527
|
+
const keyActions3 = [];
|
|
528
|
+
if (record.title) keyActions3.push(bounded(record.title, 160));
|
|
522
529
|
if (toolNames.length) {
|
|
523
|
-
|
|
530
|
+
keyActions3.push("Tools: " + toolNames.slice(0, 8).join(", "));
|
|
524
531
|
} else if (record.toolCallCount) {
|
|
525
|
-
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
1803
|
-
paths.push((0, import_node_path4.join)(
|
|
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
|
|
2380
|
+
const keyActions3 = [];
|
|
2374
2381
|
if (raw["summary"]) {
|
|
2375
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|