@starascendin/lifeos-mcp 0.7.67 → 0.7.69

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/README.md CHANGED
@@ -24,17 +24,20 @@ npx @starascendin/lifeos-mcp --url <your-convex-url> --user-id <your-user-id> --
24
24
  | User ID | `--user-id`, `-i` | `LIFEOS_USER_ID` | Your LifeOS user ID |
25
25
  | API Key | `--api-key`, `-k` | `LIFEOS_API_KEY` | Your API key for authentication |
26
26
 
27
- ### Optional SurrealDB Sidecar Parameters
27
+ ### Optional FalkorDB Sidecar Parameters
28
28
 
29
- These are required only for the Surreal graph tools.
29
+ These are required only for the Falkor graph tools.
30
30
 
31
31
  | Env Variable | Description |
32
32
  |--------------|-------------|
33
- | `SURREAL_ENDPOINT` | SurrealDB HTTP endpoint, e.g. `https://surrealdbprod.apps.rjlabs.dev` |
34
- | `SURREAL_USER` | SurrealDB username |
35
- | `SURREAL_PASS` | SurrealDB password |
36
- | `SURREAL_NS` | Namespace, defaults to `lifeos` |
37
- | `SURREAL_DB` | Database, defaults to `graph` |
33
+ | `FALKOR_BROWSER_ENDPOINT` | FalkorDB Browser HTTP endpoint, defaults to `https://falkordb.apps.rjlabs.dev` |
34
+ | `FALKOR_GRAPH` | Falkor graph name, defaults to `lifeos_ppv` |
35
+ | `FALKOR_PASS` | Falkor password, used to mint short-lived browser tokens |
36
+ | `FALKOR_TOKEN` / `FALKOR_PAT` | Optional pre-provisioned Falkor browser token |
37
+ | `FALKOR_USER` | Falkor username, defaults to `default` |
38
+ | `FALKOR_HOST` | Browser backend Redis host, defaults to `localhost` |
39
+ | `FALKOR_PORT` | Browser backend Redis port, defaults to `6379` |
40
+ | `FALKOR_TLS` | Browser backend Redis TLS flag, defaults to `false` |
38
41
 
39
42
  ### Getting Your Credentials
40
43
 
@@ -190,16 +193,16 @@ Add to your `.mcp.json` (project root or `~/.claude/mcp.json`):
190
193
  - **refresh_unified_graph_cache** - Recompute inferred graph links and refresh the cached projection
191
194
  - **upsert_unified_graph_link / delete_unified_graph_link** - Create or remove manual links between graph nodes
192
195
 
193
- ### SurrealDB Sidecar Graph
194
- - **surreal_graph_schema** - Inspect the agent graph contract, configured namespace/db, allowed table prefixes, relation tables, and examples
195
- - **surreal_graph_query** - Run guarded read-only SurrealQL (`SELECT`, `INFO`, `EXPLAIN` only)
196
- - **surreal_graph_link** - Create/update an agent-owned relationship in `lifeos_agent_links` without mutating Convex-synced entity tables
197
- - **surreal_graph_unlink** - Remove a relationship previously created by `surreal_graph_link`
196
+ ### FalkorDB Sidecar Graph
197
+ - **falkor_graph_schema** - Inspect the Falkor graph contract, configured graph, allowed labels, relationship types, and examples
198
+ - **falkor_graph_query** - Run guarded read-only Cypher against the Falkor PPV sidecar
199
+ - **falkor_graph_link** - Create/update an agent-owned `AGENT_LINK` relationship without mutating Convex-synced nodes or inferred PPV relationships
200
+ - **falkor_graph_unlink** - Remove a relationship previously created by `falkor_graph_link`
198
201
 
199
- Surreal graph rules:
202
+ Falkor graph rules:
200
203
  - Convex remains canonical for entity data.
201
- - SurrealDB is the relationship/query sidecar.
202
- - Agents may query graph data and write only agent-owned links through `lifeos_agent_links`.
204
+ - FalkorDB is a graph query/read sidecar for the PPV experiment.
205
+ - Agents may query graph data and write only agent-owned `AGENT_LINK` relationships.
203
206
  - Every agent-created link must include a reason and confidence.
204
207
  - Raw read queries should include `LIMIT`.
205
208
 
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.7.67";
2
- export declare const BUILD_TIME = "2026-05-17T16:11:21.616Z";
1
+ export declare const VERSION = "0.7.69";
2
+ export declare const BUILD_TIME = "2026-05-17T20:11:00.340Z";
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Regenerated on every build.
2
- export const VERSION = "0.7.67";
3
- export const BUILD_TIME = "2026-05-17T16:11:21.616Z";
2
+ export const VERSION = "0.7.69";
3
+ export const BUILD_TIME = "2026-05-17T20:11:00.340Z";
package/dist/index.js CHANGED
@@ -60,7 +60,7 @@ program
60
60
  });
61
61
  program
62
62
  .command("ppv <action>")
63
- .description("Manage PPV as JSON. Actions: workspace, graph, vision-graph, seed, vision, activate-vision, set-active-vision, identity, pillar, action, reflection, adjustment.")
63
+ .description("Manage PPV as JSON. Actions: workspace, graph, vision-graph, seed, vision, activate-vision, set-active-vision, identity, pillar.")
64
64
  .option("--file <file>", "JSON payload file for mutating actions. workspace, graph, vision-graph, and seed do not require a file.")
