@ai-devkit/agent-manager 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/AgentManager.d.ts +12 -0
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +45 -10
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +111 -0
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/fixtures.test.js +218 -0
  8. package/dist/__tests__/fixtures.test.js.map +1 -0
  9. package/dist/__tests__/harnesses/devin/DevinAdapter.test.js.map +1 -1
  10. package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js.map +1 -1
  11. package/dist/__tests__/harnesses/muse/MuseAdapter.test.js +247 -0
  12. package/dist/__tests__/harnesses/muse/MuseAdapter.test.js.map +1 -0
  13. package/dist/__tests__/harnesses/muse/MuseAgentMapper.test.js +81 -0
  14. package/dist/__tests__/harnesses/muse/MuseAgentMapper.test.js.map +1 -0
  15. package/dist/__tests__/harnesses/muse/MuseSessionLocator.test.js +185 -0
  16. package/dist/__tests__/harnesses/muse/MuseSessionLocator.test.js.map +1 -0
  17. package/dist/__tests__/harnesses/muse/MuseSessionParser.test.js +328 -0
  18. package/dist/__tests__/harnesses/muse/MuseSessionParser.test.js.map +1 -0
  19. package/dist/__tests__/harnesses/opencode/OpenCodeSessionParser.test.js.map +1 -1
  20. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js +42 -1
  21. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -1
  22. package/dist/__tests__/harnesses/runtimeProfiles.test.js +12 -0
  23. package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -1
  24. package/dist/__tests__/readiness-fixtures.test.js +54 -0
  25. package/dist/__tests__/readiness-fixtures.test.js.map +1 -0
  26. package/dist/__tests__/utils/process.test.js +71 -1
  27. package/dist/__tests__/utils/process.test.js.map +1 -1
  28. package/dist/adapters/AgentAdapter.d.ts +8 -1
  29. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  30. package/dist/adapters/AgentAdapter.js +2 -1
  31. package/dist/adapters/AgentAdapter.js.map +1 -1
  32. package/dist/fixtures/bundle.d.ts +67 -0
  33. package/dist/fixtures/bundle.d.ts.map +1 -0
  34. package/dist/fixtures/bundle.js +86 -0
  35. package/dist/fixtures/bundle.js.map +1 -0
  36. package/dist/fixtures/capture.d.ts +11 -0
  37. package/dist/fixtures/capture.d.ts.map +1 -0
  38. package/dist/fixtures/capture.js +318 -0
  39. package/dist/fixtures/capture.js.map +1 -0
  40. package/dist/harnesses/devin/DevinAdapter.d.ts.map +1 -1
  41. package/dist/harnesses/devin/DevinAdapter.js.map +1 -1
  42. package/dist/harnesses/devin/DevinAgentMapper.d.ts.map +1 -1
  43. package/dist/harnesses/devin/DevinAgentMapper.js.map +1 -1
  44. package/dist/harnesses/devin/DevinSessionParser.d.ts.map +1 -1
  45. package/dist/harnesses/devin/DevinSessionParser.js.map +1 -1
  46. package/dist/harnesses/index.d.ts.map +1 -1
  47. package/dist/harnesses/index.js +3 -1
  48. package/dist/harnesses/index.js.map +1 -1
  49. package/dist/harnesses/muse/MuseAdapter.d.ts +33 -0
  50. package/dist/harnesses/muse/MuseAdapter.d.ts.map +1 -0
  51. package/dist/harnesses/muse/MuseAdapter.js +122 -0
  52. package/dist/harnesses/muse/MuseAdapter.js.map +1 -0
  53. package/dist/harnesses/muse/MuseAgentMapper.d.ts +15 -0
  54. package/dist/harnesses/muse/MuseAgentMapper.d.ts.map +1 -0
  55. package/dist/harnesses/muse/MuseAgentMapper.js +29 -0
  56. package/dist/harnesses/muse/MuseAgentMapper.js.map +1 -0
  57. package/dist/harnesses/muse/MuseSessionLocator.d.ts +61 -0
  58. package/dist/harnesses/muse/MuseSessionLocator.d.ts.map +1 -0
  59. package/dist/harnesses/muse/MuseSessionLocator.js +233 -0
  60. package/dist/harnesses/muse/MuseSessionLocator.js.map +1 -0
  61. package/dist/harnesses/muse/MuseSessionParser.d.ts +77 -0
  62. package/dist/harnesses/muse/MuseSessionParser.d.ts.map +1 -0
  63. package/dist/harnesses/muse/MuseSessionParser.js +350 -0
  64. package/dist/harnesses/muse/MuseSessionParser.js.map +1 -0
  65. package/dist/harnesses/muse/readiness.d.ts +3 -0
  66. package/dist/harnesses/muse/readiness.d.ts.map +1 -0
  67. package/dist/harnesses/muse/readiness.js +37 -0
  68. package/dist/harnesses/muse/readiness.js.map +1 -0
  69. package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -1
  70. package/dist/harnesses/readiness/AgentReadiness.js +3 -1
  71. package/dist/harnesses/readiness/AgentReadiness.js.map +1 -1
  72. package/dist/harnesses/readiness/types.d.ts +1 -1
  73. package/dist/harnesses/readiness/types.d.ts.map +1 -1
  74. package/dist/harnesses/runtimeProfiles.d.ts.map +1 -1
  75. package/dist/harnesses/runtimeProfiles.js +2 -1
  76. package/dist/harnesses/runtimeProfiles.js.map +1 -1
  77. package/dist/harnesses/shared.d.ts +1 -1
  78. package/dist/harnesses/shared.d.ts.map +1 -1
  79. package/dist/harnesses/shared.js +7 -6
  80. package/dist/harnesses/shared.js.map +1 -1
  81. package/dist/utils/process.d.ts +23 -1
  82. package/dist/utils/process.d.ts.map +1 -1
  83. package/dist/utils/process.js +44 -8
  84. package/dist/utils/process.js.map +1 -1
  85. package/package.json +1 -1
  86. package/src/AgentManager.ts +71 -9
  87. package/src/__tests__/AgentManager.test.ts +84 -0
  88. package/src/__tests__/fixtures.test.ts +245 -0
  89. package/src/__tests__/harnesses/devin/DevinAdapter.test.ts +1 -3
  90. package/src/__tests__/harnesses/devin/DevinSessionParser.test.ts +3 -9
  91. package/src/__tests__/harnesses/muse/MuseAdapter.test.ts +197 -0
  92. package/src/__tests__/harnesses/muse/MuseAgentMapper.test.ts +83 -0
  93. package/src/__tests__/harnesses/muse/MuseSessionLocator.test.ts +177 -0
  94. package/src/__tests__/harnesses/muse/MuseSessionParser.test.ts +279 -0
  95. package/src/__tests__/harnesses/opencode/OpenCodeSessionParser.test.ts +3 -2
  96. package/src/__tests__/harnesses/readiness/AgentReadiness.test.ts +36 -0
  97. package/src/__tests__/harnesses/runtimeProfiles.test.ts +13 -0
  98. package/src/__tests__/readiness-fixtures.test.ts +79 -0
  99. package/src/__tests__/utils/process.test.ts +30 -0
  100. package/src/adapters/AgentAdapter.ts +9 -0
  101. package/src/fixtures/bundle.ts +130 -0
  102. package/src/fixtures/capture.ts +364 -0
  103. package/src/harnesses/devin/DevinAdapter.ts +3 -15
  104. package/src/harnesses/devin/DevinAgentMapper.ts +1 -5
  105. package/src/harnesses/devin/DevinSessionParser.ts +3 -12
  106. package/src/harnesses/index.ts +2 -0
  107. package/src/harnesses/muse/MuseAdapter.ts +157 -0
  108. package/src/harnesses/muse/MuseAgentMapper.ts +33 -0
  109. package/src/harnesses/muse/MuseSessionLocator.ts +286 -0
  110. package/src/harnesses/muse/MuseSessionParser.ts +435 -0
  111. package/src/harnesses/muse/readiness.ts +38 -0
  112. package/src/harnesses/readiness/AgentReadiness.ts +2 -0
  113. package/src/harnesses/runtimeProfiles.ts +1 -0
  114. package/src/harnesses/shared.ts +11 -6
  115. package/src/utils/process.ts +61 -9
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/harnesses/devin/DevinSessionParser.ts"],"sourcesContent":["import type { ConversationMessage, ConversationOptions } from \"../../adapters/AgentAdapter.js\";\nimport { sliceTail } from \"../../utils/jsonlTail.js\";\nimport { SUMMARY_MAX_LENGTH, truncate } from \"../shared.js\";\nimport type Database from \"better-sqlite3\";\n\nexport interface DevinSessionStats {\n lastRole: string | null;\n /** Epoch milliseconds of the newest message node. */\n lastTimeUpdated: number;\n summary: string;\n}\n\ninterface DevinChatMessage {\n message_id?: string;\n role?: string;\n content?: unknown;\n tool_calls?: { name?: string; function?: { name?: string } }[];\n thinking?: { thinking?: string } | string;\n name?: string;\n metadata?: { is_user_input?: boolean };\n}\n\ninterface MessageNodeRow {\n node_id: number;\n chat_message: string;\n}\n\nconst EMPTY_STATS: DevinSessionStats = {\n lastRole: null,\n lastTimeUpdated: 0,\n summary: \"\",\n};\n\n/** Rows fetched per requested tail message; revisions/tools eat into the window. */\nconst TAIL_OVERFETCH = 8;\nconst TOOL_CONTENT_MAX = 500;\n\nexport class DevinSessionParser {\n getConversation(\n db: Database.Database,\n sessionId: string,\n options?: ConversationOptions,\n ): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n const tail =\n options?.tail && Number.isInteger(options.tail) && options.tail > 0\n ? options.tail\n : undefined;\n\n try {\n // LIMIT -1 = unbounded in SQLite; one query serves the full and tail paths.\n const limit = tail ? tail * TAIL_OVERFETCH : -1;\n const rows = db\n .prepare<[string, number], MessageNodeRow>(`\n SELECT node_id, chat_message\n FROM message_nodes\n WHERE session_id = ?\n ORDER BY node_id DESC\n LIMIT ?\n `)\n .all(sessionId, limit)\n .reverse();\n\n const messages: ConversationMessage[] = [];\n const latestNodeByMessageId = new Map<string, number>();\n const parsed: { nodeId: number; message: DevinChatMessage }[] = [];\n\n for (const row of rows) {\n const message = this.parseChatMessage(row.chat_message);\n if (!message) continue;\n parsed.push({ nodeId: row.node_id, message });\n if (message.message_id) {\n latestNodeByMessageId.set(\n message.message_id,\n Math.max(latestNodeByMessageId.get(message.message_id) ?? 0, row.node_id),\n );\n }\n }\n\n for (const { nodeId, message } of parsed) {\n if (\n message.message_id &&\n latestNodeByMessageId.get(message.message_id) !== nodeId\n ) {\n continue;\n }\n\n const role = message.role;\n if (role === \"system\") continue;\n\n const content = typeof message.content === \"string\" ? message.content : \"\";\n\n if (role === \"user\") {\n if (message.metadata?.is_user_input === false || !content.trim()) continue;\n messages.push({ role: \"user\", content });\n continue;\n }\n\n if (role === \"assistant\") {\n const thinking =\n typeof message.thinking === \"string\"\n ? message.thinking\n : message.thinking?.thinking;\n if (verbose && thinking) {\n messages.push({ role: \"assistant\", content: `[thinking] ${thinking}` });\n }\n if (content.trim()) messages.push({ role: \"assistant\", content });\n if (verbose) {\n for (const call of message.tool_calls ?? []) {\n const name = call.function?.name ?? call.name ?? \"tool\";\n messages.push({ role: \"assistant\", content: `[tool: ${name}]` });\n }\n }\n continue;\n }\n\n if (role === \"tool\" && verbose) {\n const label = message.name ? `[tool: ${message.name}]` : \"[tool]\";\n messages.push({\n role: \"assistant\",\n content: `${label} ${truncate(content.trim(), TOOL_CONTENT_MAX)}`.trim(),\n });\n }\n }\n\n return sliceTail(messages, tail);\n } catch {\n return [];\n }\n }\n\n getSessionStats(db: Database.Database, sessionId: string): DevinSessionStats {\n try {\n const frontier = db\n .prepare<[string], { chat_message: string }>(`\n SELECT chat_message\n FROM message_nodes\n WHERE session_id = ?\n ORDER BY node_id DESC\n LIMIT 1\n `)\n .get(sessionId);\n\n const heartbeat = db\n .prepare<[string], { maxCreated: number | null }>(`\n SELECT MAX(created_at) AS maxCreated\n FROM message_nodes\n WHERE session_id = ?\n `)\n .get(sessionId);\n\n return {\n lastRole: this.parseChatMessage(frontier?.chat_message ?? \"\")?.role ?? null,\n lastTimeUpdated: (heartbeat?.maxCreated ?? 0) * 1000,\n summary: this.getLastUserPrompt(db, sessionId),\n };\n } catch {\n return EMPTY_STATS;\n }\n }\n\n /**\n * Most recent typed prompt for the \"working on\" summary, matching the\n * last-user-message convention of the other adapters.\n */\n getLastUserPrompt(db: Database.Database, sessionId: string): string {\n return this.userPrompt(db, sessionId, \"DESC\");\n }\n\n /**\n * First typed prompt for session-list identity (`firstUserMessage`).\n */\n getFirstUserMessage(db: Database.Database, sessionId: string): string {\n return this.userPrompt(db, sessionId, \"ASC\");\n }\n\n /**\n * Typed prompt in the given direction. `prompt_history` is authoritative;\n * shell `!` commands and `/` slash commands are skipped. Falls back to a\n * bounded `is_user_input` node scan in the same direction.\n */\n private userPrompt(\n db: Database.Database,\n sessionId: string,\n direction: \"ASC\" | \"DESC\",\n ): string {\n try {\n const prompt = db\n .prepare<[string], { content: string }>(`\n SELECT content\n FROM prompt_history\n WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%'\n ORDER BY timestamp ${direction}\n LIMIT 1\n `)\n .get(sessionId);\n if (prompt?.content?.trim()) return truncate(prompt.content.trim(), SUMMARY_MAX_LENGTH);\n } catch {\n /* fall through to message_nodes */\n }\n\n try {\n const rows = db\n .prepare<[string], { chat_message: string }>(`\n SELECT chat_message\n FROM message_nodes\n WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user'\n ORDER BY node_id ${direction}\n LIMIT 8\n `)\n .all(sessionId);\n for (const row of rows) {\n const message = this.parseChatMessage(row.chat_message);\n if (\n message?.role === \"user\" &&\n message.metadata?.is_user_input !== false &&\n typeof message.content === \"string\" &&\n message.content.trim()\n ) {\n return truncate(message.content.trim(), SUMMARY_MAX_LENGTH);\n }\n }\n } catch {\n /* ignore */\n }\n return \"\";\n }\n\n private parseChatMessage(raw: string): DevinChatMessage | null {\n try {\n return JSON.parse(raw) as DevinChatMessage;\n } catch {\n return null;\n }\n }\n}\n"],"names":["sliceTail","SUMMARY_MAX_LENGTH","truncate","EMPTY_STATS","lastRole","lastTimeUpdated","summary","TAIL_OVERFETCH","TOOL_CONTENT_MAX","DevinSessionParser","getConversation","db","sessionId","options","verbose","tail","Number","isInteger","undefined","limit","rows","prepare","all","reverse","messages","latestNodeByMessageId","Map","parsed","row","message","parseChatMessage","chat_message","push","nodeId","node_id","message_id","set","Math","max","get","role","content","metadata","is_user_input","trim","thinking","call","tool_calls","name","function","label","getSessionStats","frontier","heartbeat","maxCreated","getLastUserPrompt","userPrompt","getFirstUserMessage","direction","prompt","raw","JSON","parse"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/harnesses/devin/DevinSessionParser.ts"],"sourcesContent":["import type { ConversationMessage, ConversationOptions } from \"../../adapters/AgentAdapter.js\";\nimport { sliceTail } from \"../../utils/jsonlTail.js\";\nimport { SUMMARY_MAX_LENGTH, truncate } from \"../shared.js\";\nimport type Database from \"better-sqlite3\";\n\nexport interface DevinSessionStats {\n lastRole: string | null;\n /** Epoch milliseconds of the newest message node. */\n lastTimeUpdated: number;\n summary: string;\n}\n\ninterface DevinChatMessage {\n message_id?: string;\n role?: string;\n content?: unknown;\n tool_calls?: { name?: string; function?: { name?: string } }[];\n thinking?: { thinking?: string } | string;\n name?: string;\n metadata?: { is_user_input?: boolean };\n}\n\ninterface MessageNodeRow {\n node_id: number;\n chat_message: string;\n}\n\nconst EMPTY_STATS: DevinSessionStats = {\n lastRole: null,\n lastTimeUpdated: 0,\n summary: \"\",\n};\n\n/** Rows fetched per requested tail message; revisions/tools eat into the window. */\nconst TAIL_OVERFETCH = 8;\nconst TOOL_CONTENT_MAX = 500;\n\nexport class DevinSessionParser {\n getConversation(\n db: Database.Database,\n sessionId: string,\n options?: ConversationOptions,\n ): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n const tail =\n options?.tail && Number.isInteger(options.tail) && options.tail > 0\n ? options.tail\n : undefined;\n\n try {\n // LIMIT -1 = unbounded in SQLite; one query serves the full and tail paths.\n const limit = tail ? tail * TAIL_OVERFETCH : -1;\n const rows = db\n .prepare<[string, number], MessageNodeRow>(`\n SELECT node_id, chat_message\n FROM message_nodes\n WHERE session_id = ?\n ORDER BY node_id DESC\n LIMIT ?\n `)\n .all(sessionId, limit)\n .reverse();\n\n const messages: ConversationMessage[] = [];\n const latestNodeByMessageId = new Map<string, number>();\n const parsed: { nodeId: number; message: DevinChatMessage }[] = [];\n\n for (const row of rows) {\n const message = this.parseChatMessage(row.chat_message);\n if (!message) continue;\n parsed.push({ nodeId: row.node_id, message });\n if (message.message_id) {\n latestNodeByMessageId.set(\n message.message_id,\n Math.max(latestNodeByMessageId.get(message.message_id) ?? 0, row.node_id),\n );\n }\n }\n\n for (const { nodeId, message } of parsed) {\n if (message.message_id && latestNodeByMessageId.get(message.message_id) !== nodeId) {\n continue;\n }\n\n const role = message.role;\n if (role === \"system\") continue;\n\n const content = typeof message.content === \"string\" ? message.content : \"\";\n\n if (role === \"user\") {\n if (message.metadata?.is_user_input === false || !content.trim()) continue;\n messages.push({ role: \"user\", content });\n continue;\n }\n\n if (role === \"assistant\") {\n const thinking =\n typeof message.thinking === \"string\" ? message.thinking : message.thinking?.thinking;\n if (verbose && thinking) {\n messages.push({ role: \"assistant\", content: `[thinking] ${thinking}` });\n }\n if (content.trim()) messages.push({ role: \"assistant\", content });\n if (verbose) {\n for (const call of message.tool_calls ?? []) {\n const name = call.function?.name ?? call.name ?? \"tool\";\n messages.push({ role: \"assistant\", content: `[tool: ${name}]` });\n }\n }\n continue;\n }\n\n if (role === \"tool\" && verbose) {\n const label = message.name ? `[tool: ${message.name}]` : \"[tool]\";\n messages.push({\n role: \"assistant\",\n content: `${label} ${truncate(content.trim(), TOOL_CONTENT_MAX)}`.trim(),\n });\n }\n }\n\n return sliceTail(messages, tail);\n } catch {\n return [];\n }\n }\n\n getSessionStats(db: Database.Database, sessionId: string): DevinSessionStats {\n try {\n const frontier = db\n .prepare<[string], { chat_message: string }>(`\n SELECT chat_message\n FROM message_nodes\n WHERE session_id = ?\n ORDER BY node_id DESC\n LIMIT 1\n `)\n .get(sessionId);\n\n const heartbeat = db\n .prepare<[string], { maxCreated: number | null }>(`\n SELECT MAX(created_at) AS maxCreated\n FROM message_nodes\n WHERE session_id = ?\n `)\n .get(sessionId);\n\n return {\n lastRole: this.parseChatMessage(frontier?.chat_message ?? \"\")?.role ?? null,\n lastTimeUpdated: (heartbeat?.maxCreated ?? 0) * 1000,\n summary: this.getLastUserPrompt(db, sessionId),\n };\n } catch {\n return EMPTY_STATS;\n }\n }\n\n /**\n * Most recent typed prompt for the \"working on\" summary, matching the\n * last-user-message convention of the other adapters.\n */\n getLastUserPrompt(db: Database.Database, sessionId: string): string {\n return this.userPrompt(db, sessionId, \"DESC\");\n }\n\n /**\n * First typed prompt for session-list identity (`firstUserMessage`).\n */\n getFirstUserMessage(db: Database.Database, sessionId: string): string {\n return this.userPrompt(db, sessionId, \"ASC\");\n }\n\n /**\n * Typed prompt in the given direction. `prompt_history` is authoritative;\n * shell `!` commands and `/` slash commands are skipped. Falls back to a\n * bounded `is_user_input` node scan in the same direction.\n */\n private userPrompt(db: Database.Database, sessionId: string, direction: \"ASC\" | \"DESC\"): string {\n try {\n const prompt = db\n .prepare<[string], { content: string }>(`\n SELECT content\n FROM prompt_history\n WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%'\n ORDER BY timestamp ${direction}\n LIMIT 1\n `)\n .get(sessionId);\n if (prompt?.content?.trim()) return truncate(prompt.content.trim(), SUMMARY_MAX_LENGTH);\n } catch {\n /* fall through to message_nodes */\n }\n\n try {\n const rows = db\n .prepare<[string], { chat_message: string }>(`\n SELECT chat_message\n FROM message_nodes\n WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user'\n ORDER BY node_id ${direction}\n LIMIT 8\n `)\n .all(sessionId);\n for (const row of rows) {\n const message = this.parseChatMessage(row.chat_message);\n if (\n message?.role === \"user\" &&\n message.metadata?.is_user_input !== false &&\n typeof message.content === \"string\" &&\n message.content.trim()\n ) {\n return truncate(message.content.trim(), SUMMARY_MAX_LENGTH);\n }\n }\n } catch {\n /* ignore */\n }\n return \"\";\n }\n\n private parseChatMessage(raw: string): DevinChatMessage | null {\n try {\n return JSON.parse(raw) as DevinChatMessage;\n } catch {\n return null;\n }\n }\n}\n"],"names":["sliceTail","SUMMARY_MAX_LENGTH","truncate","EMPTY_STATS","lastRole","lastTimeUpdated","summary","TAIL_OVERFETCH","TOOL_CONTENT_MAX","DevinSessionParser","getConversation","db","sessionId","options","verbose","tail","Number","isInteger","undefined","limit","rows","prepare","all","reverse","messages","latestNodeByMessageId","Map","parsed","row","message","parseChatMessage","chat_message","push","nodeId","node_id","message_id","set","Math","max","get","role","content","metadata","is_user_input","trim","thinking","call","tool_calls","name","function","label","getSessionStats","frontier","heartbeat","maxCreated","getLastUserPrompt","userPrompt","getFirstUserMessage","direction","prompt","raw","JSON","parse"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAYhE;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,YAAY,EAAE,CAatD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAahE;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,YAAY,EAAE,CActD"}
@@ -8,6 +8,7 @@ import { KiroAdapter } from "./kiro/KiroAdapter.js";
8
8
  import { OpenCodeAdapter } from "./opencode/OpenCodeAdapter.js";
9
9
  import { PiAdapter } from "./pi/PiAdapter.js";
10
10
  import { DevinAdapter } from "./devin/DevinAdapter.js";
11
+ import { MuseAdapter } from "./muse/MuseAdapter.js";
11
12
  /**
12
13
  * One instance of every built-in harness adapter, in registration order
13
14
  * (which is also the order agents are listed in). Register a new harness here.
@@ -22,7 +23,8 @@ import { DevinAdapter } from "./devin/DevinAdapter.js";
22
23
  new AntigravityCliAdapter(),
23
24
  new OpenCodeAdapter(),
24
25
  new PiAdapter(),
25
- new DevinAdapter()
26
+ new DevinAdapter(),
27
+ new MuseAdapter()
26
28
  ];
27
29
  }
28
30
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/harnesses/index.ts"],"sourcesContent":["import type { AgentAdapter } from \"../adapters/AgentAdapter.js\";\nimport { AntigravityCliAdapter } from \"./antigravity/AntigravityCliAdapter.js\";\nimport { ClaudeCodeAdapter } from \"./claude/ClaudeCodeAdapter.js\";\nimport { CodexAdapter } from \"./codex/CodexAdapter.js\";\nimport { CopilotAdapter } from \"./copilot/CopilotAdapter.js\";\nimport { GeminiCliAdapter } from \"./gemini/GeminiCliAdapter.js\";\nimport { GrokCliAdapter } from \"./grok/GrokCliAdapter.js\";\nimport { KiroAdapter } from \"./kiro/KiroAdapter.js\";\nimport { OpenCodeAdapter } from \"./opencode/OpenCodeAdapter.js\";\nimport { PiAdapter } from \"./pi/PiAdapter.js\";\nimport { DevinAdapter } from \"./devin/DevinAdapter.js\";\n\n/**\n * One instance of every built-in harness adapter, in registration order\n * (which is also the order agents are listed in). Register a new harness here.\n */\nexport function createBuiltinAdapters(): AgentAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new CodexAdapter(),\n new CopilotAdapter(),\n new GeminiCliAdapter(),\n new GrokCliAdapter(),\n new KiroAdapter(),\n new AntigravityCliAdapter(),\n new OpenCodeAdapter(),\n new PiAdapter(),\n new DevinAdapter(),\n ];\n}\n"],"names":["AntigravityCliAdapter","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","KiroAdapter","OpenCodeAdapter","PiAdapter","DevinAdapter","createBuiltinAdapters"],"mappings":"AACA,SAASA,qBAAqB,QAAQ,yCAAyC;AAC/E,SAASC,iBAAiB,QAAQ,gCAAgC;AAClE,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,cAAc,QAAQ,8BAA8B;AAC7D,SAASC,gBAAgB,QAAQ,+BAA+B;AAChE,SAASC,cAAc,QAAQ,2BAA2B;AAC1D,SAASC,WAAW,QAAQ,wBAAwB;AACpD,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,oBAAoB;AAC9C,SAASC,YAAY,QAAQ,0BAA0B;AAEvD;;;CAGC,GACD,OAAO,SAASC;IACd,OAAO;QACL,IAAIT;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIN;QACJ,IAAIO;QACJ,IAAIC;QACJ,IAAIC;KACL;AACH"}
1
+ {"version":3,"sources":["../../src/harnesses/index.ts"],"sourcesContent":["import type { AgentAdapter } from \"../adapters/AgentAdapter.js\";\nimport { AntigravityCliAdapter } from \"./antigravity/AntigravityCliAdapter.js\";\nimport { ClaudeCodeAdapter } from \"./claude/ClaudeCodeAdapter.js\";\nimport { CodexAdapter } from \"./codex/CodexAdapter.js\";\nimport { CopilotAdapter } from \"./copilot/CopilotAdapter.js\";\nimport { GeminiCliAdapter } from \"./gemini/GeminiCliAdapter.js\";\nimport { GrokCliAdapter } from \"./grok/GrokCliAdapter.js\";\nimport { KiroAdapter } from \"./kiro/KiroAdapter.js\";\nimport { OpenCodeAdapter } from \"./opencode/OpenCodeAdapter.js\";\nimport { PiAdapter } from \"./pi/PiAdapter.js\";\nimport { DevinAdapter } from \"./devin/DevinAdapter.js\";\nimport { MuseAdapter } from \"./muse/MuseAdapter.js\";\n\n/**\n * One instance of every built-in harness adapter, in registration order\n * (which is also the order agents are listed in). Register a new harness here.\n */\nexport function createBuiltinAdapters(): AgentAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new CodexAdapter(),\n new CopilotAdapter(),\n new GeminiCliAdapter(),\n new GrokCliAdapter(),\n new KiroAdapter(),\n new AntigravityCliAdapter(),\n new OpenCodeAdapter(),\n new PiAdapter(),\n new DevinAdapter(),\n new MuseAdapter(),\n ];\n}\n"],"names":["AntigravityCliAdapter","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","KiroAdapter","OpenCodeAdapter","PiAdapter","DevinAdapter","MuseAdapter","createBuiltinAdapters"],"mappings":"AACA,SAASA,qBAAqB,QAAQ,yCAAyC;AAC/E,SAASC,iBAAiB,QAAQ,gCAAgC;AAClE,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,cAAc,QAAQ,8BAA8B;AAC7D,SAASC,gBAAgB,QAAQ,+BAA+B;AAChE,SAASC,cAAc,QAAQ,2BAA2B;AAC1D,SAASC,WAAW,QAAQ,wBAAwB;AACpD,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,oBAAoB;AAC9C,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,WAAW,QAAQ,wBAAwB;AAEpD;;;CAGC,GACD,OAAO,SAASC;IACd,OAAO;QACL,IAAIV;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIN;QACJ,IAAIO;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;KACL;AACH"}
@@ -0,0 +1,33 @@
1
+ import type { AgentAdapter, AgentDetectionContext, AgentInfo, ConversationMessage, ConversationOptions, ListSessionsOptions, ProcessInfo, SessionSummary } from "../../adapters/AgentAdapter.js";
2
+ export interface MuseAdapterOptions {
3
+ homeDir?: string;
4
+ }
5
+ /**
6
+ * Muse (Meta Muse Code) adapter.
7
+ *
8
+ * Detection joins two sources:
9
+ * 1. Live runtime entries (`~/.local/share/muse/runtime/muse/sessions/<id>.json`)
10
+ * matched to processes by `process_generation_hint: "pid=<n>"`.
11
+ * 2. The dated transcript archive (`sessions/yyyy/mm/dd/<uuid>/session.jsonl`).
12
+ *
13
+ * The launcher binary is versioned (`muse-bin-<version>-<build>`), so discovery
14
+ * uses `processNamePrefixes`; `canHandle` keeps the precise shape check.
15
+ */
16
+ export declare class MuseAdapter implements AgentAdapter {
17
+ readonly type: "muse";
18
+ readonly processNames: readonly ["muse"];
19
+ readonly processNamePrefixes: readonly ["muse"];
20
+ private readonly home;
21
+ private readonly parser;
22
+ private readonly mapper;
23
+ constructor(options?: MuseAdapterOptions);
24
+ canHandle(processInfo: ProcessInfo): boolean;
25
+ detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;
26
+ private detectRunningAgents;
27
+ private createLocator;
28
+ getConversation(sessionFilePath: string, options?: ConversationOptions): ConversationMessage[];
29
+ listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
30
+ findSessionsById(sessionId: string): Promise<SessionSummary[]>;
31
+ private toSessionSummary;
32
+ }
33
+ //# sourceMappingURL=MuseAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuseAdapter.d.ts","sourceRoot":"","sources":["../../../src/harnesses/muse/MuseAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACf,MAAM,gCAAgC,CAAC;AAOxC,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAChC,QAAQ,CAAC,YAAY,oBAAqB;IAC1C,QAAQ,CAAC,mBAAmB,oBAAqB;IAEjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAE7B,OAAO,GAAE,kBAAuB;IAM5C,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAKtC,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;YAQ3D,mBAAmB;IAuDjC,OAAO,CAAC,aAAa;IAIrB,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE;IAIxF,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAYnE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAOpE,OAAO,CAAC,gBAAgB;CAczB"}
@@ -0,0 +1,122 @@
1
+ import * as path from "path";
2
+ import { safeStat } from "../../utils/session.js";
3
+ import { MuseAgentMapper } from "./MuseAgentMapper.js";
4
+ import { MuseSessionLocator } from "./MuseSessionLocator.js";
5
+ import { MuseSessionParser } from "./MuseSessionParser.js";
6
+ import { findHarnessProcesses, homeDir } from "../shared.js";
7
+ /**
8
+ * Muse (Meta Muse Code) adapter.
9
+ *
10
+ * Detection joins two sources:
11
+ * 1. Live runtime entries (`~/.local/share/muse/runtime/muse/sessions/<id>.json`)
12
+ * matched to processes by `process_generation_hint: "pid=<n>"`.
13
+ * 2. The dated transcript archive (`sessions/yyyy/mm/dd/<uuid>/session.jsonl`).
14
+ *
15
+ * The launcher binary is versioned (`muse-bin-<version>-<build>`), so discovery
16
+ * uses `processNamePrefixes`; `canHandle` keeps the precise shape check.
17
+ */ export class MuseAdapter {
18
+ type = "muse";
19
+ processNames = [
20
+ "muse"
21
+ ];
22
+ processNamePrefixes = [
23
+ "muse"
24
+ ];
25
+ home;
26
+ parser;
27
+ mapper;
28
+ constructor(options = {}){
29
+ this.home = options.homeDir ?? homeDir();
30
+ this.parser = new MuseSessionParser();
31
+ this.mapper = new MuseAgentMapper(this.parser);
32
+ }
33
+ canHandle(processInfo) {
34
+ const base = path.basename(processInfo.command.trim().split(/\s+/)[0] ?? "").toLowerCase();
35
+ return base === "muse" || base === "muse.exe" || base.startsWith("muse-bin-");
36
+ }
37
+ async detectAgents(context) {
38
+ try {
39
+ return await this.detectRunningAgents(context);
40
+ } finally{
41
+ this.parser.pruneSessionCache();
42
+ }
43
+ }
44
+ async detectRunningAgents(context) {
45
+ const { processes } = await findHarnessProcesses(this, context);
46
+ if (processes.length === 0) return [];
47
+ const { direct, legacyMatches } = this.createLocator().matchRunningProcesses(processes);
48
+ const matchedPids = new Set([
49
+ ...direct.map((d)=>d.process.pid),
50
+ ...legacyMatches.map((m)=>m.process.pid)
51
+ ]);
52
+ const agents = [];
53
+ for (const match of direct){
54
+ const sessionData = this.parser.readSessionIncremental(match.sessionFile.filePath, match.sessionFile.resolvedCwd);
55
+ if (sessionData) {
56
+ agents.push(this.mapper.mapSessionToAgent({
57
+ session: sessionData,
58
+ processInfo: match.process,
59
+ sessionFile: match.sessionFile
60
+ }));
61
+ } else {
62
+ matchedPids.delete(match.process.pid);
63
+ }
64
+ }
65
+ for (const match of legacyMatches){
66
+ const sessionData = this.parser.readSessionIncremental(match.session.filePath, match.session.resolvedCwd);
67
+ if (sessionData) {
68
+ agents.push(this.mapper.mapSessionToAgent({
69
+ session: sessionData,
70
+ processInfo: match.process,
71
+ sessionFile: match.session
72
+ }));
73
+ } else {
74
+ matchedPids.delete(match.process.pid);
75
+ }
76
+ }
77
+ for (const proc of processes){
78
+ if (!matchedPids.has(proc.pid)) {
79
+ agents.push(this.mapper.mapProcessOnlyAgent(proc));
80
+ }
81
+ }
82
+ return agents;
83
+ }
84
+ createLocator() {
85
+ return new MuseSessionLocator({
86
+ homeDir: this.home
87
+ });
88
+ }
89
+ getConversation(sessionFilePath, options) {
90
+ return this.parser.getConversation(sessionFilePath, options);
91
+ }
92
+ async listSessions(opts) {
93
+ const filterCwd = opts?.cwd;
94
+ const summaries = [];
95
+ for (const { filePath, defaultCwd } of this.createLocator().discoverHistoricalSessionFiles()){
96
+ const summary = this.toSessionSummary(filePath, defaultCwd);
97
+ if (!summary) continue;
98
+ if (filterCwd !== undefined && summary.cwd !== filterCwd) continue;
99
+ summaries.push(summary);
100
+ }
101
+ return summaries;
102
+ }
103
+ async findSessionsById(sessionId) {
104
+ return this.createLocator().findHistoricalSessionFilesById(sessionId).map(({ filePath, defaultCwd })=>this.toSessionSummary(filePath, defaultCwd)).filter((summary)=>summary?.sessionId === sessionId);
105
+ }
106
+ toSessionSummary(filePath, defaultCwd) {
107
+ const session = this.parser.readSession(filePath, defaultCwd);
108
+ if (!session?.lastSignal && !session?.firstUserMessage) return null;
109
+ const stat = safeStat(filePath);
110
+ return {
111
+ type: "muse",
112
+ sessionId: session.sessionId,
113
+ cwd: session.projectPath || defaultCwd,
114
+ firstUserMessage: session.firstUserMessage || "",
115
+ lastActive: session.lastActive ?? stat?.mtime ?? new Date(),
116
+ startedAt: session.sessionStart ?? stat?.birthtime ?? stat?.mtime ?? new Date(),
117
+ sessionFilePath: filePath
118
+ };
119
+ }
120
+ }
121
+
122
+ //# sourceMappingURL=MuseAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/muse/MuseAdapter.ts"],"sourcesContent":["import * as path from \"path\";\nimport type {\n AgentAdapter,\n AgentDetectionContext,\n AgentInfo,\n ConversationMessage,\n ConversationOptions,\n ListSessionsOptions,\n ProcessInfo,\n SessionSummary,\n} from \"../../adapters/AgentAdapter.js\";\nimport { safeStat } from \"../../utils/session.js\";\nimport { MuseAgentMapper } from \"./MuseAgentMapper.js\";\nimport { MuseSessionLocator } from \"./MuseSessionLocator.js\";\nimport { MuseSessionParser } from \"./MuseSessionParser.js\";\nimport { findHarnessProcesses, homeDir } from \"../shared.js\";\n\nexport interface MuseAdapterOptions {\n homeDir?: string;\n}\n\n/**\n * Muse (Meta Muse Code) adapter.\n *\n * Detection joins two sources:\n * 1. Live runtime entries (`~/.local/share/muse/runtime/muse/sessions/<id>.json`)\n * matched to processes by `process_generation_hint: \"pid=<n>\"`.\n * 2. The dated transcript archive (`sessions/yyyy/mm/dd/<uuid>/session.jsonl`).\n *\n * The launcher binary is versioned (`muse-bin-<version>-<build>`), so discovery\n * uses `processNamePrefixes`; `canHandle` keeps the precise shape check.\n */\nexport class MuseAdapter implements AgentAdapter {\n readonly type = \"muse\" as const;\n readonly processNames = [\"muse\"] as const;\n readonly processNamePrefixes = [\"muse\"] as const;\n\n private readonly home: string;\n private readonly parser: MuseSessionParser;\n private readonly mapper: MuseAgentMapper;\n\n constructor(options: MuseAdapterOptions = {}) {\n this.home = options.homeDir ?? homeDir();\n this.parser = new MuseSessionParser();\n this.mapper = new MuseAgentMapper(this.parser);\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n const base = path.basename(processInfo.command.trim().split(/\\s+/)[0] ?? \"\").toLowerCase();\n return base === \"muse\" || base === \"muse.exe\" || base.startsWith(\"muse-bin-\");\n }\n\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n try {\n return await this.detectRunningAgents(context);\n } finally {\n this.parser.pruneSessionCache();\n }\n }\n\n private async detectRunningAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const { processes } = await findHarnessProcesses(this, context);\n if (processes.length === 0) return [];\n\n const { direct, legacyMatches } = this.createLocator().matchRunningProcesses(processes);\n const matchedPids = new Set([\n ...direct.map((d) => d.process.pid),\n ...legacyMatches.map((m) => m.process.pid),\n ]);\n const agents: AgentInfo[] = [];\n\n for (const match of direct) {\n const sessionData = this.parser.readSessionIncremental(\n match.sessionFile.filePath,\n match.sessionFile.resolvedCwd,\n );\n if (sessionData) {\n agents.push(\n this.mapper.mapSessionToAgent({\n session: sessionData,\n processInfo: match.process,\n sessionFile: match.sessionFile,\n }),\n );\n } else {\n matchedPids.delete(match.process.pid);\n }\n }\n\n for (const match of legacyMatches) {\n const sessionData = this.parser.readSessionIncremental(\n match.session.filePath,\n match.session.resolvedCwd,\n );\n if (sessionData) {\n agents.push(\n this.mapper.mapSessionToAgent({\n session: sessionData,\n processInfo: match.process,\n sessionFile: match.session,\n }),\n );\n } else {\n matchedPids.delete(match.process.pid);\n }\n }\n\n for (const proc of processes) {\n if (!matchedPids.has(proc.pid)) {\n agents.push(this.mapper.mapProcessOnlyAgent(proc));\n }\n }\n return agents;\n }\n\n private createLocator(): MuseSessionLocator {\n return new MuseSessionLocator({ homeDir: this.home });\n }\n\n getConversation(sessionFilePath: string, options?: ConversationOptions): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const filterCwd = opts?.cwd;\n const summaries: SessionSummary[] = [];\n for (const { filePath, defaultCwd } of this.createLocator().discoverHistoricalSessionFiles()) {\n const summary = this.toSessionSummary(filePath, defaultCwd);\n if (!summary) continue;\n if (filterCwd !== undefined && summary.cwd !== filterCwd) continue;\n summaries.push(summary);\n }\n return summaries;\n }\n\n async findSessionsById(sessionId: string): Promise<SessionSummary[]> {\n return this.createLocator()\n .findHistoricalSessionFilesById(sessionId)\n .map(({ filePath, defaultCwd }) => this.toSessionSummary(filePath, defaultCwd))\n .filter((summary): summary is SessionSummary => summary?.sessionId === sessionId);\n }\n\n private toSessionSummary(filePath: string, defaultCwd: string): SessionSummary | null {\n const session = this.parser.readSession(filePath, defaultCwd);\n if (!session?.lastSignal && !session?.firstUserMessage) return null;\n const stat = safeStat(filePath);\n return {\n type: \"muse\",\n sessionId: session.sessionId,\n cwd: session.projectPath || defaultCwd,\n firstUserMessage: session.firstUserMessage || \"\",\n lastActive: session.lastActive ?? stat?.mtime ?? new Date(),\n startedAt: session.sessionStart ?? stat?.birthtime ?? stat?.mtime ?? new Date(),\n sessionFilePath: filePath,\n };\n }\n}\n"],"names":["path","safeStat","MuseAgentMapper","MuseSessionLocator","MuseSessionParser","findHarnessProcesses","homeDir","MuseAdapter","type","processNames","processNamePrefixes","home","parser","mapper","options","canHandle","processInfo","base","basename","command","trim","split","toLowerCase","startsWith","detectAgents","context","detectRunningAgents","pruneSessionCache","processes","length","direct","legacyMatches","createLocator","matchRunningProcesses","matchedPids","Set","map","d","process","pid","m","agents","match","sessionData","readSessionIncremental","sessionFile","filePath","resolvedCwd","push","mapSessionToAgent","session","delete","proc","has","mapProcessOnlyAgent","getConversation","sessionFilePath","listSessions","opts","filterCwd","cwd","summaries","defaultCwd","discoverHistoricalSessionFiles","summary","toSessionSummary","undefined","findSessionsById","sessionId","findHistoricalSessionFilesById","filter","readSession","lastSignal","firstUserMessage","stat","projectPath","lastActive","mtime","Date","startedAt","sessionStart","birthtime"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { AgentInfo, ProcessInfo } from "../../adapters/AgentAdapter.js";
2
+ import type { SessionFile } from "../../utils/session.js";
3
+ import { MuseSessionParser, type MuseSession } from "./MuseSessionParser.js";
4
+ export interface MuseSessionAgentInput {
5
+ session: MuseSession;
6
+ processInfo: ProcessInfo;
7
+ sessionFile: SessionFile;
8
+ }
9
+ export declare class MuseAgentMapper {
10
+ private readonly parser;
11
+ constructor(parser?: MuseSessionParser);
12
+ mapSessionToAgent({ session, processInfo, sessionFile }: MuseSessionAgentInput): AgentInfo;
13
+ mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo;
14
+ }
15
+ //# sourceMappingURL=MuseAgentMapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuseAgentMapper.d.ts","sourceRoot":"","sources":["../../../src/harnesses/muse/MuseAgentMapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAE7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG7E,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,GAAE,iBAA2C;IAEhF,iBAAiB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,qBAAqB,GAAG,SAAS;IAc1F,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS;CAGzD"}
@@ -0,0 +1,29 @@
1
+ import { generateAgentName } from "../../utils/matching.js";
2
+ import { MuseSessionParser } from "./MuseSessionParser.js";
3
+ import { processOnlyAgent } from "../shared.js";
4
+ export class MuseAgentMapper {
5
+ parser;
6
+ constructor(parser = new MuseSessionParser()){
7
+ this.parser = parser;
8
+ }
9
+ mapSessionToAgent({ session, processInfo, sessionFile }) {
10
+ return {
11
+ name: generateAgentName(processInfo.cwd, processInfo.pid),
12
+ type: "muse",
13
+ status: this.parser.determineStatus(session),
14
+ summary: session.lastUserMessage || "Session started",
15
+ pid: processInfo.pid,
16
+ projectPath: session.projectPath || sessionFile.resolvedCwd || processInfo.cwd || "",
17
+ sessionId: sessionFile.sessionId,
18
+ lastActive: session.lastActive,
19
+ sessionFilePath: sessionFile.filePath
20
+ };
21
+ }
22
+ mapProcessOnlyAgent(processInfo) {
23
+ return processOnlyAgent("muse", processInfo, {
24
+ summary: "Muse process running"
25
+ });
26
+ }
27
+ }
28
+
29
+ //# sourceMappingURL=MuseAgentMapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/muse/MuseAgentMapper.ts"],"sourcesContent":["import type { AgentInfo, ProcessInfo } from \"../../adapters/AgentAdapter.js\";\nimport { generateAgentName } from \"../../utils/matching.js\";\nimport type { SessionFile } from \"../../utils/session.js\";\nimport { MuseSessionParser, type MuseSession } from \"./MuseSessionParser.js\";\nimport { processOnlyAgent } from \"../shared.js\";\n\nexport interface MuseSessionAgentInput {\n session: MuseSession;\n processInfo: ProcessInfo;\n sessionFile: SessionFile;\n}\n\nexport class MuseAgentMapper {\n constructor(private readonly parser: MuseSessionParser = new MuseSessionParser()) {}\n\n mapSessionToAgent({ session, processInfo, sessionFile }: MuseSessionAgentInput): AgentInfo {\n return {\n name: generateAgentName(processInfo.cwd, processInfo.pid),\n type: \"muse\",\n status: this.parser.determineStatus(session),\n summary: session.lastUserMessage || \"Session started\",\n pid: processInfo.pid,\n projectPath: session.projectPath || sessionFile.resolvedCwd || processInfo.cwd || \"\",\n sessionId: sessionFile.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: sessionFile.filePath,\n };\n }\n\n mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo {\n return processOnlyAgent(\"muse\", processInfo, { summary: \"Muse process running\" });\n }\n}\n"],"names":["generateAgentName","MuseSessionParser","processOnlyAgent","MuseAgentMapper","parser","mapSessionToAgent","session","processInfo","sessionFile","name","cwd","pid","type","status","determineStatus","summary","lastUserMessage","projectPath","resolvedCwd","sessionId","lastActive","sessionFilePath","filePath","mapProcessOnlyAgent"],"mappings":"AACA,SAASA,iBAAiB,QAAQ,0BAA0B;AAE5D,SAASC,iBAAiB,QAA0B,yBAAyB;AAC7E,SAASC,gBAAgB,QAAQ,eAAe;AAQhD,OAAO,MAAMC;;IACX,YAAY,AAAiBC,SAA4B,IAAIH,mBAAmB,CAAE;aAArDG,SAAAA;IAAsD;IAEnFC,kBAAkB,EAAEC,OAAO,EAAEC,WAAW,EAAEC,WAAW,EAAyB,EAAa;QACzF,OAAO;YACLC,MAAMT,kBAAkBO,YAAYG,GAAG,EAAEH,YAAYI,GAAG;YACxDC,MAAM;YACNC,QAAQ,IAAI,CAACT,MAAM,CAACU,eAAe,CAACR;YACpCS,SAAST,QAAQU,eAAe,IAAI;YACpCL,KAAKJ,YAAYI,GAAG;YACpBM,aAAaX,QAAQW,WAAW,IAAIT,YAAYU,WAAW,IAAIX,YAAYG,GAAG,IAAI;YAClFS,WAAWX,YAAYW,SAAS;YAChCC,YAAYd,QAAQc,UAAU;YAC9BC,iBAAiBb,YAAYc,QAAQ;QACvC;IACF;IAEAC,oBAAoBhB,WAAwB,EAAa;QACvD,OAAOL,iBAAiB,QAAQK,aAAa;YAAEQ,SAAS;QAAuB;IACjF;AACF"}
@@ -0,0 +1,61 @@
1
+ import type { ProcessInfo } from "../../adapters/AgentAdapter.js";
2
+ import { type MatchResult } from "../../utils/matching.js";
3
+ import { type SessionFile } from "../../utils/session.js";
4
+ /** Entry in `~/.local/share/muse/runtime/muse/sessions/<session-id>.json`. */
5
+ export interface MuseRuntimeEntry {
6
+ sessionId: string;
7
+ pid: number;
8
+ }
9
+ /** A live Muse process paired with its transcript through the runtime file. */
10
+ export interface MuseDirectMatch {
11
+ process: ProcessInfo;
12
+ sessionFile: SessionFile;
13
+ }
14
+ export interface MuseProcessSessionMatches {
15
+ direct: MuseDirectMatch[];
16
+ legacyMatches: MatchResult[];
17
+ /** Muse processes no strategy could pair (become process-only agents). */
18
+ fallback: ProcessInfo[];
19
+ }
20
+ export interface MuseSessionLocatorOptions {
21
+ homeDir?: string;
22
+ runtimeDir?: string;
23
+ archiveDir?: string;
24
+ }
25
+ export declare class MuseSessionLocator {
26
+ private readonly home;
27
+ private readonly runtimeDir;
28
+ private readonly archiveDir;
29
+ constructor(options?: MuseSessionLocatorOptions);
30
+ /**
31
+ * Pair live Muse processes with transcripts.
32
+ *
33
+ * Two staged strategies, each handing unmatched processes onward:
34
+ * 1. Runtime-file matching via `runtime/muse/sessions/<id>.json`
35
+ * (authoritative; guarded against recycled pids).
36
+ * 2. Legacy cwd+birthtime heuristic over recent transcripts.
37
+ */
38
+ matchRunningProcesses(processes: ProcessInfo[]): MuseProcessSessionMatches;
39
+ private tryRuntimeFileMatching;
40
+ /** Recent transcripts for legacy cwd+birthtime matching (today-first, bounded). */
41
+ discoverLiveSessions(processes: ProcessInfo[]): SessionFile[];
42
+ /**
43
+ * Every archived transcript: walks `sessions/yyyy/mm/dd/<uuid>/session.jsonl`.
44
+ * Only directories matching the session-id pattern that contain a transcript
45
+ * are returned; `subagent/`, logs, and databases never match.
46
+ */
47
+ discoverHistoricalSessionFiles(): Array<{
48
+ filePath: string;
49
+ defaultCwd: string;
50
+ }>;
51
+ findHistoricalSessionFilesById(sessionId: string): Array<{
52
+ filePath: string;
53
+ defaultCwd: string;
54
+ }>;
55
+ /** Exact transcript lookup by session id across dated dirs. */
56
+ findTranscriptById(sessionId: string): string | null;
57
+ private walkTranscripts;
58
+ /** Dated directories `sessions/yyyy/mm/dd`, each level guarded by a pattern. */
59
+ private listDayDirs;
60
+ }
61
+ //# sourceMappingURL=MuseSessionLocator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuseSessionLocator.d.ts","sourceRoot":"","sources":["../../../src/harnesses/muse/MuseSessionLocator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAA4B,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAGhC,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA6ED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,GAAE,yBAA8B;IAOnD;;;;;;;OAOG;IACH,qBAAqB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,yBAAyB;IAmB1E,OAAO,CAAC,sBAAsB;IAyC9B,mFAAmF;IACnF,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE;IA2B7D;;;;OAIG;IACH,8BAA8B,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAQjF,8BAA8B,CAC5B,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAMlD,+DAA+D;IAC/D,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASpD,OAAO,CAAC,eAAe;IAYvB,gFAAgF;IAChF,OAAO,CAAC,WAAW;CAoBpB"}
@@ -0,0 +1,233 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { matchProcessesToSessions } from "../../utils/matching.js";
4
+ import { isDirectory, isSafePathSegment, safeReaddir, safeStat } from "../../utils/session.js";
5
+ import { homeDir } from "../shared.js";
6
+ /** Session ids are UUID-shaped; anything else is never a session dir. */ const SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7
+ /** Maximum |process start − transcript birth| before a pid match is recycled. */ const PID_STALENESS_MS = 60000;
8
+ function parsePidHint(value) {
9
+ if (typeof value !== "string") return null;
10
+ const match = /^pid=(\d+)$/.exec(value.trim());
11
+ return match ? Number(match[1]) : null;
12
+ }
13
+ function parseRuntimeEntry(filePath) {
14
+ let raw;
15
+ try {
16
+ raw = fs.readFileSync(filePath, "utf-8");
17
+ } catch {
18
+ return null;
19
+ }
20
+ let parsed;
21
+ try {
22
+ parsed = JSON.parse(raw);
23
+ } catch {
24
+ return null;
25
+ }
26
+ if (!parsed || typeof parsed !== "object") return null;
27
+ const entry = parsed;
28
+ const sessionId = typeof entry.session_id === "string" ? entry.session_id : null;
29
+ const pid = parsePidHint(entry.process_generation_hint);
30
+ if (!sessionId || !SESSION_ID_PATTERN.test(sessionId) || pid === null) return null;
31
+ return {
32
+ sessionId,
33
+ pid
34
+ };
35
+ }
36
+ /** Head-read a transcript for its recorded workspace root (bounded, cheap). */ function readWorkspaceRootHead(filePath, maxBytes = 8192) {
37
+ let fd;
38
+ try {
39
+ fd = fs.openSync(filePath, "r");
40
+ } catch {
41
+ return null;
42
+ }
43
+ try {
44
+ const stat = fs.fstatSync(fd);
45
+ const buf = Buffer.alloc(Math.min(maxBytes, stat.size));
46
+ fs.readSync(fd, buf, 0, buf.length, 0);
47
+ const text = buf.toString("utf-8");
48
+ for (const line of text.split("\n")){
49
+ if (!line.includes("runtime.session.metadata")) continue;
50
+ try {
51
+ const outer = JSON.parse(line);
52
+ const inner = typeof outer.record_json === "string" ? JSON.parse(outer.record_json) : outer;
53
+ const root = inner.payload?.record?.workspace_root;
54
+ if (typeof root === "string" && root) return root;
55
+ } catch {
56
+ /* keep scanning */ }
57
+ }
58
+ return null;
59
+ } catch {
60
+ return null;
61
+ } finally{
62
+ try {
63
+ fs.closeSync(fd);
64
+ } catch {
65
+ /* best effort */ }
66
+ }
67
+ }
68
+ export class MuseSessionLocator {
69
+ home;
70
+ runtimeDir;
71
+ archiveDir;
72
+ constructor(options = {}){
73
+ this.home = options.homeDir ?? homeDir();
74
+ const store = path.join(this.home, ".local", "share", "muse");
75
+ this.runtimeDir = options.runtimeDir ?? path.join(store, "runtime", "muse", "sessions");
76
+ this.archiveDir = options.archiveDir ?? path.join(store, "sessions");
77
+ }
78
+ /**
79
+ * Pair live Muse processes with transcripts.
80
+ *
81
+ * Two staged strategies, each handing unmatched processes onward:
82
+ * 1. Runtime-file matching via `runtime/muse/sessions/<id>.json`
83
+ * (authoritative; guarded against recycled pids).
84
+ * 2. Legacy cwd+birthtime heuristic over recent transcripts.
85
+ */ matchRunningProcesses(processes) {
86
+ // Callers pass muse processes only (the adapter pre-filters via canHandle).
87
+ const { direct, fallback: noDirect } = this.tryRuntimeFileMatching(processes);
88
+ const legacySessions = this.discoverLiveSessions(noDirect);
89
+ const legacyMatches = noDirect.length > 0 && legacySessions.length > 0 ? matchProcessesToSessions(noDirect, legacySessions) : [];
90
+ const matchedPids = new Set([
91
+ ...direct.map((d)=>d.process.pid),
92
+ ...legacyMatches.map((m)=>m.process.pid)
93
+ ]);
94
+ return {
95
+ direct,
96
+ legacyMatches,
97
+ fallback: noDirect.filter((p)=>!matchedPids.has(p.pid))
98
+ };
99
+ }
100
+ tryRuntimeFileMatching(processes) {
101
+ const byPid = new Map();
102
+ for (const proc of processes){
103
+ if (!byPid.has(proc.pid)) byPid.set(proc.pid, proc);
104
+ }
105
+ const direct = [];
106
+ const usedPids = new Set();
107
+ if (!isDirectory(this.runtimeDir)) return {
108
+ direct,
109
+ fallback: processes
110
+ };
111
+ for (const name of safeReaddir(this.runtimeDir)){
112
+ if (!name.endsWith(".json")) continue;
113
+ const entry = parseRuntimeEntry(path.join(this.runtimeDir, name));
114
+ if (!entry) continue;
115
+ const proc = byPid.get(entry.pid);
116
+ if (!proc || usedPids.has(proc.pid)) continue;
117
+ const transcript = this.findTranscriptById(entry.sessionId);
118
+ if (!transcript) continue;
119
+ // Guard against pid recycling: the live process must have started
120
+ // around the transcript's creation.
121
+ const birth = safeStat(transcript)?.birthtimeMs;
122
+ const start = proc.startTime?.getTime();
123
+ if (birth === undefined || start === undefined) continue;
124
+ if (Math.abs(start - birth) > PID_STALENESS_MS) continue;
125
+ usedPids.add(proc.pid);
126
+ direct.push({
127
+ process: proc,
128
+ sessionFile: {
129
+ sessionId: entry.sessionId,
130
+ filePath: transcript,
131
+ projectDir: path.dirname(transcript),
132
+ birthtimeMs: birth,
133
+ resolvedCwd: proc.cwd ?? ""
134
+ }
135
+ });
136
+ }
137
+ return {
138
+ direct,
139
+ fallback: processes.filter((p)=>!usedPids.has(p.pid))
140
+ };
141
+ }
142
+ /** Recent transcripts for legacy cwd+birthtime matching (today-first, bounded). */ discoverLiveSessions(processes) {
143
+ const cwds = new Set(processes.map((p)=>p.cwd).filter((cwd)=>!!cwd));
144
+ if (cwds.size === 0 || !isDirectory(this.archiveDir)) return [];
145
+ const dayDirs = this.listDayDirs().slice(-2);
146
+ const sessionFiles = [];
147
+ for (const dayDir of dayDirs){
148
+ for (const name of safeReaddir(dayDir)){
149
+ if (!SESSION_ID_PATTERN.test(name)) continue;
150
+ const filePath = path.join(dayDir, name, "session.jsonl");
151
+ const stat = safeStat(filePath);
152
+ if (!stat?.isFile() || !Number.isFinite(stat.birthtimeMs) || stat.birthtimeMs <= 0) {
153
+ continue;
154
+ }
155
+ const workspaceRoot = readWorkspaceRootHead(filePath);
156
+ if (!workspaceRoot || !cwds.has(workspaceRoot)) continue;
157
+ sessionFiles.push({
158
+ sessionId: name,
159
+ filePath,
160
+ projectDir: path.dirname(filePath),
161
+ birthtimeMs: stat.birthtimeMs,
162
+ resolvedCwd: workspaceRoot
163
+ });
164
+ }
165
+ }
166
+ return sessionFiles;
167
+ }
168
+ /**
169
+ * Every archived transcript: walks `sessions/yyyy/mm/dd/<uuid>/session.jsonl`.
170
+ * Only directories matching the session-id pattern that contain a transcript
171
+ * are returned; `subagent/`, logs, and databases never match.
172
+ */ discoverHistoricalSessionFiles() {
173
+ const out = [];
174
+ for (const filePath of this.walkTranscripts()){
175
+ out.push({
176
+ filePath,
177
+ defaultCwd: ""
178
+ });
179
+ }
180
+ return out;
181
+ }
182
+ findHistoricalSessionFilesById(sessionId) {
183
+ if (!isSafePathSegment(sessionId) || !SESSION_ID_PATTERN.test(sessionId)) return [];
184
+ const filePath = this.findTranscriptById(sessionId);
185
+ return filePath ? [
186
+ {
187
+ filePath,
188
+ defaultCwd: ""
189
+ }
190
+ ] : [];
191
+ }
192
+ /** Exact transcript lookup by session id across dated dirs. */ findTranscriptById(sessionId) {
193
+ if (!isSafePathSegment(sessionId) || !SESSION_ID_PATTERN.test(sessionId)) return null;
194
+ for (const dayDir of this.listDayDirs()){
195
+ const filePath = path.join(dayDir, sessionId, "session.jsonl");
196
+ if (safeStat(filePath)?.isFile()) return filePath;
197
+ }
198
+ return null;
199
+ }
200
+ walkTranscripts() {
201
+ const out = [];
202
+ for (const dayDir of this.listDayDirs()){
203
+ for (const name of safeReaddir(dayDir)){
204
+ if (!SESSION_ID_PATTERN.test(name)) continue;
205
+ const filePath = path.join(dayDir, name, "session.jsonl");
206
+ if (safeStat(filePath)?.isFile()) out.push(filePath);
207
+ }
208
+ }
209
+ return out;
210
+ }
211
+ /** Dated directories `sessions/yyyy/mm/dd`, each level guarded by a pattern. */ listDayDirs() {
212
+ const out = [];
213
+ if (!isDirectory(this.archiveDir)) return out;
214
+ for (const yyyy of safeReaddir(this.archiveDir)){
215
+ if (!/^\d{4}$/.test(yyyy)) continue;
216
+ const yDir = path.join(this.archiveDir, yyyy);
217
+ if (!isDirectory(yDir)) continue;
218
+ for (const mm of safeReaddir(yDir)){
219
+ if (!/^\d{2}$/.test(mm)) continue;
220
+ const mDir = path.join(yDir, mm);
221
+ if (!isDirectory(mDir)) continue;
222
+ for (const dd of safeReaddir(mDir)){
223
+ if (!/^\d{2}$/.test(dd)) continue;
224
+ const dDir = path.join(mDir, dd);
225
+ if (isDirectory(dDir)) out.push(dDir);
226
+ }
227
+ }
228
+ }
229
+ return out;
230
+ }
231
+ }
232
+
233
+ //# sourceMappingURL=MuseSessionLocator.js.map