@agent-spaces/server 0.2.7 → 0.2.9
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/agents/agent-message-parts.js +1 -1
- package/dist/app.js +17 -1
- package/dist/routes/file.js +38 -0
- package/dist/services/message.js +25 -0
- package/dist/services/pty.js +10 -2
- package/dist/services/workflow.js +1 -1
- package/dist/storage/user-settings-store.js +17 -0
- package/dist/ws/agent-prompt.js +19 -0
- package/dist/ws/agent-runner.js +67 -52
- package/dist/ws/handler.js +31 -2
- package/dist/ws/message-parts.js +1 -1
- package/package.json +10 -11
|
@@ -218,7 +218,7 @@ function buildChainItems(lines, finalTextRange, finalText, workspaceRoot, toolDe
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
flushMessageBuffer();
|
|
221
|
-
return items
|
|
221
|
+
return items;
|
|
222
222
|
}
|
|
223
223
|
function buildToolTodo(line, index, workspaceRoot, toolDetails, toolDetailMatchCounts) {
|
|
224
224
|
const summary = summarizeToolLine(line, workspaceRoot);
|
package/dist/app.js
CHANGED
|
@@ -27,6 +27,7 @@ import subscriptionRouter from './routes/subscription.js';
|
|
|
27
27
|
import agentSseRouter from './routes/agent-sse.js';
|
|
28
28
|
import searchRouter from './routes/search.js';
|
|
29
29
|
import notificationRouter from './routes/notification.js';
|
|
30
|
+
import { getUserSettings, setUserAvatarUrl, removeUserAvatarUrl } from './storage/user-settings-store.js';
|
|
30
31
|
import { authMiddleware, verifyToken } from './middleware/auth.js';
|
|
31
32
|
import { handleConnection } from './ws/handler.js';
|
|
32
33
|
import { startScheduler, stopScheduler } from './agents/scheduler-agent.js';
|
|
@@ -50,7 +51,7 @@ app.use('/api', authMiddleware);
|
|
|
50
51
|
const publicDir = resolveRuntimeDir('public');
|
|
51
52
|
app.use('/public', express.static(publicDir));
|
|
52
53
|
app.get('/api/health', (_req, res) => {
|
|
53
|
-
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
54
|
+
res.json({ status: 'ok', timestamp: new Date().toISOString(), platform: process.platform });
|
|
54
55
|
});
|
|
55
56
|
app.use('/api/auth', authRouter);
|
|
56
57
|
// Avatar upload
|
|
@@ -75,6 +76,21 @@ app.post('/api/upload/avatar', async (req, res) => {
|
|
|
75
76
|
await writeFile(join(avatarsDir, name), Buffer.from(base64, 'base64'));
|
|
76
77
|
res.json({ url: `/static/avatars/${name}` });
|
|
77
78
|
});
|
|
79
|
+
// User settings
|
|
80
|
+
app.get('/api/user/settings', (_req, res) => {
|
|
81
|
+
const settings = getUserSettings();
|
|
82
|
+
res.json({ avatarUrl: settings.avatarUrl ?? null });
|
|
83
|
+
});
|
|
84
|
+
app.put('/api/user/settings', (req, res) => {
|
|
85
|
+
const { avatarUrl } = req.body;
|
|
86
|
+
if (avatarUrl === null || avatarUrl === undefined) {
|
|
87
|
+
removeUserAvatarUrl();
|
|
88
|
+
}
|
|
89
|
+
else if (typeof avatarUrl === 'string') {
|
|
90
|
+
setUserAvatarUrl(avatarUrl);
|
|
91
|
+
}
|
|
92
|
+
res.json({ ok: true });
|
|
93
|
+
});
|
|
78
94
|
app.use('/api/workspaces', workspaceRouter);
|
|
79
95
|
app.use('/api/workspaces/:id/files', fileRouter);
|
|
80
96
|
app.use('/api/workspaces/:id/channels', channelRouter);
|
package/dist/routes/file.js
CHANGED
|
@@ -3,6 +3,7 @@ import { exec } from 'child_process';
|
|
|
3
3
|
import { resolve, join } from 'path';
|
|
4
4
|
import * as fileService from '../services/file.js';
|
|
5
5
|
import { getDataDir } from '../storage/json-store.js';
|
|
6
|
+
const EDITOR_STATE_PATH = '.agentspace/editor-state.json';
|
|
6
7
|
const router = Router({ mergeParams: true });
|
|
7
8
|
router.get('/tree', async (req, res) => {
|
|
8
9
|
const ws = fileService.getWorkspace(req.params.id);
|
|
@@ -68,6 +69,43 @@ router.delete('/', async (req, res) => {
|
|
|
68
69
|
}
|
|
69
70
|
res.json({ ok: true });
|
|
70
71
|
});
|
|
72
|
+
router.get('/editor-state', async (req, res) => {
|
|
73
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
74
|
+
if (!ws) {
|
|
75
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const result = await fileService.readFileContent(ws, EDITOR_STATE_PATH);
|
|
79
|
+
if (!result) {
|
|
80
|
+
res.json({ openFilePaths: [], activeFilePath: null });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const state = JSON.parse(result.content);
|
|
85
|
+
res.json(state);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
res.json({ openFilePaths: [], activeFilePath: null });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
router.put('/editor-state', async (req, res) => {
|
|
92
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
93
|
+
if (!ws) {
|
|
94
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const { openFilePaths, activeFilePath } = req.body;
|
|
98
|
+
if (!Array.isArray(openFilePaths)) {
|
|
99
|
+
res.status(400).json({ error: 'openFilePaths is required' });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const ok = await fileService.writeFileContent(ws, EDITOR_STATE_PATH, JSON.stringify({ openFilePaths, activeFilePath }, null, 2));
|
|
103
|
+
if (!ok) {
|
|
104
|
+
res.status(500).json({ error: 'Failed to save editor state' });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
res.json({ ok: true });
|
|
108
|
+
});
|
|
71
109
|
router.post('/reveal', async (req, res) => {
|
|
72
110
|
const ws = fileService.getWorkspace(req.params.id);
|
|
73
111
|
if (!ws) {
|
package/dist/services/message.js
CHANGED
|
@@ -49,6 +49,31 @@ export function updateMessage(workspaceId, channelId, messageId, data) {
|
|
|
49
49
|
writeJsonFile(path, messages);
|
|
50
50
|
return updated;
|
|
51
51
|
}
|
|
52
|
+
export function appendMessageReply(workspaceId, channelId, messageId, data) {
|
|
53
|
+
const path = messageFilePath(workspaceId, channelId);
|
|
54
|
+
const messages = readJsonFile(path) || [];
|
|
55
|
+
const index = messages.findIndex((message) => message.id === messageId);
|
|
56
|
+
if (index === -1)
|
|
57
|
+
return null;
|
|
58
|
+
const reply = {
|
|
59
|
+
id: uuid(),
|
|
60
|
+
senderId: data.senderId,
|
|
61
|
+
senderRole: data.senderRole,
|
|
62
|
+
content: data.content,
|
|
63
|
+
status: data.status,
|
|
64
|
+
attachments: data.attachments,
|
|
65
|
+
parts: data.parts,
|
|
66
|
+
metadata: data.metadata,
|
|
67
|
+
createdAt: new Date().toISOString(),
|
|
68
|
+
};
|
|
69
|
+
const updated = {
|
|
70
|
+
...messages[index],
|
|
71
|
+
replies: [...(messages[index].replies ?? []), reply],
|
|
72
|
+
};
|
|
73
|
+
messages[index] = updated;
|
|
74
|
+
writeJsonFile(path, messages);
|
|
75
|
+
return updated;
|
|
76
|
+
}
|
|
52
77
|
export function deleteMessage(workspaceId, channelId, messageId) {
|
|
53
78
|
const path = messageFilePath(workspaceId, channelId);
|
|
54
79
|
const messages = readJsonFile(path) || [];
|
package/dist/services/pty.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pty from 'node-pty';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
|
+
const MAX_BUFFER_LINES = 100;
|
|
3
4
|
const sessions = new Map();
|
|
4
5
|
export function createSession(workspaceId, cwd, onOutput, onExit, shell, env) {
|
|
5
6
|
const id = uuid();
|
|
@@ -18,9 +19,16 @@ export function createSession(workspaceId, cwd, onOutput, onExit, shell, env) {
|
|
|
18
19
|
cwd,
|
|
19
20
|
env: ptyEnv,
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
+
const session = { id, pty: ptyProcess, workspaceId, cwd, buffer: [] };
|
|
23
|
+
ptyProcess.onData((data) => {
|
|
24
|
+
session.buffer.push(data);
|
|
25
|
+
if (session.buffer.length > MAX_BUFFER_LINES) {
|
|
26
|
+
session.buffer = session.buffer.slice(-MAX_BUFFER_LINES);
|
|
27
|
+
}
|
|
28
|
+
onOutput(id, data);
|
|
29
|
+
});
|
|
22
30
|
ptyProcess.onExit(({ exitCode }) => onExit(id, exitCode ?? 0));
|
|
23
|
-
sessions.set(id,
|
|
31
|
+
sessions.set(id, session);
|
|
24
32
|
return id;
|
|
25
33
|
}
|
|
26
34
|
export function write(sessionId, data) {
|
|
@@ -179,7 +179,7 @@ export function mapWorkflowToTaskDrafts(template) {
|
|
|
179
179
|
}
|
|
180
180
|
return template.nodes.map(node => ({
|
|
181
181
|
key: node.id,
|
|
182
|
-
title: node.data.taskTitleTemplate ||
|
|
182
|
+
title: node.data.taskTitleTemplate || node.data.label,
|
|
183
183
|
description: node.data.taskDescriptionTemplate || `Task assigned to ${node.data.label} (${node.data.role})`,
|
|
184
184
|
agentConfigId: node.data.agentConfigId,
|
|
185
185
|
dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readJsonFile, writeJsonFile, getDataDir } from './json-store.js';
|
|
3
|
+
const FILE = () => join(getDataDir(), 'user-settings.json');
|
|
4
|
+
export function getUserSettings() {
|
|
5
|
+
return readJsonFile(FILE()) ?? {};
|
|
6
|
+
}
|
|
7
|
+
export function setUserAvatarUrl(url) {
|
|
8
|
+
const settings = getUserSettings();
|
|
9
|
+
settings.avatarUrl = url;
|
|
10
|
+
writeJsonFile(FILE(), settings);
|
|
11
|
+
}
|
|
12
|
+
export function removeUserAvatarUrl() {
|
|
13
|
+
const settings = getUserSettings();
|
|
14
|
+
delete settings.avatarUrl;
|
|
15
|
+
writeJsonFile(FILE(), settings);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=user-settings-store.js.map
|
package/dist/ws/agent-prompt.js
CHANGED
|
@@ -42,6 +42,12 @@ function formatConversationHistory(history) {
|
|
|
42
42
|
if (!text.trim())
|
|
43
43
|
continue;
|
|
44
44
|
lines.push(`[${role}]: ${text}`);
|
|
45
|
+
for (const reply of msg.replies ?? []) {
|
|
46
|
+
const replyRole = reply.senderId === 'user' ? 'User' : (reply.senderRole || 'Agent');
|
|
47
|
+
const replyText = reply.senderId === 'user' ? stripHtml(reply.content) : getReplyFinalMessage(reply);
|
|
48
|
+
if (replyText.trim())
|
|
49
|
+
lines.push(`[${replyRole} reply]: ${replyText}`);
|
|
50
|
+
}
|
|
45
51
|
}
|
|
46
52
|
return lines;
|
|
47
53
|
}
|
|
@@ -58,6 +64,19 @@ function getAgentFinalMessage(message) {
|
|
|
58
64
|
return '';
|
|
59
65
|
return stripToolLikeHistoryLines(stripHtml(message.content));
|
|
60
66
|
}
|
|
67
|
+
function getReplyFinalMessage(reply) {
|
|
68
|
+
const finalTextParts = reply.parts
|
|
69
|
+
?.filter((part) => part.type === 'text')
|
|
70
|
+
.map((part) => part.text.trim())
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
if (finalTextParts?.length)
|
|
73
|
+
return finalTextParts.join('\n\n');
|
|
74
|
+
if (reply.parts?.length)
|
|
75
|
+
return '';
|
|
76
|
+
if (reply.status && reply.status !== 'completed')
|
|
77
|
+
return '';
|
|
78
|
+
return stripToolLikeHistoryLines(stripHtml(reply.content));
|
|
79
|
+
}
|
|
61
80
|
function stripToolLikeHistoryLines(content) {
|
|
62
81
|
const kept = content.split(/\r?\n/).filter((line) => {
|
|
63
82
|
const trimmed = line.trim();
|
package/dist/ws/agent-runner.js
CHANGED
|
@@ -239,13 +239,14 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
239
239
|
askUserQuestions,
|
|
240
240
|
success: true,
|
|
241
241
|
});
|
|
242
|
-
|
|
242
|
+
const displayParts = buildContinuationParts(existingMessage?.parts, session.id, options.appendUserMessage, parts);
|
|
243
|
+
if (displayParts.length === 0)
|
|
243
244
|
return;
|
|
244
245
|
const status = askUserQuestions.some((question) => !question.answer) ? 'waiting_for_user' : 'streaming';
|
|
245
246
|
const live = updateMessage(workspaceId, channelId, pending.id, {
|
|
246
247
|
content: liveOutput.join('\n') || pending.content,
|
|
247
248
|
status,
|
|
248
|
-
parts,
|
|
249
|
+
parts: displayParts,
|
|
249
250
|
metadata: {
|
|
250
251
|
...pending.metadata,
|
|
251
252
|
duration: Date.now() - startTime,
|
|
@@ -354,6 +355,21 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
354
355
|
const displayOutput = mergeRuntimeOutput(liveOutput, result.output);
|
|
355
356
|
if (shouldWaitForUserAnswer(askUserQuestions, result.summary, result.error, displayOutput)) {
|
|
356
357
|
const waitingOutput = stripAskUserQuestionErrorLines(liveOutput);
|
|
358
|
+
const waitingParts = buildContinuationParts(existingMessage?.parts, session.id, options.appendUserMessage, buildAgentMessageParts({
|
|
359
|
+
sessionId: session.id,
|
|
360
|
+
workspaceRoot: workspace?.boundDirs?.[0],
|
|
361
|
+
presetName: preset.name || preset.role,
|
|
362
|
+
role: preset.role,
|
|
363
|
+
model: preset.modelId,
|
|
364
|
+
systemPrompt: preset.systemPrompt,
|
|
365
|
+
mcpServers: Object.keys(mcpServers ?? {}),
|
|
366
|
+
skills,
|
|
367
|
+
output: waitingOutput,
|
|
368
|
+
reasoning: liveReasoning,
|
|
369
|
+
toolDetails,
|
|
370
|
+
askUserQuestions,
|
|
371
|
+
success: true,
|
|
372
|
+
}));
|
|
357
373
|
const waiting = updateMessage(workspaceId, channelId, pending.id, {
|
|
358
374
|
content: waitingOutput.join('\n') || pending.content,
|
|
359
375
|
type: 'text',
|
|
@@ -365,21 +381,7 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
365
381
|
summary: 'Waiting for user answer',
|
|
366
382
|
duration: Date.now() - startTime,
|
|
367
383
|
},
|
|
368
|
-
parts:
|
|
369
|
-
sessionId: session.id,
|
|
370
|
-
workspaceRoot: workspace?.boundDirs?.[0],
|
|
371
|
-
presetName: preset.name || preset.role,
|
|
372
|
-
role: preset.role,
|
|
373
|
-
model: preset.modelId,
|
|
374
|
-
systemPrompt: preset.systemPrompt,
|
|
375
|
-
mcpServers: Object.keys(mcpServers ?? {}),
|
|
376
|
-
skills,
|
|
377
|
-
output: waitingOutput,
|
|
378
|
-
reasoning: liveReasoning,
|
|
379
|
-
toolDetails,
|
|
380
|
-
askUserQuestions,
|
|
381
|
-
success: true,
|
|
382
|
-
}),
|
|
384
|
+
parts: waitingParts,
|
|
383
385
|
});
|
|
384
386
|
if (waiting)
|
|
385
387
|
broadcastToWorkspace(workspaceId, 'channel.message.updated', waiting);
|
|
@@ -415,6 +417,24 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
415
417
|
},
|
|
416
418
|
error: result.error,
|
|
417
419
|
});
|
|
420
|
+
const replyParts = buildContinuationParts(existingMessage?.parts, session.id, options.appendUserMessage, buildAgentMessageParts({
|
|
421
|
+
sessionId: session.id,
|
|
422
|
+
workspaceRoot: workspace?.boundDirs?.[0],
|
|
423
|
+
presetName: preset.name || preset.role,
|
|
424
|
+
role: preset.role,
|
|
425
|
+
model: preset.modelId,
|
|
426
|
+
usage: result.usage,
|
|
427
|
+
systemPrompt: preset.systemPrompt,
|
|
428
|
+
mcpServers: Object.keys(mcpServers ?? {}),
|
|
429
|
+
skills,
|
|
430
|
+
builtInTools: buildBuiltInTools(functionTools, channel, issue),
|
|
431
|
+
output: displayOutput,
|
|
432
|
+
reasoning: liveReasoning,
|
|
433
|
+
toolDetails,
|
|
434
|
+
askUserQuestions,
|
|
435
|
+
success: result.success,
|
|
436
|
+
error: result.error,
|
|
437
|
+
}));
|
|
418
438
|
const reply = updateMessage(workspaceId, channelId, pending.id, {
|
|
419
439
|
content: result.success ? displayOutput.join('\n') : result.error || displayOutput.join('\n'),
|
|
420
440
|
type: 'text',
|
|
@@ -426,24 +446,7 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
426
446
|
summary: result.summary,
|
|
427
447
|
duration: Date.now() - startTime,
|
|
428
448
|
},
|
|
429
|
-
parts:
|
|
430
|
-
sessionId: session.id,
|
|
431
|
-
workspaceRoot: workspace?.boundDirs?.[0],
|
|
432
|
-
presetName: preset.name || preset.role,
|
|
433
|
-
role: preset.role,
|
|
434
|
-
model: preset.modelId,
|
|
435
|
-
usage: result.usage,
|
|
436
|
-
systemPrompt: preset.systemPrompt,
|
|
437
|
-
mcpServers: Object.keys(mcpServers ?? {}),
|
|
438
|
-
skills,
|
|
439
|
-
builtInTools: buildBuiltInTools(functionTools, channel, issue),
|
|
440
|
-
output: displayOutput,
|
|
441
|
-
reasoning: liveReasoning,
|
|
442
|
-
toolDetails,
|
|
443
|
-
askUserQuestions,
|
|
444
|
-
success: result.success,
|
|
445
|
-
error: result.error,
|
|
446
|
-
}),
|
|
449
|
+
parts: replyParts,
|
|
447
450
|
});
|
|
448
451
|
if (reply)
|
|
449
452
|
broadcastToWorkspace(workspaceId, 'channel.message.updated', reply);
|
|
@@ -460,6 +463,23 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
460
463
|
durationMs: Date.now() - startTime,
|
|
461
464
|
});
|
|
462
465
|
broadcastToWorkspace(workspaceId, 'agent.error', { agentId: session.id, error });
|
|
466
|
+
const errorParts = buildContinuationParts(existingMessage?.parts, session.id, options.appendUserMessage, buildAgentMessageParts({
|
|
467
|
+
sessionId: session.id,
|
|
468
|
+
workspaceRoot: workspace?.boundDirs?.[0],
|
|
469
|
+
presetName: preset.name || preset.role,
|
|
470
|
+
role: preset.role,
|
|
471
|
+
model: preset.modelId,
|
|
472
|
+
systemPrompt: preset.systemPrompt,
|
|
473
|
+
mcpServers: Object.keys(mcpServers ?? {}),
|
|
474
|
+
skills,
|
|
475
|
+
builtInTools: buildBuiltInTools(functionTools, channel, issue),
|
|
476
|
+
output: [error],
|
|
477
|
+
reasoning: liveReasoning,
|
|
478
|
+
toolDetails: new Map(),
|
|
479
|
+
askUserQuestions: [],
|
|
480
|
+
success: false,
|
|
481
|
+
error,
|
|
482
|
+
}));
|
|
463
483
|
const reply = updateMessage(workspaceId, channelId, pending.id, {
|
|
464
484
|
content: error,
|
|
465
485
|
type: 'text',
|
|
@@ -470,23 +490,7 @@ export async function runMentionedAgent(workspaceId, channelId, agentConfigId, p
|
|
|
470
490
|
model: preset.modelId,
|
|
471
491
|
duration: Date.now() - startTime,
|
|
472
492
|
},
|
|
473
|
-
parts:
|
|
474
|
-
sessionId: session.id,
|
|
475
|
-
workspaceRoot: workspace?.boundDirs?.[0],
|
|
476
|
-
presetName: preset.name || preset.role,
|
|
477
|
-
role: preset.role,
|
|
478
|
-
model: preset.modelId,
|
|
479
|
-
systemPrompt: preset.systemPrompt,
|
|
480
|
-
mcpServers: Object.keys(mcpServers ?? {}),
|
|
481
|
-
skills,
|
|
482
|
-
builtInTools: buildBuiltInTools(functionTools, channel, issue),
|
|
483
|
-
output: [error],
|
|
484
|
-
reasoning: liveReasoning,
|
|
485
|
-
toolDetails: new Map(),
|
|
486
|
-
askUserQuestions: [],
|
|
487
|
-
success: false,
|
|
488
|
-
error,
|
|
489
|
-
}),
|
|
493
|
+
parts: errorParts,
|
|
490
494
|
});
|
|
491
495
|
if (reply)
|
|
492
496
|
broadcastToWorkspace(workspaceId, 'channel.message.updated', reply);
|
|
@@ -535,6 +539,17 @@ function untrackChannelRun(workspaceId, channelId, agentId) {
|
|
|
535
539
|
if (runs.size === 0)
|
|
536
540
|
activeChannelRuns.delete(key);
|
|
537
541
|
}
|
|
542
|
+
function buildContinuationParts(previousParts, sessionId, userMessage, nextParts) {
|
|
543
|
+
if (!userMessage)
|
|
544
|
+
return nextParts;
|
|
545
|
+
const appended = [{
|
|
546
|
+
id: `user-message-${sessionId}`,
|
|
547
|
+
type: 'user_message',
|
|
548
|
+
text: userMessage,
|
|
549
|
+
senderName: '用户',
|
|
550
|
+
}, ...nextParts];
|
|
551
|
+
return [...(previousParts ?? []), ...appended];
|
|
552
|
+
}
|
|
538
553
|
function questionRunKey(workspaceId, channelId, messageId, questionId) {
|
|
539
554
|
return `${workspaceId}:${channelId}:${messageId}:${questionId}`;
|
|
540
555
|
}
|
package/dist/ws/handler.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { addConnection, broadcastToWorkspace } from './connection-manager.js';
|
|
2
2
|
import { handleTerminalEvent } from './terminal-handler.js';
|
|
3
|
-
import
|
|
3
|
+
import * as ptyService from '../services/pty.js';
|
|
4
|
+
import { appendMessageReply, createMessage } from '../services/message.js';
|
|
4
5
|
import { getChannel } from '../services/channel.js';
|
|
5
6
|
import * as agentService from '../services/agent.js';
|
|
6
7
|
import { runMentionedAgent, stopChannelRuns, handleAnswerQuestion, ensureScheduler, makeContext } from './agent-runner.js';
|
|
@@ -19,6 +20,16 @@ export function handleConnection(ws, workspaceId) {
|
|
|
19
20
|
timestamp: new Date().toISOString(),
|
|
20
21
|
data: { workspaceId },
|
|
21
22
|
}));
|
|
23
|
+
// Send existing terminal sessions for reconnection
|
|
24
|
+
const existingSessions = ptyService.getSessionsByWorkspace(workspaceId);
|
|
25
|
+
ws.send(JSON.stringify({
|
|
26
|
+
event: 'terminal.sessions',
|
|
27
|
+
workspaceId,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
data: {
|
|
30
|
+
sessions: existingSessions.map(s => ({ sessionId: s.id, cwd: s.cwd, buffer: s.buffer.join('') })),
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
22
33
|
ws.on('message', (raw) => {
|
|
23
34
|
try {
|
|
24
35
|
const msg = JSON.parse(raw.toString());
|
|
@@ -49,11 +60,29 @@ for (const evt of terminalEvents) {
|
|
|
49
60
|
}
|
|
50
61
|
// Register channel handlers
|
|
51
62
|
registerHandler('channel.message', (_ws, workspaceId, data) => {
|
|
52
|
-
const { channelId, content, type, mentions, attachments } = data;
|
|
63
|
+
const { channelId, content, type, mentions, attachments, replyToMessageId } = data;
|
|
53
64
|
if (!channelId || (!content && !attachments?.length))
|
|
54
65
|
return;
|
|
55
66
|
if (!getChannel(workspaceId, channelId))
|
|
56
67
|
return;
|
|
68
|
+
if (replyToMessageId) {
|
|
69
|
+
const updated = appendMessageReply(workspaceId, channelId, replyToMessageId, {
|
|
70
|
+
senderId: 'user',
|
|
71
|
+
content,
|
|
72
|
+
attachments,
|
|
73
|
+
});
|
|
74
|
+
if (!updated)
|
|
75
|
+
return;
|
|
76
|
+
broadcastToWorkspace(workspaceId, 'channel.message.updated', updated);
|
|
77
|
+
const agentId = updated.senderId !== 'user' ? updated.senderId : undefined;
|
|
78
|
+
if (agentId) {
|
|
79
|
+
void runMentionedAgent(workspaceId, channelId, agentId, stripHtml(content), {
|
|
80
|
+
messageId: updated.id,
|
|
81
|
+
appendUserMessage: stripHtml(content),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
57
86
|
const message = createMessage(workspaceId, channelId, {
|
|
58
87
|
senderId: 'user',
|
|
59
88
|
content,
|
package/dist/ws/message-parts.js
CHANGED
|
@@ -254,7 +254,7 @@ function buildChainItems(lines, finalTextRange, finalText, workspaceRoot, toolDe
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
flushMessageBuffer();
|
|
257
|
-
return items
|
|
257
|
+
return items;
|
|
258
258
|
}
|
|
259
259
|
function buildToolTodo(line, index, workspaceRoot, toolDetails, toolDetailMatchCounts) {
|
|
260
260
|
const summary = summarizeToolLine(line, workspaceRoot);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-spaces/server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-spaces-server": "./dist/app.js"
|
|
@@ -14,15 +14,7 @@
|
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
|
-
"scripts": {
|
|
18
|
-
"dev": "tsx watch src/app.ts",
|
|
19
|
-
"build": "tsc",
|
|
20
|
-
"start": "node dist/app.js",
|
|
21
|
-
"prepublishOnly": "pnpm build",
|
|
22
|
-
"postinstall": "node -e \"try{require('node-pty')}catch(e){if(!require('fs').existsSync('node_modules/node-pty/prebuilds')){process.exit(1)}}\""
|
|
23
|
-
},
|
|
24
17
|
"dependencies": {
|
|
25
|
-
"@agent-spaces/shared": "workspace:*",
|
|
26
18
|
"@anthropic-ai/claude-agent-sdk": "^0.2.126",
|
|
27
19
|
"@codeany/open-agent-sdk": "^0.2.1",
|
|
28
20
|
"@langchain/anthropic": "^1.3.29",
|
|
@@ -38,7 +30,8 @@
|
|
|
38
30
|
"simple-git": "^3.36.0",
|
|
39
31
|
"uuid": "^11.1.0",
|
|
40
32
|
"ws": "^8.18.2",
|
|
41
|
-
"zod": "^4.0.0"
|
|
33
|
+
"zod": "^4.0.0",
|
|
34
|
+
"@agent-spaces/shared": "0.2.1"
|
|
42
35
|
},
|
|
43
36
|
"devDependencies": {
|
|
44
37
|
"@types/cors": "^2.8.17",
|
|
@@ -48,5 +41,11 @@
|
|
|
48
41
|
"@types/ws": "^8.18.1",
|
|
49
42
|
"tsx": "^4.19.4",
|
|
50
43
|
"typescript": "^5.8.3"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "tsx watch src/app.ts",
|
|
47
|
+
"build": "tsc",
|
|
48
|
+
"start": "node dist/app.js",
|
|
49
|
+
"postinstall": "node -e \"try{require('node-pty')}catch(e){if(!require('fs').existsSync('node_modules/node-pty/prebuilds')){process.exit(1)}}\""
|
|
51
50
|
}
|
|
52
|
-
}
|
|
51
|
+
}
|