@redplanethq/corebrain 2.6.2 → 2.6.4

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 (69) hide show
  1. package/dist/commands/coding/close.d.ts.map +1 -1
  2. package/dist/commands/coding/close.js +13 -25
  3. package/dist/commands/coding/close.js.map +1 -1
  4. package/dist/commands/coding/list.d.ts +4 -0
  5. package/dist/commands/coding/list.d.ts.map +1 -1
  6. package/dist/commands/coding/list.js +18 -38
  7. package/dist/commands/coding/list.js.map +1 -1
  8. package/dist/commands/coding/read.d.ts +1 -0
  9. package/dist/commands/coding/read.d.ts.map +1 -1
  10. package/dist/commands/coding/read.js +36 -35
  11. package/dist/commands/coding/read.js.map +1 -1
  12. package/dist/commands/coding/resume.d.ts +1 -0
  13. package/dist/commands/coding/resume.d.ts.map +1 -1
  14. package/dist/commands/coding/resume.js +37 -31
  15. package/dist/commands/coding/resume.js.map +1 -1
  16. package/dist/commands/coding/setup.d.ts.map +1 -1
  17. package/dist/commands/coding/setup.js +34 -5
  18. package/dist/commands/coding/setup.js.map +1 -1
  19. package/dist/commands/coding/start.d.ts.map +1 -1
  20. package/dist/commands/coding/start.js +7 -32
  21. package/dist/commands/coding/start.js.map +1 -1
  22. package/dist/server/gateway-client.d.ts.map +1 -1
  23. package/dist/server/gateway-client.js +4 -0
  24. package/dist/server/gateway-client.js.map +1 -1
  25. package/dist/server/tools/coding-tools.d.ts +0 -22
  26. package/dist/server/tools/coding-tools.d.ts.map +1 -1
  27. package/dist/server/tools/coding-tools.js +237 -233
  28. package/dist/server/tools/coding-tools.js.map +1 -1
  29. package/dist/tui/chat.d.ts.map +1 -1
  30. package/dist/tui/chat.js +57 -1
  31. package/dist/tui/chat.js.map +1 -1
  32. package/dist/tui/components/tool-call-item.d.ts.map +1 -1
  33. package/dist/tui/components/tool-call-item.js +16 -8
  34. package/dist/tui/components/tool-call-item.js.map +1 -1
  35. package/dist/tui/hooks/use-conversation.d.ts +2 -0
  36. package/dist/tui/hooks/use-conversation.d.ts.map +1 -1
  37. package/dist/tui/hooks/use-conversation.js +8 -1
  38. package/dist/tui/hooks/use-conversation.js.map +1 -1
  39. package/dist/tui/utils/stream.d.ts +1 -1
  40. package/dist/tui/utils/stream.d.ts.map +1 -1
  41. package/dist/tui/utils/stream.js +2 -2
  42. package/dist/tui/utils/stream.js.map +1 -1
  43. package/dist/types/config.d.ts +2 -0
  44. package/dist/types/config.d.ts.map +1 -1
  45. package/dist/utils/coding-agents/claude-code.d.ts +8 -6
  46. package/dist/utils/coding-agents/claude-code.d.ts.map +1 -1
  47. package/dist/utils/coding-agents/claude-code.js +77 -111
  48. package/dist/utils/coding-agents/claude-code.js.map +1 -1
  49. package/dist/utils/coding-agents/codex.d.ts +23 -0
  50. package/dist/utils/coding-agents/codex.d.ts.map +1 -0
  51. package/dist/utils/coding-agents/codex.js +243 -0
  52. package/dist/utils/coding-agents/codex.js.map +1 -0
  53. package/dist/utils/coding-agents/index.d.ts +12 -10
  54. package/dist/utils/coding-agents/index.d.ts.map +1 -1
  55. package/dist/utils/coding-agents/index.js +30 -23
  56. package/dist/utils/coding-agents/index.js.map +1 -1
  57. package/dist/utils/coding-agents/types.d.ts +37 -21
  58. package/dist/utils/coding-agents/types.d.ts.map +1 -1
  59. package/dist/utils/coding-agents/types.js +98 -1
  60. package/dist/utils/coding-agents/types.js.map +1 -1
  61. package/dist/utils/coding-runner.d.ts +1 -28
  62. package/dist/utils/coding-runner.d.ts.map +1 -1
  63. package/dist/utils/coding-runner.js +10 -85
  64. package/dist/utils/coding-runner.js.map +1 -1
  65. package/dist/utils/coding-sessions.d.ts +4 -57
  66. package/dist/utils/coding-sessions.d.ts.map +1 -1
  67. package/dist/utils/coding-sessions.js +9 -75
  68. package/dist/utils/coding-sessions.js.map +1 -1
  69. package/package.json +2 -2
