@ai-devkit/agent-manager 0.33.0 → 0.34.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 (174) hide show
  1. package/dist/AgentManager.d.ts +11 -2
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +37 -6
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +212 -5
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/capacity/codex.test.js +2 -1
  8. package/dist/__tests__/capacity/codex.test.js.map +1 -1
  9. package/dist/__tests__/capacity/index.test.js +6 -3
  10. package/dist/__tests__/capacity/index.test.js.map +1 -1
  11. package/dist/__tests__/capacity/zai.test.js +213 -0
  12. package/dist/__tests__/capacity/zai.test.js.map +1 -0
  13. package/dist/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.js +19 -14
  14. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -0
  15. package/dist/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.js +37 -29
  16. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -0
  17. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +189 -0
  18. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -0
  19. package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js +89 -0
  20. package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js.map +1 -0
  21. package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js +94 -0
  22. package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js.map +1 -0
  23. package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js +147 -0
  24. package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js.map +1 -0
  25. package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js +164 -0
  26. package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js.map +1 -0
  27. package/dist/__tests__/runtime/HerdrAgentRuntime.test.js +37 -1
  28. package/dist/__tests__/runtime/HerdrAgentRuntime.test.js.map +1 -1
  29. package/dist/__tests__/utils/AgentRegistry.test.js +20 -13
  30. package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
  31. package/dist/adapters/index.d.ts +3 -3
  32. package/dist/adapters/index.d.ts.map +1 -1
  33. package/dist/adapters/index.js +3 -3
  34. package/dist/adapters/index.js.map +1 -1
  35. package/dist/capacity/codex.d.ts +1 -0
  36. package/dist/capacity/codex.d.ts.map +1 -1
  37. package/dist/capacity/codex.js +9 -4
  38. package/dist/capacity/codex.js.map +1 -1
  39. package/dist/capacity/index.d.ts +8 -0
  40. package/dist/capacity/index.d.ts.map +1 -1
  41. package/dist/capacity/index.js +10 -9
  42. package/dist/capacity/index.js.map +1 -1
  43. package/dist/capacity/types.d.ts +8 -0
  44. package/dist/capacity/types.d.ts.map +1 -1
  45. package/dist/capacity/types.js.map +1 -1
  46. package/dist/capacity/zai.d.ts +14 -0
  47. package/dist/capacity/zai.d.ts.map +1 -0
  48. package/dist/capacity/zai.js +203 -0
  49. package/dist/capacity/zai.js.map +1 -0
  50. package/dist/index.d.ts +8 -6
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +5 -4
  53. package/dist/index.js.map +1 -1
  54. package/dist/{adapters → providers/copilot}/CopilotAdapter.d.ts +11 -25
  55. package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -0
  56. package/dist/providers/copilot/CopilotAdapter.js +108 -0
  57. package/dist/providers/copilot/CopilotAdapter.js.map +1 -0
  58. package/dist/providers/copilot/CopilotAgentMapper.d.ts +9 -0
  59. package/dist/providers/copilot/CopilotAgentMapper.d.ts.map +1 -0
  60. package/dist/providers/copilot/CopilotAgentMapper.js +37 -0
  61. package/dist/providers/copilot/CopilotAgentMapper.js.map +1 -0
  62. package/dist/providers/copilot/CopilotSessionLocator.d.ts +19 -0
  63. package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -0
  64. package/dist/providers/copilot/CopilotSessionLocator.js +41 -0
  65. package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -0
  66. package/dist/providers/copilot/CopilotSessionParser.d.ts +30 -0
  67. package/dist/providers/copilot/CopilotSessionParser.d.ts.map +1 -0
  68. package/dist/providers/copilot/CopilotSessionParser.js +181 -0
  69. package/dist/providers/copilot/CopilotSessionParser.js.map +1 -0
  70. package/dist/providers/gemini/GeminiAgentMapper.d.ts +9 -0
  71. package/dist/providers/gemini/GeminiAgentMapper.d.ts.map +1 -0
  72. package/dist/providers/gemini/GeminiAgentMapper.js +37 -0
  73. package/dist/providers/gemini/GeminiAgentMapper.js.map +1 -0
  74. package/dist/providers/gemini/GeminiCliAdapter.d.ts +52 -0
  75. package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -0
  76. package/dist/providers/gemini/GeminiCliAdapter.js +188 -0
  77. package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -0
  78. package/dist/providers/gemini/GeminiSessionLocator.d.ts +30 -0
  79. package/dist/providers/gemini/GeminiSessionLocator.d.ts.map +1 -0
  80. package/dist/providers/gemini/GeminiSessionLocator.js +142 -0
  81. package/dist/providers/gemini/GeminiSessionLocator.js.map +1 -0
  82. package/dist/providers/gemini/GeminiSessionParser.d.ts +94 -0
  83. package/dist/providers/gemini/GeminiSessionParser.d.ts.map +1 -0
  84. package/dist/providers/gemini/GeminiSessionParser.js +166 -0
  85. package/dist/providers/gemini/GeminiSessionParser.js.map +1 -0
  86. package/dist/{adapters → providers/opencode}/OpenCodeAdapter.d.ts +6 -12
  87. package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -0
  88. package/dist/providers/opencode/OpenCodeAdapter.js +86 -0
  89. package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -0
  90. package/dist/providers/opencode/OpenCodeAgentMapper.d.ts +11 -0
  91. package/dist/providers/opencode/OpenCodeAgentMapper.d.ts.map +1 -0
  92. package/dist/providers/opencode/OpenCodeAgentMapper.js +50 -0
  93. package/dist/providers/opencode/OpenCodeAgentMapper.js.map +1 -0
  94. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +21 -0
  95. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -0
  96. package/dist/providers/opencode/OpenCodeSessionLocator.js +99 -0
  97. package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -0
  98. package/dist/providers/opencode/OpenCodeSessionParser.d.ts +18 -0
  99. package/dist/providers/opencode/OpenCodeSessionParser.d.ts.map +1 -0
  100. package/dist/providers/opencode/OpenCodeSessionParser.js +104 -0
  101. package/dist/providers/opencode/OpenCodeSessionParser.js.map +1 -0
  102. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts +18 -0
  103. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -0
  104. package/dist/runtime/herdr/HerdrAgentDiscovery.js +183 -0
  105. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -0
  106. package/dist/runtime/herdr/HerdrAgentRuntime.d.ts +5 -0
  107. package/dist/runtime/herdr/HerdrAgentRuntime.d.ts.map +1 -1
  108. package/dist/runtime/herdr/HerdrAgentRuntime.js +26 -8
  109. package/dist/runtime/herdr/HerdrAgentRuntime.js.map +1 -1
  110. package/dist/runtime/herdr/HerdrResponseParsers.d.ts +2 -0
  111. package/dist/runtime/herdr/HerdrResponseParsers.d.ts.map +1 -1
  112. package/dist/runtime/herdr/HerdrResponseParsers.js +12 -1
  113. package/dist/runtime/herdr/HerdrResponseParsers.js.map +1 -1
  114. package/dist/utils/AgentRegistry.d.ts.map +1 -1
  115. package/dist/utils/AgentRegistry.js +5 -5
  116. package/dist/utils/AgentRegistry.js.map +1 -1
  117. package/package.json +1 -1
  118. package/src/AgentManager.ts +67 -5
  119. package/src/__tests__/AgentManager.test.ts +231 -5
  120. package/src/__tests__/capacity/codex.test.ts +94 -26
  121. package/src/__tests__/capacity/fixtures/zai-auth.json +6 -0
  122. package/src/__tests__/capacity/fixtures/zai-quota-invalid.json +7 -0
  123. package/src/__tests__/capacity/fixtures/zai-quota.json +38 -0
  124. package/src/__tests__/capacity/index.test.ts +10 -3
  125. package/src/__tests__/capacity/zai.test.ts +186 -0
  126. package/src/__tests__/{adapters → providers/copilot}/CopilotAdapter.test.ts +13 -16
  127. package/src/__tests__/{adapters → providers/gemini}/GeminiCliAdapter.test.ts +39 -31
  128. package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +197 -0
  129. package/src/__tests__/providers/opencode/OpenCodeAgentMapper.test.ts +83 -0
  130. package/src/__tests__/providers/opencode/OpenCodeSessionLocator.test.ts +84 -0
  131. package/src/__tests__/providers/opencode/OpenCodeSessionParser.test.ts +148 -0
  132. package/src/__tests__/runtime/HerdrAgentDiscovery.test.ts +131 -0
  133. package/src/__tests__/runtime/HerdrAgentRuntime.test.ts +20 -1
  134. package/src/__tests__/utils/AgentRegistry.test.ts +20 -10
  135. package/src/adapters/index.ts +3 -3
  136. package/src/capacity/codex.ts +96 -28
  137. package/src/capacity/index.ts +29 -11
  138. package/src/capacity/types.ts +8 -0
  139. package/src/capacity/zai.ts +237 -0
  140. package/src/index.ts +18 -6
  141. package/src/providers/copilot/CopilotAdapter.ts +145 -0
  142. package/src/providers/copilot/CopilotAgentMapper.ts +36 -0
  143. package/src/providers/copilot/CopilotSessionLocator.ts +57 -0
  144. package/src/providers/copilot/CopilotSessionParser.ts +289 -0
  145. package/src/providers/gemini/GeminiAgentMapper.ts +36 -0
  146. package/src/providers/gemini/GeminiCliAdapter.ts +266 -0
  147. package/src/providers/gemini/GeminiSessionLocator.ts +183 -0
  148. package/src/providers/gemini/GeminiSessionParser.ts +271 -0
  149. package/src/providers/opencode/OpenCodeAdapter.ts +112 -0
  150. package/src/providers/opencode/OpenCodeAgentMapper.ts +61 -0
  151. package/src/providers/opencode/OpenCodeSessionLocator.ts +123 -0
  152. package/src/providers/opencode/OpenCodeSessionParser.ts +145 -0
  153. package/src/runtime/herdr/HerdrAgentDiscovery.ts +178 -0
  154. package/src/runtime/herdr/HerdrAgentRuntime.ts +33 -7
  155. package/src/runtime/herdr/HerdrResponseParsers.ts +11 -1
  156. package/src/utils/AgentRegistry.ts +8 -6
  157. package/dist/__tests__/adapters/CopilotAdapter.test.js.map +0 -1
  158. package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +0 -1
  159. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +0 -635
  160. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +0 -1
  161. package/dist/adapters/CopilotAdapter.d.ts.map +0 -1
  162. package/dist/adapters/CopilotAdapter.js +0 -329
  163. package/dist/adapters/CopilotAdapter.js.map +0 -1
  164. package/dist/adapters/GeminiCliAdapter.d.ts +0 -98
  165. package/dist/adapters/GeminiCliAdapter.d.ts.map +0 -1
  166. package/dist/adapters/GeminiCliAdapter.js +0 -513
  167. package/dist/adapters/GeminiCliAdapter.js.map +0 -1
  168. package/dist/adapters/OpenCodeAdapter.d.ts.map +0 -1
  169. package/dist/adapters/OpenCodeAdapter.js +0 -286
  170. package/dist/adapters/OpenCodeAdapter.js.map +0 -1
  171. package/src/__tests__/adapters/OpenCodeAdapter.test.ts +0 -543
  172. package/src/adapters/CopilotAdapter.ts +0 -483
  173. package/src/adapters/GeminiCliAdapter.ts +0 -699
  174. package/src/adapters/OpenCodeAdapter.ts +0 -369
