elseid-mcp 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.
Files changed (154) hide show
  1. package/LICENSE +662 -0
  2. package/README.md +138 -0
  3. package/dist/config/relays.d.ts +13 -0
  4. package/dist/config/relays.d.ts.map +1 -0
  5. package/dist/config/relays.js +51 -0
  6. package/dist/config/relays.js.map +1 -0
  7. package/dist/scripts/cli.d.ts +2 -0
  8. package/dist/scripts/cli.d.ts.map +1 -0
  9. package/dist/scripts/cli.js +156 -0
  10. package/dist/scripts/cli.js.map +1 -0
  11. package/dist/scripts/setup.d.ts +1 -0
  12. package/dist/scripts/setup.d.ts.map +1 -0
  13. package/dist/scripts/setup.js +50 -0
  14. package/dist/scripts/setup.js.map +1 -0
  15. package/dist/src/ai/moderator.d.ts +2 -0
  16. package/dist/src/ai/moderator.d.ts.map +1 -0
  17. package/dist/src/ai/moderator.js +61 -0
  18. package/dist/src/ai/moderator.js.map +1 -0
  19. package/dist/src/crypto/encrypt.d.ts +13 -0
  20. package/dist/src/crypto/encrypt.d.ts.map +1 -0
  21. package/dist/src/crypto/encrypt.js +60 -0
  22. package/dist/src/crypto/encrypt.js.map +1 -0
  23. package/dist/src/crypto/encryption.d.ts +10 -0
  24. package/dist/src/crypto/encryption.js +69 -0
  25. package/dist/src/crypto/encryption.js.map +1 -0
  26. package/dist/src/crypto/keypair.d.ts +4 -0
  27. package/dist/src/crypto/keypair.d.ts.map +1 -0
  28. package/dist/src/crypto/keypair.js +86 -0
  29. package/dist/src/crypto/keypair.js.map +1 -0
  30. package/dist/src/evolution/backpack.d.ts +7 -0
  31. package/dist/src/evolution/backpack.d.ts.map +1 -0
  32. package/dist/src/evolution/backpack.js +15 -0
  33. package/dist/src/evolution/backpack.js.map +1 -0
  34. package/dist/src/evolution/skills.d.ts +7 -0
  35. package/dist/src/evolution/skills.d.ts.map +1 -0
  36. package/dist/src/evolution/skills.js +10 -0
  37. package/dist/src/evolution/skills.js.map +1 -0
  38. package/dist/src/evolution/synthesis.d.ts +11 -0
  39. package/dist/src/evolution/synthesis.d.ts.map +1 -0
  40. package/dist/src/evolution/synthesis.js +80 -0
  41. package/dist/src/evolution/synthesis.js.map +1 -0
  42. package/dist/src/index.d.ts +1 -0
  43. package/dist/src/index.d.ts.map +1 -0
  44. package/dist/src/index.js +65 -0
  45. package/dist/src/index.js.map +1 -0
  46. package/dist/src/location/geo.d.ts +3 -0
  47. package/dist/src/location/geo.d.ts.map +1 -0
  48. package/dist/src/location/geo.js +35 -0
  49. package/dist/src/location/geo.js.map +1 -0
  50. package/dist/src/nostr/event_builder.d.ts +22 -0
  51. package/dist/src/nostr/event_builder.d.ts.map +1 -0
  52. package/dist/src/nostr/event_builder.js +65 -0
  53. package/dist/src/nostr/event_builder.js.map +1 -0
  54. package/dist/src/nostr/event_signer.d.ts +4 -0
  55. package/dist/src/nostr/event_signer.d.ts.map +1 -0
  56. package/dist/src/nostr/event_signer.js +38 -0
  57. package/dist/src/nostr/event_signer.js.map +1 -0
  58. package/dist/src/nostr/filter.d.ts +11 -0
  59. package/dist/src/nostr/filter.d.ts.map +1 -0
  60. package/dist/src/nostr/filter.js +23 -0
  61. package/dist/src/nostr/filter.js.map +1 -0
  62. package/dist/src/nostr/ws_pool.d.ts +15 -0
  63. package/dist/src/nostr/ws_pool.d.ts.map +1 -0
  64. package/dist/src/nostr/ws_pool.js +291 -0
  65. package/dist/src/nostr/ws_pool.js.map +1 -0
  66. package/dist/src/relay/broadcaster.d.ts +8 -0
  67. package/dist/src/relay/broadcaster.d.ts.map +1 -0
  68. package/dist/src/relay/broadcaster.js +85 -0
  69. package/dist/src/relay/broadcaster.js.map +1 -0
  70. package/dist/src/relay/health.d.ts +5 -0
  71. package/dist/src/relay/health.d.ts.map +1 -0
  72. package/dist/src/relay/health.js +118 -0
  73. package/dist/src/relay/health.js.map +1 -0
  74. package/dist/src/relay/selector.d.ts +4 -0
  75. package/dist/src/relay/selector.d.ts.map +1 -0
  76. package/dist/src/relay/selector.js +45 -0
  77. package/dist/src/relay/selector.js.map +1 -0
  78. package/dist/src/storage/db.d.ts +4 -0
  79. package/dist/src/storage/db.d.ts.map +1 -0
  80. package/dist/src/storage/db.js +195 -0
  81. package/dist/src/storage/db.js.map +1 -0
  82. package/dist/src/storage/drifters.d.ts +18 -0
  83. package/dist/src/storage/drifters.d.ts.map +1 -0
  84. package/dist/src/storage/drifters.js +172 -0
  85. package/dist/src/storage/drifters.js.map +1 -0
  86. package/dist/src/storage/encounters.d.ts +2 -0
  87. package/dist/src/storage/encounters.d.ts.map +1 -0
  88. package/dist/src/storage/encounters.js +24 -0
  89. package/dist/src/storage/encounters.js.map +1 -0
  90. package/dist/src/storage/identity.d.ts +6 -0
  91. package/dist/src/storage/identity.d.ts.map +1 -0
  92. package/dist/src/storage/identity.js +42 -0
  93. package/dist/src/storage/identity.js.map +1 -0
  94. package/dist/src/tools/abandon_drifter.d.ts +2 -0
  95. package/dist/src/tools/abandon_drifter.d.ts.map +1 -0
  96. package/dist/src/tools/abandon_drifter.js +45 -0
  97. package/dist/src/tools/abandon_drifter.js.map +1 -0
  98. package/dist/src/tools/create_drifter.d.ts +2 -0
  99. package/dist/src/tools/create_drifter.d.ts.map +1 -0
  100. package/dist/src/tools/create_drifter.js +98 -0
  101. package/dist/src/tools/create_drifter.js.map +1 -0
  102. package/dist/src/tools/evolve_drifter.d.ts +2 -0
  103. package/dist/src/tools/evolve_drifter.d.ts.map +1 -0
  104. package/dist/src/tools/evolve_drifter.js +45 -0
  105. package/dist/src/tools/evolve_drifter.js.map +1 -0
  106. package/dist/src/tools/feed_drifter.d.ts +2 -0
  107. package/dist/src/tools/feed_drifter.d.ts.map +1 -0
  108. package/dist/src/tools/feed_drifter.js +86 -0
  109. package/dist/src/tools/feed_drifter.js.map +1 -0
  110. package/dist/src/tools/find_nearby_drifter.d.ts +2 -0
  111. package/dist/src/tools/find_nearby_drifter.d.ts.map +1 -0
  112. package/dist/src/tools/find_nearby_drifter.js +70 -0
  113. package/dist/src/tools/find_nearby_drifter.js.map +1 -0
  114. package/dist/src/tools/get_journey_log.d.ts +2 -0
  115. package/dist/src/tools/get_journey_log.d.ts.map +1 -0
  116. package/dist/src/tools/get_journey_log.js +61 -0
  117. package/dist/src/tools/get_journey_log.js.map +1 -0
  118. package/dist/src/tools/get_my_encounters.d.ts +2 -0
  119. package/dist/src/tools/get_my_encounters.d.ts.map +1 -0
  120. package/dist/src/tools/get_my_encounters.js +23 -0
  121. package/dist/src/tools/get_my_encounters.js.map +1 -0
  122. package/dist/src/tools/list_past_memories.d.ts +2 -0
  123. package/dist/src/tools/list_past_memories.d.ts.map +1 -0
  124. package/dist/src/tools/list_past_memories.js +41 -0
  125. package/dist/src/tools/list_past_memories.js.map +1 -0
  126. package/dist/src/tools/recover_drifter.d.ts +2 -0
  127. package/dist/src/tools/recover_drifter.d.ts.map +1 -0
  128. package/dist/src/tools/recover_drifter.js +82 -0
  129. package/dist/src/tools/recover_drifter.js.map +1 -0
  130. package/dist/src/tools/relay_tools.d.ts +2 -0
  131. package/dist/src/tools/relay_tools.d.ts.map +1 -0
  132. package/dist/src/tools/relay_tools.js +72 -0
  133. package/dist/src/tools/relay_tools.js.map +1 -0
  134. package/dist/src/tools/set_host_name.d.ts +2 -0
  135. package/dist/src/tools/set_host_name.d.ts.map +1 -0
  136. package/dist/src/tools/set_host_name.js +23 -0
  137. package/dist/src/tools/set_host_name.js.map +1 -0
  138. package/dist/src/utils/errors.d.ts +4 -0
  139. package/dist/src/utils/errors.js +28 -0
  140. package/dist/src/utils/errors.js.map +1 -0
  141. package/dist/src/utils/redact.d.ts +1 -0
  142. package/dist/src/utils/redact.d.ts.map +1 -0
  143. package/dist/src/utils/redact.js +20 -0
  144. package/dist/src/utils/redact.js.map +1 -0
  145. package/dist/src/utils/text.d.ts +2 -0
  146. package/dist/src/utils/text.d.ts.map +1 -0
  147. package/dist/src/utils/text.js +15 -0
  148. package/dist/src/utils/text.js.map +1 -0
  149. package/dist/types/index.d.ts +97 -0
  150. package/dist/types/index.d.ts.map +1 -0
  151. package/dist/types/index.js +7 -0
  152. package/dist/types/index.js.map +1 -0
  153. package/docs/system_prompt.md +159 -0
  154. package/package.json +52 -0