@@ -1,127 +1,149 @@
1
1
  import zod from 'zod';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { existsSync } from 'node:fs';
4
- import { getSession, updateSession, listSessions, createSession, closeSession as closeStoredSession, } from '../../utils/coding-sessions.js';
4
+ import { getPreferences } from '../../config/preferences.js';
5
+ import { getSession, upsertSession, deleteSession, listRunningSessions, } from '../../utils/coding-sessions.js';
5
6
  import { getAgentConfig, buildStartArgs, buildResumeArgs, startAgentProcess, isProcessRunning, stopProcess, } from '../../utils/coding-runner.js';
6
- import { readAgentSessionOutput, agentSessionExists, getAgentReader } from '../../utils/coding-agents/index.js';
7
- // ============ Zod Schemas ============
8
- export const StartSessionSchema = zod.object({
9
- agent: zod.string(),
7
+ import { readAgentSessionOutput, agentSessionExists, getAgentReader, scanAllSessions, searchSessions } from '../../utils/coding-agents/index.js';
8
+ // ============ Schemas ============
9
+ const AskSchema = zod.object({
10
+ agent: zod.string().optional(),
10
11
  prompt: zod.string(),
11
12
  dir: zod.string(),
13
+ sessionId: zod.string().optional(),
12
14
  model: zod.string().optional(),
13
15
  systemPrompt: zod.string().optional(),
14
16
  });
15
- export const ResumeSessionSchema = zod.object({
16
- sessionId: zod.string(),
17
- prompt: zod.string(),
18
- });
19
- export const CloseSessionSchema = zod.object({
17
+ const CloseSessionSchema = zod.object({
20
18
  sessionId: zod.string(),
21
19
  });
22
- export const ReadSessionSchema = zod.object({
20
+ const ReadSessionSchema = zod.object({
23
21
  sessionId: zod.string(),
22
+ dir: zod.string(),
24
23
  lines: zod.number().optional(),
25
24
  offset: zod.number().optional(),
26
25
  tail: zod.boolean().optional(),
27
26
  });
28
- export const ListSessionsSchema = zod.object({});
27
+ const ListSessionsSchema = zod.object({
28
+ agent: zod.string().optional(), // e.g. "claude-code" or "codex-cli"
29
+ since: zod.string().optional(), // ISO date string e.g. "2024-01-01"
30
+ dir: zod.string().optional(),
31
+ limit: zod.number().optional(),
32
+ offset: zod.number().optional(),
33
+ });
34
+ const SearchSessionsSchema = zod.object({
35
+ query: zod.string(),
36
+ dir: zod.string().optional(),
37
+ limit: zod.number().optional(),
38
+ });
29
39
  // ============ JSON Schemas ============
30
40
  const jsonSchemas = {
31
- coding_start_session: {
41
+ coding_ask: {
32
42
  type: 'object',
33
43
  properties: {
34
44
  agent: {
35
45
  type: 'string',
36
- description: 'Coding agent to use (e.g., "claude-code")',
46
+ description: 'Coding agent to use (e.g., "claude-code", "codex-cli"). Omit to use the configured default.',
37
47
  },
38
48
  prompt: {
39
49
  type: 'string',
40
- description: 'The task/prompt to send to the agent',
50
+ description: 'The question or task to send to the agent',
41
51
  },
42
52
  dir: {
43
53
  type: 'string',
44
54
  description: 'Working directory for the session (must exist)',
45
55
  },
56
+ sessionId: {
57
+ type: 'string',
58
+ description: 'Existing session ID to continue. Omit to start a new session.',
59
+ },
46
60
  model: {
47
61
  type: 'string',
48
- description: 'Model to use (optional)',
62
+ description: 'Model override (optional)',
49
63
  },
50
64
  systemPrompt: {
51
65
  type: 'string',
52
- description: 'System prompt (optional)',
66
+ description: 'System prompt override (optional, new sessions only)',
53
67
  },
54
68
  },
55
69
  required: ['agent', 'prompt', 'dir'],
56
70
  },
57
- coding_resume_session: {
71
+ coding_close_session: {
58
72
  type: 'object',
59
73
  properties: {
60
- sessionId: {
61
- type: 'string',
62
- description: 'Session ID to resume',
63
- },
64
- prompt: {
65
- type: 'string',
66
- description: 'The prompt to send to continue the session',
67
- },
74
+ sessionId: { type: 'string', description: 'Session ID to close' },
68
75
  },
69
- required: ['sessionId', 'prompt'],
76
+ required: ['sessionId'],
70
77
  },
71
- coding_close_session: {
78
+ coding_read_session: {
72
79
  type: 'object',
73
80
  properties: {
74
- sessionId: {
75
- type: 'string',
76
- description: 'Session ID to close',
81
+ sessionId: { type: 'string', description: 'Session ID to read output from' },
82
+ dir: { type: 'string', description: 'Working directory of the session' },
83
+ lines: { type: 'number', description: 'Number of lines to return' },
84
+ offset: { type: 'number', description: 'Line offset to start from (0-indexed)' },
85
+ tail: {
86
+ type: 'boolean',
87
+ description: 'If true, return the last N lines instead of first N',
77
88
  },
78
89
  },
79
- required: ['sessionId'],
90
+ required: ['sessionId', 'dir'],
80
91
  },
81
- coding_read_session: {
92
+ coding_list_sessions: {
82
93
  type: 'object',
83
94
  properties: {
84
- sessionId: {
95
+ agent: {
85
96
  type: 'string',
86
- description: 'Session ID to read output from',
97
+ description: 'Filter to a specific agent (e.g. "claude-code", "codex-cli")',
87
98
  },
88
- lines: {
99
+ since: {
100
+ type: 'string',
101
+ description: 'ISO date string to filter sessions updated after this date (e.g. "2024-03-01")',
102
+ },
103
+ dir: {
104
+ type: 'string',
105
+ description: 'Filter to a specific working directory (optional)',
106
+ },
107
+ limit: {
89
108
  type: 'number',
90
- description: 'Number of lines to return (default: all)',
109
+ description: 'Max sessions to return per page (default: 20)',
91
110
  },
92
111
  offset: {
93
112
  type: 'number',
94
- description: 'Line offset to start reading from (0-indexed, default: 0)',
95
- },
96
- tail: {
97
- type: 'boolean',
98
- description: 'If true, return the last N lines instead of first N (default: false)',
113
+ description: 'Sessions to skip for pagination (default: 0)',
99
114
  },
100
115
  },
101
- required: ['sessionId'],
116
+ required: [],
102
117
  },
103
- coding_list_sessions: {
118
+ coding_search_sessions: {
104
119
  type: 'object',
105
- properties: {},
106
- required: [],
107
- description: 'List all coding sessions',
120
+ properties: {
121
+ query: {
122
+ type: 'string',
123
+ description: 'Search term to match against session titles',
124
+ },
125
+ dir: {
126
+ type: 'string',
127
+ description: 'Restrict search to a specific working directory (optional)',
128
+ },
129
+ limit: {
130
+ type: 'number',
131
+ description: 'Max results to return (default: 10)',
132
+ },
133
+ },
134
+ required: ['query'],
108
135
  },
109
136
  };
110
137
  // ============ Tool Definitions ============
111
138
  export const codingTools = [
112
139
  {
113
- name: 'coding_start_session',
114
- description: 'Start a new coding session with the specified agent (runs in background). After starting, the first coding_read_session call may return status="initializing" this is normal while the agent boots. Wait a few seconds and retry before concluding failure.',
115
- inputSchema: jsonSchemas.coding_start_session,
116
- },
117
- {
118
- name: 'coding_resume_session',
119
- description: 'Resume an existing coding session',
120
- inputSchema: jsonSchemas.coding_resume_session,
140
+ name: 'coding_ask',
141
+ description: 'Send a prompt to a coding agent. Omit sessionId to start a new session; include it to continue an existing one. After calling, use coding_read_session to check output.',
142
+ inputSchema: jsonSchemas.coding_ask,
121
143
  },
122
144
  {
123
145
  name: 'coding_close_session',
124
- description: 'Close/stop a coding session',
146
+ description: 'Stop a running coding session',
125
147
  inputSchema: jsonSchemas.coding_close_session,
126
148
  },
127
149
  {
@@ -131,79 +153,126 @@ export const codingTools = [
131
153
  },
132
154
  {
133
155
  name: 'coding_list_sessions',
134
- description: 'List all coding sessions',
156
+ description: 'List all coding sessions from Claude\'s session history. Sorted by most recent. Supports date filtering.',
135
157
  inputSchema: jsonSchemas.coding_list_sessions,
136
158
  },
159
+ {
160
+ name: 'coding_search_sessions',
161
+ description: 'Search past coding sessions by title or first message content',
162
+ inputSchema: jsonSchemas.coding_search_sessions,
163
+ },
137
164
  ];
138
- // ============ Tool Handlers ============
139
- async function handleStartSession(params, logger) {
140
- // Check if directory exists
165
+ // ============ Helpers ============
166
+ /**
167
+ * Resolve which agent to use.
168
+ * Priority: explicit param → defaultCodingAgent pref → only configured agent → error
169
+ */
170
+ function resolveAgent(agentParam) {
171
+ if (agentParam)
172
+ return { agent: agentParam };
173
+ const prefs = getPreferences();
174
+ const coding = (prefs.coding ?? {});
175
+ const configured = Object.keys(coding);
176
+ if (configured.length === 0) {
177
+ return { error: 'No coding agents configured. Run: corebrain coding setup' };
178
+ }
179
+ if (prefs.defaultCodingAgent && coding[prefs.defaultCodingAgent]) {
180
+ return { agent: prefs.defaultCodingAgent };
181
+ }
182
+ if (configured.length === 1) {
183
+ return { agent: configured[0] };
184
+ }
185
+ return {
186
+ error: `Multiple agents configured (${configured.join(', ')}). Specify which to use or set a default with: corebrain coding setup`,
187
+ };
188
+ }
189
+ /**
190
+ * Auto-detect the agent for a session ID by trying all registered readers.
191
+ * Falls back to default agent or claude-code.
192
+ */
193
+ function detectAgentForSession(sessionId, dir) {
194
+ // Check running session store first
195
+ const stored = getSession(sessionId);
196
+ if (stored?.agent)
197
+ return stored.agent;
198
+ // Try each reader's sessionExists
199
+ const readers = ['claude-code', 'codex-cli'];
200
+ for (const agentName of readers) {
201
+ const reader = getAgentReader(agentName);
202
+ if (reader?.sessionExists(dir, sessionId))
203
+ return agentName;
204
+ }
205
+ // Fall back to default
206
+ const prefs = getPreferences();
207
+ return prefs.defaultCodingAgent ?? 'claude-code';
208
+ }
209
+ // ============ Handlers ============
210
+ async function handleAsk(params, logger) {
141
211
  if (!existsSync(params.dir)) {
212
+ return { success: false, error: `Directory "${params.dir}" does not exist` };
213
+ }
214
+ const resolved = resolveAgent(params.agent);
215
+ if ('error' in resolved)
216
+ return { success: false, error: resolved.error };
217
+ const agentName = resolved.agent;
218
+ const config = getAgentConfig(agentName);
219
+ if (!config) {
142
220
  return {
143
221
  success: false,
144
- error: `Directory "${params.dir}" does not exist`,
222
+ error: `Agent "${agentName}" not configured. Run 'corebrain coding config --agent ${agentName}' to set up.`,
145
223
  };
146
224
  }
147
- // Get agent config
148
- const config = getAgentConfig(params.agent);
149
- if (!config) {
225
+ const isResume = Boolean(params.sessionId);
226
+ const sessionId = params.sessionId ?? randomUUID();
227
+ // For resume, verify process is not already running
228
+ if (isResume && isProcessRunning(sessionId)) {
150
229
  return {
151
230
  success: false,
152
- error: `Agent "${params.agent}" not configured. Run 'corebrain coding config --agent ${params.agent}' to set up.`,
231
+ error: `Session "${sessionId}" is already running. Wait for it to finish before sending another prompt.`,
153
232
  };
154
233
  }
155
- // Generate session ID and create session
156
- const sessionId = randomUUID();
157
- const session = createSession({
234
+ // Build args
235
+ const args = isResume
236
+ ? buildResumeArgs(config, { prompt: params.prompt, sessionId })
237
+ : buildStartArgs(config, {
238
+ prompt: params.prompt,
239
+ sessionId,
240
+ model: params.model,
241
+ systemPrompt: params.systemPrompt,
242
+ });
243
+ // Upsert running session record
244
+ upsertSession({
158
245
  sessionId,
159
- agent: params.agent,
160
- prompt: params.prompt,
246
+ agent: agentName,
161
247
  dir: params.dir,
248
+ startedAt: Date.now(),
162
249
  });
163
- updateSession(session);
164
- // Build command args
165
- const args = buildStartArgs(config, {
166
- prompt: params.prompt,
167
- sessionId,
168
- model: params.model,
169
- systemPrompt: params.systemPrompt,
170
- });
171
- // Start process in background
172
250
  const { pid, error } = startAgentProcess(sessionId, config, args, params.dir, logger);
173
251
  if (error) {
174
- return {
175
- success: false,
176
- error: `Failed to start session: ${error}`,
177
- };
252
+ deleteSession(sessionId);
253
+ return { success: false, error: `Failed to start: ${error}` };
178
254
  }
179
- // If this agent has a session reader, wait until the session file appears
180
- // before returning this prevents callers from seeing a false "not started" state.
181
- const hasReader = getAgentReader(params.agent) !== null;
182
- if (hasReader) {
183
- const POLL_INTERVAL_MS = 500;
184
- const STARTUP_TIMEOUT_MS = 30_000;
185
- const deadline = Date.now() + STARTUP_TIMEOUT_MS;
255
+ // Store pid
256
+ upsertSession({ sessionId, agent: agentName, dir: params.dir, pid, startedAt: Date.now() });
257
+ // For agents with a session reader, wait until the session file appears
258
+ const hasReader = getAgentReader(agentName) !== null;
259
+ if (hasReader && !isResume) {
260
+ const deadline = Date.now() + 30_000;
186
261
  const sessionReady = await new Promise((resolve) => {
187
262
  function check() {
188
- if (agentSessionExists(params.agent, params.dir, sessionId)) {
263
+ if (agentSessionExists(agentName, params.dir, sessionId))
189
264
  return resolve(true);
190
- }
191
- if (!isProcessRunning(sessionId)) {
265
+ if (!isProcessRunning(sessionId))
192
266
  return resolve(false);
193
- }
194
- if (Date.now() >= deadline) {
267
+ if (Date.now() >= deadline)
195
268
  return resolve(false);
196
- }
197
- setTimeout(check, POLL_INTERVAL_MS);
269
+ setTimeout(check, 500);
198
270
  }
199
- setTimeout(check, POLL_INTERVAL_MS);
271
+ setTimeout(check, 500);
200
272
  });
201
273
  if (!sessionReady) {
202
274
  stopProcess(sessionId);
203
- session.status = 'error';
204
- session.error = 'Session failed to start within 30 seconds';
205
- session.updatedAt = Date.now();
206
- updateSession(session);
275
+ deleteSession(sessionId);
207
276
  return {
208
277
  success: false,
209
278
  error: 'Session failed to start: agent did not produce output within 30 seconds',
@@ -215,142 +284,51 @@ async function handleStartSession(params, logger) {
215
284
  result: {
216
285
  sessionId,
217
286
  pid,
218
- status: 'running',
219
- message: 'Session started. Use coding_read_session to check output.',
220
- },
221
- };
222
- }
223
- async function handleResumeSession(params, logger) {
224
- // Get existing session
225
- const session = getSession(params.sessionId);
226
- if (!session) {
227
- return {
228
- success: false,
229
- error: `Session "${params.sessionId}" not found`,
230
- };
231
- }
232
- // Check if already running
233
- if (isProcessRunning(params.sessionId)) {
234
- return {
235
- success: false,
236
- error: `Session "${params.sessionId}" is already running`,
237
- };
238
- }
239
- // Check if directory still exists
240
- if (!existsSync(session.dir)) {
241
- return {
242
- success: false,
243
- error: `Session directory "${session.dir}" no longer exists`,
244
- };
245
- }
246
- // Get agent config
247
- const config = getAgentConfig(session.agent);
248
- if (!config) {
249
- return {
250
- success: false,
251
- error: `Agent "${session.agent}" not configured`,
252
- };
253
- }
254
- // Update session status
255
- session.status = 'running';
256
- session.prompt = params.prompt;
257
- session.updatedAt = Date.now();
258
- updateSession(session);
259
- // Build resume args
260
- const args = buildResumeArgs(config, {
261
- prompt: params.prompt,
262
- sessionId: params.sessionId,
263
- });
264
- // Start process in background
265
- const { pid, error } = startAgentProcess(params.sessionId, config, args, session.dir, logger);
266
- if (error) {
267
- return {
268
- success: false,
269
- error: `Failed to resume session: ${error}`,
270
- };
271
- }
272
- return {
273
- success: true,
274
- result: {
275
- sessionId: params.sessionId,
276
- pid,
277
- status: 'running',
278
- message: 'Session resumed. Use coding_read_session to check output.',
287
+ resumed: isResume,
288
+ message: 'Use coding_read_session to check output.',
279
289
  },
280
290
  };
281
291
  }
282
292
  function handleCloseSession(params) {
283
- const session = getSession(params.sessionId);
284
- if (!session) {
285
- return {
286
- success: false,
287
- error: `Session "${params.sessionId}" not found`,
288
- };
289
- }
290
- // Stop the process if running (this triggers auto-save in runner)
291
- const wasRunning = stopProcess(params.sessionId);
292
- // Mark as closed in sessions.json
293
- closeStoredSession(params.sessionId);
294
- return {
295
- success: true,
296
- result: {
297
- sessionId: params.sessionId,
298
- wasRunning,
299
- message: 'Session closed',
300
- },
301
- };
293
+ stopProcess(params.sessionId);
294
+ deleteSession(params.sessionId);
295
+ return { success: true, result: { sessionId: params.sessionId, message: 'Session closed' } };
302
296
  }
303
297
  async function handleReadSession(params) {
304
- // First check stored session exists
305
- const session = getSession(params.sessionId);
306
- if (!session) {
307
- return {
308
- success: false,
309
- error: `Session "${params.sessionId}" not found`,
310
- };
311
- }
312
- // Check if process is still running
313
298
  const running = isProcessRunning(params.sessionId);
314
- // Read from agent's session file using the appropriate reader
315
- const { entries, totalLines, returnedLines, fileExists, fileSizeBytes, fileSizeHuman, error: readError, } = await readAgentSessionOutput(session.agent, session.dir, params.sessionId, {
299
+ // Detect agent: running store reader probe → default
300
+ const agent = detectAgentForSession(params.sessionId, params.dir);
301
+ const { entries, totalLines, returnedLines, fileExists, fileSizeBytes, fileSizeHuman, error: readError, } = await readAgentSessionOutput(agent, params.dir, params.sessionId, {
316
302
  lines: params.lines,
317
303
  offset: params.offset,
318
304
  tail: params.tail,
319
305
  });
320
- // Determine status
321
- let status = session.status;
306
+ let status;
322
307
  let statusMessage;
323
308
  if (running && !fileExists) {
324
- // Process is alive but hasn't written its session file yet — still booting
325
309
  status = 'initializing';
326
- statusMessage =
327
- 'Session is starting up. The agent has not written output yet. Wait a few seconds and read again.';
310
+ statusMessage = 'Agent is booting. Wait a few seconds and read again.';
328
311
  }
329
312
  else if (running) {
330
313
  status = 'running';
331
314
  }
332
- else if (fileExists && status === 'running') {
333
- // Process finished, update status
315
+ else {
316
+ // Process finished clean up running session record
317
+ const stored = getSession(params.sessionId);
318
+ if (stored)
319
+ deleteSession(params.sessionId);
334
320
  status = 'completed';
335
- session.status = 'completed';
336
- session.updatedAt = Date.now();
337
- updateSession(session);
338
321
  }
339
322
  return {
340
323
  success: true,
341
324
  result: {
342
- sessionId: session.sessionId,
343
- agent: session.agent,
344
- prompt: session.prompt,
345
- dir: session.dir,
325
+ sessionId: params.sessionId,
326
+ dir: params.dir,
346
327
  status,
347
328
  ...(statusMessage ? { statusMessage } : {}),
348
329
  running,
349
330
  entries,
350
- error: readError || session.error,
351
- exitCode: null,
352
- startedAt: session.startedAt,
353
- updatedAt: session.updatedAt,
331
+ error: readError,
354
332
  totalLines,
355
333
  returnedLines,
356
334
  fileExists,
@@ -359,43 +337,69 @@ async function handleReadSession(params) {
359
337
  },
360
338
  };
361
339
  }
362
- function handleListSessions() {
363
- const sessions = listSessions();
340
+ async function handleListSessions(params) {
341
+ const since = params.since ? new Date(params.since).getTime() : undefined;
342
+ const { sessions, total, hasMore } = await scanAllSessions({
343
+ agent: params.agent,
344
+ dir: params.dir,
345
+ since,
346
+ limit: params.limit ?? 20,
347
+ offset: params.offset ?? 0,
348
+ });
349
+ const runningIds = new Set(listRunningSessions().map((s) => s.sessionId));
350
+ return {
351
+ success: true,
352
+ result: {
353
+ sessions: sessions.map((s) => ({
354
+ sessionId: s.sessionId,
355
+ agent: s.agent,
356
+ dir: s.dir,
357
+ title: s.title,
358
+ running: runningIds.has(s.sessionId),
359
+ createdAt: new Date(s.createdAt).toISOString(),
360
+ updatedAt: new Date(s.updatedAt).toISOString(),
361
+ fileSizeBytes: s.fileSizeBytes,
362
+ })),
363
+ total,
364
+ hasMore,
365
+ offset: params.offset ?? 0,
366
+ },
367
+ };
368
+ }
369
+ async function handleSearchSessions(params) {
370
+ const sessions = await searchSessions(params.query, {
371
+ dir: params.dir,
372
+ limit: params.limit ?? 10,
373
+ });
374
+ const runningIds = new Set(listRunningSessions().map((s) => s.sessionId));
364
375
  return {
365
376
  success: true,
366
377
  result: {
367
- sessions: sessions.map(s => {
368
- // Check if process is still running
369
- const running = isProcessRunning(s.sessionId);
370
- return {
371
- sessionId: s.sessionId,
372
- agent: s.agent,
373
- dir: s.dir,
374
- status: running ? 'running' : s.status,
375
- running,
376
- startedAt: s.startedAt,
377
- updatedAt: s.updatedAt,
378
- };
379
- }),
378
+ sessions: sessions.map((s) => ({
379
+ sessionId: s.sessionId,
380
+ dir: s.dir,
381
+ title: s.title,
382
+ running: runningIds.has(s.sessionId),
383
+ updatedAt: new Date(s.updatedAt).toISOString(),
384
+ })),
380
385
  count: sessions.length,
381
386
  },
382
387
  };
383
388
  }
384
- // ============ Tool Execution ============
389
+ // ============ Dispatch ============
385
390
  export async function executeCodingTool(toolName, params, logger) {
386
391
  try {
387
392
  switch (toolName) {
388
- case 'coding_start_session':
389
- return await handleStartSession(StartSessionSchema.parse(params), logger);
390
- case 'coding_resume_session':
391
- return await handleResumeSession(ResumeSessionSchema.parse(params), logger);
393
+ case 'coding_ask':
394
+ return await handleAsk(AskSchema.parse(params), logger);
392
395
  case 'coding_close_session':
393
396
  return handleCloseSession(CloseSessionSchema.parse(params));
394
397
  case 'coding_read_session':
395
398
  return await handleReadSession(ReadSessionSchema.parse(params));
396
399
  case 'coding_list_sessions':
397
- ListSessionsSchema.parse(params);
398
- return handleListSessions();
400
+ return await handleListSessions(ListSessionsSchema.parse(params));
401
+ case 'coding_search_sessions':
402
+ return await handleSearchSessions(SearchSessionsSchema.parse(params));
399
403
  default:
400
404
  return { success: false, error: `Unknown tool: ${toolName}` };
401
405
  }