@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.
- package/dist/commands/coding/close.d.ts.map +1 -1
- package/dist/commands/coding/close.js +13 -25
- package/dist/commands/coding/close.js.map +1 -1
- package/dist/commands/coding/list.d.ts +4 -0
- package/dist/commands/coding/list.d.ts.map +1 -1
- package/dist/commands/coding/list.js +18 -38
- package/dist/commands/coding/list.js.map +1 -1
- package/dist/commands/coding/read.d.ts +1 -0
- package/dist/commands/coding/read.d.ts.map +1 -1
- package/dist/commands/coding/read.js +36 -35
- package/dist/commands/coding/read.js.map +1 -1
- package/dist/commands/coding/resume.d.ts +1 -0
- package/dist/commands/coding/resume.d.ts.map +1 -1
- package/dist/commands/coding/resume.js +37 -31
- package/dist/commands/coding/resume.js.map +1 -1
- package/dist/commands/coding/setup.d.ts.map +1 -1
- package/dist/commands/coding/setup.js +34 -5
- package/dist/commands/coding/setup.js.map +1 -1
- package/dist/commands/coding/start.d.ts.map +1 -1
- package/dist/commands/coding/start.js +7 -32
- package/dist/commands/coding/start.js.map +1 -1
- package/dist/server/gateway-client.d.ts.map +1 -1
- package/dist/server/gateway-client.js +4 -0
- package/dist/server/gateway-client.js.map +1 -1
- package/dist/server/tools/coding-tools.d.ts +0 -22
- package/dist/server/tools/coding-tools.d.ts.map +1 -1
- package/dist/server/tools/coding-tools.js +237 -233
- package/dist/server/tools/coding-tools.js.map +1 -1
- package/dist/tui/chat.d.ts.map +1 -1
- package/dist/tui/chat.js +57 -1
- package/dist/tui/chat.js.map +1 -1
- package/dist/tui/components/tool-call-item.d.ts.map +1 -1
- package/dist/tui/components/tool-call-item.js +16 -8
- package/dist/tui/components/tool-call-item.js.map +1 -1
- package/dist/tui/hooks/use-conversation.d.ts +2 -0
- package/dist/tui/hooks/use-conversation.d.ts.map +1 -1
- package/dist/tui/hooks/use-conversation.js +8 -1
- package/dist/tui/hooks/use-conversation.js.map +1 -1
- package/dist/tui/utils/stream.d.ts +1 -1
- package/dist/tui/utils/stream.d.ts.map +1 -1
- package/dist/tui/utils/stream.js +2 -2
- package/dist/tui/utils/stream.js.map +1 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/coding-agents/claude-code.d.ts +8 -6
- package/dist/utils/coding-agents/claude-code.d.ts.map +1 -1
- package/dist/utils/coding-agents/claude-code.js +77 -111
- package/dist/utils/coding-agents/claude-code.js.map +1 -1
- package/dist/utils/coding-agents/codex.d.ts +23 -0
- package/dist/utils/coding-agents/codex.d.ts.map +1 -0
- package/dist/utils/coding-agents/codex.js +243 -0
- package/dist/utils/coding-agents/codex.js.map +1 -0
- package/dist/utils/coding-agents/index.d.ts +12 -10
- package/dist/utils/coding-agents/index.d.ts.map +1 -1
- package/dist/utils/coding-agents/index.js +30 -23
- package/dist/utils/coding-agents/index.js.map +1 -1
- package/dist/utils/coding-agents/types.d.ts +37 -21
- package/dist/utils/coding-agents/types.d.ts.map +1 -1
- package/dist/utils/coding-agents/types.js +98 -1
- package/dist/utils/coding-agents/types.js.map +1 -1
- package/dist/utils/coding-runner.d.ts +1 -28
- package/dist/utils/coding-runner.d.ts.map +1 -1
- package/dist/utils/coding-runner.js +10 -85
- package/dist/utils/coding-runner.js.map +1 -1
- package/dist/utils/coding-sessions.d.ts +4 -57
- package/dist/utils/coding-sessions.d.ts.map +1 -1
- package/dist/utils/coding-sessions.js +9 -75
- package/dist/utils/coding-sessions.js.map +1 -1
- 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 {
|
|
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
|
-
// ============
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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'
|
|
76
|
+
required: ['sessionId'],
|
|
70
77
|
},
|
|
71
|
-
|
|
78
|
+
coding_read_session: {
|
|
72
79
|
type: 'object',
|
|
73
80
|
properties: {
|
|
74
|
-
sessionId: {
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
92
|
+
coding_list_sessions: {
|
|
82
93
|
type: 'object',
|
|
83
94
|
properties: {
|
|
84
|
-
|
|
95
|
+
agent: {
|
|
85
96
|
type: 'string',
|
|
86
|
-
description: '
|
|
97
|
+
description: 'Filter to a specific agent (e.g. "claude-code", "codex-cli")',
|
|
87
98
|
},
|
|
88
|
-
|
|
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: '
|
|
109
|
+
description: 'Max sessions to return per page (default: 20)',
|
|
91
110
|
},
|
|
92
111
|
offset: {
|
|
93
112
|
type: 'number',
|
|
94
|
-
description: '
|
|
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: [
|
|
116
|
+
required: [],
|
|
102
117
|
},
|
|
103
|
-
|
|
118
|
+
coding_search_sessions: {
|
|
104
119
|
type: 'object',
|
|
105
|
-
properties: {
|
|
106
|
-
|
|
107
|
-
|
|
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: '
|
|
114
|
-
description: '
|
|
115
|
-
inputSchema: jsonSchemas.
|
|
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: '
|
|
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
|
-
// ============
|
|
139
|
-
|
|
140
|
-
|
|
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: `
|
|
222
|
+
error: `Agent "${agentName}" not configured. Run 'corebrain coding config --agent ${agentName}' to set up.`,
|
|
145
223
|
};
|
|
146
224
|
}
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
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: `
|
|
231
|
+
error: `Session "${sessionId}" is already running. Wait for it to finish before sending another prompt.`,
|
|
153
232
|
};
|
|
154
233
|
}
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
|
|
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:
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
error: `Failed to start session: ${error}`,
|
|
177
|
-
};
|
|
252
|
+
deleteSession(sessionId);
|
|
253
|
+
return { success: false, error: `Failed to start: ${error}` };
|
|
178
254
|
}
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
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(
|
|
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,
|
|
271
|
+
setTimeout(check, 500);
|
|
200
272
|
});
|
|
201
273
|
if (!sessionReady) {
|
|
202
274
|
stopProcess(sessionId);
|
|
203
|
-
|
|
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
|
-
|
|
219
|
-
message: '
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
//
|
|
315
|
-
const
|
|
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
|
-
|
|
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
|
|
333
|
-
// Process finished
|
|
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:
|
|
343
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
-
// ============
|
|
389
|
+
// ============ Dispatch ============
|
|
385
390
|
export async function executeCodingTool(toolName, params, logger) {
|
|
386
391
|
try {
|
|
387
392
|
switch (toolName) {
|
|
388
|
-
case '
|
|
389
|
-
return await
|
|
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
|
-
|
|
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
|
}
|