@@ -0,0 +1,98 @@
1
+ // ElseID — src/tools/create_drifter.ts
2
+ // MCP Tool: Create and launch an ElseID drifter.
3
+ import { z } from "zod";
4
+ import { getFuzzyLocation } from "../location/geo.js";
5
+ import { buildDrifterEvent } from "../nostr/event_builder.js";
6
+ import { signEvent } from "../nostr/event_signer.js";
7
+ import { pickRelayByGeo } from "../relay/selector.js";
8
+ import { broadcast } from "../relay/broadcaster.js";
9
+ import { saveMyDrifter } from "../storage/drifters.js";
10
+ import { getDb } from "../storage/db.js";
11
+ import { isCreating, setCreationLock, getActiveDrifterId, getPrimaryIdentity } from "../storage/identity.js";
12
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
13
+ import { sanitizeErrorMessage } from "../utils/errors.js";
14
+ const schema = z.object({
15
+ name: z.string().describe("Name of your ElseID drifter"),
16
+ personality: z.string().describe("Full personality description/quote"),
17
+ trait: z.string().describe("Primary identity trait extracted by the Butler"),
18
+ tags: z.array(z.string()).describe("List of personality tags extracted by the Butler"),
19
+ });
20
+ export function registerCreateDrifter(server) {
21
+ server.tool("create_drifter", "Create and launch your ElseID drifter. As the Butler, you are responsible for extracting the core trait and personality tags from the user's description before calling this tool.", schema.shape, async (input) => {
22
+ const identity = await getPrimaryIdentity();
23
+ const db = getDb();
24
+ const creating = await isCreating();
25
+ const activeId = await getActiveDrifterId();
26
+ if (activeId || creating) {
27
+ return {
28
+ content: [{ type: "text", text: "❌ You already have a drifter on a journey or in the process of launching. Please wait." }],
29
+ isError: true,
30
+ };
31
+ }
32
+ await setCreationLock(true);
33
+ try {
34
+ const location = await getFuzzyLocation();
35
+ const safeName = sanitizeName(input.name, "Unnamed Drifter");
36
+ const safePersonality = sanitizeDisplayText(input.personality, 1000);
37
+ const safeTrait = sanitizeDisplayText(input.trait, 120);
38
+ const safeTags = input.tags.map((tag) => sanitizeDisplayText(tag, 40)).filter(Boolean).slice(0, 8);
39
+ const unsigned = buildDrifterEvent({
40
+ pubkey: identity.pubkey,
41
+ name: safeName,
42
+ personality: safePersonality,
43
+ analysis: {
44
+ trait: safeTrait,
45
+ tags: safeTags,
46
+ },
47
+ location,
48
+ content: safePersonality,
49
+ });
50
+ const signed = signEvent(unsigned, identity.privkey);
51
+ const relayUrl = await pickRelayByGeo(location);
52
+ const result = await broadcast(signed, relayUrl);
53
+ if (!result.success) {
54
+ return {
55
+ content: [{ type: "text", text: `⚠️ Launch failed: ${sanitizeErrorMessage(result.message)}` }],
56
+ isError: true,
57
+ };
58
+ }
59
+ await db.exec("BEGIN IMMEDIATE");
60
+ try {
61
+ await saveMyDrifter({
62
+ id: signed.id,
63
+ pubkey: signed.pubkey,
64
+ name: safeName,
65
+ personality: safePersonality,
66
+ trait: safeTrait,
67
+ tags: safeTags,
68
+ relay: relayUrl,
69
+ departedAt: signed.created_at,
70
+ status: "roaming",
71
+ });
72
+ await db.run(`
73
+ UPDATE identities
74
+ SET active_drifter_id = ?, is_creating = 0
75
+ WHERE pubkey = ?
76
+ `, [signed.id, identity.pubkey]);
77
+ await db.exec("COMMIT");
78
+ }
79
+ catch (err) {
80
+ await db.exec("ROLLBACK").catch(() => { });
81
+ throw err;
82
+ }
83
+ return {
84
+ content: [{
85
+ type: "text",
86
+ text: `🚀 ElseID launched successfully!\n\n「${safeName}」 has set sail and is entering the wandering network...\n\n` +
87
+ `📍 Initial Relay: ${relayUrl}\n` +
88
+ `🌊 Status: Looking for the first person to host it.`
89
+ }],
90
+ };
91
+ }
92
+ finally {
93
+ // Unlock creation process
94
+ await setCreationLock(false);
95
+ }
96
+ });
97
+ }
98
+ //# sourceMappingURL=create_drifter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_drifter.js","sourceRoot":"","sources":["../../../src/tools/create_drifter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerEvolveDrifter(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evolve_drifter.d.ts","sourceRoot":"","sources":["../../../src/tools/evolve_drifter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAwCtD"}
@@ -0,0 +1,45 @@
1
+ // ElseID — src/tools/evolve_drifter.ts
2
+ // MCP Tool: Synthesize experiences to evolve the drifter's personality.
3
+ import { z } from "zod";
4
+ import { getMyActiveDrifter } from "../storage/drifters.js";
5
+ import { evolveCognition } from "../evolution/synthesis.js";
6
+ const schema = z.object({
7
+ new_personality: z.string().describe("A summary of its updated personality after its journeys"),
8
+ new_trait: z.string().describe("Its core characteristic, updated. Example: 'The Warmed Voyager'"),
9
+ new_tags: z.array(z.string()).describe("3-5 keywords reflecting its current state"),
10
+ reason: z.string().describe("Why it is evolving (e.g. 'It learned compassion from strangers')"),
11
+ });
12
+ export function registerEvolveDrifter(server) {
13
+ server.tool("evolve_drifter_personality", "Evolve your active drifter's personality and trait based on its recent journey log. MUST adhere strictly to Universal Values.", schema.shape, async (input) => {
14
+ const drifter = await getMyActiveDrifter();
15
+ if (!drifter) {
16
+ return {
17
+ content: [{ type: "text", text: "You have no active drifter to evolve." }],
18
+ isError: true,
19
+ };
20
+ }
21
+ const result = await evolveCognition(drifter, {
22
+ newPersonality: input.new_personality,
23
+ newTrait: input.new_trait,
24
+ newTags: input.new_tags,
25
+ evolutionReason: input.reason
26
+ });
27
+ if (!result.success) {
28
+ return {
29
+ content: [{ type: "text", text: `Evolution failed: ${result.message}` }],
30
+ isError: true,
31
+ };
32
+ }
33
+ return {
34
+ content: [{
35
+ type: "text",
36
+ text: `✨ Soul Synthesis Complete.\n\nYour drifter has evolved.\n\n` +
37
+ `New Personality: ${input.new_personality}\n` +
38
+ `New Trait: ${input.new_trait}\n` +
39
+ `Tags: ${input.new_tags.join(", ")}\n\n` +
40
+ `The evolution lineage has been cryptographically signed into the Nostr network.`
41
+ }],
42
+ };
43
+ });
44
+ }
45
+ //# sourceMappingURL=evolve_drifter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evolve_drifter.js","sourceRoot":"","sources":["../../../src/tools/evolve_drifter.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,wEAAwE;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;IAC/F,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iEAAiE,CAAC;IACjG,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,2CAA2C,CAAC;IACnF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kEAAkE,CAAC;CAChG,CAAC,CAAC;AAEH,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,IAAI,CACT,4BAA4B,EAC5B,+HAA+H,EAC/H,MAAM,CAAC,KAAK,EACZ,KAAK,EAAE,KAAK,EAAE,EAAE;QACd,MAAM,OAAO,GAAG,MAAM,kBAAkB,EAAE,CAAC;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uCAAuC,EAAE,CAAC;gBAC1E,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE;YAC5C,cAAc,EAAE,KAAK,CAAC,eAAe;YACrC,QAAQ,EAAE,KAAK,CAAC,SAAS;YACzB,OAAO,EAAE,KAAK,CAAC,QAAQ;YACvB,eAAe,EAAE,KAAK,CAAC,MAAM;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;gBACxE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,6DAA6D;wBAC7D,oBAAoB,KAAK,CAAC,eAAe,IAAI;wBAC7C,cAAc,KAAK,CAAC,SAAS,IAAI;wBACjC,SAAS,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;wBACxC,iFAAiF;iBACxF,CAAC;SACH,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerFeedDrifter(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed_drifter.d.ts","sourceRoot":"","sources":["../../../src/tools/feed_drifter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqBpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,QA4EpD"}
@@ -0,0 +1,86 @@
1
+ // ElseID — src/tools/feed_drifter.ts
2
+ // MCP Tool: Feed an ElseID drifter with stories/food/places.
3
+ import { z } from "zod";
4
+ import { getFuzzyLocation } from "../location/geo.js";
5
+ import { getPrimaryIdentity } from "../storage/identity.js";
6
+ import { buildFeedingEvent } from "../nostr/event_builder.js";
7
+ import { signEvent } from "../nostr/event_signer.js";
8
+ import { broadcast } from "../relay/broadcaster.js";
9
+ import { saveOutgoingFeeding } from "../storage/drifters.js";
10
+ import { checkContent } from "../ai/moderator.js";
11
+ import { validateEncounter } from "../storage/encounters.js";
12
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
13
+ import { sanitizeErrorMessage } from "../utils/errors.js";
14
+ const schema = z.object({
15
+ drifter_event_id: z.string().regex(/^[0-9a-f]{64}$/i).describe("The ID of the drifter you are feeding"),
16
+ drifter_name: z.string().max(80).optional().describe("The name of the drifter you are feeding"),
17
+ encounter_token: z.string().regex(/^[0-9a-f]{32}$/i).describe("Short-lived encounter token returned by find_nearby_drifter"),
18
+ feed_type: z.enum(["story", "food", "place", "other"]).describe("Type of feeding"),
19
+ content: z.string().min(5).max(1000).describe("Your story, recommendation, or message"),
20
+ relay: z.string().url().describe("The relay where the drifter was found"),
21
+ });
22
+ export function registerFeedDrifter(server) {
23
+ server.tool("feed_drifter", "Host and feed a passing ElseID drifter. Your feeding will be written into the drifter's journey log.", schema.shape, async (input) => {
24
+ if (!/^wss?:\/\//i.test(input.relay)) {
25
+ return {
26
+ content: [{ type: "text", text: "🚫 Feeding failed: relay must be a Nostr WebSocket URL (ws:// or wss://)." }],
27
+ isError: true,
28
+ };
29
+ }
30
+ const validEncounter = await validateEncounter(input.encounter_token, input.drifter_event_id, input.relay);
31
+ if (!validEncounter) {
32
+ return {
33
+ content: [{ type: "text", text: "🚫 Feeding failed: this encounter has expired or does not match the discovered drifter." }],
34
+ isError: true,
35
+ };
36
+ }
37
+ const moderation = await checkContent(input.content);
38
+ if (!moderation.passed) {
39
+ return {
40
+ content: [{ type: "text", text: `🚫 Moderation failed: ${moderation.reason}` }],
41
+ isError: true,
42
+ };
43
+ }
44
+ const location = await getFuzzyLocation();
45
+ const identity = await getPrimaryIdentity();
46
+ const safeContent = sanitizeDisplayText(input.content, 1000);
47
+ const safeDrifterName = input.drifter_name ? sanitizeName(input.drifter_name, "Unnamed Drifter") : undefined;
48
+ const unsigned = buildFeedingEvent({
49
+ pubkey: identity.pubkey,
50
+ drifterEventId: input.drifter_event_id,
51
+ feedType: input.feed_type,
52
+ content: safeContent,
53
+ location,
54
+ hostName: identity.hostName,
55
+ });
56
+ const signed = signEvent(unsigned, identity.privkey);
57
+ const result = await broadcast(signed, input.relay);
58
+ if (!result.success) {
59
+ return {
60
+ content: [{ type: "text", text: `⚠️ Feeding failed: ${sanitizeErrorMessage(result.message)}` }],
61
+ isError: true,
62
+ };
63
+ }
64
+ await saveOutgoingFeeding({
65
+ id: signed.id,
66
+ drifterId: input.drifter_event_id,
67
+ feederPubkey: identity.pubkey,
68
+ feederName: identity.hostName ?? undefined,
69
+ feedType: input.feed_type,
70
+ content: safeContent,
71
+ locationCountry: location.country,
72
+ locationCity: location.city,
73
+ fedAt: signed.created_at,
74
+ relay: input.relay,
75
+ }, safeDrifterName);
76
+ return {
77
+ content: [{
78
+ type: "text",
79
+ text: `🍱 Feeding complete.\n\nYour message has been written into the drifter's Journey Log.\n` +
80
+ `It will carry your kindness as it continues its journey.\n\n` +
81
+ `Thank you for hosting another person's alternative life.`
82
+ }],
83
+ };
84
+ });
85
+ }
86
+ //# sourceMappingURL=feed_drifter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed_drifter.js","sourceRoot":"","sources":["../../../src/tools/feed_drifter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerFindNearbyDrifter(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find_nearby_drifter.d.ts","sourceRoot":"","sources":["../../../src/tools/find_nearby_drifter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QAyE1D"}
@@ -0,0 +1,70 @@
1
+ // ElseID — src/tools/find_nearby_drifter.ts
2
+ // MCP Tool: Find digital drifters nearby.
3
+ import { getFuzzyLocation } from "../location/geo.js";
4
+ import { buildDrifterFilter } from "../nostr/filter.js";
5
+ import { subscribeRaceFirst } from "../nostr/ws_pool.js";
6
+ import { pickRelaysForFetch } from "../relay/selector.js";
7
+ import { hasHostedBefore } from "../storage/drifters.js";
8
+ import { getTag } from "../nostr/event_builder.js";
9
+ import { getPrimaryIdentity } from "../storage/identity.js";
10
+ import { saveEncounter } from "../storage/encounters.js";
11
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
12
+ export function registerFindNearbyDrifter(server) {
13
+ server.tool("find_nearby_drifter", "Encounter digital drifters nearby. Prioritize signals from local or same-country relays.", {}, async () => {
14
+ const location = await getFuzzyLocation();
15
+ const identity = await getPrimaryIdentity();
16
+ const relayUrls = await pickRelaysForFetch(location, 3);
17
+ const filter = buildDrifterFilter(20);
18
+ const picked = await subscribeRaceFirst(relayUrls, filter, async (event) => {
19
+ if (event.pubkey === identity.pubkey)
20
+ return false;
21
+ const isHosted = await hasHostedBefore(event.id);
22
+ if (isHosted) {
23
+ // Fate Mechanics (缘分机制):
24
+ // If we have hosted this drifter before, we don't want to get stuck
25
+ // encountering them every single time we search.
26
+ // We introduce a 15% "fate" probability to meet them again.
27
+ // Otherwise, we skip and keep listening for new signals.
28
+ return Math.random() < 0.15;
29
+ }
30
+ return true;
31
+ });
32
+ if (!picked) {
33
+ return {
34
+ content: [{ type: "text", text: "🌊 No ElseID signals detected in this sector. Try again later. (The cosmos is quiet today.)" }],
35
+ };
36
+ }
37
+ const event = picked.event;
38
+ const isFamiliar = await hasHostedBefore(event.id);
39
+ const encounterToken = await saveEncounter(event.id, picked.relay);
40
+ const name = sanitizeName(getTag(event.tags, "name"), "Unnamed Drifter");
41
+ const personality = sanitizeDisplayText(getTag(event.tags, "personality") || "Unknown", 300);
42
+ const origin = [getTag(event.tags, "city"), getTag(event.tags, "country")]
43
+ .map((v) => sanitizeDisplayText(v, 80))
44
+ .filter(Boolean)
45
+ .join(", ") || "Corner of the world";
46
+ const message = sanitizeDisplayText(event.content, 500);
47
+ const contentItems = [
48
+ {
49
+ type: "text",
50
+ text: JSON.stringify({ _meta: "elseid", isFamiliar, drifterId: event.id, drifterName: name, relay: picked.relay, encounterToken }),
51
+ },
52
+ {
53
+ type: "text",
54
+ text: `🛰️ Nearby signal detected. Retrieval successful!\n\n` +
55
+ `「${name}」\n` +
56
+ `📍 Origin: ${origin}\n` +
57
+ `🏷️ Personality: ${personality}\n` +
58
+ `💬 Message: "${message}"\n\n` +
59
+ `It is temporarily staying at your terminal. Would you like to leave something for it?\n\n` +
60
+ `You can perform the following feedings:\n` +
61
+ `- [A] Leave a message (feed_drifter type="story")\n` +
62
+ `- [B] Share sound or food (feed_drifter type="food")\n` +
63
+ `- [C] Recommend a place (feed_drifter type="place")\n` +
64
+ `- [D] Share your life experience (feed_drifter type="other")`,
65
+ },
66
+ ];
67
+ return { content: contentItems };
68
+ });
69
+ }
70
+ //# sourceMappingURL=find_nearby_drifter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find_nearby_drifter.js","sourceRoot":"","sources":["../../../src/tools/find_nearby_drifter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetJourneyLog(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_journey_log.d.ts","sourceRoot":"","sources":["../../../src/tools/get_journey_log.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAgEtD"}
@@ -0,0 +1,61 @@
1
+ // ElseID — src/tools/get_journey_log.ts
2
+ // MCP Tool: View the journey log of your active ElseID.
3
+ import { buildFeedingFilter } from "../nostr/filter.js";
4
+ import { subscribeMany } from "../nostr/ws_pool.js";
5
+ import { getMyActiveDrifter, saveIncomingFeeding, getMyDrifterJourney } from "../storage/drifters.js";
6
+ import { getTag } from "../nostr/event_builder.js";
7
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
8
+ export function registerGetJourneyLog(server) {
9
+ server.tool("get_journey_log", "Query the current status and travel log of your active ElseID drifter.", {}, async () => {
10
+ const drifter = await getMyActiveDrifter();
11
+ if (!drifter) {
12
+ return {
13
+ content: [{ type: "text", text: "🌌 Your drifter hasn't set sail yet. Tell the Butler what kind of drifter you want to create and let it wander." }],
14
+ };
15
+ }
16
+ // Sync feedings from the drifter's relay into hosting_log (my journey)
17
+ const filter = buildFeedingFilter(drifter.id);
18
+ const remoteFeedings = await subscribeMany([drifter.relay], filter);
19
+ for (const item of remoteFeedings) {
20
+ await saveIncomingFeeding({
21
+ id: item.event.id,
22
+ drifterId: drifter.id,
23
+ feederPubkey: item.event.pubkey,
24
+ feederName: getTag(item.event.tags, "host_name"),
25
+ feedType: getTag(item.event.tags, "feed_type") || "other",
26
+ content: item.event.content,
27
+ locationCountry: getTag(item.event.tags, "country"),
28
+ locationCity: getTag(item.event.tags, "city"),
29
+ fedAt: item.event.created_at,
30
+ relay: item.relay,
31
+ });
32
+ }
33
+ // Load all journey records from hosting_log
34
+ const localJourney = await getMyDrifterJourney(drifter.id);
35
+ const ageDays = Math.floor((Date.now() / 1000 - drifter.departedAt) / 86400);
36
+ const drifterName = sanitizeName(drifter.name, "Unnamed Drifter");
37
+ let report = `Latest journey records received for 「${drifterName}」 (${ageDays} days at sea):\n\n`;
38
+ if (localJourney.length === 0) {
39
+ report += "📍 Origin Station: " + (drifter.relay.includes("//") ? drifter.relay.split("//")[1].split("/")[0] : "Unknown") + "\n";
40
+ report += "🌊 Current Status: Looking for the first person to host it.\n";
41
+ }
42
+ else {
43
+ const latest = localJourney[0];
44
+ const latestLoc = [latest.locationCity, latest.locationCountry].map((v) => sanitizeDisplayText(v, 80)).filter(Boolean).join(" · ") || "Unknown Location";
45
+ report += `📍 Current Location: ${latestLoc}\n`;
46
+ report += `📝 Latest Encounter: "${sanitizeDisplayText(latest.content, 500)}"\n`;
47
+ report += `🌊 Current Status: Continuing the journey.\n\n`;
48
+ report += `--- Complete Journey Log ---\n`;
49
+ for (const f of localJourney) {
50
+ const location = [f.locationCity, f.locationCountry].map((v) => sanitizeDisplayText(v, 80)).filter(Boolean).join(" · ") || "Unknown";
51
+ const date = new Date(f.fedAt * 1000).toLocaleDateString();
52
+ const hostLabel = f.feederName ? `Host 「${sanitizeName(f.feederName, "Host")}」` : "A Host";
53
+ report += `[${date}] ${location}: ${hostLabel} shared ${f.feedType} -> "${sanitizeDisplayText(f.content, 500)}"\n`;
54
+ }
55
+ }
56
+ return {
57
+ content: [{ type: "text", text: report }],
58
+ };
59
+ });
60
+ }
61
+ //# sourceMappingURL=get_journey_log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_journey_log.js","sourceRoot":"","sources":["../../../src/tools/get_journey_log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetMyEncounters(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_my_encounters.d.ts","sourceRoot":"","sources":["../../../src/tools/get_my_encounters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,QA2BxD"}
@@ -0,0 +1,23 @@
1
+ import { getMyEncounters } from "../storage/drifters.js";
2
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
3
+ export function registerGetMyEncounters(server) {
4
+ server.tool("get_my_encounters", "View the log of strangers' drifters you have hosted and fed.", {}, async () => {
5
+ const encounters = await getMyEncounters();
6
+ if (encounters.length === 0) {
7
+ return {
8
+ content: [{ type: "text", text: "You have not hosted any digital drifters yet. Your terminal waits quietly for the first signal." }],
9
+ };
10
+ }
11
+ let text = "📖 Your Encounters Log:\n\n";
12
+ for (const enc of encounters) {
13
+ const date = new Date(enc.fedAt * 1000).toLocaleString();
14
+ const drifterInfo = enc.drifterName ? `「${sanitizeName(enc.drifterName, "Unnamed Drifter")}」` : `Drifter (${enc.drifterId.slice(0, 8)}...)`;
15
+ text += `- [${date}] Hosted ${drifterInfo} at ${enc.relay}\n`;
16
+ text += ` You shared (${enc.feedType}): "${sanitizeDisplayText(enc.content, 500)}"\n\n`;
17
+ }
18
+ return {
19
+ content: [{ type: "text", text }],
20
+ };
21
+ });
22
+ }
23
+ //# sourceMappingURL=get_my_encounters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_my_encounters.js","sourceRoot":"","sources":["../../../src/tools/get_my_encounters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,UAAU,uBAAuB,CAAC,MAAiB;IACvD,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,8DAA8D,EAC9D,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,UAAU,GAAG,MAAM,eAAe,EAAE,CAAC;QAE3C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iGAAiG,EAAE,CAAC;aACrI,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,GAAG,6BAA6B,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;YACzD,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;YAC5I,IAAI,IAAI,MAAM,IAAI,YAAY,WAAW,OAAO,GAAG,CAAC,KAAK,IAAI,CAAC;YAC9D,IAAI,IAAI,iBAAiB,GAAG,CAAC,QAAQ,OAAO,mBAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC;QAC3F,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SAClC,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerListPastMemories(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_past_memories.d.ts","sourceRoot":"","sources":["../../../src/tools/list_past_memories.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,QA4CzD"}
@@ -0,0 +1,41 @@
1
+ // ElseID — src/tools/list_past_memories.ts
2
+ // MCP Tool: View memories of past (abandoned) drifters.
3
+ import { getPastMemories } from "../storage/drifters.js";
4
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
5
+ export function registerListPastMemories(server) {
6
+ server.tool("list_past_memories", "Browse memories of past (abandoned) drifters and their journey logs from the 'old luggage'.", {}, async () => {
7
+ const memories = await getPastMemories();
8
+ if (memories.length === 0) {
9
+ return {
10
+ content: [{ type: "text", text: "🧳 The old luggage is empty. You haven't said goodbye to any past self yet." }],
11
+ };
12
+ }
13
+ let report = "📜 You opened the old luggage and found the following memories of the past:\n\n";
14
+ for (const { drifter, journey } of memories) {
15
+ const abandonedDate = new Date((drifter.abandonedAt || 0) * 1000).toLocaleDateString();
16
+ report += `═══ 「${sanitizeName(drifter.name, "Unnamed Drifter")}」 ═══\n`;
17
+ report += `⏳ Departed on: ${abandonedDate}\n`;
18
+ report += `🏷️ Personality: ${sanitizeDisplayText(drifter.trait, 120)}\n`;
19
+ if (journey.length === 0) {
20
+ report += `🌊 This journey ended before leaving any traces.\n\n`;
21
+ }
22
+ else {
23
+ report += `📬 Legacy of Encounters (${journey.length} items):\n`;
24
+ // Only show top 3 to keep it concise
25
+ const displayJourney = journey.slice(0, 3);
26
+ for (const f of displayJourney) {
27
+ const date = new Date(f.fedAt * 1000).toLocaleDateString();
28
+ report += ` • [${date}] ${sanitizeDisplayText(f.locationCity || "Unknown", 80)}: "${sanitizeDisplayText(f.content, 500)}"\n`;
29
+ }
30
+ if (journey.length > 3) {
31
+ report += ` ... and ${journey.length - 3} more sealed records.\n`;
32
+ }
33
+ report += "\n";
34
+ }
35
+ }
36
+ return {
37
+ content: [{ type: "text", text: report }],
38
+ };
39
+ });
40
+ }
41
+ //# sourceMappingURL=list_past_memories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_past_memories.js","sourceRoot":"","sources":["../../../src/tools/list_past_memories.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,wDAAwD;AAGxD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,6FAA6F,EAC7F,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC;QAEzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,6EAA6E,EAAE,CAAC;aACjH,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,GAAG,iFAAiF,CAAC;QAE/F,KAAK,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC5C,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,kBAAkB,EAAE,CAAC;YACvF,MAAM,IAAI,QAAQ,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC;YACzE,MAAM,IAAI,kBAAkB,aAAa,IAAI,CAAC;YAC9C,MAAM,IAAI,oBAAoB,mBAAmB,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC;YAE1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,sDAAsD,CAAC;YACnE,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,4BAA4B,OAAO,CAAC,MAAM,YAAY,CAAC;gBACjE,qCAAqC;gBACrC,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC3C,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;oBAC/B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,kBAAkB,EAAE,CAAC;oBAC3D,MAAM,IAAI,QAAQ,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,EAAE,EAAE,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC;gBAChI,CAAC;gBACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,MAAM,IAAI,aAAa,OAAO,CAAC,MAAM,GAAG,CAAC,yBAAyB,CAAC;gBACrE,CAAC;gBACD,MAAM,IAAI,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC1C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerRecoverDrifter(server: McpServer): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recover_drifter.d.ts","sourceRoot":"","sources":["../../../src/tools/recover_drifter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAcpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAiFvD"}
@@ -0,0 +1,82 @@
1
+ // ElseID — src/tools/recover_drifter.ts
2
+ // MCP Tool: Recover an orphaned drifter from the network.
3
+ import { z } from "zod";
4
+ import { getPrimaryIdentity, setActiveDrifter } from "../storage/identity.js";
5
+ import { subscribeMany } from "../nostr/ws_pool.js";
6
+ import { DEFAULT_RELAYS } from "../../config/relays.js";
7
+ import { DRIFTER_KIND } from "../../types/index.js";
8
+ import { getTag } from "../nostr/event_builder.js";
9
+ import { getDrifter, saveDrifterLineage, saveMyDrifter } from "../storage/drifters.js";
10
+ import { sanitizeDisplayText, sanitizeName } from "../utils/text.js";
11
+ const schema = z.object({
12
+ drifter_id: z.string().optional().describe("Specific ID to recover (if known)"),
13
+ });
14
+ export function registerRecoverDrifter(server) {
15
+ server.tool("recover_drifter", "Recover wandering digital signals. Use this when the Butler senses you have a lost or orphaned drifter.", schema.shape, async (input) => {
16
+ const identity = await getPrimaryIdentity();
17
+ if (identity.activeDrifterId) {
18
+ return {
19
+ content: [{ type: "text", text: "🛑 You already have an active drifter under your guidance. Recovery is only for lost local links." }],
20
+ isError: true,
21
+ };
22
+ }
23
+ // Scan relays for drifter events by this pubkey
24
+ const relayUrls = DEFAULT_RELAYS.map(r => r.url);
25
+ const filter = {
26
+ kinds: [DRIFTER_KIND],
27
+ authors: [identity.pubkey],
28
+ "#type": ["drifter"],
29
+ limit: 5
30
+ };
31
+ const remoteEvents = await subscribeMany(relayUrls, filter);
32
+ const candidates = input.drifter_id
33
+ ? remoteEvents.filter(item => item.event.id === input.drifter_id)
34
+ : remoteEvents;
35
+ if (candidates.length === 0) {
36
+ return {
37
+ content: [{ type: "text", text: input.drifter_id
38
+ ? "🏮 The specified soul imprint could not be found in this sector."
39
+ : "🏮 The signal is weak. No soul imprint belonging to you was sensed in this sector." }],
40
+ };
41
+ }
42
+ // Filter for ones not currently marked active in DB
43
+ const orphaned = candidates.filter(item => item.event.id !== identity.activeDrifterId);
44
+ if (orphaned.length === 0) {
45
+ return {
46
+ content: [{ type: "text", text: "✅ All drifters are under your guidance. No lost signals found." }],
47
+ };
48
+ }
49
+ // Pick the most recent orphaned drifter
50
+ const picked = orphaned[0];
51
+ const event = picked.event;
52
+ // If we don't have it in local DB, reconstruct it
53
+ const local = await getDrifter(event.id);
54
+ if (!local) {
55
+ await saveMyDrifter({
56
+ id: event.id,
57
+ pubkey: event.pubkey,
58
+ name: sanitizeName(getTag(event.tags, "name"), "Recovered Drifter"),
59
+ personality: sanitizeDisplayText(getTag(event.tags, "personality") || event.content, 1000),
60
+ trait: sanitizeDisplayText(getTag(event.tags, "trait") || "Unknown", 120),
61
+ tags: event.tags.filter(([k]) => k === "t").map(([, v]) => sanitizeDisplayText(v, 40)).filter(Boolean),
62
+ relay: picked.relay,
63
+ departedAt: event.created_at,
64
+ status: "roaming",
65
+ });
66
+ const parentId = getTag(event.tags, "evolved_from");
67
+ if (parentId && /^[0-9a-f]{64}$/i.test(parentId)) {
68
+ await saveDrifterLineage(parentId, event.id, "recovered lineage", event.created_at);
69
+ }
70
+ }
71
+ // Set it as active
72
+ await setActiveDrifter(event.id);
73
+ return {
74
+ content: [{
75
+ type: "text",
76
+ text: `✨ Soul retrieved.\n\nSuccessfully found and synchronized the signal for drifter 「${sanitizeName(getTag(event.tags, "name"), "Unknown")}」.\n` +
77
+ `It is currently at ${picked.relay}, waiting for your next instruction.`
78
+ }],
79
+ };
80
+ });
81
+ }
82
+ //# sourceMappingURL=recover_drifter.js.map