65
65
  .action((action, commandOptions) => {
66
66
  directCliCommand = {
@@ -685,6 +685,19 @@ const TOOLS = [
685
685
  type: "string",
686
686
  description: "Updated goals (optional)",
687
687
  },
688
+ whatWentWell: {
689
+ type: "string",
690
+ description: "Retrospective notes on what went well (optional)",
691
+ },
692
+ whatCouldImprove: {
693
+ type: "string",
694
+ description: "Retrospective notes on what could improve (optional)",
695
+ },
696
+ actionItems: {
697
+ type: "array",
698
+ items: { type: "string" },
699
+ description: "Retrospective action items (optional)",
700
+ },
688
701
  },
689
702
  required: ["cycleId"],
690
703
  },
@@ -4542,7 +4555,7 @@ const TOOLS = [
4542
4555
  // PPV v1 tools
4543
4556
  {
4544
4557
  name: "get_ppv_workspace",
4545
- description: "Get the PPV workspace for the selected vision, plus all owned visions, active-vision context, identity, pillars, linked projects, weekly actions, reflections, and adjustments.",
4558
+ description: "Get the PPV workspace for the selected vision, plus all owned visions, active-vision context, identity, pillars, and linked projects.",
4546
4559
  inputSchema: {
4547
4560
  type: "object",
4548
4561
  properties: {
@@ -4553,7 +4566,7 @@ const TOOLS = [
4553
4566
  },
4554
4567
  {
4555
4568
  name: "get_active_vision_graph",
4556
- description: "Best default graph read for PPV questions. Get the unified active-vision graph linking PPV vision, identity, pillars, weekly actions, reflections, adjustments, related projects/issues, and recent voice memos.",
4569
+ description: "Best default graph read for PPV questions. Get the unified active-vision graph linking PPV vision, identity, pillars, related projects/issues, and recent voice memos.",
4557
4570
  inputSchema: {
4558
4571
  type: "object",
4559
4572
  properties: {
@@ -4730,52 +4743,52 @@ const TOOLS = [
4730
4743
  },
4731
4744
  },
4732
4745
  {
4733
- name: "surreal_graph_schema",
4734
- description: "Inspect the SurrealDB sidecar graph contract for agents: connection state, allowed node-table prefixes, safe query rules, relation tables, and example SurrealQL. Use before raw SurrealQL graph work.",
4746
+ name: "falkor_graph_schema",
4747
+ description: "Inspect the FalkorDB sidecar graph contract for agents: configured graph, mirrored PPV labels, safe Cypher rules, relationship types, and examples. Use before raw Falkor Cypher graph work.",
4735
4748
  inputSchema: {
4736
4749
  type: "object",
4737
4750
  properties: {},
4738
4751
  },
4739
4752
  },
4740
4753
  {
4741
- name: "surreal_graph_query",
4742
- description: "Run guarded read-only SurrealQL against the LifeOS SurrealDB graph sidecar. Allowed statements are SELECT, INFO, and EXPLAIN only. Use LIMIT for SELECT queries. Do not use this for Convex-canonical writes.",
4754
+ name: "falkor_graph_query",
4755
+ description: "Run guarded read-only Cypher against the LifeOS FalkorDB graph sidecar. Allowed statements are MATCH, WITH, RETURN, EXPLAIN, and PROFILE query shapes only. MATCH queries should include LIMIT unless doing count aggregation. Do not use this for Convex-canonical writes.",
4743
4756
  inputSchema: {
4744
4757
  type: "object",
4745
4758
  properties: {
4746
4759
  query: {
4747
4760
  type: "string",
4748
- description: "Read-only SurrealQL. SELECT statements must include LIMIT unless they are aggregate count queries.",
4761
+ description: "Read-only Cypher. MATCH queries should include LIMIT unless they are aggregate count queries.",
4749
4762
  },
4750
4763
  maxRows: {
4751
4764
  type: "number",
4752
- description: "Maximum rows returned per statement result after execution. Default 50, max 200.",
4765
+ description: "Maximum rows returned after execution. Default 50, max 200.",
4753
4766
  },
4754
4767
  },
4755
4768
  required: ["query"],
4756
4769
  },
4757
4770
  },
4758
4771
  {
4759
- name: "surreal_graph_link",
4760
- description: "Create or update an agent-owned graph relationship in SurrealDB between two LifeOS records. This writes only to the lifeos_agent_links relation table and never mutates Convex-synced entity records.",
4772
+ name: "falkor_graph_link",
4773
+ description: "Create or update an agent-owned AGENT_LINK relationship in FalkorDB between two existing LifeOS graph records. This never mutates Convex-synced nodes or inferred PPV relationships.",
4761
4774
  inputSchema: {
4762
4775
  type: "object",
4763
4776
  properties: {
4764
- fromTable: {
4777
+ fromLabel: {
4765
4778
  type: "string",
4766
- description: "Source Surreal table, e.g. ppv1_pillars or lifeos_pmProjects.",
4779
+ description: "Source Falkor node label, e.g. PpvPillar.",
4767
4780
  },
4768
4781
  fromId: {
4769
4782
  type: "string",
4770
- description: "Source record id without the table prefix.",
4783
+ description: "Source Convex record id stored in node.convexId.",
4771
4784
  },
4772
- toTable: {
4785
+ toLabel: {
4773
4786
  type: "string",
4774
- description: "Target Surreal table, e.g. lifeos_pmProjects or lifeos_frmPeople.",
4787
+ description: "Target Falkor node label, e.g. PpvProject.",
4775
4788
  },
4776
4789
  toId: {
4777
4790
  type: "string",
4778
- description: "Target record id without the table prefix.",
4791
+ description: "Target Convex record id stored in node.convexId.",
4779
4792
  },
4780
4793
  kind: {
4781
4794
  type: "string",
@@ -4799,31 +4812,31 @@ const TOOLS = [
4799
4812
  additionalProperties: true,
4800
4813
  },
4801
4814
  },
4802
- required: ["fromTable", "fromId", "toTable", "toId", "kind", "reason"],
4815
+ required: ["fromLabel", "fromId", "toLabel", "toId", "kind", "reason"],
4803
4816
  },
4804
4817
  },
4805
4818
  {
4806
- name: "surreal_graph_unlink",
4807
- description: "Remove an agent-owned SurrealDB graph relationship previously created by surreal_graph_link. This only deletes from lifeos_agent_links.",
4819
+ name: "falkor_graph_unlink",
4820
+ description: "Remove an agent-owned FalkorDB AGENT_LINK relationship previously created by falkor_graph_link.",
4808
4821
  inputSchema: {
4809
4822
  type: "object",
4810
4823
  properties: {
4811
- fromTable: { type: "string", description: "Source Surreal table." },
4824
+ fromLabel: { type: "string", description: "Source Falkor node label." },
4812
4825
  fromId: {
4813
4826
  type: "string",
4814
- description: "Source record id without table prefix.",
4827
+ description: "Source Convex record id stored in node.convexId.",
4815
4828
  },
4816
- toTable: { type: "string", description: "Target Surreal table." },
4829
+ toLabel: { type: "string", description: "Target Falkor node label." },
4817
4830
  toId: {
4818
4831
  type: "string",
4819
- description: "Target record id without table prefix.",
4832
+ description: "Target Convex record id stored in node.convexId.",
4820
4833
  },
4821
4834
  kind: {
4822
4835
  type: "string",
4823
4836
  description: "Relationship kind used when linking.",
4824
4837
  },
4825
4838
  },
4826
- required: ["fromTable", "fromId", "toTable", "toId", "kind"],
4839
+ required: ["fromLabel", "fromId", "toLabel", "toId", "kind"],
4827
4840
  },
4828
4841
  },
4829
4842
  {
@@ -4914,7 +4927,7 @@ const TOOLS = [
4914
4927
  },
4915
4928
  {
4916
4929
  name: "delete_ppv_vision",
4917
- description: "Delete a PPV vision and cascade-delete its identity, pillars, weekly actions, reflections, and adjustments.",
4930
+ description: "Delete a PPV vision and cascade-delete its identity and pillars.",
4918
4931
  inputSchema: {
4919
4932
  type: "object",
4920
4933
  properties: {
@@ -5017,143 +5030,6 @@ const TOOLS = [
5017
5030
  required: ["pillarId"],
5018
5031
  },
5019
5032
  },
5020
- {
5021
- name: "create_ppv_weekly_action",
5022
- description: "Create an identity-aligned PPV weekly action.",
5023
- inputSchema: {
5024
- type: "object",
5025
- properties: {
5026
- userId: { type: "string", description: "Override user ID" },
5027
- visionId: { type: "string", description: "PPV vision ID" },
5028
- pillarId: { type: "string", description: "PPV pillar ID" },
5029
- projectId: {
5030
- type: "string",
5031
- description: "Existing LifeOS project ID",
5032
- },
5033
- category: { type: "string", description: "Action category" },
5034
- action: { type: "string", description: "Small action to take" },
5035
- status: {
5036
- type: "string",
5037
- enum: ["pending", "in_progress", "completed", "skipped"],
5038
- description: "Action status",
5039
- },
5040
- scheduledDate: { type: "string", description: "ISO scheduled date" },
5041
- week: { type: "string", description: "ISO week label" },
5042
- },
5043
- required: ["visionId", "category", "action"],
5044
- },
5045
- },
5046
- {
5047
- name: "update_ppv_weekly_action",
5048
- description: "Update a PPV weekly action.",
5049
- inputSchema: {
5050
- type: "object",
5051
- properties: {
5052
- userId: { type: "string", description: "Override user ID" },
5053
- actionId: { type: "string", description: "PPV weekly action ID" },
5054
- pillarId: { type: "string", description: "PPV pillar ID" },
5055
- projectId: {
5056
- type: "string",
5057
- description: "Existing LifeOS project ID",
5058
- },
5059
- category: { type: "string", description: "Updated category" },
5060
- action: { type: "string", description: "Updated action" },
5061
- status: {
5062
- type: "string",
5063
- enum: ["pending", "in_progress", "completed", "skipped"],
5064
- description: "Updated status",
5065
- },
5066
- scheduledDate: { type: "string", description: "ISO scheduled date" },
5067
- week: { type: "string", description: "ISO week label" },
5068
- },
5069
- required: ["actionId"],
5070
- },
5071
- },
5072
- {
5073
- name: "delete_ppv_weekly_action",
5074
- description: "Delete a PPV weekly action.",
5075
- inputSchema: {
5076
- type: "object",
5077
- properties: {
5078
- userId: { type: "string", description: "Override user ID" },
5079
- actionId: { type: "string", description: "PPV weekly action ID" },
5080
- },
5081
- required: ["actionId"],
5082
- },
5083
- },
5084
- {
5085
- name: "create_ppv_reflection",
5086
- description: "Create a PPV weekly reflection.",
5087
- inputSchema: {
5088
- type: "object",
5089
- properties: {
5090
- userId: { type: "string", description: "Override user ID" },
5091
- visionId: { type: "string", description: "PPV vision ID" },
5092
- week: { type: "string", description: "ISO week label" },
5093
- questions: {
5094
- type: "array",
5095
- items: {
5096
- type: "object",
5097
- properties: {
5098
- question: { type: "string" },
5099
- answer: { type: "string" },
5100
- },
5101
- required: ["question", "answer"],
5102
- },
5103
- description: "Reflection questions and answers",
5104
- },
5105
- alignmentScore: { type: "number", description: "Optional score" },
5106
- },
5107
- required: ["visionId", "week", "questions"],
5108
- },
5109
- },
5110
- {
5111
- name: "delete_ppv_reflection",
5112
- description: "Delete a PPV reflection.",
5113
- inputSchema: {
5114
- type: "object",
5115
- properties: {
5116
- userId: { type: "string", description: "Override user ID" },
5117
- reflectionId: { type: "string", description: "PPV reflection ID" },
5118
- },
5119
- required: ["reflectionId"],
5120
- },
5121
- },
5122
- {
5123
- name: "create_ppv_adjustment",
5124
- description: "Create a PPV adjustment from reflection or lived evidence.",
5125
- inputSchema: {
5126
- type: "object",
5127
- properties: {
5128
- userId: { type: "string", description: "Override user ID" },
5129
- visionId: { type: "string", description: "PPV vision ID" },
5130
- sourceReflectionId: {
5131
- type: "string",
5132
- description: "Source PPV reflection ID",
5133
- },
5134
- observation: { type: "string", description: "What was observed" },
5135
- decision: { type: "string", description: "What will change" },
5136
- affectedComponents: {
5137
- type: "array",
5138
- items: { type: "string" },
5139
- description: "Components affected by the adjustment",
5140
- },
5141
- },
5142
- required: ["visionId", "observation", "decision", "affectedComponents"],
5143
- },
5144
- },
5145
- {
5146
- name: "delete_ppv_adjustment",
5147
- description: "Delete a PPV adjustment.",
5148
- inputSchema: {
5149
- type: "object",
5150
- properties: {
5151
- userId: { type: "string", description: "Override user ID" },
5152
- adjustmentId: { type: "string", description: "PPV adjustment ID" },
5153
- },
5154
- required: ["adjustmentId"],
5155
- },
5156
- },
5157
5033
  // MCP Server Info
5158
5034
  {
5159
5035
  name: "get_version",
@@ -5233,22 +5109,22 @@ const PROMPTS = [
5233
5109
  },
5234
5110
  {
5235
5111
  name: "ppv",
5236
- description: "Manage the PPV life design system: vision, identity, pillars, projects, weekly actions, reflections, and adjustments.",
5112
+ description: "Manage the PPV life design system: vision, identity, pillars, and projects.",
5237
5113
  arguments: [
5238
5114
  {
5239
5115
  name: "intent",
5240
- description: "What to do with PPV, e.g. create vision, add weekly action, reflect, adjust, link project.",
5116
+ description: "What to do with PPV, e.g. create vision, update identity, add pillar, link project.",
5241
5117
  required: false,
5242
5118
  },
5243
5119
  ],
5244
5120
  },
5245
5121
  {
5246
- name: "surreal-graph",
5247
- description: "Use the SurrealDB sidecar graph: query LifeOS graph relationships with SurrealQL and create controlled agent-owned graph links.",
5122
+ name: "falkor-graph",
5123
+ description: "Use the FalkorDB sidecar graph: query PPV graph relationships with Cypher and create controlled agent-owned graph links.",
5248
5124
  arguments: [
5249
5125
  {
5250
5126
  name: "intent",
5251
- description: "What to do with the Surreal graph, e.g. inspect PPV-project links, connect a chat to a project, or find related people.",
5127
+ description: "What to do with the Falkor graph, e.g. inspect PPV-project links or add an agent-owned PPV relationship.",
5252
5128
  required: false,
5253
5129
  },
5254
5130
  ],
@@ -5614,7 +5490,7 @@ Do not ask for confirmation; the user intends this planning workflow to mutate L
5614
5490
  ${intentClause}
5615
5491
 
5616
5492
  Use the LifeOS MCP PPV tools:
5617
- 1. Call get_ppv_workspace first to inspect all owned visions, the selected/current vision, identity, pillars, linked projects, weekly actions, reflections, adjustments, and available LifeOS projects.
5493
+ 1. Call get_ppv_workspace first to inspect all owned visions, the selected/current vision, identity, pillars, linked projects, and available LifeOS projects.
5618
5494
  2. If there is no vision and the user wants the example, call seed_ppv_beijing_workspace.
5619
5495
  3. For vision changes, call upsert_ppv_vision.
5620
5496
  4. If the user wants to change a vision lifecycle without editing content, call set_ppv_vision_status.
@@ -5623,8 +5499,6 @@ Use the LifeOS MCP PPV tools:
5623
5499
  7. If the user wants to permanently remove a vision and its PPV subcomponents, call delete_ppv_vision.
5624
5500
  8. For identity work, call upsert_ppv_identity.
5625
5501
  9. For pillars, call create_ppv_pillar or update_ppv_pillar. Link only existing LifeOS project IDs from get_ppv_workspace.projects.
5626
- 10. For execution, call create_ppv_weekly_action or update_ppv_weekly_action.
5627
- 11. For learning loops, call create_ppv_reflection and create_ppv_adjustment.
5628
5502
 
5629
5503
  Keep the system simple:
5630
5504
  - Vision is directional and experiential, not a task list.
@@ -5632,38 +5506,37 @@ Keep the system simple:
5632
5506
  - Pillars are ongoing systems.
5633
5507
  - Projects are existing LifeOS projects.
5634
5508
  - Weekly actions should be small, concrete, and identity-aligned.
5635
- - Reflections and adjustments should update behavior based on evidence.
5636
5509
 
5637
5510
  After mutating, report exactly what changed and any IDs needed for future updates.`,
5638
5511
  },
5639
5512
  },
5640
5513
  ];
5641
5514
  },
5642
- "surreal-graph": (args) => {
5515
+ "falkor-graph": (args) => {
5643
5516
  const intentClause = args.intent
5644
5517
  ? `User intent: ${args.intent}`
5645
- : "Start by inspecting the Surreal graph schema and current agent links.";
5518
+ : "Start by inspecting the Falkor graph schema and current agent links.";
5646
5519
  return [
5647
5520
  {
5648
5521
  role: "user",
5649
5522
  content: {
5650
5523
  type: "text",
5651
- text: `Use the LifeOS SurrealDB sidecar graph.
5524
+ text: `Use the LifeOS FalkorDB sidecar graph.
5652
5525
 
5653
5526
  ${intentClause}
5654
5527
 
5655
5528
  Use these MCP tools:
5656
- 1. Call surreal_graph_schema first to inspect the allowed graph contract and examples.
5657
- 2. Use surreal_graph_query for read-only SurrealQL. Only SELECT, INFO, and EXPLAIN are allowed; SELECT queries must include LIMIT unless doing count aggregation.
5658
- 3. Use surreal_graph_link when you need to create a relationship between existing LifeOS records. This writes only to lifeos_agent_links.
5659
- 4. Use surreal_graph_unlink only to remove relationships previously created by surreal_graph_link.
5529
+ 1. Call falkor_graph_schema first to inspect the allowed graph contract and examples.
5530
+ 2. Use falkor_graph_query for read-only Cypher. MATCH/WITH queries must include LIMIT unless doing count aggregation.
5531
+ 3. Use falkor_graph_link when you need to create a relationship between existing Falkor nodes. This writes only AGENT_LINK relationships.
5532
+ 4. Use falkor_graph_unlink only to remove relationships previously created by falkor_graph_link.
5660
5533
 
5661
5534
  Rules:
5662
- - Convex remains canonical for entity data. Do not use SurrealDB to edit projects, PPV records, CRM people, chats, or notes.
5663
- - SurrealDB is the graph memory / relationship layer.
5535
+ - Convex remains canonical for entity data. Do not use FalkorDB to edit projects or PPV records.
5536
+ - FalkorDB is the graph sidecar.
5664
5537
  - Always include a concrete reason and confidence when linking.
5665
- - Prefer precise ids from prior reads; do not invent ids.
5666
- - Keep queries scoped with LIMIT and return concise findings plus the SurrealQL you used when useful.`,
5538
+ - Prefer precise node labels and convexId values from prior reads; do not invent ids.
5539
+ - Keep queries scoped with LIMIT and return concise findings plus the Cypher you used when useful.`,
5667
5540
  },
5668
5541
  },
5669
5542
  ];
@@ -6550,11 +6423,14 @@ Keep it concise and direct. This is an accountability report, not a feel-good su
6550
6423
  const CONVEX_URL = options.url || process.env.CONVEX_URL;
6551
6424
  const API_KEY = options.apiKey || process.env.LIFEOS_API_KEY;
6552
6425
  const USER_ID = options.userId || process.env.LIFEOS_USER_ID;
6553
- const SURREAL_ENDPOINT = process.env.SURREAL_ENDPOINT;
6554
- const SURREAL_USER = process.env.SURREAL_USER;
6555
- const SURREAL_PASS = process.env.SURREAL_PASS;
6556
- const SURREAL_NS = process.env.SURREAL_NS || "lifeos";
6557
- const SURREAL_DB = process.env.SURREAL_DB || "graph";
6426
+ const FALKOR_BROWSER_ENDPOINT = process.env.FALKOR_BROWSER_ENDPOINT || "https://falkordb.apps.rjlabs.dev";
6427
+ const FALKOR_GRAPH = process.env.FALKOR_GRAPH || "lifeos_ppv";
6428
+ const FALKOR_USER = process.env.FALKOR_USER || "default";
6429
+ const FALKOR_PASS = process.env.FALKOR_PASS;
6430
+ const FALKOR_TOKEN = process.env.FALKOR_TOKEN || process.env.FALKOR_PAT;
6431
+ const FALKOR_HOST = process.env.FALKOR_HOST || "localhost";
6432
+ const FALKOR_PORT = process.env.FALKOR_PORT || "6379";
6433
+ const FALKOR_TLS = process.env.FALKOR_TLS || "false";
6558
6434
  // Validate required configuration
6559
6435
  const missingConfig = [];
6560
6436
  if (!CONVEX_URL)
@@ -6666,13 +6542,6 @@ async function runDirectCliCommand(command) {
6666
6542
  pillar: "create_ppv_pillar",
6667
6543
  "update-pillar": "update_ppv_pillar",
6668
6544
  "delete-pillar": "delete_ppv_pillar",
6669
- action: "create_ppv_weekly_action",
6670
- "update-action": "update_ppv_weekly_action",
6671
- "delete-action": "delete_ppv_weekly_action",
6672
- reflection: "create_ppv_reflection",
6673
- "delete-reflection": "delete_ppv_reflection",
6674
- adjustment: "create_ppv_adjustment",
6675
- "delete-adjustment": "delete_ppv_adjustment",
6676
6545
  };
6677
6546
  const tool = actionToTool[command.action];
6678
6547
  if (!tool) {
@@ -6708,55 +6577,6 @@ async function callConvexJsonEndpoint(path, body) {
6708
6577
  }
6709
6578
  return await response.json();
6710
6579
  }
6711
- const SURREAL_AGENT_LINK_TABLE = "lifeos_agent_links";
6712
- const SURREAL_TABLE_PREFIXES = ["lifeos_", "life_", "ppv1_"];
6713
- const SURREAL_RELATION_TABLES = [
6714
- "ppv_has_identity",
6715
- "ppv_has_pillar",
6716
- "ppv_has_action",
6717
- "ppv_has_reflection",
6718
- "ppv_has_adjustment",
6719
- "ppv_pillar_drives_action",
6720
- "ppv_pillar_supports_project",
6721
- "ppv_project_executes_action",
6722
- "ppv_reflection_inspires_adjustment",
6723
- SURREAL_AGENT_LINK_TABLE,
6724
- ];
6725
- const SURREAL_MUTATING_KEYWORDS = [
6726
- "ALTER",
6727
- "CREATE",
6728
- "DEFINE",
6729
- "DELETE",
6730
- "INSERT",
6731
- "KILL",
6732
- "LIVE",
6733
- "REBUILD",
6734
- "RELATE",
6735
- "REMOVE",
6736
- "SLEEP",
6737
- "THROW",
6738
- "UPDATE",
6739
- "UPSERT",
6740
- ];
6741
- function requireSurrealConfig() {
6742
- const missing = [];
6743
- if (!SURREAL_ENDPOINT)
6744
- missing.push("SURREAL_ENDPOINT");
6745
- if (!SURREAL_USER)
6746
- missing.push("SURREAL_USER");
6747
- if (!SURREAL_PASS)
6748
- missing.push("SURREAL_PASS");
6749
- if (missing.length > 0) {
6750
- throw new Error(`Missing SurrealDB configuration: ${missing.join(", ")}. Set SURREAL_ENDPOINT, SURREAL_USER, SURREAL_PASS, SURREAL_NS, and SURREAL_DB in the agent runtime.`);
6751
- }
6752
- return {
6753
- endpoint: SURREAL_ENDPOINT,
6754
- user: SURREAL_USER,
6755
- pass: SURREAL_PASS,
6756
- ns: SURREAL_NS,
6757
- db: SURREAL_DB,
6758
- };
6759
- }
6760
6580
  function stripSqlCommentsAndStrings(sql) {
6761
6581
  let output = "";
6762
6582
  let index = 0;
@@ -6809,74 +6629,11 @@ function stripSqlCommentsAndStrings(sql) {
6809
6629
  }
6810
6630
  return output;
6811
6631
  }
6812
- function splitSqlStatements(sql) {
6813
- return stripSqlCommentsAndStrings(sql)
6814
- .split(";")
6815
- .map((statement) => statement.trim())
6816
- .filter(Boolean);
6817
- }
6818
- function assertReadOnlySurrealSql(sql) {
6819
- const statements = splitSqlStatements(sql);
6820
- if (statements.length === 0) {
6821
- throw new Error("SurrealQL query is empty.");
6822
- }
6823
- if (statements.length > 5) {
6824
- throw new Error("SurrealQL query is too broad. Use 5 statements or fewer.");
6825
- }
6826
- for (const statement of statements) {
6827
- const upper = statement.toUpperCase();
6828
- const startsReadOnly = upper.startsWith("SELECT ") ||
6829
- upper.startsWith("INFO ") ||
6830
- upper.startsWith("EXPLAIN ");
6831
- if (!startsReadOnly) {
6832
- throw new Error("surreal_graph_query only allows SELECT, INFO, and EXPLAIN statements. Use surreal_graph_link for controlled relationship writes.");
6833
- }
6834
- for (const keyword of SURREAL_MUTATING_KEYWORDS) {
6835
- if (new RegExp(`\\b${keyword}\\b`, "i").test(statement)) {
6836
- throw new Error(`surreal_graph_query blocked mutating keyword ${keyword}. Use guarded graph write tools instead.`);
6837
- }
6838
- }
6839
- const isAggregateCount = /\bCOUNT\s*\(/i.test(statement);
6840
- if (upper.startsWith("SELECT ") &&
6841
- !/\bLIMIT\b/i.test(statement) &&
6842
- !isAggregateCount) {
6843
- throw new Error("SELECT queries must include LIMIT unless they are aggregate count queries.");
6844
- }
6845
- }
6846
- }
6847
6632
  function clampMaxRows(value) {
6848
6633
  if (typeof value !== "number" || !Number.isFinite(value))
6849
6634
  return 50;
6850
6635
  return Math.max(1, Math.min(200, Math.floor(value)));
6851
6636
  }
6852
- function capSurrealResults(results, maxRows) {
6853
- return results.map((entry) => {
6854
- if (!Array.isArray(entry.result) || entry.result.length <= maxRows) {
6855
- return entry;
6856
- }
6857
- return {
6858
- ...entry,
6859
- result: entry.result.slice(0, maxRows),
6860
- detail: `${entry.result.length - maxRows} additional rows omitted by lifeos-mcp maxRows=${maxRows}`,
6861
- };
6862
- });
6863
- }
6864
- function assertSurrealIdentifier(value, label) {
6865
- if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) {
6866
- throw new Error(`${label} must be a simple Surreal identifier.`);
6867
- }
6868
- return value;
6869
- }
6870
- function assertLifeOsNodeTable(value, label) {
6871
- const table = assertSurrealIdentifier(value, label);
6872
- if (!SURREAL_TABLE_PREFIXES.some((prefix) => table.startsWith(prefix))) {
6873
- throw new Error(`${label} must start with one of: ${SURREAL_TABLE_PREFIXES.join(", ")}`);
6874
- }
6875
- if (SURREAL_RELATION_TABLES.includes(table)) {
6876
- throw new Error(`${label} must be an entity table, not a relation table.`);
6877
- }
6878
- return table;
6879
- }
6880
6637
  function assertRecordId(value, label) {
6881
6638
  if (typeof value !== "string" || value.trim().length === 0) {
6882
6639
  throw new Error(`${label} is required.`);
@@ -6892,79 +6649,237 @@ function assertRelationKind(value) {
6892
6649
  }
6893
6650
  return value;
6894
6651
  }
6895
- function escapeSurrealRecordId(id) {
6896
- return id.replace(/`/g, "\\`");
6652
+ const FALKOR_AGENT_RELATIONSHIP = "AGENT_LINK";
6653
+ const FALKOR_NODE_LABELS = [
6654
+ "PpvVision",
6655
+ "PpvIdentity",
6656
+ "PpvPillar",
6657
+ "PpvProject",
6658
+ ];
6659
+ const FALKOR_INFERRED_RELATIONSHIPS = [
6660
+ "HAS_IDENTITY",
6661
+ "HAS_PILLAR",
6662
+ "PILLAR_SUPPORTS_PROJECT",
6663
+ ];
6664
+ const FALKOR_MUTATING_KEYWORDS = [
6665
+ "CALL",
6666
+ "CREATE",
6667
+ "DELETE",
6668
+ "DROP",
6669
+ "FOREACH",
6670
+ "LOAD",
6671
+ "MERGE",
6672
+ "REMOVE",
6673
+ "SET",
6674
+ ];
6675
+ function requireFalkorConfig() {
6676
+ if (!FALKOR_TOKEN && !FALKOR_PASS) {
6677
+ throw new Error("Missing FalkorDB configuration: set FALKOR_TOKEN/FALKOR_PAT or FALKOR_PASS in the agent runtime.");
6678
+ }
6679
+ return {
6680
+ endpoint: FALKOR_BROWSER_ENDPOINT,
6681
+ graph: FALKOR_GRAPH,
6682
+ username: FALKOR_USER,
6683
+ password: FALKOR_PASS,
6684
+ token: FALKOR_TOKEN,
6685
+ host: FALKOR_HOST,
6686
+ port: FALKOR_PORT,
6687
+ tls: FALKOR_TLS,
6688
+ };
6689
+ }
6690
+ function sleep(ms) {
6691
+ return new Promise((resolve) => setTimeout(resolve, ms));
6897
6692
  }
6898
- function surrealThing(table, id) {
6899
- return `${table}:\`${escapeSurrealRecordId(id)}\``;
6693
+ function cypherString(value) {
6694
+ return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
6900
6695
  }
6901
- function agentLinkId(parts) {
6902
- return [parts.kind, parts.fromTable, parts.fromId, parts.toTable, parts.toId]
6903
- .join("__")
6904
- .replace(/[^A-Za-z0-9_:-]/g, "_")
6905
- .slice(0, 512);
6696
+ function cypherValue(value) {
6697
+ if (value === null || value === undefined)
6698
+ return "NULL";
6699
+ if (typeof value === "number")
6700
+ return Number.isFinite(value) ? String(value) : "NULL";
6701
+ if (typeof value === "boolean")
6702
+ return value ? "true" : "false";
6703
+ if (typeof value === "string")
6704
+ return cypherString(value);
6705
+ if (Array.isArray(value))
6706
+ return `[${value.map(cypherValue).join(", ")}]`;
6707
+ return cypherString(JSON.stringify(value));
6708
+ }
6709
+ function cypherProps(props) {
6710
+ return `{${Object.entries(props)
6711
+ .filter(([, value]) => value !== undefined)
6712
+ .map(([key, value]) => `${key}: ${cypherValue(value)}`)
6713
+ .join(", ")}}`;
6906
6714
  }
6907
- async function runSurrealSql(sql) {
6908
- const cfg = requireSurrealConfig();
6909
- const credentials = Buffer.from(`${cfg.user}:${cfg.pass}`).toString("base64");
6910
- const response = await fetch(`${cfg.endpoint.replace(/\/$/, "")}/sql`, {
6715
+ async function requestFalkorToken(config, attempt) {
6716
+ if (!config.password) {
6717
+ throw new Error("FALKOR_PASS is required when FALKOR_TOKEN is not set.");
6718
+ }
6719
+ const tokenName = [
6720
+ "lifeos-mcp",
6721
+ attempt === 0 ? "graph" : "retry",
6722
+ Date.now(),
6723
+ Math.random().toString(36).slice(2, 10),
6724
+ ].join("-");
6725
+ const response = await fetch(`${config.endpoint.replace(/\/$/, "")}/api/auth/tokens/credentials`, {
6911
6726
  method: "POST",
6727
+ headers: { "Content-Type": "application/json" },
6728
+ body: JSON.stringify({
6729
+ username: config.username,
6730
+ password: config.password,
6731
+ host: config.host,
6732
+ port: config.port,
6733
+ tls: config.tls,
6734
+ name: tokenName,
6735
+ ttlSeconds: 3600,
6736
+ }),
6737
+ });
6738
+ const text = await response.text();
6739
+ if (!response.ok) {
6740
+ throw new Error(`FalkorDB auth failed (${response.status}): ${text}`);
6741
+ }
6742
+ const parsed = JSON.parse(text);
6743
+ if (!parsed.token) {
6744
+ throw new Error(`FalkorDB auth returned no token: ${text}`);
6745
+ }
6746
+ return parsed.token;
6747
+ }
6748
+ async function getFalkorToken(config) {
6749
+ if (config.token)
6750
+ return config.token;
6751
+ let lastError;
6752
+ for (let attempt = 0; attempt < 3; attempt += 1) {
6753
+ try {
6754
+ return await requestFalkorToken(config, attempt);
6755
+ }
6756
+ catch (error) {
6757
+ lastError = error;
6758
+ if (attempt < 2)
6759
+ await sleep(250 * (attempt + 1));
6760
+ }
6761
+ }
6762
+ throw lastError;
6763
+ }
6764
+ function parseFalkorEventStream(text) {
6765
+ const data = text
6766
+ .split(/\r?\n/)
6767
+ .filter((line) => line.startsWith("data:"))
6768
+ .map((line) => line.slice(5).trimStart())
6769
+ .join("\n")
6770
+ .trim();
6771
+ if (!data)
6772
+ return { data: [], metadata: [] };
6773
+ return JSON.parse(data);
6774
+ }
6775
+ async function runFalkorQuery(query) {
6776
+ const config = requireFalkorConfig();
6777
+ const token = await getFalkorToken(config);
6778
+ const url = `${config.endpoint.replace(/\/$/, "")}/api/graph/${encodeURIComponent(config.graph)}?query=${encodeURIComponent(query)}&timeout=30000`;
6779
+ const response = await fetch(url, {
6780
+ method: "GET",
6912
6781
  headers: {
6913
- Authorization: `Basic ${credentials}`,
6914
- "Content-Type": "text/plain",
6915
- Accept: "application/json",
6916
- "Surreal-NS": cfg.ns,
6917
- "Surreal-DB": cfg.db,
6782
+ Authorization: `Bearer ${token}`,
6783
+ Accept: "text/event-stream",
6918
6784
  },
6919
- body: sql,
6920
6785
  });
6921
6786
  const text = await response.text();
6922
6787
  if (!response.ok) {
6923
- throw new Error(`SurrealDB SQL failed (${response.status}): ${text}`);
6788
+ throw new Error(`FalkorDB query failed (${response.status}): ${text}`);
6924
6789
  }
6925
- const parsed = JSON.parse(text);
6926
- const failed = parsed.find((entry) => entry.status !== "OK");
6927
- if (failed) {
6928
- throw new Error(`SurrealDB SQL failed: ${JSON.stringify(failed, null, 2)}`);
6790
+ return parseFalkorEventStream(text);
6791
+ }
6792
+ function assertReadOnlyFalkorCypher(query) {
6793
+ const stripped = stripSqlCommentsAndStrings(query).trim();
6794
+ if (!stripped)
6795
+ throw new Error("FalkorDB Cypher query is empty.");
6796
+ if (stripped.includes(";")) {
6797
+ throw new Error("falkor_graph_query allows one Cypher statement at a time.");
6798
+ }
6799
+ const upper = stripped.toUpperCase();
6800
+ const startsReadOnly = upper.startsWith("MATCH ") ||
6801
+ upper.startsWith("WITH ") ||
6802
+ upper.startsWith("RETURN ") ||
6803
+ upper.startsWith("EXPLAIN ") ||
6804
+ upper.startsWith("PROFILE ");
6805
+ if (!startsReadOnly) {
6806
+ throw new Error("falkor_graph_query only allows read-only Cypher. Start with MATCH, WITH, RETURN, EXPLAIN, or PROFILE.");
6807
+ }
6808
+ for (const keyword of FALKOR_MUTATING_KEYWORDS) {
6809
+ if (new RegExp(`\\b${keyword}\\b`, "i").test(stripped)) {
6810
+ throw new Error(`falkor_graph_query blocked mutating keyword ${keyword}. Use falkor_graph_link for guarded relationship writes.`);
6811
+ }
6812
+ }
6813
+ const isAggregateCount = /\bCOUNT\s*\(/i.test(stripped);
6814
+ if ((upper.startsWith("MATCH ") || upper.startsWith("WITH ")) &&
6815
+ !/\bLIMIT\b/i.test(stripped) &&
6816
+ !isAggregateCount) {
6817
+ throw new Error("MATCH/WITH queries must include LIMIT unless they are aggregate count queries.");
6818
+ }
6819
+ }
6820
+ function capFalkorResult(result, maxRows) {
6821
+ const rows = result.data ?? [];
6822
+ if (rows.length <= maxRows)
6823
+ return result;
6824
+ return {
6825
+ ...result,
6826
+ data: rows.slice(0, maxRows),
6827
+ detail: `${rows.length - maxRows} additional rows omitted by lifeos-mcp maxRows=${maxRows}`,
6828
+ };
6829
+ }
6830
+ function assertFalkorIdentifier(value, label) {
6831
+ if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) {
6832
+ throw new Error(`${label} must be a simple Falkor identifier.`);
6833
+ }
6834
+ return value;
6835
+ }
6836
+ function assertFalkorLabel(value, label) {
6837
+ const nodeLabel = assertFalkorIdentifier(value, label);
6838
+ if (!FALKOR_NODE_LABELS.includes(nodeLabel)) {
6839
+ throw new Error(`${label} must be one of: ${FALKOR_NODE_LABELS.join(", ")}`);
6929
6840
  }
6930
- return parsed;
6841
+ return nodeLabel;
6931
6842
  }
6932
- function surrealGraphSchema() {
6843
+ function falkorLinkId(parts) {
6844
+ return [parts.kind, parts.fromLabel, parts.fromId, parts.toLabel, parts.toId]
6845
+ .join("__")
6846
+ .replace(/[^A-Za-z0-9_:-]/g, "_")
6847
+ .slice(0, 512);
6848
+ }
6849
+ function falkorGraphSchema() {
6933
6850
  return {
6934
- configured: Boolean(SURREAL_ENDPOINT && SURREAL_USER && SURREAL_PASS),
6935
- endpointConfigured: Boolean(SURREAL_ENDPOINT),
6936
- namespace: SURREAL_NS,
6937
- database: SURREAL_DB,
6938
- nodeTablePrefixes: SURREAL_TABLE_PREFIXES,
6939
- relationTables: SURREAL_RELATION_TABLES,
6940
- agentWritableRelationTable: SURREAL_AGENT_LINK_TABLE,
6851
+ configured: Boolean(FALKOR_TOKEN || FALKOR_PASS),
6852
+ endpointConfigured: Boolean(FALKOR_BROWSER_ENDPOINT),
6853
+ graph: FALKOR_GRAPH,
6854
+ nodeLabels: FALKOR_NODE_LABELS,
6855
+ inferredRelationships: FALKOR_INFERRED_RELATIONSHIPS,
6856
+ agentWritableRelationshipType: FALKOR_AGENT_RELATIONSHIP,
6941
6857
  rules: [
6942
- "Use surreal_graph_query for read-only SELECT, INFO, and EXPLAIN statements.",
6943
- "SELECT queries must include LIMIT unless they are aggregate count queries.",
6944
- "Use surreal_graph_link for agent-created relationships; it writes only to lifeos_agent_links.",
6945
- "Do not mutate Convex-synced entity tables from SurrealDB. Convex remains canonical.",
6858
+ "Use falkor_graph_query for read-only Cypher only.",
6859
+ "MATCH/WITH queries must include LIMIT unless they are aggregate count queries.",
6860
+ "Use falkor_graph_link for agent-created relationships; it writes only AGENT_LINK edges.",
6861
+ "Do not mutate Convex-synced nodes or inferred PPV relationships from FalkorDB. Convex remains canonical.",
6946
6862
  "Every agent link must include a reason and confidence.",
6947
6863
  ],
6948
6864
  examples: {
6949
- activePpvProjects: 'SELECT id, title, ->ppv_has_pillar->ppv1_pillars.{id, name, ->ppv_pillar_supports_project->lifeos_pmProjects.{id, name, status}} AS pillars FROM ppv1_visions WHERE status = "in_progress" LIMIT 1;',
6950
- projectInboundLinks: "SELECT <-ppv_pillar_supports_project<-ppv1_pillars.{id, name, purpose} AS supportedByPillars FROM lifeos_pmProjects:`PROJECT_ID` LIMIT 1;",
6951
- agentLinks: "SELECT id, in, out, kind, reason, confidence, createdBy, createdAt FROM lifeos_agent_links LIMIT 25;",
6865
+ ppvOverview: "MATCH (v:PpvVision)-[:HAS_PILLAR]->(p:PpvPillar)-[:PILLAR_SUPPORTS_PROJECT]->(project:PpvProject) RETURN v.convexId, v.title, p.convexId, p.title, project.convexId, project.title LIMIT 25",
6866
+ projectInboundLinks: "MATCH (pillar:PpvPillar)-[:PILLAR_SUPPORTS_PROJECT]->(project:PpvProject {convexId: 'PROJECT_ID'}) RETURN pillar.convexId, pillar.title, project.title LIMIT 10",
6867
+ agentLinks: "MATCH (a)-[r:AGENT_LINK]->(b) RETURN a.convexId, type(r), r.kind, r.reason, r.confidence, b.convexId LIMIT 25",
6952
6868
  },
6953
6869
  };
6954
6870
  }
6955
- async function surrealGraphQuery(args) {
6871
+ async function falkorGraphQuery(args) {
6956
6872
  const query = args.query;
6957
6873
  if (typeof query !== "string") {
6958
6874
  throw new Error("query is required.");
6959
6875
  }
6960
- assertReadOnlySurrealSql(query);
6961
- const results = await runSurrealSql(query);
6962
- return capSurrealResults(results, clampMaxRows(args.maxRows));
6876
+ assertReadOnlyFalkorCypher(query);
6877
+ return capFalkorResult(await runFalkorQuery(query), clampMaxRows(args.maxRows));
6963
6878
  }
6964
- async function surrealGraphLink(args) {
6965
- const fromTable = assertLifeOsNodeTable(args.fromTable, "fromTable");
6879
+ async function falkorGraphLink(args) {
6880
+ const fromLabel = assertFalkorLabel(args.fromLabel, "fromLabel");
6966
6881
  const fromId = assertRecordId(args.fromId, "fromId");
6967
- const toTable = assertLifeOsNodeTable(args.toTable, "toTable");
6882
+ const toLabel = assertFalkorLabel(args.toLabel, "toLabel");
6968
6883
  const toId = assertRecordId(args.toId, "toId");
6969
6884
  const kind = assertRelationKind(args.kind);
6970
6885
  const reason = assertRecordId(args.reason, "reason");
@@ -6980,8 +6895,9 @@ async function surrealGraphLink(args) {
6980
6895
  ? args.metadata
6981
6896
  : {};
6982
6897
  const now = new Date().toISOString();
6983
- const linkId = agentLinkId({ fromTable, fromId, toTable, toId, kind });
6984
- const content = {
6898
+ const linkId = falkorLinkId({ fromLabel, fromId, toLabel, toId, kind });
6899
+ const props = cypherProps({
6900
+ linkId,
6985
6901
  kind,
6986
6902
  reason,
6987
6903
  confidence,
@@ -6989,41 +6905,35 @@ async function surrealGraphLink(args) {
6989
6905
  sourceSystem: "agent",
6990
6906
  sourceUserId: USER_ID,
6991
6907
  userId: USER_ID,
6992
- sourceTable: fromTable,
6993
- targetTable: toTable,
6994
- metadata,
6908
+ metadataJson: JSON.stringify(metadata),
6995
6909
  createdAt: now,
6996
6910
  updatedAt: now,
6997
- };
6998
- const sql = [
6999
- `DEFINE TABLE IF NOT EXISTS ${SURREAL_AGENT_LINK_TABLE} TYPE RELATION SCHEMALESS;`,
7000
- `DEFINE INDEX IF NOT EXISTS ${SURREAL_AGENT_LINK_TABLE}_kind ON TABLE ${SURREAL_AGENT_LINK_TABLE} COLUMNS kind;`,
7001
- `DEFINE INDEX IF NOT EXISTS ${SURREAL_AGENT_LINK_TABLE}_createdBy ON TABLE ${SURREAL_AGENT_LINK_TABLE} COLUMNS createdBy;`,
7002
- `DELETE ${SURREAL_AGENT_LINK_TABLE}:\`${escapeSurrealRecordId(linkId)}\`;`,
7003
- `RELATE ${surrealThing(fromTable, fromId)}->${SURREAL_AGENT_LINK_TABLE}:\`${escapeSurrealRecordId(linkId)}\`->${surrealThing(toTable, toId)} CONTENT ${JSON.stringify(content)};`,
7004
- ].join("\n");
7005
- const results = await runSurrealSql(sql);
6911
+ });
6912
+ await runFalkorQuery(`MATCH (a:${fromLabel} {convexId: ${cypherString(fromId)}})-[r:${FALKOR_AGENT_RELATIONSHIP} {linkId: ${cypherString(linkId)}}]->(b:${toLabel} {convexId: ${cypherString(toId)}}) DELETE r`);
6913
+ const result = await runFalkorQuery(`MATCH (a:${fromLabel} {convexId: ${cypherString(fromId)}}), (b:${toLabel} {convexId: ${cypherString(toId)}}) CREATE (a)-[r:${FALKOR_AGENT_RELATIONSHIP} ${props}]->(b) RETURN r.linkId AS linkId, r.kind AS kind, r.reason AS reason, r.confidence AS confidence`);
7006
6914
  return {
7007
6915
  linked: true,
7008
- relationId: `${SURREAL_AGENT_LINK_TABLE}:${linkId}`,
7009
- from: `${fromTable}:${fromId}`,
7010
- to: `${toTable}:${toId}`,
6916
+ relationType: FALKOR_AGENT_RELATIONSHIP,
6917
+ relationId: linkId,
6918
+ from: `${fromLabel}:${fromId}`,
6919
+ to: `${toLabel}:${toId}`,
7011
6920
  kind,
7012
- results,
6921
+ result,
7013
6922
  };
7014
6923
  }
7015
- async function surrealGraphUnlink(args) {
7016
- const fromTable = assertLifeOsNodeTable(args.fromTable, "fromTable");
6924
+ async function falkorGraphUnlink(args) {
6925
+ const fromLabel = assertFalkorLabel(args.fromLabel, "fromLabel");
7017
6926
  const fromId = assertRecordId(args.fromId, "fromId");
7018
- const toTable = assertLifeOsNodeTable(args.toTable, "toTable");
6927
+ const toLabel = assertFalkorLabel(args.toLabel, "toLabel");
7019
6928
  const toId = assertRecordId(args.toId, "toId");
7020
6929
  const kind = assertRelationKind(args.kind);
7021
- const linkId = agentLinkId({ fromTable, fromId, toTable, toId, kind });
7022
- const results = await runSurrealSql(`DELETE ${SURREAL_AGENT_LINK_TABLE}:\`${escapeSurrealRecordId(linkId)}\`;`);
6930
+ const linkId = falkorLinkId({ fromLabel, fromId, toLabel, toId, kind });
6931
+ const result = await runFalkorQuery(`MATCH (a:${fromLabel} {convexId: ${cypherString(fromId)}})-[r:${FALKOR_AGENT_RELATIONSHIP} {linkId: ${cypherString(linkId)}}]->(b:${toLabel} {convexId: ${cypherString(toId)}}) DELETE r`);
7023
6932
  return {
7024
6933
  unlinked: true,
7025
- relationId: `${SURREAL_AGENT_LINK_TABLE}:${linkId}`,
7026
- results,
6934
+ relationType: FALKOR_AGENT_RELATIONSHIP,
6935
+ relationId: linkId,
6936
+ result,
7027
6937
  };
7028
6938
  }
7029
6939
  const STATIC_RESOURCES = [
@@ -7408,18 +7318,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7408
7318
  ],
7409
7319
  };
7410
7320
  }
7411
- if (name === "surreal_graph_schema") {
7321
+ if (name === "falkor_graph_schema") {
7412
7322
  return {
7413
7323
  content: [
7414
7324
  {
7415
7325
  type: "text",
7416
- text: JSON.stringify(surrealGraphSchema(), null, 2),
7326
+ text: JSON.stringify(falkorGraphSchema(), null, 2),
7417
7327
  },
7418
7328
  ],
7419
7329
  };
7420
7330
  }
7421
- if (name === "surreal_graph_query") {
7422
- const result = await surrealGraphQuery(args || {});
7331
+ if (name === "falkor_graph_query") {
7332
+ const result = await falkorGraphQuery(args || {});
7423
7333
  return {
7424
7334
  content: [
7425
7335
  {
@@ -7429,8 +7339,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7429
7339
  ],
7430
7340
  };
7431
7341
  }
7432
- if (name === "surreal_graph_link") {
7433
- const result = await surrealGraphLink(args || {});
7342
+ if (name === "falkor_graph_link") {
7343
+ const result = await falkorGraphLink(args || {});
7434
7344
  return {
7435
7345
  content: [
7436
7346
  {
@@ -7440,8 +7350,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7440
7350
  ],
7441
7351
  };
7442
7352
  }
7443
- if (name === "surreal_graph_unlink") {
7444
- const result = await surrealGraphUnlink(args || {});
7353
+ if (name === "falkor_graph_unlink") {
7354
+ const result = await falkorGraphUnlink(args || {});
7445
7355
  return {
7446
7356
  content: [
7447
7357
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starascendin/lifeos-mcp",
3
- "version": "0.7.67",
3
+ "version": "0.7.69",
4
4
  "description": "MCP server for LifeOS Project Management - manage projects, tasks, notes, and contacts via AI assistants",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",