@getbourdon/participants 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ BOLT_CONFIG: () => BOLT_CONFIG,
24
+ BoltParticipant: () => BoltParticipant,
23
25
  CONTRACT_VERSION: () => CONTRACT_VERSION,
24
26
  CacheEntry: () => CacheEntry,
25
27
  CascadeParticipant: () => CascadeParticipant,
@@ -37,9 +39,12 @@ __export(index_exports, {
37
39
  DEFAULT_CACHE_TTL_SECONDS: () => DEFAULT_CACHE_TTL_SECONDS,
38
40
  DEFAULT_RECENT_THREAD_LIMIT: () => DEFAULT_RECENT_THREAD_LIMIT,
39
41
  DEFAULT_TITLE_LIMIT: () => DEFAULT_TITLE_LIMIT,
42
+ ExportStoreParticipant: () => ExportStoreParticipant,
40
43
  FIRST_PARTY: () => FIRST_PARTY,
41
44
  GitHubCopilotParticipant: () => GitHubCopilotParticipant,
42
45
  HermesParticipant: () => HermesParticipant,
46
+ LOVABLE_CONFIG: () => LOVABLE_CONFIG,
47
+ LovableParticipant: () => LovableParticipant,
43
48
  MIN_PATCHED_VERSION: () => MIN_PATCHED_VERSION,
44
49
  NETWORK_CONTRACT_VERSION: () => NETWORK_CONTRACT_VERSION,
45
50
  NetworkParticipant: () => NetworkParticipant,
@@ -54,31 +59,37 @@ __export(index_exports, {
54
59
  ParticipantVersionMismatchError: () => ParticipantVersionMismatchError,
55
60
  PayloadCache: () => PayloadCache,
56
61
  SPEC_VERSION: () => SPEC_VERSION,
57
- Visibility: () => import_l516.Visibility,
58
- applyVisibility: () => import_l516.applyVisibility,
62
+ V0Participant: () => V0Participant,
63
+ V0_CONFIG: () => V0_CONFIG,
64
+ Visibility: () => import_l520.Visibility,
65
+ applyVisibility: () => import_l520.applyVisibility,
59
66
  defaultCacheRoot: () => defaultCacheRoot,
60
67
  discoverParticipants: () => discoverParticipants,
61
68
  envAuthProvider: () => envAuthProvider,
62
69
  epochToIsoDate: () => epochToIsoDate,
63
70
  epochToIsoDatetime: () => epochToIsoDatetime,
64
- filterForFederation: () => import_l516.filterForFederation,
71
+ filterForFederation: () => import_l520.filterForFederation,
65
72
  friendlyLabel: () => friendlyLabel,
66
73
  ghTokenProvider: () => ghTokenProvider,
67
74
  isParticipant: () => isParticipant,
75
+ isoDate: () => isoDate2,
68
76
  loadProjectHints: () => loadProjectHints,
69
77
  openReadonly: () => openReadonly,
70
78
  parseSessionIndex: () => parseSessionIndex,
79
+ parseVisibility: () => parseVisibility,
71
80
  projectKeyFromCwd: () => projectKeyFromCwd,
72
81
  recentSessionThreads: () => recentSessionThreads,
73
82
  resolveCodexHome: () => resolveCodexHome,
74
83
  resolvePluginParticipants: () => resolvePluginParticipants,
84
+ safeStr: () => safeStr,
85
+ safeStrList: () => safeStrList,
75
86
  sessionThreadTitles: () => sessionThreadTitles,
76
87
  sqliteAvailable: () => sqliteAvailable,
77
88
  tableColumns: () => tableColumns,
78
89
  tableExists: () => tableExists,
79
- toDict: () => import_l516.toDict,
90
+ toDict: () => import_l520.toDict,
80
91
  tryOpenReadonly: () => tryOpenReadonly,
81
- validateManifest: () => import_l516.validateManifest,
92
+ validateManifest: () => import_l520.validateManifest,
82
93
  verifyInstance: () => verifyInstance
83
94
  });
84
95
  module.exports = __toCommonJS(index_exports);
@@ -6436,8 +6447,475 @@ function recentSessionThreads(codexHome, opts = {}) {
6436
6447
  return threads;
6437
6448
  }
6438
6449
 
6450
+ // src/readers/bolt.ts
6451
+ var import_l517 = require("@getbourdon/l5");
6452
+ var import_redaction17 = require("@getbourdon/redaction");
6453
+
6454
+ // src/export-store-base.ts
6455
+ var import_node_fs17 = require("fs");
6456
+ var import_node_os17 = require("os");
6457
+ var import_node_path17 = require("path");
6458
+ var import_l516 = require("@getbourdon/l5");
6459
+ var import_redaction16 = require("@getbourdon/redaction");
6460
+ var VISIBILITY_VALUES = new Set(Object.values(import_l516.Visibility));
6461
+ function parseVisibility(raw) {
6462
+ if (typeof raw !== "string") return null;
6463
+ const value = raw.trim().toLowerCase();
6464
+ return VISIBILITY_VALUES.has(value) ? value : null;
6465
+ }
6466
+ function isoDate2(raw) {
6467
+ if (typeof raw !== "string" || !raw.trim()) return null;
6468
+ const text = raw.trim();
6469
+ if (text.includes("T")) {
6470
+ const parsed2 = new Date(text.replace(/Z$/, "+00:00"));
6471
+ if (Number.isNaN(parsed2.getTime())) return null;
6472
+ return parsed2.toISOString().slice(0, 10);
6473
+ }
6474
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(text)) return null;
6475
+ const parsed = /* @__PURE__ */ new Date(`${text}T00:00:00Z`);
6476
+ if (Number.isNaN(parsed.getTime())) return null;
6477
+ return parsed.toISOString().slice(0, 10) === text ? text : null;
6478
+ }
6479
+ function safeStr(raw, limit = 240) {
6480
+ if (raw === null || raw === void 0) return "";
6481
+ const text = String(raw).trim();
6482
+ return text ? (0, import_redaction16.redactText)(text, limit) : "";
6483
+ }
6484
+ function safeStrList(raw, limit = 200) {
6485
+ if (!Array.isArray(raw)) return [];
6486
+ const out = [];
6487
+ for (const item of raw) {
6488
+ const cleaned = safeStr(item, limit);
6489
+ if (cleaned) out.push(cleaned);
6490
+ }
6491
+ return out;
6492
+ }
6493
+ var ExportStoreParticipant = class _ExportStoreParticipant {
6494
+ agentId;
6495
+ agentType;
6496
+ displayName;
6497
+ config;
6498
+ stateDir;
6499
+ env;
6500
+ constructor(config, options = {}) {
6501
+ this.config = config;
6502
+ this.agentId = config.agentId;
6503
+ this.agentType = config.agentType;
6504
+ this.displayName = config.displayName;
6505
+ this.stateDir = options.stateDir;
6506
+ this.env = options.env ?? process.env;
6507
+ }
6508
+ static defaultNativePath(directoryName, home) {
6509
+ return (0, import_node_path17.join)(home ?? (0, import_node_os17.homedir)(), directoryName);
6510
+ }
6511
+ get nativePath() {
6512
+ return this.resolveStateDir();
6513
+ }
6514
+ resolveStateDir() {
6515
+ if (this.stateDir !== void 0) return this.stateDir;
6516
+ const override = (this.env[this.config.stateEnvVar] ?? "").trim();
6517
+ if (override) return override;
6518
+ return _ExportStoreParticipant.defaultNativePath(this.config.directoryName);
6519
+ }
6520
+ requireStateDir() {
6521
+ const dir = this.resolveStateDir();
6522
+ if (!isDir15(dir)) {
6523
+ throw new ParticipantDiscoveryError(
6524
+ `${this.config.label} state directory not found at '${dir}'.`
6525
+ );
6526
+ }
6527
+ return dir;
6528
+ }
6529
+ /**
6530
+ * `projects.json`, accepting either a bare array or `{projects: [...]}`.
6531
+ *
6532
+ * A MISSING file is empty (the store may legitimately hold only sessions);
6533
+ * a PRESENT but unreadable or wrongly-shaped file throws. The distinction is
6534
+ * the point: absence is a state, corruption is an error.
6535
+ */
6536
+ loadProjects(stateDir) {
6537
+ const path = (0, import_node_path17.join)(stateDir, "projects.json");
6538
+ if (!isFile12(path)) return [];
6539
+ let payload;
6540
+ try {
6541
+ payload = JSON.parse(stripBom((0, import_node_fs17.readFileSync)(path, "utf-8")));
6542
+ } catch (exc) {
6543
+ throw new ParticipantExportError(
6544
+ `${this.config.label} projects store present but unreadable at ${path}: ${exc instanceof Error ? exc.message : String(exc)}`
6545
+ );
6546
+ }
6547
+ if (Array.isArray(payload)) return payload.filter(isDict);
6548
+ if (isDict(payload) && Array.isArray(payload.projects)) return payload.projects.filter(isDict);
6549
+ throw new ParticipantExportError(
6550
+ `${this.config.label} projects store at ${path} must be a JSON array or object with projects list.`
6551
+ );
6552
+ }
6553
+ sessionFiles(stateDir) {
6554
+ const dir = (0, import_node_path17.join)(stateDir, "sessions");
6555
+ if (!isDir15(dir)) return [];
6556
+ return (0, import_node_fs17.readdirSync)(dir).filter((f) => f.endsWith(".json")).sort().map((f) => (0, import_node_path17.join)(dir, f));
6557
+ }
6558
+ /** Newest first. An unreadable session is skipped, never fatal. */
6559
+ loadSessions(stateDir) {
6560
+ const out = [];
6561
+ for (const path of this.sessionFiles(stateDir)) {
6562
+ let payload;
6563
+ try {
6564
+ payload = JSON.parse(stripBom((0, import_node_fs17.readFileSync)(path, "utf-8")));
6565
+ } catch {
6566
+ continue;
6567
+ }
6568
+ if (!isDict(payload)) continue;
6569
+ const session = this.config.sessionToRecord(payload, null);
6570
+ if (session !== null) out.push(session);
6571
+ }
6572
+ out.sort((a, b) => a.date < b.date ? 1 : a.date > b.date ? -1 : 0);
6573
+ return out;
6574
+ }
6575
+ discover() {
6576
+ const stateDir = this.resolveStateDir();
6577
+ if (!isDir15(stateDir)) {
6578
+ throw new ParticipantDiscoveryError(
6579
+ `${this.config.label} state directory not found at '${stateDir}'. Create it and add a projects.json export or run \`${this.config.initCommand}\`.`
6580
+ );
6581
+ }
6582
+ return {
6583
+ path: stateDir,
6584
+ version: "export-v1",
6585
+ metadata: {
6586
+ projects_file: isFile12((0, import_node_path17.join)(stateDir, "projects.json")),
6587
+ session_files: this.sessionFiles(stateDir).length,
6588
+ env_override: Boolean((this.env[this.config.stateEnvVar] ?? "").trim())
6589
+ }
6590
+ };
6591
+ }
6592
+ exportSessions(since, limit = 100) {
6593
+ const stateDir = this.requireStateDir();
6594
+ const sinceIso = since.toISOString().slice(0, 10);
6595
+ return this.loadSessions(stateDir).filter((s) => s.date >= sinceIso).slice(0, limit);
6596
+ }
6597
+ exportL5(since) {
6598
+ const stateDir = this.requireStateDir();
6599
+ let sessions = this.loadSessions(stateDir);
6600
+ if (since !== void 0 && since !== null) {
6601
+ const sinceIso = since.toISOString().slice(0, 10);
6602
+ sessions = sessions.filter((s) => s.date >= sinceIso);
6603
+ }
6604
+ const entities = [];
6605
+ const seen = /* @__PURE__ */ new Set();
6606
+ for (const record of this.loadProjects(stateDir)) {
6607
+ const entity = this.config.projectToEntity(record);
6608
+ if (entity === null) continue;
6609
+ const key = entity.name.toLowerCase();
6610
+ if (seen.has(key)) continue;
6611
+ seen.add(key);
6612
+ entities.push(entity);
6613
+ }
6614
+ const visibleEntities = (0, import_l516.filterForFederation)(entities, this.config.policy);
6615
+ return (0, import_l516.makeManifest)({
6616
+ spec_version: SPEC_VERSION,
6617
+ agent: (0, import_l516.makeAgentInfo)({
6618
+ id: this.config.agentId,
6619
+ type: this.config.agentType,
6620
+ role_narrative: this.config.roleNarrative,
6621
+ spec_version_compat: SPEC_VERSION
6622
+ }),
6623
+ last_updated: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00"),
6624
+ capabilities: [...this.config.capabilities],
6625
+ recent_sessions: sessions,
6626
+ known_entities: visibleEntities,
6627
+ visibility_policy: this.config.policy
6628
+ });
6629
+ }
6630
+ /** MUST NOT throw: the L6 server calls this in a polling loop. */
6631
+ healthCheck() {
6632
+ try {
6633
+ const stateDir = this.resolveStateDir();
6634
+ if (!isDir15(stateDir)) {
6635
+ return {
6636
+ status: "blocked",
6637
+ reason: `${this.config.label} state directory not found at '${stateDir}'.`,
6638
+ details: { expected: stateDir },
6639
+ proposedFix: `Run \`${this.config.initCommand}\` or create ${stateDir} and add projects.json.`
6640
+ };
6641
+ }
6642
+ const projectsFile = (0, import_node_path17.join)(stateDir, "projects.json");
6643
+ const sessionFiles = this.sessionFiles(stateDir);
6644
+ if (!isFile12(projectsFile) && sessionFiles.length === 0) {
6645
+ return {
6646
+ status: "degraded",
6647
+ reason: "State directory exists but contains no projects.json or sessions.",
6648
+ details: { path: stateDir },
6649
+ proposedFix: `Run \`${this.config.initCommand}\` or export projects into projects.json.`
6650
+ };
6651
+ }
6652
+ this.loadProjects(stateDir);
6653
+ this.loadSessions(stateDir);
6654
+ return {
6655
+ status: "ok",
6656
+ details: {
6657
+ path: stateDir,
6658
+ projects_file: isFile12(projectsFile),
6659
+ session_files: sessionFiles.length
6660
+ }
6661
+ };
6662
+ } catch (exc) {
6663
+ return {
6664
+ status: "degraded",
6665
+ reason: exc instanceof Error ? exc.message : String(exc),
6666
+ details: {},
6667
+ proposedFix: `Inspect ${this.resolveStateDirSafe()}/projects.json for syntax errors.`
6668
+ };
6669
+ }
6670
+ }
6671
+ resolveStateDirSafe() {
6672
+ try {
6673
+ return this.resolveStateDir();
6674
+ } catch {
6675
+ return `~/${this.config.directoryName}`;
6676
+ }
6677
+ }
6678
+ };
6679
+ function isDict(v) {
6680
+ return v !== null && typeof v === "object" && !Array.isArray(v);
6681
+ }
6682
+ function isDir15(p) {
6683
+ try {
6684
+ return (0, import_node_fs17.statSync)(p).isDirectory();
6685
+ } catch {
6686
+ return false;
6687
+ }
6688
+ }
6689
+ function isFile12(p) {
6690
+ try {
6691
+ return (0, import_node_fs17.statSync)(p).isFile();
6692
+ } catch {
6693
+ return false;
6694
+ }
6695
+ }
6696
+ function stripBom(text) {
6697
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
6698
+ }
6699
+
6700
+ // src/readers/bolt.ts
6701
+ var AGENT_ID16 = "bolt";
6702
+ var AGENT_TYPE16 = "code-assistant";
6703
+ var ROLE_NARRATIVE16 = "StackBlitz Bolt.new in-browser AI development environment. Builds, executes, and iterates full-stack applications in WebContainers from chat. Bourdon reads exported project and session state so other agents recognize what Bolt.new has developed.";
6704
+ var DEFAULT_POLICY15 = (0, import_l517.makeVisibilityPolicy)({
6705
+ default: import_l517.Visibility.TEAM,
6706
+ private_tags: ["personal", "credential", "secret", "client-confidential", "private"],
6707
+ team_tags: ["bolt", "project", "webcontainer", "stackblitz"]
6708
+ });
6709
+ var MAX_SUMMARY_CHARS4 = 320;
6710
+ var MAX_KEY_ACTION_CHARS8 = 300;
6711
+ var NAME_LIMIT = 80;
6712
+ function projectToEntity(record) {
6713
+ const rawName = record.name;
6714
+ if (typeof rawName !== "string" || !rawName) return null;
6715
+ const name = safeStr(rawName, NAME_LIMIT);
6716
+ if (!name) return null;
6717
+ const summary = safeStr(record.description ?? "", MAX_SUMMARY_CHARS4) || `Bolt.new project ${name}`;
6718
+ const tags = safeStrList(record.tags, 40);
6719
+ if (!tags.includes("bolt")) tags.unshift("bolt");
6720
+ const updated = isoDate2(record.updated_at);
6721
+ const archived = Boolean(record.archived ?? false);
6722
+ return (0, import_l517.makeEntity)({
6723
+ name,
6724
+ type: "project",
6725
+ summary,
6726
+ tags,
6727
+ last_touched: updated,
6728
+ valid_from: isoDate2(record.created_at),
6729
+ valid_to: archived ? updated : null,
6730
+ visibility: parseVisibility(record.visibility)
6731
+ });
6732
+ }
6733
+ function sessionToRecord(record, fallbackDate) {
6734
+ const date = isoDate2(record.date) ?? fallbackDate;
6735
+ if (!date) return null;
6736
+ const project = String(record.project ?? "").trim();
6737
+ return (0, import_l517.makeSession)({
6738
+ date,
6739
+ cwd: null,
6740
+ // cloud agent: there is no local working directory to name
6741
+ project_focus: project ? [(0, import_redaction17.redactText)(project, 120)] : [],
6742
+ key_actions: safeStrList(record.key_actions, MAX_KEY_ACTION_CHARS8),
6743
+ files_touched: safeStrList(record.files_touched),
6744
+ visibility: parseVisibility(record.visibility)
6745
+ });
6746
+ }
6747
+ var BOLT_CONFIG = {
6748
+ agentId: AGENT_ID16,
6749
+ agentType: AGENT_TYPE16,
6750
+ displayName: "Bolt.new",
6751
+ directoryName: ".bolt",
6752
+ stateEnvVar: "BOLT_STATE_DIR",
6753
+ roleNarrative: ROLE_NARRATIVE16,
6754
+ capabilities: ["in-browser-dev", "webcontainers", "full-stack-generation"],
6755
+ policy: DEFAULT_POLICY15,
6756
+ label: "Bolt.new",
6757
+ initCommand: "bourdon bolt init",
6758
+ projectToEntity,
6759
+ sessionToRecord
6760
+ };
6761
+ var BoltParticipant = class extends ExportStoreParticipant {
6762
+ constructor(options = {}) {
6763
+ super(BOLT_CONFIG, options);
6764
+ }
6765
+ static defaultNativePath(home) {
6766
+ return ExportStoreParticipant.defaultNativePath(".bolt", home);
6767
+ }
6768
+ };
6769
+
6770
+ // src/readers/lovable.ts
6771
+ var import_l518 = require("@getbourdon/l5");
6772
+ var import_redaction18 = require("@getbourdon/redaction");
6773
+ var AGENT_ID17 = "lovable";
6774
+ var AGENT_TYPE17 = "code-assistant";
6775
+ var ROLE_NARRATIVE17 = "Cloud app-building agent. Designs, builds, and iterates full-stack web applications from chat. Bourdon reads exported project and session state so other agents recognize what Lovable has built.";
6776
+ var DEFAULT_POLICY16 = (0, import_l518.makeVisibilityPolicy)({
6777
+ default: import_l518.Visibility.TEAM,
6778
+ private_tags: ["personal", "credential", "secret", "client-confidential"],
6779
+ team_tags: ["lovable", "project", "client"]
6780
+ });
6781
+ var MAX_SUMMARY_CHARS5 = 320;
6782
+ var MAX_KEY_ACTION_CHARS9 = 300;
6783
+ var NAME_LIMIT2 = 120;
6784
+ function projectToEntity2(record) {
6785
+ const name = String(record.name ?? "").trim();
6786
+ if (!name) return null;
6787
+ const summary = record.description;
6788
+ const summaryText = summary === null || summary === void 0 || summary === "" ? null : (0, import_redaction18.redactText)(String(summary), MAX_SUMMARY_CHARS5);
6789
+ const tags = safeStrList(record.tags);
6790
+ tags.push("lovable");
6791
+ const archived = Boolean(record.archived);
6792
+ const updated = isoDate2(record.updated_at) ?? isoDate2(record.created_at);
6793
+ return (0, import_l518.makeEntity)({
6794
+ name: (0, import_redaction18.redactText)(name, NAME_LIMIT2),
6795
+ // (1) 120, not 80
6796
+ type: "project",
6797
+ summary: summaryText,
6798
+ tags,
6799
+ last_touched: updated,
6800
+ valid_from: isoDate2(record.created_at),
6801
+ valid_to: archived ? updated : null,
6802
+ visibility: parseVisibility(record.visibility)
6803
+ });
6804
+ }
6805
+ function sessionToRecord2(record, fallbackDate) {
6806
+ const date = isoDate2(record.date) ?? fallbackDate;
6807
+ if (!date) return null;
6808
+ const project = String(record.project ?? "").trim();
6809
+ return (0, import_l518.makeSession)({
6810
+ date,
6811
+ cwd: null,
6812
+ // cloud agent: there is no local working directory to name
6813
+ project_focus: project ? [(0, import_redaction18.redactText)(project, 120)] : [],
6814
+ key_actions: safeStrList(record.key_actions, MAX_KEY_ACTION_CHARS9),
6815
+ // ⚠ No limit, so these are NOT redacted — the oracle's behaviour. Bolt and
6816
+ // v0 redact the same field. Flagged, not silently corrected.
6817
+ files_touched: safeStrList(record.files_touched),
6818
+ visibility: parseVisibility(record.visibility)
6819
+ });
6820
+ }
6821
+ var LOVABLE_CONFIG = {
6822
+ agentId: AGENT_ID17,
6823
+ agentType: AGENT_TYPE17,
6824
+ displayName: "Lovable",
6825
+ directoryName: ".lovable",
6826
+ stateEnvVar: "LOVABLE_STATE_DIR",
6827
+ roleNarrative: ROLE_NARRATIVE17,
6828
+ capabilities: ["app-building", "full-stack-generation", "deploy-preview"],
6829
+ policy: DEFAULT_POLICY16,
6830
+ label: "Lovable",
6831
+ initCommand: "bourdon lovable init",
6832
+ projectToEntity: projectToEntity2,
6833
+ sessionToRecord: sessionToRecord2
6834
+ };
6835
+ var LovableParticipant = class extends ExportStoreParticipant {
6836
+ constructor(options = {}) {
6837
+ super(LOVABLE_CONFIG, options);
6838
+ }
6839
+ static defaultNativePath(home) {
6840
+ return ExportStoreParticipant.defaultNativePath(".lovable", home);
6841
+ }
6842
+ };
6843
+
6844
+ // src/readers/v0.ts
6845
+ var import_l519 = require("@getbourdon/l5");
6846
+ var import_redaction19 = require("@getbourdon/redaction");
6847
+ var AGENT_ID18 = "v0";
6848
+ var AGENT_TYPE18 = "code-assistant";
6849
+ var ROLE_NARRATIVE18 = "Vercel v0 generative UI and full-stack web builder. Designs and iterates React, Next.js, and Tailwind components and web applications from chat. Bourdon reads exported project and session state so other agents recognize what v0 has designed and constructed.";
6850
+ var DEFAULT_POLICY17 = (0, import_l519.makeVisibilityPolicy)({
6851
+ default: import_l519.Visibility.TEAM,
6852
+ private_tags: ["personal", "credential", "secret", "client-confidential", "private"],
6853
+ team_tags: ["v0", "project", "client", "ui", "component"]
6854
+ });
6855
+ var MAX_SUMMARY_CHARS6 = 320;
6856
+ var MAX_KEY_ACTION_CHARS10 = 300;
6857
+ var NAME_LIMIT3 = 80;
6858
+ function projectToEntity3(record) {
6859
+ const rawName = record.name;
6860
+ if (typeof rawName !== "string" || !rawName) return null;
6861
+ const name = safeStr(rawName, NAME_LIMIT3);
6862
+ if (!name) return null;
6863
+ const summary = safeStr(record.description ?? "", MAX_SUMMARY_CHARS6) || `v0 project ${name}`;
6864
+ const tags = safeStrList(record.tags, 40);
6865
+ if (!tags.includes("v0")) tags.unshift("v0");
6866
+ const updated = isoDate2(record.updated_at);
6867
+ const archived = Boolean(record.archived ?? false);
6868
+ return (0, import_l519.makeEntity)({
6869
+ name,
6870
+ type: "project",
6871
+ summary,
6872
+ tags,
6873
+ last_touched: updated,
6874
+ valid_from: isoDate2(record.created_at),
6875
+ valid_to: archived ? updated : null,
6876
+ visibility: parseVisibility(record.visibility)
6877
+ });
6878
+ }
6879
+ function sessionToRecord3(record, fallbackDate) {
6880
+ const date = isoDate2(record.date) ?? fallbackDate;
6881
+ if (!date) return null;
6882
+ const project = String(record.project ?? "").trim();
6883
+ return (0, import_l519.makeSession)({
6884
+ date,
6885
+ cwd: null,
6886
+ // cloud agent: there is no local working directory to name
6887
+ project_focus: project ? [(0, import_redaction19.redactText)(project, 120)] : [],
6888
+ key_actions: safeStrList(record.key_actions, MAX_KEY_ACTION_CHARS10),
6889
+ files_touched: safeStrList(record.files_touched),
6890
+ visibility: parseVisibility(record.visibility)
6891
+ });
6892
+ }
6893
+ var V0_CONFIG = {
6894
+ agentId: AGENT_ID18,
6895
+ agentType: AGENT_TYPE18,
6896
+ displayName: "Vercel v0",
6897
+ directoryName: ".v0",
6898
+ stateEnvVar: "V0_STATE_DIR",
6899
+ roleNarrative: ROLE_NARRATIVE18,
6900
+ capabilities: ["generative-ui", "full-stack-web", "react-nextjs"],
6901
+ policy: DEFAULT_POLICY17,
6902
+ label: "v0",
6903
+ initCommand: "bourdon v0 init",
6904
+ projectToEntity: projectToEntity3,
6905
+ sessionToRecord: sessionToRecord3
6906
+ };
6907
+ var V0Participant = class extends ExportStoreParticipant {
6908
+ constructor(options = {}) {
6909
+ super(V0_CONFIG, options);
6910
+ }
6911
+ static defaultNativePath(home) {
6912
+ return ExportStoreParticipant.defaultNativePath(".v0", home);
6913
+ }
6914
+ };
6915
+
6439
6916
  // src/registry.ts
6440
6917
  var FIRST_PARTY = [
6918
+ BoltParticipant,
6441
6919
  CascadeParticipant,
6442
6920
  ClaudeCodeParticipant,
6443
6921
  ClaudeCodeAutomationsParticipant,
@@ -6450,6 +6928,8 @@ var FIRST_PARTY = [
6450
6928
  CopilotVscodeParticipant,
6451
6929
  CursorParticipant,
6452
6930
  CursorAutomationsParticipant,
6931
+ LovableParticipant,
6932
+ V0Participant,
6453
6933
  GitHubCopilotParticipant,
6454
6934
  HermesParticipant,
6455
6935
  OpenClawParticipant
@@ -6484,9 +6964,11 @@ function resolvePluginParticipants() {
6484
6964
  }
6485
6965
 
6486
6966
  // src/index.ts
6487
- var import_l516 = require("@getbourdon/l5");
6967
+ var import_l520 = require("@getbourdon/l5");
6488
6968
  // Annotate the CommonJS export names for ESM import in node:
6489
6969
  0 && (module.exports = {
6970
+ BOLT_CONFIG,
6971
+ BoltParticipant,
6490
6972
  CONTRACT_VERSION,
6491
6973
  CacheEntry,
6492
6974
  CascadeParticipant,
@@ -6504,9 +6986,12 @@ var import_l516 = require("@getbourdon/l5");
6504
6986
  DEFAULT_CACHE_TTL_SECONDS,
6505
6987
  DEFAULT_RECENT_THREAD_LIMIT,
6506
6988
  DEFAULT_TITLE_LIMIT,
6989
+ ExportStoreParticipant,
6507
6990
  FIRST_PARTY,
6508
6991
  GitHubCopilotParticipant,
6509
6992
  HermesParticipant,
6993
+ LOVABLE_CONFIG,
6994
+ LovableParticipant,
6510
6995
  MIN_PATCHED_VERSION,
6511
6996
  NETWORK_CONTRACT_VERSION,
6512
6997
  NetworkParticipant,
@@ -6521,6 +7006,8 @@ var import_l516 = require("@getbourdon/l5");
6521
7006
  ParticipantVersionMismatchError,
6522
7007
  PayloadCache,
6523
7008
  SPEC_VERSION,
7009
+ V0Participant,
7010
+ V0_CONFIG,
6524
7011
  Visibility,
6525
7012
  applyVisibility,
6526
7013
  defaultCacheRoot,
@@ -6532,13 +7019,17 @@ var import_l516 = require("@getbourdon/l5");
6532
7019
  friendlyLabel,
6533
7020
  ghTokenProvider,
6534
7021
  isParticipant,
7022
+ isoDate,
6535
7023
  loadProjectHints,
6536
7024
  openReadonly,
6537
7025
  parseSessionIndex,
7026
+ parseVisibility,
6538
7027
  projectKeyFromCwd,
6539
7028
  recentSessionThreads,
6540
7029
  resolveCodexHome,
6541
7030
  resolvePluginParticipants,
7031
+ safeStr,
7032
+ safeStrList,
6542
7033
  sessionThreadTitles,
6543
7034
  sqliteAvailable,
6544
7035
  tableColumns,