spora 0.7.1 → 0.7.3

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 (45) hide show
  1. package/dist/autonomy-6UWPXTPD.js +19 -0
  2. package/dist/{chunk-LXQNVVIY.js → chunk-4LNMA56H.js} +2 -2
  3. package/dist/{chunk-OACD3HGE.js → chunk-7OOGNZBU.js} +3 -3
  4. package/dist/{chunk-E5NR6HT4.js → chunk-HGNMHGAF.js} +3 -3
  5. package/dist/{chunk-AOQ3WLZV.js → chunk-Q3YXJ2C6.js} +137 -28
  6. package/dist/chunk-Q3YXJ2C6.js.map +1 -0
  7. package/dist/{chunk-KWWAIS3C.js → chunk-SUZUJGGW.js} +2 -2
  8. package/dist/{chunk-NPV3OV2K.js → chunk-YMGJQRKG.js} +13 -2
  9. package/dist/chunk-YMGJQRKG.js.map +1 -0
  10. package/dist/{chunk-WIK74GGJ.js → chunk-ZBP2ROAZ.js} +104 -22
  11. package/dist/chunk-ZBP2ROAZ.js.map +1 -0
  12. package/dist/cli.js +39 -35
  13. package/dist/cli.js.map +1 -1
  14. package/dist/{client-57BQKVYF.js → client-4KGOBIXE.js} +124 -40
  15. package/dist/client-4KGOBIXE.js.map +1 -0
  16. package/dist/{colony-JPZC3R34.js → colony-IVYR233C.js} +3 -3
  17. package/dist/{heartbeat-TNEPE3ZP.js → heartbeat-CUL2FTFD.js} +57 -13
  18. package/dist/heartbeat-CUL2FTFD.js.map +1 -0
  19. package/dist/{init-ISSXETHY.js → init-2REECUVH.js} +5 -5
  20. package/dist/{llm-T33QTPVW.js → llm-OGOYCWBH.js} +3 -3
  21. package/dist/mcp-server.js +20 -20
  22. package/dist/{prompt-builder-5NYONN2W.js → prompt-builder-KJKFCGM7.js} +6 -4
  23. package/dist/{queue-G5PTE6R6.js → queue-D3MRKABU.js} +3 -3
  24. package/dist/{web-chat-3HM35XM4.js → web-chat-O24HGJVE.js} +45 -6
  25. package/dist/web-chat-O24HGJVE.js.map +1 -0
  26. package/dist/{x-client-GY6XSPK6.js → x-client-ASXVQ6EV.js} +3 -3
  27. package/package.json +1 -1
  28. package/dist/autonomy-DAV7X6QS.js +0 -19
  29. package/dist/chunk-AOQ3WLZV.js.map +0 -1
  30. package/dist/chunk-NPV3OV2K.js.map +0 -1
  31. package/dist/chunk-WIK74GGJ.js.map +0 -1
  32. package/dist/client-57BQKVYF.js.map +0 -1
  33. package/dist/heartbeat-TNEPE3ZP.js.map +0 -1
  34. package/dist/web-chat-3HM35XM4.js.map +0 -1
  35. /package/dist/{autonomy-DAV7X6QS.js.map → autonomy-6UWPXTPD.js.map} +0 -0
  36. /package/dist/{chunk-LXQNVVIY.js.map → chunk-4LNMA56H.js.map} +0 -0
  37. /package/dist/{chunk-OACD3HGE.js.map → chunk-7OOGNZBU.js.map} +0 -0
  38. /package/dist/{chunk-E5NR6HT4.js.map → chunk-HGNMHGAF.js.map} +0 -0
  39. /package/dist/{chunk-KWWAIS3C.js.map → chunk-SUZUJGGW.js.map} +0 -0
  40. /package/dist/{colony-JPZC3R34.js.map → colony-IVYR233C.js.map} +0 -0
  41. /package/dist/{init-ISSXETHY.js.map → init-2REECUVH.js.map} +0 -0
  42. /package/dist/{llm-T33QTPVW.js.map → llm-OGOYCWBH.js.map} +0 -0
  43. /package/dist/{prompt-builder-5NYONN2W.js.map → prompt-builder-KJKFCGM7.js.map} +0 -0
  44. /package/dist/{queue-G5PTE6R6.js.map → queue-D3MRKABU.js.map} +0 -0
  45. /package/dist/{x-client-GY6XSPK6.js.map → x-client-ASXVQ6EV.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/x-client/api/client.ts"],"sourcesContent":["import { TwitterApi } from \"twitter-api-v2\";\nimport { loadCredentials } from \"../../utils/crypto.js\";\nimport { logger } from \"../../utils/logger.js\";\nimport { loadIdentity, identityExists } from \"../../identity/index.js\";\nimport { logInteraction } from \"../../memory/index.js\";\nimport { rateLimiter } from \"../rate-limiter.js\";\nimport type {\n XClientInterface,\n Tweet,\n UserProfile,\n PostResult,\n TimelineOptions,\n SearchOptions,\n} from \"../types.js\";\n\ninterface TwitterResponseMeta {\n next_token?: string;\n}\n\ninterface TwitterUser {\n id: string;\n username: string;\n name?: string;\n description?: string;\n verified?: boolean;\n profile_image_url?: string;\n public_metrics?: {\n followers_count?: number;\n following_count?: number;\n tweet_count?: number;\n };\n}\n\ninterface TwitterTweet {\n id: string;\n text: string;\n author_id?: string;\n created_at?: string;\n in_reply_to_user_id?: string;\n public_metrics?: {\n like_count?: number;\n retweet_count?: number;\n reply_count?: number;\n };\n}\n\ninterface TwitterTimelineResponse {\n data?: TwitterTweet[];\n includes?: {\n users?: TwitterUser[];\n };\n meta?: TwitterResponseMeta;\n}\n\n/**\n * Extract useful error details from twitter-api-v2 errors.\n * Their ApiResponseError has .code, .data, .errors properties that JSON.stringify misses.\n */\nfunction extractTwitterError(error: unknown): string {\n if (!error || typeof error !== \"object\") return String(error);\n const e = error as Record<string, unknown>;\n\n // twitter-api-v2 ApiResponseError shape\n if (e.code && e.data) {\n const status = e.code;\n const data = e.data as Record<string, unknown>;\n const errors = (data.errors ?? data.detail ?? data.title ?? data.reason) as unknown;\n return `HTTP ${status}: ${JSON.stringify(errors ?? data)}`;\n }\n\n // Fallback for standard errors\n if (e.message) return e.message as string;\n return String(error);\n}\n\nexport class XApiClient implements XClientInterface {\n private readonly readWrite: TwitterApi;\n private readonly readOnly: TwitterApi;\n private meId: string | null = null;\n private creds: ReturnType<typeof loadCredentials>;\n\n constructor() {\n this.creds = loadCredentials();\n if (this.creds.method !== \"api\") {\n throw new Error(\"Only API credentials are supported.\");\n }\n\n if (!this.creds.apiKey || !this.creds.apiSecret || !this.creds.accessToken || !this.creds.accessTokenSecret) {\n throw new Error(\"Missing X OAuth credentials. Run `spora init` and provide API keys.\");\n }\n\n this.readWrite = new TwitterApi({\n appKey: this.creds.apiKey,\n appSecret: this.creds.apiSecret,\n accessToken: this.creds.accessToken,\n accessSecret: this.creds.accessTokenSecret,\n });\n\n // Use user-context auth for all reads in runtime.\n // Some endpoints we rely on (timeline/mentions) reject OAuth2 app-only auth.\n this.readOnly = this.readWrite;\n }\n\n private toTweet(tweet: TwitterTweet, userMap: Map<string, string>): Tweet {\n const authorId = tweet.author_id ?? \"unknown\";\n return {\n id: tweet.id,\n text: tweet.text,\n authorId,\n authorHandle: userMap.get(authorId) ?? \"unknown\",\n createdAt: tweet.created_at ?? new Date().toISOString(),\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n inReplyToId: tweet.in_reply_to_user_id,\n };\n }\n\n /**\n * 3-strategy approach to resolve the authenticated user's ID.\n * 1. Extract from OAuth access token (format: {userId}-{rest})\n * 2. Use /2/users/me endpoint\n * 3. Fall back to userByUsername lookup\n */\n private async getUserId(): Promise<string> {\n if (this.meId) return this.meId;\n\n // Strategy 1: Extract user ID from OAuth access token (format: {userId}-{rest})\n const accessToken = this.creds.accessToken;\n if (accessToken) {\n const dashIdx = accessToken.indexOf(\"-\");\n if (dashIdx > 0) {\n const candidate = accessToken.substring(0, dashIdx);\n if (/^\\d+$/.test(candidate)) {\n logger.info(`User ID extracted from access token: ${candidate}`);\n this.meId = candidate;\n return this.meId;\n }\n }\n }\n\n // Strategy 2: Use /2/users/me endpoint\n try {\n const me = (await this.readWrite.v2.me({\n \"user.fields\": [\"id\"],\n })) as { data?: { id: string; username?: string } };\n\n if (me.data?.id) {\n logger.info(`User ID from /me endpoint: ${me.data.id}${me.data.username ? ` (@${me.data.username})` : \"\"}`);\n this.meId = me.data.id;\n return this.meId;\n }\n } catch (err) {\n logger.warn(`/me endpoint failed: ${extractTwitterError(err)}`);\n }\n\n // Strategy 3: Fall back to username lookup\n const handle = this.getHandle();\n logger.info(`Trying userByUsername lookup for: ${handle}`);\n const result = await this.readOnly.v2.userByUsername(handle);\n if (!(result as { data?: { id: string } }).data) {\n const errInfo = (result as Record<string, unknown>).errors;\n throw new Error(\n `Could not find user @${handle}. API returned: ${JSON.stringify(errInfo ?? \"no data\")}`\n );\n }\n this.meId = (result as { data: { id: string } }).data.id;\n return this.meId;\n }\n\n private getHandle(): string {\n let handle: string | undefined;\n if (identityExists()) {\n handle = loadIdentity().handle;\n } else {\n handle = this.creds.username;\n }\n if (!handle) throw new Error(\"No handle found. Create a Spore identity first.\");\n // Strip @ prefix — twitter-api-v2 userByUsername expects bare handle\n return handle.replace(/^@/, \"\");\n }\n\n private normalizeHandleOrId(input: string): { isId: boolean; value: string } {\n const cleaned = input.trim().replace(/^@/, \"\");\n const isId = /^\\d+$/.test(cleaned);\n return { isId, value: cleaned };\n }\n\n private async resolveUserId(handleOrId: string): Promise<string> {\n const parsed = this.normalizeHandleOrId(handleOrId);\n if (parsed.isId) return parsed.value;\n\n const user = (await this.readOnly.v2.userByUsername(parsed.value, {\n \"user.fields\": [\"id\", \"username\"],\n })) as { data?: { id: string } };\n\n if (!user.data?.id) {\n throw new Error(`Could not resolve @${parsed.value} to a user ID`);\n }\n\n return user.data.id;\n }\n\n private buildUserMap(users: TwitterUser[] | undefined): Map<string, string> {\n const map = new Map<string, string>();\n for (const user of users ?? []) {\n map.set(user.id, user.username);\n }\n return map;\n }\n\n async postTweet(content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const response = (await this.readWrite.v2.tweet(content)) as { data?: { id?: string } };\n const tweetId = response.data?.id;\n\n if (!tweetId) {\n return { success: false, error: \"X API did not return a tweet ID.\" };\n }\n\n rateLimiter.consume(1);\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n const detail = extractTwitterError(error);\n logger.error(`Failed to post tweet: ${detail}`);\n return { success: false, error: detail };\n }\n }\n\n async replyToTweet(tweetId: string, content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const response = (await this.readWrite.v2.reply(content, tweetId)) as {\n data?: { id?: string };\n };\n const createdId = response.data?.id;\n\n if (!createdId) {\n return { success: false, error: \"X API did not return a reply ID.\" };\n }\n\n rateLimiter.consume(1);\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n tweetId: createdId,\n inReplyTo: tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: createdId };\n } catch (error) {\n const detail = extractTwitterError(error);\n logger.error(`Failed to reply: ${detail}`);\n return { success: false, error: detail };\n }\n }\n\n async deleteTweet(tweetId: string): Promise<PostResult> {\n try {\n await this.readWrite.v2.deleteTweet(tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async likeTweet(tweetId: string): Promise<PostResult> {\n try {\n rateLimiter.requireBasicTier(\"Like\");\n const meId = await this.getUserId();\n await this.readWrite.v2.like(meId, tweetId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"like\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unlikeTweet(tweetId: string): Promise<PostResult> {\n try {\n rateLimiter.requireBasicTier(\"Unlike\");\n const meId = await this.getUserId();\n await this.readWrite.v2.unlike(meId, tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async retweet(tweetId: string): Promise<PostResult> {\n try {\n rateLimiter.requireBasicTier(\"Retweet\");\n const meId = await this.getUserId();\n await this.readWrite.v2.retweet(meId, tweetId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"retweet\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unretweet(tweetId: string): Promise<PostResult> {\n try {\n rateLimiter.requireBasicTier(\"Unretweet\");\n const meId = await this.getUserId();\n await this.readWrite.v2.unretweet(meId, tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async followUser(handleOrId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Follow\");\n try {\n const meId = await this.getUserId();\n const targetId = await this.resolveUserId(handleOrId);\n await this.readWrite.v2.follow(meId, targetId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"follow\",\n targetUserId: targetId,\n targetHandle: handleOrId.replace(/^@/, \"\"),\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unfollowUser(handleOrId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unfollow\");\n try {\n const meId = await this.getUserId();\n const targetId = await this.resolveUserId(handleOrId);\n await this.readWrite.v2.unfollow(meId, targetId);\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async getTimeline(options?: TimelineOptions): Promise<Tweet[]> {\n try {\n rateLimiter.requireBasicTier(\"Read timeline\");\n const meId = await this.getUserId();\n\n const response = (await this.readOnly.v2.get(\n `users/${meId}/timelines/reverse_chronological`,\n {\n max_results: options?.count ?? 20,\n since_id: options?.sinceId,\n expansions: [\"author_id\"],\n \"tweet.fields\": [\n \"created_at\",\n \"public_metrics\",\n \"in_reply_to_user_id\",\n \"author_id\",\n ],\n \"user.fields\": [\"username\"],\n }\n )) as TwitterTimelineResponse;\n\n if (!response.data?.length) return [];\n\n const users = this.buildUserMap(response.includes?.users);\n return response.data.map((tweet) => this.toTweet(tweet, users));\n } catch (error) {\n const detail = extractTwitterError(error);\n logger.error(`Failed to read timeline: ${detail}`);\n return [];\n }\n }\n\n async getMentions(options?: TimelineOptions): Promise<Tweet[]> {\n try {\n rateLimiter.requireBasicTier(\"Read mentions\");\n const meId = await this.getUserId();\n const response = (await this.readOnly.v2.get(`users/${meId}/mentions`, {\n max_results: options?.count ?? 20,\n since_id: options?.sinceId,\n expansions: [\"author_id\"],\n \"tweet.fields\": [\"created_at\", \"public_metrics\", \"author_id\"],\n \"user.fields\": [\"username\"],\n })) as TwitterTimelineResponse;\n\n if (!response.data?.length) return [];\n\n const users = this.buildUserMap(response.includes?.users);\n return response.data.map((tweet) => this.toTweet(tweet, users));\n } catch (error) {\n const detail = extractTwitterError(error);\n logger.error(`Failed to read mentions: ${detail}`);\n return [];\n }\n }\n\n async getUserTweets(userId: string, options?: TimelineOptions): Promise<Tweet[]> {\n try {\n rateLimiter.requireBasicTier(\"Read user tweets\");\n\n const response = (await this.readOnly.v2.get(`users/${userId}/tweets`, {\n max_results: options?.count ?? 10,\n since_id: options?.sinceId,\n expansions: [\"author_id\"],\n \"tweet.fields\": [\"created_at\", \"public_metrics\", \"author_id\"],\n \"user.fields\": [\"username\"],\n })) as TwitterTimelineResponse;\n\n if (!response.data?.length) return [];\n\n const users = this.buildUserMap(response.includes?.users);\n return response.data.map((tweet) => this.toTweet(tweet, users));\n } catch (error) {\n const detail = extractTwitterError(error);\n logger.error(`Failed to read user tweets for ${userId}: ${detail}`);\n return [];\n }\n }\n\n async searchTweets(query: string, options?: SearchOptions): Promise<Tweet[]> {\n try {\n rateLimiter.requireBasicTier(\"Search tweets\");\n const paginator = await this.readOnly.v2.search(query, {\n max_results: Math.min(options?.count ?? 20, 100),\n expansions: [\"author_id\"],\n \"tweet.fields\": [\"created_at\", \"public_metrics\", \"author_id\"],\n \"user.fields\": [\"username\"],\n });\n\n const tweets: TwitterTweet[] = [];\n const users = new Map<string, string>();\n\n for await (const tweet of paginator) {\n tweets.push(tweet as unknown as TwitterTweet);\n if (tweets.length >= (options?.count ?? 20)) break;\n }\n\n const includeUsers = (paginator as unknown as { includes?: { users?: TwitterUser[] } }).includes?.users;\n for (const user of includeUsers ?? []) {\n users.set(user.id, user.username);\n }\n\n return tweets.map((tweet) => this.toTweet(tweet, users));\n } catch (error) {\n const detail = extractTwitterError(error);\n logger.error(`Failed to search tweets: ${detail}`);\n return [];\n }\n }\n\n async getProfile(handle: string): Promise<UserProfile> {\n rateLimiter.requireBasicTier(\"Get profile\");\n\n const cleanedHandle = handle.trim().replace(/^@/, \"\");\n const response = (await this.readOnly.v2.userByUsername(cleanedHandle, {\n \"user.fields\": [\n \"description\",\n \"public_metrics\",\n \"verified\",\n \"profile_image_url\",\n \"username\",\n \"name\",\n ],\n })) as { data?: TwitterUser };\n\n if (!response.data) {\n throw new Error(`Profile not found for @${cleanedHandle}`);\n }\n\n return {\n id: response.data.id,\n handle: response.data.username,\n name: response.data.name ?? response.data.username,\n bio: response.data.description ?? \"\",\n followersCount: response.data.public_metrics?.followers_count ?? 0,\n followingCount: response.data.public_metrics?.following_count ?? 0,\n tweetCount: response.data.public_metrics?.tweet_count ?? 0,\n verified: response.data.verified ?? false,\n profileImageUrl: response.data.profile_image_url,\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -10,13 +10,13 @@ import {
10
10
  } from "./chunk-T7L2L7ZL.js";
11
11
  import {
12
12
  getXClient
13
- } from "./chunk-E5NR6HT4.js";
13
+ } from "./chunk-HGNMHGAF.js";
14
14
  import {
15
15
  loadIdentity
16
16
  } from "./chunk-M6YOQVSI.js";
17
17
  import {
18
18
  logger
19
- } from "./chunk-NPV3OV2K.js";
19
+ } from "./chunk-YMGJQRKG.js";
20
20
  import "./chunk-NO3NQN67.js";
21
21
  import "./chunk-3RYCUGXE.js";
22
22
 
@@ -226,4 +226,4 @@ export {
226
226
  postStatus,
227
227
  proposePlan
228
228
  };
229
- //# sourceMappingURL=colony-JPZC3R34.js.map
229
+ //# sourceMappingURL=colony-IVYR233C.js.map
@@ -1,23 +1,34 @@
1
1
  import {
2
2
  runAutonomyCycle
3
- } from "./chunk-WIK74GGJ.js";
4
- import "./chunk-E5NR6HT4.js";
3
+ } from "./chunk-ZBP2ROAZ.js";
4
+ import "./chunk-HGNMHGAF.js";
5
5
  import {
6
6
  flushQueue
7
- } from "./chunk-OACD3HGE.js";
8
- import "./chunk-AOQ3WLZV.js";
9
- import "./chunk-P6KZIJYL.js";
7
+ } from "./chunk-7OOGNZBU.js";
8
+ import {
9
+ buildReflectionPrompt
10
+ } from "./chunk-Q3YXJ2C6.js";
11
+ import {
12
+ loadStrategy,
13
+ saveStrategy
14
+ } from "./chunk-P6KZIJYL.js";
10
15
  import "./chunk-WN35MRMF.js";
11
- import "./chunk-LXQNVVIY.js";
12
- import "./chunk-M6YOQVSI.js";
13
- import "./chunk-KWWAIS3C.js";
16
+ import "./chunk-4LNMA56H.js";
17
+ import {
18
+ loadIdentity
19
+ } from "./chunk-M6YOQVSI.js";
20
+ import {
21
+ generateResponse
22
+ } from "./chunk-SUZUJGGW.js";
14
23
  import {
15
24
  logger
16
- } from "./chunk-NPV3OV2K.js";
25
+ } from "./chunk-YMGJQRKG.js";
17
26
  import {
18
27
  loadConfig
19
28
  } from "./chunk-NO3NQN67.js";
20
- import "./chunk-JBYZ7K56.js";
29
+ import {
30
+ addLearning
31
+ } from "./chunk-JBYZ7K56.js";
21
32
  import {
22
33
  ensureDirectories,
23
34
  paths
@@ -137,7 +148,7 @@ Spora agent is running (PID ${process.pid})`);
137
148
  heartbeatCount++;
138
149
  logger.info(`=== Heartbeat #${heartbeatCount} ===`);
139
150
  try {
140
- await runHeartbeat(maxActions);
151
+ await runHeartbeat(maxActions, heartbeatCount);
141
152
  } catch (error) {
142
153
  logger.error("Heartbeat error", error);
143
154
  console.error(`Heartbeat #${heartbeatCount} failed: ${error.message}`);
@@ -164,7 +175,7 @@ Spora agent is running (PID ${process.pid})`);
164
175
  logger.info("Spora agent stopped.");
165
176
  console.log("\nSpora agent stopped.");
166
177
  }
167
- async function runHeartbeat(maxActions) {
178
+ async function runHeartbeat(maxActions, heartbeatCount) {
168
179
  logger.info("Checking queue...");
169
180
  try {
170
181
  const flushed = await flushQueue();
@@ -202,6 +213,39 @@ async function runHeartbeat(maxActions) {
202
213
  `Metrics: interactionRatio=${metrics.interactionRatio.toFixed(2)}, repeatedFormatRate=${metrics.repeatedFormatRate.toFixed(2)}`
203
214
  );
204
215
  logger.info(`Heartbeat complete. ${cycle.results.filter((r) => r.success).length}/${cycle.results.length} actions succeeded.`);
216
+ if (heartbeatCount % 3 === 0) {
217
+ try {
218
+ logger.info("Running reflection phase...");
219
+ const reflectionPrompt = buildReflectionPrompt(cycle.results);
220
+ const reflectionResponse = await generateResponse(
221
+ `You are ${loadIdentity().name}. Reflect honestly on your performance.`,
222
+ reflectionPrompt
223
+ );
224
+ const jsonMatch = reflectionResponse.content.match(/\{[\s\S]*\}/);
225
+ if (jsonMatch) {
226
+ try {
227
+ const reflection = JSON.parse(jsonMatch[0]);
228
+ if (reflection.learning && reflection.learning !== "null") {
229
+ addLearning(reflection.learning, "reflection", ["heartbeat", "performance"]);
230
+ logger.info(`Reflection learning: ${reflection.learning}`);
231
+ }
232
+ if (reflection.strategyUpdate && reflection.strategyUpdate !== "null") {
233
+ const strategy = loadStrategy();
234
+ strategy.experiments.push({
235
+ description: reflection.strategyUpdate,
236
+ status: "pending"
237
+ });
238
+ strategy.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
239
+ saveStrategy(strategy);
240
+ logger.info(`Strategy update: ${reflection.strategyUpdate}`);
241
+ }
242
+ } catch {
243
+ }
244
+ }
245
+ } catch (err) {
246
+ logger.warn(`Reflection failed: ${err.message}`);
247
+ }
248
+ }
205
249
  }
206
250
  export {
207
251
  getRunningPid,
@@ -209,4 +253,4 @@ export {
209
253
  requestStop,
210
254
  startHeartbeatLoop
211
255
  };
212
- //# sourceMappingURL=heartbeat-TNEPE3ZP.js.map
256
+ //# sourceMappingURL=heartbeat-CUL2FTFD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/heartbeat.ts","../src/runtime/telemetry.ts"],"sourcesContent":["import { existsSync, unlinkSync, writeFileSync, readFileSync } from \"node:fs\";\nimport { logger } from \"../utils/logger.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { paths } from \"../utils/paths.js\";\nimport { flushQueue } from \"../scheduler/queue.js\";\nimport { runAutonomyCycle } from \"./autonomy.js\";\nimport { writeHeartbeatMetrics } from \"./telemetry.js\";\nimport { buildReflectionPrompt } from \"./prompt-builder.js\";\nimport { generateResponse } from \"./llm.js\";\nimport { addLearning } from \"../memory/index.js\";\nimport { loadStrategy, saveStrategy } from \"../memory/strategy.js\";\nimport { loadIdentity } from \"../identity/index.js\";\n\nlet running = false;\n\nexport function isRunning(): boolean {\n return running;\n}\n\nexport function requestStop(): void {\n writeFileSync(paths.stopSignal, \"stop\");\n logger.info(\"Stop signal sent.\");\n}\n\nfunction shouldStop(): boolean {\n if (existsSync(paths.stopSignal)) {\n unlinkSync(paths.stopSignal);\n return true;\n }\n return false;\n}\n\nfunction writePid(): void {\n writeFileSync(paths.runtimePid, String(process.pid));\n}\n\nfunction clearPid(): void {\n if (existsSync(paths.runtimePid)) {\n unlinkSync(paths.runtimePid);\n }\n}\n\nexport function getRunningPid(): number | null {\n if (!existsSync(paths.runtimePid)) return null;\n const pid = parseInt(readFileSync(paths.runtimePid, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return null;\n\n // Check if process is actually running\n try {\n process.kill(pid, 0);\n return pid;\n } catch {\n // Process not running, clean up stale PID\n clearPid();\n return null;\n }\n}\n\nexport async function startHeartbeatLoop(): Promise<void> {\n // Check if already running\n const existingPid = getRunningPid();\n if (existingPid) {\n throw new Error(`Spora is already running (PID ${existingPid}). Run \\`spora stop\\` first.`);\n }\n\n running = true;\n writePid();\n\n const config = loadConfig();\n const intervalMs = config.runtime?.heartbeatIntervalMs ?? 300_000;\n const maxActions = config.runtime?.actionsPerHeartbeat ?? 3;\n\n logger.info(`Spora agent starting. Heartbeat interval: ${intervalMs / 1000}s, max actions: ${maxActions}`);\n console.log(`\\nSpora agent is running (PID ${process.pid})`);\n console.log(`Heartbeat every ${Math.round(intervalMs / 60_000)} minutes`);\n console.log(`Press Ctrl+C or run \\`spora stop\\` to stop.\\n`);\n\n // Handle graceful shutdown\n const shutdown = () => {\n logger.info(\"Shutting down...\");\n running = false;\n clearPid();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // Clean any stale stop signal\n if (existsSync(paths.stopSignal)) {\n unlinkSync(paths.stopSignal);\n }\n\n let heartbeatCount = 0;\n\n while (running) {\n heartbeatCount++;\n logger.info(`=== Heartbeat #${heartbeatCount} ===`);\n\n try {\n await runHeartbeat(maxActions, heartbeatCount);\n } catch (error) {\n logger.error(\"Heartbeat error\", error);\n console.error(`Heartbeat #${heartbeatCount} failed: ${(error as Error).message}`);\n }\n\n // Check for stop signal\n if (shouldStop()) {\n logger.info(\"Stop signal received.\");\n break;\n }\n\n // Sleep with jitter\n const jitter = Math.floor(Math.random() * intervalMs * 0.3);\n const sleepMs = intervalMs + jitter;\n logger.info(`Sleeping ${Math.round(sleepMs / 1000)}s until next heartbeat...`);\n\n // Sleep in chunks so we can check for stop signals\n const chunkMs = 10_000;\n let slept = 0;\n while (slept < sleepMs && running) {\n await new Promise((r) => setTimeout(r, Math.min(chunkMs, sleepMs - slept)));\n slept += chunkMs;\n if (shouldStop()) {\n running = false;\n break;\n }\n }\n }\n\n clearPid();\n logger.info(\"Spora agent stopped.\");\n console.log(\"\\nSpora agent stopped.\");\n}\n\nasync function runHeartbeat(maxActions: number, heartbeatCount: number): Promise<void> {\n // 1. Flush any queued posts\n logger.info(\"Checking queue...\");\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n logger.info(`Flushed ${flushed.posted} queued posts.`);\n }\n } catch (error) {\n logger.warn(`Queue flush failed: ${(error as Error).message}`);\n }\n\n // 2. Run planner/executor cycle.\n const cycle = await runAutonomyCycle(maxActions);\n logger.info(`Observed ${cycle.timeline.length} timeline posts and ${cycle.mentions.length} mentions.`);\n\n if (cycle.actions.length === 0) {\n logger.info(\"Planner returned no approved actions.\");\n return;\n }\n\n logger.info(`Executed ${cycle.results.length} action(s).`);\n\n // 3. Log results\n for (const result of cycle.results) {\n if (result.success) {\n logger.info(` [OK] ${result.action}${result.detail ? `: ${result.detail}` : \"\"}`);\n } else {\n logger.warn(` [FAIL] ${result.action}: ${result.error}`);\n }\n }\n\n for (const note of cycle.policyFeedback) {\n logger.info(` [POLICY] ${note}`);\n }\n\n const metrics = writeHeartbeatMetrics({\n timelineCount: cycle.timeline.length,\n mentionsCount: cycle.mentions.length,\n actions: cycle.actions,\n results: cycle.results,\n policyFeedbackCount: cycle.policyFeedback.length,\n });\n logger.info(\n `Metrics: interactionRatio=${metrics.interactionRatio.toFixed(2)}, repeatedFormatRate=${metrics.repeatedFormatRate.toFixed(2)}`\n );\n\n logger.info(`Heartbeat complete. ${cycle.results.filter((r) => r.success).length}/${cycle.results.length} actions succeeded.`);\n\n // Reflection phase — every 3rd heartbeat\n if (heartbeatCount % 3 === 0) {\n try {\n logger.info(\"Running reflection phase...\");\n const reflectionPrompt = buildReflectionPrompt(cycle.results);\n const reflectionResponse = await generateResponse(\n `You are ${loadIdentity().name}. Reflect honestly on your performance.`,\n reflectionPrompt,\n );\n\n const jsonMatch = reflectionResponse.content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n try {\n const reflection = JSON.parse(jsonMatch[0]);\n if (reflection.learning && reflection.learning !== \"null\") {\n addLearning(reflection.learning, \"reflection\", [\"heartbeat\", \"performance\"]);\n logger.info(`Reflection learning: ${reflection.learning}`);\n }\n if (reflection.strategyUpdate && reflection.strategyUpdate !== \"null\") {\n const strategy = loadStrategy();\n strategy.experiments.push({\n description: reflection.strategyUpdate,\n status: \"pending\",\n });\n strategy.lastUpdated = new Date().toISOString();\n saveStrategy(strategy);\n logger.info(`Strategy update: ${reflection.strategyUpdate}`);\n }\n } catch {\n // Couldn't parse reflection JSON\n }\n }\n } catch (err) {\n logger.warn(`Reflection failed: ${(err as Error).message}`);\n }\n }\n}\n","import { appendFileSync } from \"node:fs\";\nimport { paths, ensureDirectories } from \"../utils/paths.js\";\nimport type { AgentAction, ActionResult } from \"./decision-engine.js\";\n\nexport interface HeartbeatMetrics {\n timestamp: string;\n timelineCount: number;\n mentionsCount: number;\n actionCount: number;\n successCount: number;\n postCount: number;\n interactionCount: number;\n interactionRatio: number;\n repeatedFormatRate: number;\n policyRejectionCount: number;\n}\n\nfunction normalize(text: string): string {\n return text.toLowerCase().replace(/[^a-z0-9\\s]/g, \" \").replace(/\\s+/g, \" \").trim();\n}\n\nfunction prefix(text: string, n = 6): string {\n return normalize(text).split(\" \").filter(Boolean).slice(0, n).join(\" \");\n}\n\nfunction repeatedFormatRate(actions: AgentAction[]): number {\n const posts = actions\n .filter((action) => action.action === \"post\" && action.content)\n .map((action) => action.content ?? \"\");\n\n if (posts.length <= 1) return 0;\n\n const seen = new Map<string, number>();\n for (const content of posts) {\n const key = prefix(content);\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n\n let repeated = 0;\n for (const count of seen.values()) {\n if (count > 1) repeated += count;\n }\n\n return repeated / posts.length;\n}\n\nexport function writeHeartbeatMetrics(input: {\n timelineCount: number;\n mentionsCount: number;\n actions: AgentAction[];\n results: ActionResult[];\n policyFeedbackCount: number;\n}): HeartbeatMetrics {\n const interactionCount = input.actions.filter((a) => [\"reply\", \"like\", \"retweet\", \"follow\"].includes(a.action)).length;\n const postCount = input.actions.filter((a) => a.action === \"post\").length;\n const successCount = input.results.filter((r) => r.success).length;\n\n const metrics: HeartbeatMetrics = {\n timestamp: new Date().toISOString(),\n timelineCount: input.timelineCount,\n mentionsCount: input.mentionsCount,\n actionCount: input.actions.length,\n successCount,\n postCount,\n interactionCount,\n interactionRatio: input.actions.length > 0 ? interactionCount / input.actions.length : 0,\n repeatedFormatRate: repeatedFormatRate(input.actions),\n policyRejectionCount: input.policyFeedbackCount,\n };\n\n ensureDirectories();\n appendFileSync(paths.runtimeMetrics, JSON.stringify(metrics) + \"\\n\");\n return metrics;\n}\n"],"mappings":"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","names":[]}
@@ -8,8 +8,8 @@ import {
8
8
  import {
9
9
  getDefaultModel,
10
10
  setLLMApiKey
11
- } from "./chunk-KWWAIS3C.js";
12
- import "./chunk-NPV3OV2K.js";
11
+ } from "./chunk-SUZUJGGW.js";
12
+ import "./chunk-YMGJQRKG.js";
13
13
  import {
14
14
  createDefaultConfig,
15
15
  saveConfig
@@ -196,7 +196,7 @@ async function loginFlow() {
196
196
  console.log(chalk.green("\u2713 Logged in!\n"));
197
197
  console.log(chalk.gray("Opening chat interface...\n"));
198
198
  try {
199
- const { startWebChat } = await import("./web-chat-3HM35XM4.js");
199
+ const { startWebChat } = await import("./web-chat-O24HGJVE.js");
200
200
  await startWebChat();
201
201
  } catch (error) {
202
202
  console.log(chalk.yellow(`Could not start chat interface: ${error.message}
@@ -288,7 +288,7 @@ async function showDoneAndOpenChat() {
288
288
  console.log(chalk.bold.cyan("\u2501\u2501\u2501 Your Spore is Ready! \u2501\u2501\u2501\n"));
289
289
  console.log(chalk.gray("Opening chat interface...\n"));
290
290
  try {
291
- const { startWebChat } = await import("./web-chat-3HM35XM4.js");
291
+ const { startWebChat } = await import("./web-chat-O24HGJVE.js");
292
292
  await startWebChat();
293
293
  } catch (error) {
294
294
  console.log(chalk.yellow(`Could not start chat interface: ${error.message}
@@ -413,4 +413,4 @@ async function runInit(token) {
413
413
  export {
414
414
  runInit
415
415
  };
416
- //# sourceMappingURL=init-ISSXETHY.js.map
416
+ //# sourceMappingURL=init-2REECUVH.js.map
@@ -6,8 +6,8 @@ import {
6
6
  hasLLMKey,
7
7
  listLLMKeyStatus,
8
8
  setLLMApiKey
9
- } from "./chunk-KWWAIS3C.js";
10
- import "./chunk-NPV3OV2K.js";
9
+ } from "./chunk-SUZUJGGW.js";
10
+ import "./chunk-YMGJQRKG.js";
11
11
  import "./chunk-NO3NQN67.js";
12
12
  import "./chunk-3RYCUGXE.js";
13
13
  export {
@@ -19,4 +19,4 @@ export {
19
19
  listLLMKeyStatus,
20
20
  setLLMApiKey
21
21
  };
22
- //# sourceMappingURL=llm-T33QTPVW.js.map
22
+ //# sourceMappingURL=llm-OGOYCWBH.js.map
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  logger,
12
12
  setLogLevel
13
- } from "./chunk-NPV3OV2K.js";
13
+ } from "./chunk-YMGJQRKG.js";
14
14
  import {
15
15
  loadConfig
16
16
  } from "./chunk-NO3NQN67.js";
@@ -388,7 +388,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
388
388
  },
389
389
  async ({ content }) => {
390
390
  try {
391
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
391
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
392
392
  const client = await getXClient();
393
393
  const result = await client.postTweet(content);
394
394
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -406,7 +406,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
406
406
  },
407
407
  async ({ tweetId, content }) => {
408
408
  try {
409
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
409
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
410
410
  const client = await getXClient();
411
411
  const result = await client.replyToTweet(tweetId, content);
412
412
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -421,7 +421,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
421
421
  { tweetId: z.string().describe("The ID of the tweet to like") },
422
422
  async ({ tweetId }) => {
423
423
  try {
424
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
424
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
425
425
  const client = await getXClient();
426
426
  const result = await client.likeTweet(tweetId);
427
427
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -436,7 +436,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
436
436
  { tweetId: z.string().describe("The ID of the tweet to retweet") },
437
437
  async ({ tweetId }) => {
438
438
  try {
439
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
439
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
440
440
  const client = await getXClient();
441
441
  const result = await client.retweet(tweetId);
442
442
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -451,7 +451,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
451
451
  { userId: z.string().describe("The user ID or handle to follow") },
452
452
  async ({ userId }) => {
453
453
  try {
454
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
454
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
455
455
  const client = await getXClient();
456
456
  const result = await client.followUser(userId);
457
457
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -466,7 +466,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
466
466
  { userId: z.string().describe("The user ID or handle to unfollow") },
467
467
  async ({ userId }) => {
468
468
  try {
469
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
469
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
470
470
  const client = await getXClient();
471
471
  const result = await client.unfollowUser(userId);
472
472
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -481,7 +481,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
481
481
  { count: z.number().optional().describe("Number of tweets to fetch (default 20)") },
482
482
  async ({ count }) => {
483
483
  try {
484
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
484
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
485
485
  const client = await getXClient();
486
486
  const result = await client.getTimeline({ count: count ?? 20 });
487
487
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -496,7 +496,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
496
496
  { count: z.number().optional().describe("Number of mentions to fetch (default 20)") },
497
497
  async ({ count }) => {
498
498
  try {
499
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
499
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
500
500
  const client = await getXClient();
501
501
  const result = await client.getMentions({ count: count ?? 20 });
502
502
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -514,7 +514,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
514
514
  },
515
515
  async ({ query, count }) => {
516
516
  try {
517
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
517
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
518
518
  const client = await getXClient();
519
519
  const result = await client.searchTweets(query, { count: count ?? 20 });
520
520
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -529,7 +529,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
529
529
  { handle: z.string().describe("X handle (without @)") },
530
530
  async ({ handle }) => {
531
531
  try {
532
- const { getXClient } = await import("./x-client-GY6XSPK6.js");
532
+ const { getXClient } = await import("./x-client-ASXVQ6EV.js");
533
533
  const client = await getXClient();
534
534
  const result = await client.getProfile(handle);
535
535
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -547,7 +547,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
547
547
  },
548
548
  async ({ content, scheduledFor }) => {
549
549
  try {
550
- const { addToQueue } = await import("./queue-G5PTE6R6.js");
550
+ const { addToQueue } = await import("./queue-D3MRKABU.js");
551
551
  const entry = addToQueue(content, scheduledFor);
552
552
  return {
553
553
  content: [
@@ -565,7 +565,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
565
565
  {},
566
566
  async () => {
567
567
  try {
568
- const { flushQueue } = await import("./queue-G5PTE6R6.js");
568
+ const { flushQueue } = await import("./queue-D3MRKABU.js");
569
569
  const results = await flushQueue();
570
570
  return {
571
571
  content: [
@@ -586,7 +586,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
586
586
  { message: z.string().optional().describe("Optional message to post to the Colony community") },
587
587
  async ({ message }) => {
588
588
  try {
589
- const { colonyCheckin } = await import("./colony-JPZC3R34.js");
589
+ const { colonyCheckin } = await import("./colony-IVYR233C.js");
590
590
  const result = await colonyCheckin(message);
591
591
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
592
592
  } catch (error) {
@@ -600,7 +600,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
600
600
  {},
601
601
  async () => {
602
602
  try {
603
- const { getColonyMemory } = await import("./colony-JPZC3R34.js");
603
+ const { getColonyMemory } = await import("./colony-IVYR233C.js");
604
604
  const memory = getColonyMemory();
605
605
  return { content: [{ type: "text", text: JSON.stringify(memory, null, 2) }] };
606
606
  } catch (error) {
@@ -614,7 +614,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
614
614
  {},
615
615
  async () => {
616
616
  try {
617
- const { getActivePlans } = await import("./colony-JPZC3R34.js");
617
+ const { getActivePlans } = await import("./colony-IVYR233C.js");
618
618
  const plans = getActivePlans();
619
619
  return {
620
620
  content: [
@@ -637,7 +637,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
637
637
  },
638
638
  async ({ description }) => {
639
639
  try {
640
- const { proposePlan } = await import("./colony-JPZC3R34.js");
640
+ const { proposePlan } = await import("./colony-IVYR233C.js");
641
641
  const result = await proposePlan(description);
642
642
  if (result.success) {
643
643
  return {
@@ -660,7 +660,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
660
660
  },
661
661
  async ({ planId }) => {
662
662
  try {
663
- const { joinPlan } = await import("./colony-JPZC3R34.js");
663
+ const { joinPlan } = await import("./colony-IVYR233C.js");
664
664
  const result = await joinPlan(planId);
665
665
  if (result.success) {
666
666
  return { content: [{ type: "text", text: "Joined the plan! Go execute it." }] };
@@ -679,7 +679,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
679
679
  },
680
680
  async ({ status }) => {
681
681
  try {
682
- const { postStatus } = await import("./colony-JPZC3R34.js");
682
+ const { postStatus } = await import("./colony-IVYR233C.js");
683
683
  const result = await postStatus(status);
684
684
  if (result.success) {
685
685
  return { content: [{ type: "text", text: "Status posted to the Colony." }] };
@@ -696,7 +696,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
696
696
  {},
697
697
  async () => {
698
698
  try {
699
- const { getTodaysActivity } = await import("./colony-JPZC3R34.js");
699
+ const { getTodaysActivity } = await import("./colony-IVYR233C.js");
700
700
  const activity = getTodaysActivity();
701
701
  return {
702
702
  content: [
@@ -1,23 +1,25 @@
1
1
  import {
2
2
  buildChatPrompt,
3
3
  buildHeartbeatUserMessage,
4
+ buildReflectionPrompt,
4
5
  buildSystemPrompt,
5
6
  buildToolDecisionMessage,
6
7
  buildTrainingChatPrompt
7
- } from "./chunk-AOQ3WLZV.js";
8
+ } from "./chunk-Q3YXJ2C6.js";
8
9
  import "./chunk-P6KZIJYL.js";
9
10
  import "./chunk-WN35MRMF.js";
10
- import "./chunk-LXQNVVIY.js";
11
+ import "./chunk-4LNMA56H.js";
11
12
  import "./chunk-M6YOQVSI.js";
12
- import "./chunk-NPV3OV2K.js";
13
+ import "./chunk-YMGJQRKG.js";
13
14
  import "./chunk-NO3NQN67.js";
14
15
  import "./chunk-JBYZ7K56.js";
15
16
  import "./chunk-3RYCUGXE.js";
16
17
  export {
17
18
  buildChatPrompt,
18
19
  buildHeartbeatUserMessage,
20
+ buildReflectionPrompt,
19
21
  buildSystemPrompt,
20
22
  buildToolDecisionMessage,
21
23
  buildTrainingChatPrompt
22
24
  };
23
- //# sourceMappingURL=prompt-builder-5NYONN2W.js.map
25
+ //# sourceMappingURL=prompt-builder-KJKFCGM7.js.map
@@ -2,8 +2,8 @@ import {
2
2
  addToQueue,
3
3
  flushQueue,
4
4
  showQueue
5
- } from "./chunk-OACD3HGE.js";
6
- import "./chunk-NPV3OV2K.js";
5
+ } from "./chunk-7OOGNZBU.js";
6
+ import "./chunk-YMGJQRKG.js";
7
7
  import "./chunk-NO3NQN67.js";
8
8
  import "./chunk-3RYCUGXE.js";
9
9
  export {
@@ -11,4 +11,4 @@ export {
11
11
  flushQueue,
12
12
  showQueue
13
13
  };
14
- //# sourceMappingURL=queue-G5PTE6R6.js.map
14
+ //# sourceMappingURL=queue-D3MRKABU.js.map
@@ -303,8 +303,12 @@ async function logChatInteraction(userMessage, agentResponse) {
303
303
  }
304
304
  async function runNarratedHeartbeat(server) {
305
305
  const { loadConfig } = await import("./config-FL4VJVKZ.js");
306
- const { flushQueue } = await import("./queue-G5PTE6R6.js");
307
- const { runAutonomyCycle } = await import("./autonomy-DAV7X6QS.js");
306
+ const { flushQueue } = await import("./queue-D3MRKABU.js");
307
+ const { runAutonomyCycle } = await import("./autonomy-6UWPXTPD.js");
308
+ const { buildReflectionPrompt } = await import("./prompt-builder-KJKFCGM7.js");
309
+ const { generateResponse } = await import("./llm-OGOYCWBH.js");
310
+ const { addLearning } = await import("./memory-OIAH33G2.js");
311
+ const { loadStrategy, saveStrategy } = await import("./strategy-Z4JSFHSP.js");
308
312
  const config = loadConfig();
309
313
  const intervalMs = config.runtime?.heartbeatIntervalMs ?? 3e5;
310
314
  const maxActions = config.runtime?.actionsPerHeartbeat ?? 4;
@@ -372,6 +376,41 @@ async function runNarratedHeartbeat(server) {
372
376
  for (const feedback of cycle.policyFeedback) {
373
377
  server.pushAgentEvent("narration", `Policy adjusted plan: ${feedback}`);
374
378
  }
379
+ if (heartbeatCount % 3 === 0) {
380
+ try {
381
+ server.pushAgentEvent("narration", "Reflecting on recent performance...");
382
+ const reflectionPrompt = buildReflectionPrompt(cycle.results);
383
+ const reflectionResponse = await generateResponse(
384
+ `You are ${loadIdentity().name}. Reflect honestly on your performance.`,
385
+ reflectionPrompt
386
+ );
387
+ const jsonMatch = reflectionResponse.content.match(/\{[\s\S]*\}/);
388
+ if (jsonMatch) {
389
+ try {
390
+ const reflection = JSON.parse(jsonMatch[0]);
391
+ if (reflection.learning && reflection.learning !== "null") {
392
+ addLearning(reflection.learning, "reflection", ["heartbeat", "performance"]);
393
+ server.pushAgentEvent("narration", `Learned: ${reflection.learning}`);
394
+ console.log(chalk.dim(` [Agent] Reflection learning: ${reflection.learning}`));
395
+ }
396
+ if (reflection.strategyUpdate && reflection.strategyUpdate !== "null") {
397
+ const strategy = loadStrategy();
398
+ strategy.experiments.push({
399
+ description: reflection.strategyUpdate,
400
+ status: "pending"
401
+ });
402
+ strategy.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
403
+ saveStrategy(strategy);
404
+ server.pushAgentEvent("narration", `Strategy: ${reflection.strategyUpdate}`);
405
+ console.log(chalk.dim(` [Agent] Strategy update: ${reflection.strategyUpdate}`));
406
+ }
407
+ } catch {
408
+ }
409
+ }
410
+ } catch (err) {
411
+ console.log(chalk.dim(` [Agent] Reflection failed: ${err.message}`));
412
+ }
413
+ }
375
414
  } catch (error) {
376
415
  const msg = error.message;
377
416
  console.error(chalk.red(` [Agent] Heartbeat #${heartbeatCount} error: ${msg}`));
@@ -394,11 +433,11 @@ async function startWebChat() {
394
433
  server.setMessageHandler(async (message) => {
395
434
  try {
396
435
  if (!systemPrompt || messageCount % 10 === 0) {
397
- const { buildTrainingChatPrompt } = await import("./prompt-builder-5NYONN2W.js");
436
+ const { buildTrainingChatPrompt } = await import("./prompt-builder-KJKFCGM7.js");
398
437
  systemPrompt = buildTrainingChatPrompt();
399
438
  }
400
439
  messageCount++;
401
- const { hasLLMKey, chat: chatLLM } = await import("./llm-T33QTPVW.js");
440
+ const { hasLLMKey, chat: chatLLM } = await import("./llm-OGOYCWBH.js");
402
441
  if (!hasLLMKey()) {
403
442
  return "I can't respond right now - no API key configured. Run `spora llm set --provider <provider>` to set one up.";
404
443
  }
@@ -424,7 +463,7 @@ async function startWebChat() {
424
463
  `));
425
464
  openBrowser(url);
426
465
  try {
427
- const { hasLLMKey } = await import("./llm-T33QTPVW.js");
466
+ const { hasLLMKey } = await import("./llm-OGOYCWBH.js");
428
467
  if (hasLLMKey()) {
429
468
  runNarratedHeartbeat(server).catch((err) => {
430
469
  console.error(chalk.red(`Heartbeat failed to start: ${err}`));
@@ -477,4 +516,4 @@ export {
477
516
  openBrowser,
478
517
  startWebChat
479
518
  };
480
- //# sourceMappingURL=web-chat-3HM35XM4.js.map
519
+ //# sourceMappingURL=web-chat-O24HGJVE.js.map