@agent-spaces/server 0.3.67 → 0.4.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 (64) hide show
  1. package/dist/adapters/agent-runtime.js +4 -0
  2. package/dist/adapters/codex-function-tool-bridge.js +9 -1
  3. package/dist/adapters/git.js +13 -2
  4. package/dist/adapters/hermes-runtime.js +711 -108
  5. package/dist/adapters/langchain-runtime.js +384 -21
  6. package/dist/adapters/oh-my-pi-runtime.js +858 -0
  7. package/dist/adapters/open-agent-sdk-runtime.js +202 -5
  8. package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
  9. package/dist/agents/agent-message-parts.js +52 -2
  10. package/dist/agents/issue-task-controller.js +4 -2
  11. package/dist/agents/title-generator-agent.js +120 -0
  12. package/dist/app.js +59 -6
  13. package/dist/routes/agent-sse.js +70 -7
  14. package/dist/routes/channel.js +23 -2
  15. package/dist/routes/chat-run.js +191 -0
  16. package/dist/routes/chat.js +142 -0
  17. package/dist/routes/command.js +16 -0
  18. package/dist/routes/data.js +189 -0
  19. package/dist/routes/git.js +10 -1
  20. package/dist/routes/import.js +199 -0
  21. package/dist/routes/issue.js +16 -4
  22. package/dist/routes/plugin.js +69 -0
  23. package/dist/routes/version.js +2 -1
  24. package/dist/routes/workflow-hook.js +71 -0
  25. package/dist/routes/workflow.js +282 -7
  26. package/dist/routes/workspace.js +13 -4
  27. package/dist/services/agent.js +123 -36
  28. package/dist/services/ai-text.js +185 -0
  29. package/dist/services/builtin-tools/index.js +1 -0
  30. package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
  31. package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
  32. package/dist/services/chat.js +134 -0
  33. package/dist/services/command-process-manager.js +16 -0
  34. package/dist/services/execution-manager.js +1346 -0
  35. package/dist/services/generated-title.js +59 -0
  36. package/dist/services/gitignore.js +22 -18
  37. package/dist/services/interaction-manager.js +114 -0
  38. package/dist/services/issue-retry.js +25 -0
  39. package/dist/services/output-style.js +8 -1
  40. package/dist/services/plugin.js +257 -0
  41. package/dist/services/prompt-template.js +8 -1
  42. package/dist/services/pty.js +20 -0
  43. package/dist/services/search.js +16 -6
  44. package/dist/services/skill.js +2 -0
  45. package/dist/services/version.js +17 -7
  46. package/dist/services/workflow-command-runner.js +5 -4
  47. package/dist/services/workflow-trigger-service.js +137 -0
  48. package/dist/services/workflow.js +199 -36
  49. package/dist/storage/agent-store.js +8 -0
  50. package/dist/storage/chat-store.js +151 -0
  51. package/dist/storage/database-store.js +6 -0
  52. package/dist/storage/json-store.js +2 -1
  53. package/dist/storage/kanban-store.js +6 -0
  54. package/dist/storage/workflow-store.js +386 -22
  55. package/dist/ws/agent-prompt.js +6 -2
  56. package/dist/ws/agent-runner.js +29 -18
  57. package/dist/ws/chat-handler.js +89 -0
  58. package/dist/ws/connection-manager.js +49 -2
  59. package/dist/ws/execution-channels.js +88 -0
  60. package/dist/ws/handler.js +32 -5
  61. package/dist/ws/message-parts.js +130 -12
  62. package/dist/ws/terminal-handler.js +26 -0
  63. package/package.json +12 -1
  64. package/public/avatars/user.jpg +0 -0
@@ -6,6 +6,7 @@ import * as workspaceService from '../services/workspace.js';
6
6
  import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
7
7
  import { buildAgentPrompt } from '../ws/agent-prompt.js';
8
8
  import { wrapOnEventWithHooks } from '../services/hook-engine.js';