@@ -0,0 +1,145 @@
1
+ import type { ConversationMessage } from "../../adapters/AgentAdapter.js";
2
+ import type Database from "better-sqlite3";
3
+
4
+ export interface OpenCodeSessionStats {
5
+ lastRole: string | null;
6
+ lastTimeUpdated: number;
7
+ /** OpenCode writes `time.completed` on the assistant message only when the turn finishes. */
8
+ lastAssistantCompleted: boolean;
9
+ lastAssistantErrored: boolean;
10
+ summary: string;
11
+ }
12
+
13
+ interface OpenCodePartData {
14
+ type?: string;
15
+ text?: string;
16
+ reasoning?: string;
17
+ tool?: string;
18
+ }
19
+
20
+ const EMPTY_STATS: OpenCodeSessionStats = {
21
+ lastRole: null,
22
+ lastTimeUpdated: 0,
23
+ lastAssistantCompleted: false,
24
+ lastAssistantErrored: false,
25
+ summary: "",
26
+ };
27
+
28
+ export class OpenCodeSessionParser {
29
+ getConversation(
30
+ db: Database.Database,
31
+ sessionId: string,
32
+ options?: { verbose?: boolean },
33
+ ): ConversationMessage[] {
34
+ const verbose = options?.verbose ?? false;
35
+
36
+ try {
37
+ const rows = db
38
+ .prepare<[string], { role: string; partData: string; timeCreated: number }>(`
39
+ SELECT json_extract(m.data, '$.role') AS role,
40
+ p.data AS partData,
41
+ p.time_created AS timeCreated
42
+ FROM part p
43
+ JOIN message m ON p.message_id = m.id
44
+ WHERE p.session_id = ?
45
+ ORDER BY p.time_created ASC
46
+ `)
47
+ .all(sessionId);
48
+
49
+ const messages: ConversationMessage[] = [];
50
+
51
+ for (const row of rows) {
52
+ const partData = this.parsePartData(row.partData);
53
+ if (!partData) continue;
54
+
55
+ const role = row.role === "user" ? "user" : "assistant";
56
+
57
+ if (partData.type === "text" && partData.text) {
58
+ messages.push({ role, content: partData.text });
59
+ } else if (partData.type === "reasoning" && verbose) {
60
+ const text = partData.reasoning || partData.text || "";
61
+ if (text) messages.push({ role: "assistant", content: `[thinking] ${text}` });
62
+ } else if (partData.type === "tool" && verbose) {
63
+ const toolName = partData.tool || "tool";
64
+ messages.push({ role: "assistant", content: `[tool: ${toolName}]` });
65
+ }
66
+ }
67
+
68
+ return messages;
69
+ } catch {
70
+ return [];
71
+ }
72
+ }
73
+
74
+ getSessionStats(db: Database.Database, sessionId: string): OpenCodeSessionStats {
75
+ try {
76
+ // Order by time_created — time_updated can lag when OpenCode appends
77
+ // metadata (e.g. summary diffs) to user messages after a turn finishes.
78
+ const last = db
79
+ .prepare<[string], { role: string; timeUpdated: number }>(`
80
+ SELECT json_extract(data, '$.role') AS role,
81
+ time_updated AS timeUpdated
82
+ FROM message
83
+ WHERE session_id = ?
84
+ ORDER BY time_created DESC
85
+ LIMIT 1
86
+ `)
87
+ .get(sessionId);
88
+
89
+ const heartbeat = db
90
+ .prepare<[string], { maxUpdated: number }>(`
91
+ SELECT MAX(time_updated) AS maxUpdated FROM message WHERE session_id = ?
92
+ `)
93
+ .get(sessionId);
94
+
95
+ const lastAssistant = db
96
+ .prepare<
97
+ [string],
98
+ {
99
+ completed: number | null;
100
+ errored: number | null;
101
+ }
102
+ >(`
103
+ SELECT json_extract(data, '$.time.completed') AS completed,
104
+ json_extract(data, '$.time.error') AS errored
105
+ FROM message
106
+ WHERE session_id = ? AND json_extract(data, '$.role') = 'assistant'
107
+ ORDER BY time_created DESC
108
+ LIMIT 1
109
+ `)
110
+ .get(sessionId);
111
+
112
+ const first = db
113
+ .prepare<[string], { text: string }>(`
114
+ SELECT json_extract(p.data, '$.text') AS text
115
+ FROM part p
116
+ JOIN message m ON p.message_id = m.id
117
+ WHERE p.session_id = ?
118
+ AND json_extract(m.data, '$.role') = 'user'
119
+ AND json_extract(p.data, '$.type') = 'text'
120
+ AND json_extract(p.data, '$.text') IS NOT NULL
121
+ ORDER BY p.time_created ASC
122
+ LIMIT 1
123
+ `)
124
+ .get(sessionId);
125
+
126
+ return {
127
+ lastRole: last?.role ?? null,
128
+ lastTimeUpdated: heartbeat?.maxUpdated ?? last?.timeUpdated ?? 0,
129
+ lastAssistantCompleted: lastAssistant?.completed != null,
130
+ lastAssistantErrored: lastAssistant?.errored != null,
131
+ summary: first?.text?.trim() ?? "",
132
+ };
133
+ } catch {
134
+ return EMPTY_STATS;
135
+ }
136
+ }
137
+
138
+ private parsePartData(partData: string): OpenCodePartData | null {
139
+ try {
140
+ return JSON.parse(partData) as OpenCodePartData;
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
145
+ }
@@ -0,0 +1,178 @@
1
+ import type { AgentInfo, AgentType } from "../../adapters/AgentAdapter.js";
2
+ import { HerdrCliClient, type HerdrCommandRunner } from "./HerdrCliClient.js";
3
+ import type { HerdrRuntimeRef } from "./HerdrRuntimeRef.js";
4
+
5
+ export interface HerdrAgentPane {
6
+ agent: string;
7
+ agentSessionId?: string;
8
+ cwd?: string;
9
+ paneId: string;
10
+ workspaceId?: string;
11
+ tabId?: string;
12
+ foregroundPids?: number[];
13
+ shellPid?: number;
14
+ }
15
+
16
+ export async function fetchHerdrAgentPanes(runner?: HerdrCommandRunner): Promise<HerdrAgentPane[]> {
17
+ const client = new HerdrCliClient(runner);
18
+ const response = await client.runJson(["pane", "list"]);
19
+ return enrichHerdrAgentPanes(client, extractHerdrAgentPanes(response));
20
+ }
21
+
22
+ export function extractHerdrAgentPanes(value: unknown): HerdrAgentPane[] {
23
+ const snapshot = getObject(value, ["result", "snapshot"]);
24
+ const rawPanes = getArray(value, ["result", "panes"]) ?? snapshot?.agents;
25
+ if (!Array.isArray(rawPanes)) return [];
26
+
27
+ const panes: HerdrAgentPane[] = [];
28
+ for (const rawPane of rawPanes) {
29
+ if (!rawPane || typeof rawPane !== "object" || Array.isArray(rawPane)) continue;
30
+ const pane = rawPane as Record<string, unknown>;
31
+ const agent = nonEmptyString(pane.agent);
32
+ const paneId = nonEmptyString(pane.pane_id);
33
+ if (!agent || !paneId) continue;
34
+
35
+ const session = getObject(pane, ["agent_session"]);
36
+ const agentSessionId = nonEmptyString(session?.value);
37
+ const cwd = nonEmptyString(pane.cwd);
38
+ const workspaceId = nonEmptyString(pane.workspace_id);
39
+ const tabId = nonEmptyString(pane.tab_id);
40
+
41
+ panes.push({
42
+ agent,
43
+ paneId,
44
+ ...(agentSessionId ? { agentSessionId } : {}),
45
+ ...(cwd ? { cwd } : {}),
46
+ ...(workspaceId ? { workspaceId } : {}),
47
+ ...(tabId ? { tabId } : {}),
48
+ });
49
+ }
50
+
51
+ return panes;
52
+ }
53
+
54
+ export function findMatchingHerdrPane(
55
+ agent: Pick<AgentInfo, "type" | "projectPath" | "sessionId" | "pid">,
56
+ panes: readonly HerdrAgentPane[],
57
+ ): HerdrAgentPane | null {
58
+ if (agent.sessionId) {
59
+ const sessionMatches = panes.filter((pane) => pane.agentSessionId === agent.sessionId);
60
+ if (sessionMatches.length === 1) return sessionMatches[0]!;
61
+ if (sessionMatches.length > 1) return null;
62
+ }
63
+
64
+ const foregroundPidMatches = panes.filter((pane) => pane.foregroundPids?.includes(agent.pid));
65
+ if (foregroundPidMatches.length === 1) return foregroundPidMatches[0]!;
66
+ if (foregroundPidMatches.length > 1) return null;
67
+
68
+ const shellPidMatches = panes.filter((pane) => pane.shellPid === agent.pid);
69
+ if (shellPidMatches.length === 1) return shellPidMatches[0]!;
70
+ if (shellPidMatches.length > 1) return null;
71
+
72
+ const cwdMatches = panes.filter(
73
+ (pane) => pane.cwd === agent.projectPath && herdrAgentMatchesType(pane.agent, agent.type),
74
+ );
75
+ return cwdMatches.length === 1 ? cwdMatches[0]! : null;
76
+ }
77
+
78
+ export function herdrPaneToRuntimeRef(pane: HerdrAgentPane, agentName: string): HerdrRuntimeRef {
79
+ return {
80
+ session: "default",
81
+ paneId: pane.paneId,
82
+ agentName,
83
+ ...(pane.workspaceId ? { workspaceId: pane.workspaceId } : {}),
84
+ ...(pane.tabId ? { tabId: pane.tabId } : {}),
85
+ };
86
+ }
87
+
88
+ function herdrAgentMatchesType(herdrAgent: string, agentType: AgentType): boolean {
89
+ const normalized = herdrAgent.toLowerCase();
90
+ const candidates: Record<AgentType, readonly string[]> = {
91
+ claude: ["claude"],
92
+ codex: ["codex"],
93
+ copilot: ["copilot"],
94
+ gemini_cli: ["gemini", "gemini_cli"],
95
+ grok_cli: ["grok", "grok_cli"],
96
+ opencode: ["opencode"],
97
+ pi: ["pi"],
98
+ other: ["other"],
99
+ };
100
+ return candidates[agentType]?.includes(normalized) ?? false;
101
+ }
102
+
103
+ async function enrichHerdrAgentPanes(
104
+ client: HerdrCliClient,
105
+ panes: HerdrAgentPane[],
106
+ ): Promise<HerdrAgentPane[]> {
107
+ return Promise.all(
108
+ panes.map(async (pane) => {
109
+ try {
110
+ const response = await client.runJson(["pane", "process-info", "--pane", pane.paneId]);
111
+ return {
112
+ ...pane,
113
+ ...extractPaneProcessIds(response),
114
+ };
115
+ } catch {
116
+ return pane;
117
+ }
118
+ }),
119
+ );
120
+ }
121
+
122
+ function extractPaneProcessIds(value: unknown): Pick<HerdrAgentPane, "foregroundPids" | "shellPid"> {
123
+ const processInfo = getObject(value, ["result", "process_info"]);
124
+ if (!processInfo) return {};
125
+
126
+ const foregroundProcesses = processInfo.foreground_processes;
127
+ const foregroundPids: number[] = [];
128
+ if (Array.isArray(foregroundProcesses)) {
129
+ for (const foregroundProcess of foregroundProcesses) {
130
+ if (
131
+ !foregroundProcess ||
132
+ typeof foregroundProcess !== "object" ||
133
+ Array.isArray(foregroundProcess)
134
+ )
135
+ continue;
136
+ const pid = positiveIntegerOrNull((foregroundProcess as { pid?: unknown }).pid);
137
+ if (pid !== null) foregroundPids.push(pid);
138
+ }
139
+ }
140
+ const shellPid = positiveIntegerOrNull(processInfo.shell_pid);
141
+
142
+ return {
143
+ ...(foregroundPids.length > 0 ? { foregroundPids } : {}),
144
+ ...(shellPid !== null ? { shellPid } : {}),
145
+ };
146
+ }
147
+
148
+ function getArray(value: unknown, path: string[]): unknown[] | null {
149
+ let current = value;
150
+ for (const part of path) {
151
+ if (!current || typeof current !== "object" || Array.isArray(current) || !(part in current)) {
152
+ return null;
153
+ }
154
+ current = (current as Record<string, unknown>)[part];
155
+ }
156
+ return Array.isArray(current) ? current : null;
157
+ }
158
+
159
+ function getObject(value: unknown, path: string[]): Record<string, unknown> | null {
160
+ let current = value;
161
+ for (const part of path) {
162
+ if (!current || typeof current !== "object" || Array.isArray(current) || !(part in current)) {
163
+ return null;
164
+ }
165
+ current = (current as Record<string, unknown>)[part];
166
+ }
167
+ return current && typeof current === "object" && !Array.isArray(current)
168
+ ? (current as Record<string, unknown>)
169
+ : null;
170
+ }
171
+
172
+ function nonEmptyString(value: unknown): string | undefined {
173
+ return typeof value === "string" && value.trim() ? value : undefined;
174
+ }
175
+
176
+ function positiveIntegerOrNull(value: unknown): number | null {
177
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
178
+ }
@@ -3,6 +3,7 @@ import { HerdrRuntimeError } from "./HerdrErrors.js";
3
3
  import { parseHerdrRuntimeRef } from "./HerdrRuntimeRef.js";
4
4
  import {
5
5
  extractAgentRuntimeRef,
6
+ extractForegroundProcessInfoPid,
6
7
  extractProcessInfoPid,
7
8
  extractWorkspaceStart,
8
9
  } from "./HerdrResponseParsers.js";
@@ -18,12 +19,16 @@ import type {
18
19
 
19
20
  const AGENT_START_BUSY_RETRY_MS = 5000;
20
21
  const AGENT_START_BUSY_RETRY_INTERVAL_MS = 100;
22
+ const PROCESS_INFO_POLL_MS = 3000;
23
+ const PROCESS_INFO_POLL_INTERVAL_MS = 100;
21
24
 
22
25
  export interface HerdrRuntimeOptions {
23
26
  runner?: HerdrCommandRunner;
24
27
  env?: NodeJS.ProcessEnv;
25
28
  agentStartBusyRetryMs?: number;
26
29
  agentStartBusyRetryIntervalMs?: number;
30
+ processInfoPollMs?: number;
31
+ processInfoPollIntervalMs?: number;
27
32
  }
28
33
 
29
34
  export class HerdrAgentRuntime implements InteractiveAgentRuntime {
@@ -33,6 +38,8 @@ export class HerdrAgentRuntime implements InteractiveAgentRuntime {
33
38
  private readonly env: NodeJS.ProcessEnv;
34
39
  private readonly agentStartBusyRetryMs: number;
35
40
  private readonly agentStartBusyRetryIntervalMs: number;
41
+ private readonly processInfoPollMs: number;
42
+ private readonly processInfoPollIntervalMs: number;
36
43
 
37
44
  constructor(options: HerdrRuntimeOptions = {}) {
38
45
  this.client = new HerdrCliClient(options.runner);
@@ -40,6 +47,9 @@ export class HerdrAgentRuntime implements InteractiveAgentRuntime {
40
47
  this.agentStartBusyRetryMs = options.agentStartBusyRetryMs ?? AGENT_START_BUSY_RETRY_MS;
41
48
  this.agentStartBusyRetryIntervalMs =
42
49
  options.agentStartBusyRetryIntervalMs ?? AGENT_START_BUSY_RETRY_INTERVAL_MS;
50
+ this.processInfoPollMs = options.processInfoPollMs ?? PROCESS_INFO_POLL_MS;
51
+ this.processInfoPollIntervalMs =
52
+ options.processInfoPollIntervalMs ?? PROCESS_INFO_POLL_INTERVAL_MS;
43
53
  }
44
54
 
45
55
  async isAvailable(): Promise<AgentRuntimeAvailability> {
@@ -99,13 +109,7 @@ export class HerdrAgentRuntime implements InteractiveAgentRuntime {
99
109
  paneId: workspace.paneId,
100
110
  workspaceId: workspace.workspaceId,
101
111
  });
102
- const processInfoResponse = await this.client.runJson([
103
- "pane",
104
- "process-info",
105
- "--pane",
106
- runtimeRef.paneId,
107
- ]);
108
- const pid = extractProcessInfoPid(processInfoResponse);
112
+ const pid = await this.readStartedAgentPid(runtimeRef.paneId);
109
113
  if (pid === null) {
110
114
  throw new HerdrRuntimeError("Herdr pane process-info response did not include process pid.");
111
115
  }
@@ -178,6 +182,28 @@ export class HerdrAgentRuntime implements InteractiveAgentRuntime {
178
182
  })
179
183
  );
180
184
  }
185
+
186
+ private async readStartedAgentPid(paneId: string): Promise<number | null> {
187
+ const deadline = Date.now() + this.processInfoPollMs;
188
+ let fallbackPid: number | null = null;
189
+
190
+ do {
191
+ const processInfoResponse = await this.client.runJson([
192
+ "pane",
193
+ "process-info",
194
+ "--pane",
195
+ paneId,
196
+ ]);
197
+ const foregroundPid = extractForegroundProcessInfoPid(processInfoResponse);
198
+ if (foregroundPid !== null) return foregroundPid;
199
+ fallbackPid ??= extractProcessInfoPid(processInfoResponse);
200
+
201
+ if (Date.now() >= deadline) break;
202
+ await sleep(this.processInfoPollIntervalMs);
203
+ } while (Date.now() < deadline);
204
+
205
+ return fallbackPid;
206
+ }
181
207
  }
182
208
 
183
209
  function sleep(ms: number): Promise<void> {
@@ -30,15 +30,25 @@ export function extractAgentRuntimeRef(
30
30
  }
31
31
 
32
32
  export function extractProcessInfoPid(value: unknown): number | null {
33
+ return extractForegroundProcessInfoPid(value) ?? extractShellPid(value);
34
+ }
35
+
36
+ export function extractForegroundProcessInfoPid(value: unknown): number | null {
33
37
  const processInfo = getObject(value, ["result", "process_info"]);
34
38
  if (!processInfo) return null;
35
39
  const processes = processInfo.foreground_processes;
36
- if (!Array.isArray(processes)) return numberOrNull(processInfo.shell_pid);
40
+ if (!Array.isArray(processes)) return null;
37
41
  for (const processInfo of processes) {
38
42
  if (!processInfo || typeof processInfo !== "object") continue;
39
43
  const pid = numberOrNull((processInfo as { pid?: unknown }).pid);
40
44
  if (pid !== null) return pid;
41
45
  }
46
+ return null;
47
+ }
48
+
49
+ export function extractShellPid(value: unknown): number | null {
50
+ const processInfo = getObject(value, ["result", "process_info"]);
51
+ if (!processInfo) return null;
42
52
  return numberOrNull(processInfo.shell_pid);
43
53
  }
44
54
 
@@ -130,14 +130,16 @@ export class AgentRegistry {
130
130
 
131
131
  private mergeEntry(incoming: RegistryEntry, existing: RegistryEntry | undefined): RegistryEntry {
132
132
  if (!existing) return incoming;
133
- const incomingIsManaged =
134
- incoming.runtime === "herdr" || Boolean(parseTmuxRuntimeRef(incoming.runtimeRef));
133
+ const keepExistingRuntime =
134
+ existing.runtime === "herdr" ||
135
+ Boolean(parseTmuxRuntimeRef(existing.runtimeRef)) ||
136
+ incoming.runtime !== "herdr";
135
137
  return {
136
138
  ...existing,
137
- name: incomingIsManaged ? incoming.name : existing.name,
138
- runtime: incoming.runtime ?? existing.runtime,
139
- runtimeRef: incoming.runtimeRef ?? existing.runtimeRef,
140
- cwd: incoming.cwd || existing.cwd,
139
+ name: existing.name,
140
+ runtime: keepExistingRuntime ? existing.runtime : incoming.runtime,
141
+ runtimeRef: keepExistingRuntime ? existing.runtimeRef : incoming.runtimeRef,
142
+ cwd: existing.cwd || incoming.cwd,
141
143
  startedAt: existing.startedAt || incoming.startedAt,
142
144
  sessionId: incoming.sessionId || existing.sessionId,
143
145
  sessionFilePath: incoming.sessionFilePath || existing.sessionFilePath,
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/__tests__/adapters/CopilotAdapter.test.ts"],"sourcesContent":["/**\n * Tests for CopilotAdapter\n */\n\nimport type { MockedFunction } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\n\nimport { CopilotAdapter } from \"../../adapters/CopilotAdapter.js\";\nimport type { ProcessInfo } from \"../../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../../adapters/AgentAdapter.js\";\nimport {\n listAgentProcesses,\n enrichProcesses,\n captureProcessSnapshot,\n} from \"../../utils/process.js\";\nimport { generateAgentName } from \"../../utils/matching.js\";\nimport { AgentRegistry } from \"../../utils/AgentRegistry.js\";\n\nvi.mock(\"../../utils/process.js\", async (importOriginal) => {\n const actual = (await importOriginal()) as typeof import(\"../../utils/process.js\");\n return {\n ...actual,\n listAgentProcesses: vi.fn(),\n enrichProcesses: vi.fn(),\n captureProcessSnapshot: vi.fn(),\n };\n});\n\nvi.mock(\"../../utils/matching.js\", () => ({\n generateAgentName: vi.fn(),\n}));\n\nconst mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;\nconst mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;\nconst mockedCaptureProcessSnapshot = captureProcessSnapshot as MockedFunction<\n typeof captureProcessSnapshot\n>;\nconst mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;\n\ndescribe(\"CopilotAdapter\", () => {\n let adapter: CopilotAdapter;\n let tmpDir: string;\n let sessionStateDir: string;\n\n beforeEach(() => {\n adapter = new CopilotAdapter();\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"copilot-test-\"));\n sessionStateDir = path.join(tmpDir, \"session-state\");\n fs.mkdirSync(sessionStateDir, { recursive: true });\n (adapter as any).sessionStateDir = sessionStateDir;\n\n mockedListAgentProcesses.mockReset();\n mockedEnrichProcesses.mockReset();\n mockedCaptureProcessSnapshot.mockReset();\n mockedGenerateAgentName.mockReset();\n mockedEnrichProcesses.mockImplementation((procs) => procs);\n // Compatibility shim for standalone adapter discovery; the manager captures once and slices by name.\n mockedCaptureProcessSnapshot.mockImplementation(async (names) =>\n enrichProcesses(names.flatMap((name) => listAgentProcesses(name))),\n );\n mockedGenerateAgentName.mockImplementation(\n (cwd, pid) => `${path.basename(cwd) || \"unknown\"} (${pid})`,\n );\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n function writeSession(\n sessionId: string,\n options: {\n events?: Array<object | string>;\n workspace?: Record<string, string>;\n lockPid?: number | string;\n },\n ): string {\n const sessionDir = path.join(sessionStateDir, sessionId);\n fs.mkdirSync(sessionDir, { recursive: true });\n\n if (options.events) {\n const lines = options.events.map((entry) =>\n typeof entry === \"string\" ? entry : JSON.stringify(entry),\n );\n fs.writeFileSync(path.join(sessionDir, \"events.jsonl\"), lines.join(\"\\n\"));\n }\n\n if (options.workspace) {\n const lines = Object.entries(options.workspace).map(([key, value]) => `${key}: ${value}`);\n fs.writeFileSync(path.join(sessionDir, \"workspace.yaml\"), lines.join(\"\\n\"));\n }\n\n if (options.lockPid !== undefined) {\n fs.writeFileSync(path.join(sessionDir, `inuse.${options.lockPid}.lock`), \"\");\n }\n\n return sessionDir;\n }\n\n function sessionStart(\n sessionId: string,\n cwd: string,\n startTime = \"2026-06-09T09:50:00.000Z\",\n ): object {\n return {\n type: \"session.start\",\n data: {\n sessionId,\n startTime,\n context: {\n cwd,\n gitRoot: cwd,\n branch: \"main\",\n },\n },\n timestamp: startTime,\n };\n }\n\n describe(\"initialization\", () => {\n it(\"exposes copilot type\", () => {\n expect(adapter.type).toBe(\"copilot\");\n });\n });\n\n describe(\"canHandle\", () => {\n it(\"returns true for copilot commands\", () => {\n expect(adapter.canHandle({ pid: 1, command: \"copilot\", cwd: \"/repo\", tty: \"ttys001\" })).toBe(\n true,\n );\n });\n\n it(\"returns true for full-path Homebrew copilot commands\", () => {\n expect(\n adapter.canHandle({\n pid: 2,\n command: \"/opt/homebrew/Caskroom/copilot-cli/1.0.60/copilot\",\n cwd: \"/repo\",\n tty: \"ttys002\",\n }),\n ).toBe(true);\n });\n\n it(\"returns true for copilot.exe commands\", () => {\n expect(\n adapter.canHandle({\n pid: 3,\n command: \"/usr/bin/copilot.exe\",\n cwd: \"/repo\",\n tty: \"ttys003\",\n }),\n ).toBe(true);\n });\n\n it(\"returns false when copilot appears only in an argument\", () => {\n expect(\n adapter.canHandle({\n pid: 4,\n command: \"node /repo/copilot-plugin/index.js\",\n cwd: \"/repo\",\n tty: \"ttys004\",\n }),\n ).toBe(false);\n });\n });\n\n describe(\"detectAgents\", () => {\n it(\"returns empty list when no copilot process is running\", async () => {\n mockedListAgentProcesses.mockReturnValue([]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toEqual([]);\n expect(mockedListAgentProcesses).toHaveBeenCalledWith(\"copilot\");\n });\n\n it(\"maps matching inuse lock and events to an active agent\", async () => {\n const processes: ProcessInfo[] = [\n {\n pid: 14096,\n command: \"/opt/homebrew/Caskroom/copilot-cli/1.0.60/copilot\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n },\n ];\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n writeSession(\"sess-a\", {\n lockPid: 14096,\n events: [\n sessionStart(\"sess-a\", \"/repo\", \"2026-06-09T09:50:00.000Z\"),\n {\n type: \"user.message\",\n data: { content: \"Build the Copilot adapter\" },\n timestamp: new Date().toISOString(),\n },\n {\n type: \"assistant.message\",\n data: { content: \"I will inspect the code.\" },\n timestamp: new Date().toISOString(),\n },\n ],\n workspace: {\n id: \"sess-a\",\n cwd: \"/fallback\",\n name: \"Fallback Name\",\n created_at: \"2026-06-09T09:49:00.000Z\",\n updated_at: \"2026-06-09T09:51:00.000Z\",\n },\n });\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"copilot\",\n status: AgentStatus.WAITING,\n pid: 14096,\n projectPath: \"/repo\",\n sessionId: \"sess-a\",\n summary: \"Build the Copilot adapter\",\n sessionFilePath: path.join(sessionStateDir, \"sess-a\", \"events.jsonl\"),\n });\n });\n\n it(\"ignores invalid and unmatched lock PIDs\", async () => {\n const processes: ProcessInfo[] = [\n { pid: 100, command: \"copilot\", cwd: \"/repo\", tty: \"ttys001\" },\n ];\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n writeSession(\"invalid\", {\n lockPid: \"not-a-pid\",\n events: [sessionStart(\"invalid\", \"/invalid\")],\n });\n writeSession(\"unmatched\", {\n lockPid: 999999,\n events: [sessionStart(\"unmatched\", \"/unmatched\")],\n });\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n pid: 100,\n sessionId: \"pid-100\",\n summary: \"Copilot process running\",\n });\n });\n\n it(\"falls back to process-only agent when no session lock matches\", async () => {\n const processes: ProcessInfo[] = [\n { pid: 200, command: \"copilot\", cwd: \"/repo-b\", tty: \"ttys002\" },\n ];\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"copilot\",\n status: AgentStatus.RUNNING,\n pid: 200,\n projectPath: \"/repo-b\",\n sessionId: \"pid-200\",\n summary: \"Copilot process running\",\n });\n });\n\n it(\"suppresses wrapper process-only agents before the session lock exists\", async () => {\n const processes: ProcessInfo[] = [\n { pid: 86800, command: \"copilot\", cwd: \"/repo\", tty: \"ttys001\", ppid: 84174 },\n {\n pid: 86810,\n command: \"/custom/install/copilot\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n ppid: 86800,\n },\n ];\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n pid: 86810,\n sessionId: \"pid-86810\",\n summary: \"Copilot process running\",\n });\n });\n\n it(\"carries the managed wrapper name to a process-only child before the session lock exists\", async () => {\n const registry = new AgentRegistry(path.join(tmpDir, \"agents.json\"));\n adapter = new CopilotAdapter(registry);\n (adapter as any).sessionStateDir = sessionStateDir;\n const processes: ProcessInfo[] = [\n { pid: 86800, command: \"copilot\", cwd: \"/repo\", tty: \"ttys001\", ppid: 84174 },\n {\n pid: 86810,\n command: \"/custom/install/copilot\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n ppid: 86800,\n },\n ];\n registry.register({\n name: \"copilot-started\",\n type: \"copilot\",\n pid: 86800,\n runtime: \"tmux\",\n runtimeRef: { session: \"copilot-started\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-13T19:15:16.211Z\",\n sessionId: \"pid-86800\",\n sessionFilePath: \"\",\n });\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n name: \"copilot-started\",\n pid: 86810,\n sessionId: \"pid-86810\",\n });\n });\n\n it(\"does not add duplicate process-only agent for wrapper process in the same terminal\", async () => {\n const processes: ProcessInfo[] = [\n { pid: 14095, command: \"copilot\", cwd: \"/repo\", tty: \"ttys001\" },\n {\n pid: 14096,\n command: \"/opt/homebrew/Caskroom/copilot-cli/1.0.60/copilot\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n },\n ];\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n writeSession(\"sess-wrapper\", {\n lockPid: 14096,\n events: [\n sessionStart(\"sess-wrapper\", \"/repo\", \"2026-06-09T09:50:00.000Z\"),\n { type: \"user.message\", data: { content: \"hello\" }, timestamp: new Date().toISOString() },\n ],\n });\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n pid: 14096,\n sessionId: \"sess-wrapper\",\n });\n });\n\n it(\"carries the managed wrapper name to a lock-backed child process\", async () => {\n const registry = new AgentRegistry(path.join(tmpDir, \"agents.json\"));\n adapter = new CopilotAdapter(registry);\n (adapter as any).sessionStateDir = sessionStateDir;\n const processes: ProcessInfo[] = [\n { pid: 14095, command: \"copilot\", cwd: \"/repo\", tty: \"ttys001\", ppid: 84174 },\n {\n pid: 14096,\n command: \"/opt/homebrew/Caskroom/copilot-cli/1.0.60/copilot\",\n cwd: \"/repo\",\n tty: \"ttys001\",\n ppid: 14095,\n },\n ];\n registry.register({\n name: \"copilot-started\",\n type: \"copilot\",\n pid: 14095,\n runtime: \"tmux\",\n runtimeRef: { session: \"copilot-started\" },\n cwd: \"/repo\",\n startedAt: \"2026-06-13T19:15:16.211Z\",\n sessionId: \"pid-14095\",\n sessionFilePath: \"\",\n });\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n writeSession(\"sess-wrapper\", {\n lockPid: 14096,\n events: [\n sessionStart(\"sess-wrapper\", \"/repo\", \"2026-06-09T09:50:00.000Z\"),\n { type: \"user.message\", data: { content: \"hello\" }, timestamp: new Date().toISOString() },\n ],\n });\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n name: \"copilot-started\",\n pid: 14096,\n sessionId: \"sess-wrapper\",\n });\n });\n\n it(\"uses workspace metadata when events are missing\", async () => {\n const processes: ProcessInfo[] = [\n { pid: 300, command: \"copilot\", cwd: \"/proc-cwd\", tty: \"ttys003\" },\n ];\n mockedListAgentProcesses.mockReturnValue(processes);\n mockedEnrichProcesses.mockReturnValue(processes);\n writeSession(\"workspace-only\", {\n lockPid: 300,\n workspace: {\n id: \"workspace-only\",\n cwd: \"/workspace-cwd\",\n name: \"Workspace Session\",\n created_at: \"2026-06-09T09:00:00.000Z\",\n updated_at: \"2026-06-09T09:10:00.000Z\",\n },\n });\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n pid: 300,\n projectPath: \"/workspace-cwd\",\n sessionId: \"workspace-only\",\n summary: \"Workspace Session\",\n });\n });\n });\n\n describe(\"getConversation\", () => {\n it(\"parses user and assistant message events\", () => {\n const sessionDir = writeSession(\"conv\", {\n events: [\n sessionStart(\"conv\", \"/repo\"),\n {\n type: \"user.message\",\n data: { content: \"hello\" },\n timestamp: \"2026-06-09T09:50:01.000Z\",\n },\n {\n type: \"assistant.message\",\n data: { content: \"Hi there\" },\n timestamp: \"2026-06-09T09:50:02.000Z\",\n },\n ],\n });\n\n const messages = adapter.getConversation(path.join(sessionDir, \"events.jsonl\"));\n\n expect(messages).toEqual([\n { role: \"user\", content: \"hello\", timestamp: \"2026-06-09T09:50:01.000Z\" },\n { role: \"assistant\", content: \"Hi there\", timestamp: \"2026-06-09T09:50:02.000Z\" },\n ]);\n });\n\n it(\"includes system/info/warning messages only in verbose mode\", () => {\n const sessionDir = writeSession(\"verbose\", {\n events: [\n {\n type: \"session.warning\",\n data: { message: \"MCP failed\" },\n timestamp: \"2026-06-09T09:50:01.000Z\",\n },\n {\n type: \"tool.execution_complete\",\n data: { result: { content: \"Tool result\" } },\n timestamp: \"2026-06-09T09:50:02.000Z\",\n },\n ],\n });\n const filePath = path.join(sessionDir, \"events.jsonl\");\n\n expect(adapter.getConversation(filePath)).toEqual([]);\n expect(adapter.getConversation(filePath, { verbose: true })).toEqual([\n { role: \"system\", content: \"MCP failed\", timestamp: \"2026-06-09T09:50:01.000Z\" },\n { role: \"system\", content: \"Tool result\", timestamp: \"2026-06-09T09:50:02.000Z\" },\n ]);\n });\n\n it(\"skips malformed JSONL lines and missing text\", () => {\n const sessionDir = writeSession(\"malformed\", {\n events: [\n \"not json\",\n {\n type: \"user.message\",\n data: { content: \"valid\" },\n timestamp: \"2026-06-09T09:50:01.000Z\",\n },\n { type: \"assistant.message\", data: {}, timestamp: \"2026-06-09T09:50:02.000Z\" },\n ],\n });\n\n const messages = adapter.getConversation(path.join(sessionDir, \"events.jsonl\"));\n\n expect(messages).toEqual([\n { role: \"user\", content: \"valid\", timestamp: \"2026-06-09T09:50:01.000Z\" },\n ]);\n });\n\n it(\"returns empty array for missing file\", () => {\n expect(adapter.getConversation(path.join(tmpDir, \"missing.jsonl\"))).toEqual([]);\n });\n });\n\n describe(\"listSessions\", () => {\n it(\"returns historical sessions without active locks\", async () => {\n writeSession(\"history-a\", {\n events: [\n sessionStart(\"history-a\", \"/repo-a\", \"2026-06-09T09:00:00.000Z\"),\n {\n type: \"user.message\",\n data: { content: \"first user\" },\n timestamp: \"2026-06-09T09:00:01.000Z\",\n },\n {\n type: \"assistant.message\",\n data: { content: \"answer\" },\n timestamp: \"2026-06-09T09:00:02.000Z\",\n },\n ],\n });\n writeSession(\"history-b\", {\n events: [\n sessionStart(\"history-b\", \"/repo-b\", \"2026-06-09T10:00:00.000Z\"),\n {\n type: \"user.message\",\n data: { content: \"other user\" },\n timestamp: \"2026-06-09T10:00:01.000Z\",\n },\n ],\n });\n\n const sessions = await adapter.listSessions();\n\n expect(sessions).toHaveLength(2);\n const byId = Object.fromEntries(sessions.map((session) => [session.sessionId, session]));\n expect(byId[\"history-a\"]).toMatchObject({\n type: \"copilot\",\n cwd: \"/repo-a\",\n firstUserMessage: \"first user\",\n sessionFilePath: path.join(sessionStateDir, \"history-a\", \"events.jsonl\"),\n });\n expect(byId[\"history-b\"]).toMatchObject({\n type: \"copilot\",\n cwd: \"/repo-b\",\n firstUserMessage: \"other user\",\n });\n });\n\n it(\"applies strict cwd filter\", async () => {\n writeSession(\"keep\", { events: [sessionStart(\"keep\", \"/repo\")] });\n writeSession(\"drop\", { events: [sessionStart(\"drop\", \"/other\")] });\n\n const sessions = await adapter.listSessions({ cwd: \"/repo\" });\n\n expect(sessions).toHaveLength(1);\n expect(sessions[0].sessionId).toBe(\"keep\");\n });\n\n it(\"uses workspace fallback for historical sessions without events\", async () => {\n writeSession(\"workspace-history\", {\n workspace: {\n id: \"workspace-history\",\n cwd: \"/workspace-repo\",\n name: \"Workspace History\",\n created_at: \"2026-06-09T08:00:00.000Z\",\n updated_at: \"2026-06-09T08:10:00.000Z\",\n },\n });\n\n const sessions = await adapter.listSessions();\n\n expect(sessions).toHaveLength(1);\n expect(sessions[0]).toMatchObject({\n type: \"copilot\",\n sessionId: \"workspace-history\",\n cwd: \"/workspace-repo\",\n firstUserMessage: \"\",\n sessionFilePath: path.join(sessionStateDir, \"workspace-history\", \"events.jsonl\"),\n });\n });\n\n it(\"skips session directories without events or workspace metadata\", async () => {\n fs.mkdirSync(path.join(sessionStateDir, \"empty\"), { recursive: true });\n\n const sessions = await adapter.listSessions();\n\n expect(sessions).toEqual([]);\n });\n\n it(\"returns empty when session-state directory does not exist\", async () => {\n (adapter as any).sessionStateDir = path.join(tmpDir, \"missing\");\n\n await expect(adapter.listSessions()).resolves.toEqual([]);\n });\n });\n});\n"],"names":["fs","os","path","CopilotAdapter","AgentStatus","listAgentProcesses","enrichProcesses","captureProcessSnapshot","generateAgentName","AgentRegistry","vi","mock","importOriginal","actual","fn","mockedListAgentProcesses","mockedEnrichProcesses","mockedCaptureProcessSnapshot","mockedGenerateAgentName","describe","adapter","tmpDir","sessionStateDir","beforeEach","mkdtempSync","join","tmpdir","mkdirSync","recursive","mockReset","mockImplementation","procs","names","flatMap","name","cwd","pid","basename","afterEach","rmSync","force","writeSession","sessionId","options","sessionDir","events","lines","map","entry","JSON","stringify","writeFileSync","workspace","Object","entries","key","value","lockPid","undefined","sessionStart","startTime","type","data","context","gitRoot","branch","timestamp","it","expect","toBe","canHandle","command","tty","mockReturnValue","agents","detectAgents","toEqual","toHaveBeenCalledWith","processes","content","Date","toISOString","id","created_at","updated_at","toHaveLength","toMatchObject","status","WAITING","projectPath","summary","sessionFilePath","RUNNING","ppid","registry","register","runtime","runtimeRef","session","startedAt","messages","getConversation","role","message","result","filePath","verbose","sessions","listSessions","byId","fromEntries","firstUserMessage","resolves"],"mappings":"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"}