9
+ import { buildWorkflowEditorSystemPrompt, createWorkflowEditorFunctionTools } from '../services/builtin-tools/workflow-editor-tools.js';
9
10
  const router = Router();
10
11
  router.post('/run', async (req, res) => {
11
12
  const body = req.body;
@@ -45,13 +46,24 @@ router.post('/run', async (req, res) => {
45
46
  const requestedSkills = normalizeSkills(body.skills ?? body.skill) ?? preset.skills;
46
47
  const configDir = agentService.getAgentConfigDir(workspaceId, { ...preset, skills: requestedSkills });
47
48
  const skills = agentService.getAvailableSkillNames(configDir, requestedSkills);
49
+ const workflowAgent = normalizeWorkflowAgent(body.workflowAgent);
50
+ const functionTools = workflowAgent
51
+ ? createWorkflowEditorFunctionTools({
52
+ workflow: workflowAgent.workflow,
53
+ nodeDefinitions: workflowAgent.nodeDefinitions,
54
+ })
55
+ : [];
56
+ const runtimeKind = workflowAgent ? 'langchain' : preset.runtimeKind;
57
+ const systemPrompt = workflowAgent
58
+ ? buildWorkflowEditorSystemPrompt(workflowAgent.workflow, workflowAgent.selectedNodes)
59
+ : body.systemPrompt ?? preset.systemPrompt;
48
60
  const output = [];
49
61
  const workingDir = agentService.resolveWorkingDir(workspaceId, preset);
50
62
  let completed = false;
51
63
  prepareSse(res);
52
64
  writeSse(res, 'session', { session, workspaceId });
53
65
  const runtime = createAgentRuntime({
54
- kind: preset.runtimeKind,
66
+ kind: runtimeKind,
55
67
  provider: preset.modelProvider,
56
68
  model: preset.modelId,
57
69
  apiKey: preset.apiKey,
@@ -66,18 +78,19 @@ router.post('/run', async (req, res) => {
66
78
  try {
67
79
  agentService.updateStatus(workspaceId, session.id, 'active');
68
80
  writeSse(res, 'status', { agentId: session.id, status: 'active' });
69
- const result = await runtime.execute(buildAgentPrompt(workspaceId, body.systemPrompt ?? preset.systemPrompt, userPrompt, normalizeMessages(body.messages), {
70
- runtimeKind: preset.runtimeKind,
81
+ const result = await runtime.execute(buildAgentPrompt(workspaceId, systemPrompt, userPrompt, normalizeMessages(body.messages), {
82
+ runtimeKind,
71
83
  mcpServers: Object.keys(mcpServers ?? {}),
72
84
  skills,
73
85
  boundDirs: workspace.boundDirs,
74
86
  workingDir,
75
- excludeNativeClaudeMd: preset.runtimeKind === 'claude-code',
76
- builtInTools: [],
87
+ excludeNativeClaudeMd: runtimeKind === 'claude-code',
88
+ builtInTools: functionTools.map((tool) => ({ name: tool.name, description: tool.description })),
77
89
  }), workingDir, {
78
90
  maxTurns: normalizeMaxTurns(body.maxTurns),
79
91
  mcpServers,
80
92
  skills,
93
+ functionTools,
81
94
  configDir,
82
95
  sandboxDirs: preset.sandboxDirs,
83
96
  userPrompt,
@@ -91,7 +104,7 @@ router.post('/run', async (req, res) => {
91
104
  completed = true;
92
105
  const displayOutput = output.length ? output : result.output;
93
106
  agentService.complete(workspaceId, session.id, result.success ? undefined : result.error, {
94
- runtime: preset.runtimeKind,
107
+ runtime: runtimeKind,
95
108
  model: preset.modelId,
96
109
  summary: result.summary,
97
110
  output: displayOutput,
@@ -116,7 +129,7 @@ router.post('/run', async (req, res) => {
116
129
  completed = true;
117
130
  const error = err instanceof Error ? err.message : String(err);
118
131
  agentService.complete(workspaceId, session.id, error, {
119
- runtime: preset.runtimeKind,
132
+ runtime: runtimeKind,
120
133
  model: preset.modelId,
121
134
  summary: error,
122
135
  output: output.length ? output : [error],
@@ -192,6 +205,56 @@ function normalizeSkills(input) {
192
205
  function normalizeMaxTurns(value) {
193
206
  return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 100;
194
207
  }
208
+ function normalizeWorkflowAgent(input) {
209
+ if (!input || typeof input !== 'object')
210
+ return null;
211
+ if (!isWorkflow(input.workflow))
212
+ return null;
213
+ const nodeDefinitions = Array.isArray(input.nodeDefinitions)
214
+ ? input.nodeDefinitions.filter(isNodeDefinition)
215
+ : [];
216
+ if (!nodeDefinitions.length)
217
+ return null;
218
+ const selectedNodes = Array.isArray(input.selectedNodes)
219
+ ? input.selectedNodes.filter(isWorkflowNode)
220
+ : undefined;
221
+ return { workflow: input.workflow, nodeDefinitions, selectedNodes };
222
+ }
223
+ function isWorkflow(value) {
224
+ if (!value || typeof value !== 'object' || Array.isArray(value))
225
+ return false;
226
+ const record = value;
227
+ return typeof record.id === 'string'
228
+ && typeof record.name === 'string'
229
+ && Array.isArray(record.nodes)
230
+ && Array.isArray(record.edges)
231
+ && record.nodes.every(isWorkflowNode);
232
+ }
233
+ function isWorkflowNode(value) {
234
+ if (!value || typeof value !== 'object' || Array.isArray(value))
235
+ return false;
236
+ const record = value;
237
+ const position = record.position;
238
+ return typeof record.id === 'string'
239
+ && typeof record.type === 'string'
240
+ && typeof record.label === 'string'
241
+ && Boolean(position)
242
+ && typeof position?.x === 'number'
243
+ && typeof position?.y === 'number'
244
+ && typeof record.data === 'object'
245
+ && record.data !== null
246
+ && !Array.isArray(record.data);
247
+ }
248
+ function isNodeDefinition(value) {
249
+ if (!value || typeof value !== 'object' || Array.isArray(value))
250
+ return false;
251
+ const record = value;
252
+ return typeof record.type === 'string'
253
+ && typeof record.label === 'string'
254
+ && typeof record.category === 'string'
255
+ && typeof record.description === 'string'
256
+ && Array.isArray(record.properties);
257
+ }
195
258
  function serializeRuntimeEvent(event) {
196
259
  if (event.type === 'tool_use') {
197
260
  return {
@@ -5,6 +5,8 @@ import { broadcastToWorkspace } from '../ws/connection-manager.js';
5
5
  import { hasActiveChannelRuns, stopChannelRuns } from '../ws/agent-runner.js';
6
6
  import { getToolDetail } from '../services/tool-detail.js';
7
7
  import * as issueService from '../services/issue.js';
8
+ import { scheduleChannelTitleGeneration } from '../services/generated-title.js';
9
+ import { stripHtml } from '../ws/html-utils.js';
8
10
  const router = Router({ mergeParams: true });
9
11
  // GET /api/workspaces/:id/channels
10
12
  router.get('/', (req, res) => {
@@ -12,10 +14,20 @@ router.get('/', (req, res) => {
12
14
  });
13
15
  // POST /api/workspaces/:id/channels
14
16
  router.post('/', (req, res) => {
15
- const { name, type, members } = req.body;
16
- const channel = createChannel(req.params.id, { name: name || 'General', type: type || 'general', members });
17
+ const name = typeof req.body.name === 'string' ? req.body.name.trim() : '';
18
+ const titlePrompt = typeof req.body.titlePrompt === 'string' ? req.body.titlePrompt.trim() : '';
19
+ const { type, members } = req.body;
20
+ const channel = createChannel(req.params.id, { name, type: type || 'general', members });
17
21
  broadcastToWorkspace(req.params.id, 'channel.updated', channel);
18
22
  res.status(201).json(channel);
23
+ if (!name) {
24
+ scheduleChannelTitleGeneration({
25
+ workspaceId: req.params.id,
26
+ channelId: channel.id,
27
+ requirement: titlePrompt,
28
+ broadcast: (event, data) => broadcastToWorkspace(req.params.id, event, data),
29
+ });
30
+ }
19
31
  });
20
32
  // PUT /api/workspaces/:id/channels/:channelId
21
33
  router.put('/:channelId', (req, res) => {
@@ -119,6 +131,15 @@ router.post('/:channelId/messages', (req, res) => {
119
131
  }
120
132
  const message = createMessage(id, channelId, { senderId: 'user', content, type, attachments });
121
133
  broadcastToWorkspace(id, 'channel.message', message);
134
+ const channel = getChannel(id, channelId);
135
+ if (channel && !channel.name.trim()) {
136
+ scheduleChannelTitleGeneration({
137
+ workspaceId: id,
138
+ channelId: channelId,
139
+ requirement: stripHtml(content),
140
+ broadcast: (event, data) => broadcastToWorkspace(id, event, data),
141
+ });
142
+ }
122
143
  res.status(201).json(message);
123
144
  });
124
145
  // PUT /api/workspaces/:id/channels/:channelId/messages/:messageId
@@ -0,0 +1,191 @@
1
+ import { Router } from 'express';
2
+ import * as chatService from '../services/chat.js';
3
+ import * as agentService from '../services/agent.js';
4
+ import { LangChainRuntime } from '../adapters/langchain-runtime.js';
5
+ import { createCommandFunctionTools, createDatabaseFunctionTools, createKanbanFunctionTools, createWorkflowExecutionFunctionTools, } from '../services/builtin-tools/index.js';
6
+ const router = Router();
7
+ router.post('/agents/:id/run', async (req, res) => {
8
+ const { id } = req.params;
9
+ const { content, regenerateFromMessageId } = req.body;
10
+ const regenerateContext = regenerateFromMessageId
11
+ ? resolveRegenerateContext(id, regenerateFromMessageId)
12
+ : null;
13
+ const trimmedContent = (regenerateContext?.content ?? content)?.trim();
14
+ if (!trimmedContent) {
15
+ res.status(400).json({ error: 'content is required' });
16
+ return;
17
+ }
18
+ const agent = chatService.findAgent(id);
19
+ if (!agent) {
20
+ res.status(404).json({ error: 'Agent not found' });
21
+ return;
22
+ }
23
+ const baseURL = resolveChatAgentBaseURL(agent);
24
+ prepareSse(res);
25
+ if (!baseURL && requiresBaseURL(agent.provider)) {
26
+ writeSse(res, 'error', {
27
+ error: `${agent.provider} requires an API Base URL. Edit this chat agent and save the provider API address again.`,
28
+ });
29
+ res.end();
30
+ return;
31
+ }
32
+ if (!regenerateContext) {
33
+ const userMsg = chatService.saveMessage({
34
+ agentId: id,
35
+ role: 'user',
36
+ content: trimmedContent,
37
+ });
38
+ writeSse(res, 'message_saved', userMsg);
39
+ }
40
+ const config = {
41
+ kind: 'langchain',
42
+ provider: agent.provider,
43
+ model: agent.model,
44
+ apiKey: agent.apiKey,
45
+ baseURL,
46
+ };
47
+ const runtime = new LangChainRuntime(config);
48
+ let completed = false;
49
+ res.on('close', () => {
50
+ if (!completed && !res.writableEnded)
51
+ runtime.stop();
52
+ });
53
+ try {
54
+ const historyMessages = regenerateContext?.historyMessages ?? chatService.getRecentMessages(id, 20).slice(0, -1);
55
+ const historyPrompt = historyMessages
56
+ .filter(shouldIncludeHistoryMessage)
57
+ .map((message) => `${message.role === 'user' ? 'User' : 'Assistant'}: ${message.content}`)
58
+ .join('\n\n');
59
+ const prompt = historyPrompt
60
+ ? `${historyPrompt}\n\nUser: ${trimmedContent}\nAssistant:`
61
+ : trimmedContent;
62
+ const workingDir = chatService.getAgentWorkingDir(id) || process.cwd();
63
+ const configDir = chatService.getAgentConfigDir(id) || undefined;
64
+ const tools = normalizeToolNames(agent.tools);
65
+ const functionTools = [
66
+ ...createCommandFunctionTools(id, tools),
67
+ ...createDatabaseFunctionTools(id, tools),
68
+ ...createKanbanFunctionTools(id, tools),
69
+ ...createWorkflowExecutionFunctionTools(tools),
70
+ ];
71
+ const result = await runtime.execute(prompt, workingDir, {
72
+ maxTurns: 100,
73
+ functionTools,
74
+ mcpServers: agentService.getMcpServers(agent.mcps),
75
+ skills: normalizeSkillNames(agent.skills),
76
+ configDir,
77
+ systemPrompt: agent.systemPrompt,
78
+ outputStyle: agent.outputStyle,
79
+ onEvent: (event) => {
80
+ writeRuntimeEvent(res, event);
81
+ },
82
+ });
83
+ if (!result.success) {
84
+ completed = true;
85
+ writeSse(res, 'error', { error: result.error ?? result.summary });
86
+ return;
87
+ }
88
+ const agentContent = result.output
89
+ .filter((line) => !line.startsWith('Tool:') && !line.startsWith('[Usage]'))
90
+ .join('\n')
91
+ || result.summary;
92
+ const agentMsg = chatService.saveMessage({
93
+ agentId: id,
94
+ role: 'agent',
95
+ content: agentContent,
96
+ usage: result.usage,
97
+ });
98
+ completed = true;
99
+ writeSse(res, 'completed', { message: agentMsg, success: result.success, error: result.error });
100
+ }
101
+ catch (err) {
102
+ completed = true;
103
+ writeSse(res, 'error', { error: err instanceof Error ? err.message : String(err) });
104
+ }
105
+ finally {
106
+ res.end();
107
+ }
108
+ });
109
+ function prepareSse(res) {
110
+ res.status(200);
111
+ res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
112
+ res.setHeader('Cache-Control', 'no-cache, no-transform');
113
+ res.setHeader('Connection', 'keep-alive');
114
+ res.setHeader('X-Accel-Buffering', 'no');
115
+ res.flushHeaders?.();
116
+ res.socket?.setNoDelay?.(true);
117
+ }
118
+ function writeSse(res, event, data) {
119
+ res.write(`event: ${event}\n`);
120
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
121
+ const flushable = res;
122
+ if (typeof flushable.flush === 'function')
123
+ flushable.flush();
124
+ }
125
+ function writeRuntimeEvent(res, event) {
126
+ switch (event.type) {
127
+ case 'output':
128
+ if (!event.line.startsWith('[Usage]'))
129
+ writeSse(res, 'output', { chunk: event.line });
130
+ break;
131
+ case 'reasoning':
132
+ writeSse(res, 'thinking', { chunk: event.text, status: event.status });
133
+ break;
134
+ case 'tool_use':
135
+ writeSse(res, 'tool_use', { name: event.name, input: event.input });
136
+ break;
137
+ case 'tool_result':
138
+ writeSse(res, 'tool_result', { name: event.toolUseId, result: event.result });
139
+ break;
140
+ case 'session':
141
+ case 'hook_event':
142
+ break;
143
+ }
144
+ }
145
+ function shouldIncludeHistoryMessage(message) {
146
+ return !(message.role === 'agent' && message.content.trim() === 'LangChain execution failed');
147
+ }
148
+ function resolveRegenerateContext(agentId, messageId) {
149
+ const messages = chatService.listMessages(agentId);
150
+ const targetIndex = messages.findIndex((message) => message.id === messageId && message.role === 'agent');
151
+ if (targetIndex === -1)
152
+ return null;
153
+ for (let index = targetIndex - 1; index >= 0; index -= 1) {
154
+ const message = messages[index];
155
+ if (message?.role === 'user') {
156
+ return {
157
+ content: message.content,
158
+ historyMessages: messages.slice(0, index),
159
+ };
160
+ }
161
+ }
162
+ return null;
163
+ }
164
+ function resolveChatAgentBaseURL(agent) {
165
+ return agent.baseURL?.trim() || agent.apiBase?.trim() || undefined;
166
+ }
167
+ function requiresBaseURL(provider) {
168
+ return provider === 'openai-chat-completions'
169
+ || provider === 'openai-responses'
170
+ || provider === 'anthropic-messages'
171
+ || provider === 'gemini-generate-content';
172
+ }
173
+ function normalizeSkillNames(skills) {
174
+ if (!Array.isArray(skills))
175
+ return [];
176
+ return skills
177
+ .map((skill) => typeof skill === 'string'
178
+ ? skill
179
+ : skill && typeof skill === 'object' && 'name' in skill && typeof skill.name === 'string'
180
+ ? skill.name
181
+ : '')
182
+ .map((skill) => skill.trim())
183
+ .filter(Boolean);
184
+ }
185
+ function normalizeToolNames(tools) {
186
+ if (!Array.isArray(tools))
187
+ return undefined;
188
+ return tools.filter((tool) => typeof tool === 'string');
189
+ }
190
+ export default router;
191
+ //# sourceMappingURL=chat-run.js.map
@@ -0,0 +1,142 @@
1
+ import { Router } from 'express';
2
+ import * as svc from '../services/chat.js';
3
+ import * as fileService from '../services/file.js';
4
+ const router = Router();
5
+ // GET /api/chat/agents — list all chat agents
6
+ router.get('/agents', (_req, res) => {
7
+ try {
8
+ res.json(svc.listAgents());
9
+ }
10
+ catch (error) {
11
+ res.status(500).json({ error: error.message });
12
+ }
13
+ });
14
+ // POST /api/chat/agents — create agent (requires name, provider, model, apiKey)
15
+ router.post('/agents', (req, res) => {
16
+ const body = req.body;
17
+ const name = typeof body.name === 'string' ? body.name.trim() : '';
18
+ const provider = stringValue(body.provider) || stringValue(body.modelProvider);
19
+ const model = stringValue(body.model) || stringValue(body.modelId);
20
+ if (!name || !provider || !model) {
21
+ res.status(400).json({ error: 'name, provider, and model are required' });
22
+ return;
23
+ }
24
+ try {
25
+ const agent = svc.createAgent(body);
26
+ res.status(201).json(agent);
27
+ }
28
+ catch (error) {
29
+ res.status(500).json({ error: error.message });
30
+ }
31
+ });
32
+ // PUT /api/chat/agents/:id — update agent
33
+ router.put('/agents/:id', (req, res) => {
34
+ const { id } = req.params;
35
+ if (!id) {
36
+ res.status(400).json({ error: 'id is required' });
37
+ return;
38
+ }
39
+ try {
40
+ const patch = normalizeChatAgentPatch(req.body);
41
+ const agent = svc.updateAgent(id, patch);
42
+ if (!agent) {
43
+ res.status(404).json({ error: 'Agent not found' });
44
+ return;
45
+ }
46
+ res.json(agent);
47
+ }
48
+ catch (error) {
49
+ res.status(500).json({ error: error.message });
50
+ }
51
+ });
52
+ function normalizeChatAgentPatch(body) {
53
+ if (!body || typeof body !== 'object' || Array.isArray(body))
54
+ return {};
55
+ const patch = { ...body };
56
+ if (!patch.baseURL && patch.apiBase) {
57
+ patch.baseURL = patch.apiBase;
58
+ }
59
+ delete patch.apiBase;
60
+ return patch;
61
+ }
62
+ // DELETE /api/chat/agents/:id — delete agent + its messages
63
+ router.delete('/agents/:id', (req, res) => {
64
+ const { id } = req.params;
65
+ if (!id) {
66
+ res.status(400).json({ error: 'id is required' });
67
+ return;
68
+ }
69
+ try {
70
+ const deleted = svc.deleteAgent(id);
71
+ if (!deleted) {
72
+ res.status(404).json({ error: 'Agent not found' });
73
+ return;
74
+ }
75
+ res.status(204).send();
76
+ }
77
+ catch (error) {
78
+ res.status(500).json({ error: error.message });
79
+ }
80
+ });
81
+ // GET /api/chat/agents/:id/messages — list messages (?limit=50&before=msgId)
82
+ router.get('/agents/:id/messages', (req, res) => {
83
+ const { id } = req.params;
84
+ if (!id) {
85
+ res.status(400).json({ error: 'id is required' });
86
+ return;
87
+ }
88
+ try {
89
+ const agent = svc.findAgent(id);
90
+ if (!agent) {
91
+ res.status(404).json({ error: 'Agent not found' });
92
+ return;
93
+ }
94
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : undefined;
95
+ const before = req.query.before;
96
+ res.json(svc.listMessages(id, limit, before));
97
+ }
98
+ catch (error) {
99
+ res.status(500).json({ error: error.message });
100
+ }
101
+ });
102
+ // DELETE /api/chat/agents/:id/messages — clear messages
103
+ router.delete('/agents/:id/messages', (req, res) => {
104
+ const { id } = req.params;
105
+ if (!id) {
106
+ res.status(400).json({ error: 'id is required' });
107
+ return;
108
+ }
109
+ try {
110
+ svc.clearMessages(id);
111
+ res.status(204).send();
112
+ }
113
+ catch (error) {
114
+ res.status(500).json({ error: error.message });
115
+ }
116
+ });
117
+ // GET /api/chat/agents/:id/workspace/tree - read chat agent working directory
118
+ router.get('/agents/:id/workspace/tree', async (req, res) => {
119
+ const { id } = req.params;
120
+ if (!id) {
121
+ res.status(400).json({ error: 'id is required' });
122
+ return;
123
+ }
124
+ try {
125
+ const workspace = svc.getAgentWorkspace(id);
126
+ if (!workspace) {
127
+ res.status(404).json({ error: 'Agent workspace not found' });
128
+ return;
129
+ }
130
+ const relPath = typeof req.query.path === 'string' ? req.query.path : '';
131
+ const depth = req.query.depth ? Number(req.query.depth) : Infinity;
132
+ res.json(await fileService.readTree(workspace, relPath, depth));
133
+ }
134
+ catch (error) {
135
+ res.status(500).json({ error: error.message });
136
+ }
137
+ });
138
+ function stringValue(value) {
139
+ return typeof value === 'string' ? value.trim() : '';
140
+ }
141
+ export default router;
142
+ //# sourceMappingURL=chat.js.map
@@ -104,5 +104,21 @@ router.post('/:commandId/stop', (req, res) => {
104
104
  res.status(400).json({ error: error.message });
105
105
  }
106
106
  });
107
+ router.post('/:commandId/restart', (req, res) => {
108
+ const { id: workspaceId, commandId } = req.params;
109
+ if (!workspaceId || !commandId) {
110
+ res.status(400).json({ error: 'workspaceId and commandId required' });
111
+ return;
112
+ }
113
+ try {
114
+ const sessionId = processManager.restartCommand(workspaceId, commandId);
115
+ console.log(`[command] POST restart: workspace=${workspaceId} command=${commandId} -> session=${sessionId}`);
116
+ res.json({ sessionId });
117
+ }
118
+ catch (error) {
119
+ console.error(`[command] POST restart error: workspace=${workspaceId} command=${commandId}`, error.message);
120
+ res.status(400).json({ error: error.message });
121
+ }
122
+ });
107
123
  export default router;
108
124
  //# sourceMappingURL=command.js.map