stashes 0.1.20 → 0.1.22
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/cli.js
CHANGED
|
@@ -622,15 +622,20 @@ class PersistenceService {
|
|
|
622
622
|
var {spawn } = globalThis.Bun;
|
|
623
623
|
var CLAUDE_BIN = "/opt/homebrew/bin/claude";
|
|
624
624
|
var processes = new Map;
|
|
625
|
-
function startAiProcess(id, prompt, cwd) {
|
|
625
|
+
function startAiProcess(id, prompt, cwd, resumeSessionId) {
|
|
626
626
|
killAiProcess(id);
|
|
627
627
|
logger.info("claude", `spawning process: ${id}`, {
|
|
628
628
|
cwd,
|
|
629
629
|
promptLength: prompt.length,
|
|
630
|
-
promptPreview: prompt.substring(0, 100)
|
|
630
|
+
promptPreview: prompt.substring(0, 100),
|
|
631
|
+
resumeSessionId
|
|
631
632
|
});
|
|
633
|
+
const cmd = [CLAUDE_BIN, "-p", prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
|
|
634
|
+
if (resumeSessionId) {
|
|
635
|
+
cmd.push("--resume", resumeSessionId);
|
|
636
|
+
}
|
|
632
637
|
const proc = spawn({
|
|
633
|
-
cmd
|
|
638
|
+
cmd,
|
|
634
639
|
stdin: "ignore",
|
|
635
640
|
stdout: "pipe",
|
|
636
641
|
stderr: "pipe",
|
|
@@ -684,6 +689,9 @@ async function* parseClaudeStream(proc) {
|
|
|
684
689
|
} catch {
|
|
685
690
|
continue;
|
|
686
691
|
}
|
|
692
|
+
if (parsed.type === "system" && parsed.subtype === "init" && parsed.session_id) {
|
|
693
|
+
yield { type: "session_id", content: "", sessionId: parsed.session_id };
|
|
694
|
+
}
|
|
687
695
|
if (parsed.type === "assistant" && parsed.message) {
|
|
688
696
|
const message = parsed.message;
|
|
689
697
|
for (const block of message.content || []) {
|
|
@@ -1323,6 +1331,9 @@ class StashService {
|
|
|
1323
1331
|
broadcast;
|
|
1324
1332
|
previewPool;
|
|
1325
1333
|
selectedComponent = null;
|
|
1334
|
+
messageQueue = [];
|
|
1335
|
+
isProcessingMessage = false;
|
|
1336
|
+
chatSessions = new Map;
|
|
1326
1337
|
constructor(projectPath, worktreeManager, persistence, broadcast) {
|
|
1327
1338
|
this.projectPath = projectPath;
|
|
1328
1339
|
this.worktreeManager = worktreeManager;
|
|
@@ -1375,6 +1386,20 @@ class StashService {
|
|
|
1375
1386
|
}
|
|
1376
1387
|
}
|
|
1377
1388
|
async message(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1389
|
+
this.messageQueue.push({ projectId, chatId, message, referenceStashIds, componentContext });
|
|
1390
|
+
if (!this.isProcessingMessage) {
|
|
1391
|
+
await this.processMessageQueue();
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
async processMessageQueue() {
|
|
1395
|
+
this.isProcessingMessage = true;
|
|
1396
|
+
while (this.messageQueue.length > 0) {
|
|
1397
|
+
const msg = this.messageQueue.shift();
|
|
1398
|
+
await this.processMessage(msg.projectId, msg.chatId, msg.message, msg.referenceStashIds, msg.componentContext);
|
|
1399
|
+
}
|
|
1400
|
+
this.isProcessingMessage = false;
|
|
1401
|
+
}
|
|
1402
|
+
async processMessage(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1378
1403
|
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1379
1404
|
let sourceCode = "";
|
|
1380
1405
|
const filePath = component?.filePath || "";
|
|
@@ -1394,34 +1419,66 @@ ${refs.join(`
|
|
|
1394
1419
|
`)}`;
|
|
1395
1420
|
}
|
|
1396
1421
|
}
|
|
1397
|
-
const
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1422
|
+
const existingSessionId = this.chatSessions.get(chatId);
|
|
1423
|
+
let chatPrompt;
|
|
1424
|
+
if (existingSessionId) {
|
|
1425
|
+
const parts = [message];
|
|
1426
|
+
if (stashContext)
|
|
1427
|
+
parts.push(stashContext);
|
|
1428
|
+
if (component && !existingSessionId)
|
|
1429
|
+
parts.push(`Component: ${component.name}`);
|
|
1430
|
+
chatPrompt = parts.join(`
|
|
1431
|
+
`);
|
|
1432
|
+
} else {
|
|
1433
|
+
chatPrompt = [
|
|
1434
|
+
"You are helping the user explore UI design variations for their project.",
|
|
1435
|
+
"You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
|
|
1436
|
+
"If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
|
|
1437
|
+
"If the user asks to vary an existing stash, use the stashes_vary tool.",
|
|
1438
|
+
"If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
|
|
1439
|
+
'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
|
|
1440
|
+
'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
|
|
1441
|
+
"Otherwise, respond conversationally about their project and stashes.",
|
|
1442
|
+
"",
|
|
1443
|
+
component ? `Component: ${component.name}` : "",
|
|
1444
|
+
filePath !== "auto-detect" ? `File: ${filePath}` : "",
|
|
1445
|
+
sourceCode ? `
|
|
1410
1446
|
Source:
|
|
1411
1447
|
\`\`\`
|
|
1412
1448
|
${sourceCode.substring(0, 3000)}
|
|
1413
1449
|
\`\`\`` : "",
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1450
|
+
stashContext,
|
|
1451
|
+
"",
|
|
1452
|
+
`User: ${message}`
|
|
1453
|
+
].filter(Boolean).join(`
|
|
1418
1454
|
`);
|
|
1419
|
-
|
|
1420
|
-
|
|
1455
|
+
}
|
|
1456
|
+
const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath, existingSessionId);
|
|
1457
|
+
let thinkingBuf = "";
|
|
1458
|
+
let textBuf = "";
|
|
1459
|
+
const pendingMessages = [];
|
|
1460
|
+
const now = new Date().toISOString();
|
|
1461
|
+
function flushThinking() {
|
|
1462
|
+
if (!thinkingBuf)
|
|
1463
|
+
return;
|
|
1464
|
+
pendingMessages.push({ id: crypto.randomUUID(), role: "assistant", content: thinkingBuf, type: "thinking", createdAt: now });
|
|
1465
|
+
thinkingBuf = "";
|
|
1466
|
+
}
|
|
1467
|
+
function flushText() {
|
|
1468
|
+
if (!textBuf)
|
|
1469
|
+
return;
|
|
1470
|
+
pendingMessages.push({ id: crypto.randomUUID(), role: "assistant", content: textBuf, type: "text", createdAt: now });
|
|
1471
|
+
textBuf = "";
|
|
1472
|
+
}
|
|
1421
1473
|
try {
|
|
1422
1474
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
1475
|
+
if (chunk.type === "session_id" && chunk.sessionId) {
|
|
1476
|
+
this.chatSessions.set(chatId, chunk.sessionId);
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1423
1479
|
if (chunk.type === "text") {
|
|
1424
|
-
|
|
1480
|
+
flushThinking();
|
|
1481
|
+
textBuf += chunk.content;
|
|
1425
1482
|
this.broadcast({
|
|
1426
1483
|
type: "ai_stream",
|
|
1427
1484
|
content: chunk.content,
|
|
@@ -1429,6 +1486,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1429
1486
|
source: "chat"
|
|
1430
1487
|
});
|
|
1431
1488
|
} else if (chunk.type === "thinking") {
|
|
1489
|
+
thinkingBuf += chunk.content;
|
|
1432
1490
|
this.broadcast({
|
|
1433
1491
|
type: "ai_stream",
|
|
1434
1492
|
content: chunk.content,
|
|
@@ -1436,36 +1494,66 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1436
1494
|
source: "chat"
|
|
1437
1495
|
});
|
|
1438
1496
|
} else if (chunk.type === "tool_use") {
|
|
1497
|
+
flushThinking();
|
|
1498
|
+
flushText();
|
|
1499
|
+
let toolName = "unknown";
|
|
1500
|
+
let toolParams = {};
|
|
1501
|
+
try {
|
|
1502
|
+
const parsed = JSON.parse(chunk.content);
|
|
1503
|
+
toolName = parsed.tool ?? "unknown";
|
|
1504
|
+
toolParams = parsed.input ?? {};
|
|
1505
|
+
} catch {}
|
|
1506
|
+
pendingMessages.push({
|
|
1507
|
+
id: crypto.randomUUID(),
|
|
1508
|
+
role: "assistant",
|
|
1509
|
+
content: chunk.content,
|
|
1510
|
+
type: "tool_start",
|
|
1511
|
+
toolName,
|
|
1512
|
+
toolParams,
|
|
1513
|
+
toolStatus: "running",
|
|
1514
|
+
createdAt: now
|
|
1515
|
+
});
|
|
1439
1516
|
this.broadcast({
|
|
1440
1517
|
type: "ai_stream",
|
|
1441
1518
|
content: chunk.content,
|
|
1442
1519
|
streamType: "tool_start",
|
|
1443
1520
|
source: "chat",
|
|
1444
|
-
toolName
|
|
1445
|
-
toolParams
|
|
1521
|
+
toolName,
|
|
1522
|
+
toolParams,
|
|
1446
1523
|
toolStatus: "running"
|
|
1447
1524
|
});
|
|
1448
1525
|
} else if (chunk.type === "tool_result") {
|
|
1526
|
+
let toolResult = chunk.content;
|
|
1527
|
+
let isError = false;
|
|
1528
|
+
try {
|
|
1529
|
+
const parsed = JSON.parse(chunk.content);
|
|
1530
|
+
toolResult = parsed.result ?? chunk.content;
|
|
1531
|
+
isError = !!parsed.is_error;
|
|
1532
|
+
} catch {}
|
|
1533
|
+
pendingMessages.push({
|
|
1534
|
+
id: crypto.randomUUID(),
|
|
1535
|
+
role: "assistant",
|
|
1536
|
+
content: chunk.content,
|
|
1537
|
+
type: "tool_end",
|
|
1538
|
+
toolStatus: isError ? "error" : "completed",
|
|
1539
|
+
toolResult: toolResult.substring(0, 300),
|
|
1540
|
+
createdAt: now
|
|
1541
|
+
});
|
|
1449
1542
|
this.broadcast({
|
|
1450
1543
|
type: "ai_stream",
|
|
1451
1544
|
content: chunk.content,
|
|
1452
1545
|
streamType: "tool_end",
|
|
1453
1546
|
source: "chat",
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
toolResult: chunk.content.substring(0, 300)
|
|
1547
|
+
toolStatus: isError ? "error" : "completed",
|
|
1548
|
+
toolResult: toolResult.substring(0, 300)
|
|
1457
1549
|
});
|
|
1458
1550
|
}
|
|
1459
1551
|
}
|
|
1460
1552
|
await aiProcess.process.exited;
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
content: fullResponse,
|
|
1466
|
-
type: "text",
|
|
1467
|
-
createdAt: new Date().toISOString()
|
|
1468
|
-
});
|
|
1553
|
+
flushThinking();
|
|
1554
|
+
flushText();
|
|
1555
|
+
for (const msg of pendingMessages) {
|
|
1556
|
+
this.persistence.saveChatMessage(projectId, chatId, msg);
|
|
1469
1557
|
}
|
|
1470
1558
|
} catch (err) {
|
|
1471
1559
|
this.broadcast({
|
package/dist/mcp.js
CHANGED
|
@@ -504,15 +504,20 @@ class PersistenceService {
|
|
|
504
504
|
var {spawn } = globalThis.Bun;
|
|
505
505
|
var CLAUDE_BIN = "/opt/homebrew/bin/claude";
|
|
506
506
|
var processes = new Map;
|
|
507
|
-
function startAiProcess(id, prompt, cwd) {
|
|
507
|
+
function startAiProcess(id, prompt, cwd, resumeSessionId) {
|
|
508
508
|
killAiProcess(id);
|
|
509
509
|
logger.info("claude", `spawning process: ${id}`, {
|
|
510
510
|
cwd,
|
|
511
511
|
promptLength: prompt.length,
|
|
512
|
-
promptPreview: prompt.substring(0, 100)
|
|
512
|
+
promptPreview: prompt.substring(0, 100),
|
|
513
|
+
resumeSessionId
|
|
513
514
|
});
|
|
515
|
+
const cmd = [CLAUDE_BIN, "-p", prompt, "--output-format=stream-json", "--verbose", "--dangerously-skip-permissions"];
|
|
516
|
+
if (resumeSessionId) {
|
|
517
|
+
cmd.push("--resume", resumeSessionId);
|
|
518
|
+
}
|
|
514
519
|
const proc = spawn({
|
|
515
|
-
cmd
|
|
520
|
+
cmd,
|
|
516
521
|
stdin: "ignore",
|
|
517
522
|
stdout: "pipe",
|
|
518
523
|
stderr: "pipe",
|
|
@@ -566,6 +571,9 @@ async function* parseClaudeStream(proc) {
|
|
|
566
571
|
} catch {
|
|
567
572
|
continue;
|
|
568
573
|
}
|
|
574
|
+
if (parsed.type === "system" && parsed.subtype === "init" && parsed.session_id) {
|
|
575
|
+
yield { type: "session_id", content: "", sessionId: parsed.session_id };
|
|
576
|
+
}
|
|
569
577
|
if (parsed.type === "assistant" && parsed.message) {
|
|
570
578
|
const message = parsed.message;
|
|
571
579
|
for (const block of message.content || []) {
|
|
@@ -1519,6 +1527,9 @@ class StashService {
|
|
|
1519
1527
|
broadcast;
|
|
1520
1528
|
previewPool;
|
|
1521
1529
|
selectedComponent = null;
|
|
1530
|
+
messageQueue = [];
|
|
1531
|
+
isProcessingMessage = false;
|
|
1532
|
+
chatSessions = new Map;
|
|
1522
1533
|
constructor(projectPath, worktreeManager, persistence, broadcast) {
|
|
1523
1534
|
this.projectPath = projectPath;
|
|
1524
1535
|
this.worktreeManager = worktreeManager;
|
|
@@ -1571,6 +1582,20 @@ class StashService {
|
|
|
1571
1582
|
}
|
|
1572
1583
|
}
|
|
1573
1584
|
async message(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1585
|
+
this.messageQueue.push({ projectId, chatId, message, referenceStashIds, componentContext });
|
|
1586
|
+
if (!this.isProcessingMessage) {
|
|
1587
|
+
await this.processMessageQueue();
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
async processMessageQueue() {
|
|
1591
|
+
this.isProcessingMessage = true;
|
|
1592
|
+
while (this.messageQueue.length > 0) {
|
|
1593
|
+
const msg = this.messageQueue.shift();
|
|
1594
|
+
await this.processMessage(msg.projectId, msg.chatId, msg.message, msg.referenceStashIds, msg.componentContext);
|
|
1595
|
+
}
|
|
1596
|
+
this.isProcessingMessage = false;
|
|
1597
|
+
}
|
|
1598
|
+
async processMessage(projectId, chatId, message, referenceStashIds, componentContext) {
|
|
1574
1599
|
const component = componentContext ? { name: componentContext.name, filePath: this.selectedComponent?.filePath || "" } : this.selectedComponent;
|
|
1575
1600
|
let sourceCode = "";
|
|
1576
1601
|
const filePath = component?.filePath || "";
|
|
@@ -1590,34 +1615,66 @@ ${refs.join(`
|
|
|
1590
1615
|
`)}`;
|
|
1591
1616
|
}
|
|
1592
1617
|
}
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1618
|
+
const existingSessionId = this.chatSessions.get(chatId);
|
|
1619
|
+
let chatPrompt;
|
|
1620
|
+
if (existingSessionId) {
|
|
1621
|
+
const parts = [message];
|
|
1622
|
+
if (stashContext)
|
|
1623
|
+
parts.push(stashContext);
|
|
1624
|
+
if (component && !existingSessionId)
|
|
1625
|
+
parts.push(`Component: ${component.name}`);
|
|
1626
|
+
chatPrompt = parts.join(`
|
|
1627
|
+
`);
|
|
1628
|
+
} else {
|
|
1629
|
+
chatPrompt = [
|
|
1630
|
+
"You are helping the user explore UI design variations for their project.",
|
|
1631
|
+
"You have access to stashes MCP tools to generate, list, show, browse, vary, and apply stashes.",
|
|
1632
|
+
"If the user asks you to generate, create, or make variations, use the stashes_generate tool.",
|
|
1633
|
+
"If the user asks to vary an existing stash, use the stashes_vary tool.",
|
|
1634
|
+
"If the user asks about what a stash changed, its diff, or its contents, use stashes_show to inspect it.",
|
|
1635
|
+
'IMPORTANT: NEVER call stashes_apply unless the user explicitly asks to "apply" or "merge" a stash.',
|
|
1636
|
+
'IMPORTANT: NEVER call stashes_remove unless the user explicitly asks to "delete" or "remove" a stash.',
|
|
1637
|
+
"Otherwise, respond conversationally about their project and stashes.",
|
|
1638
|
+
"",
|
|
1639
|
+
component ? `Component: ${component.name}` : "",
|
|
1640
|
+
filePath !== "auto-detect" ? `File: ${filePath}` : "",
|
|
1641
|
+
sourceCode ? `
|
|
1606
1642
|
Source:
|
|
1607
1643
|
\`\`\`
|
|
1608
1644
|
${sourceCode.substring(0, 3000)}
|
|
1609
1645
|
\`\`\`` : "",
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1646
|
+
stashContext,
|
|
1647
|
+
"",
|
|
1648
|
+
`User: ${message}`
|
|
1649
|
+
].filter(Boolean).join(`
|
|
1614
1650
|
`);
|
|
1615
|
-
|
|
1616
|
-
|
|
1651
|
+
}
|
|
1652
|
+
const aiProcess = startAiProcess("chat", chatPrompt, this.projectPath, existingSessionId);
|
|
1653
|
+
let thinkingBuf = "";
|
|
1654
|
+
let textBuf = "";
|
|
1655
|
+
const pendingMessages = [];
|
|
1656
|
+
const now = new Date().toISOString();
|
|
1657
|
+
function flushThinking() {
|
|
1658
|
+
if (!thinkingBuf)
|
|
1659
|
+
return;
|
|
1660
|
+
pendingMessages.push({ id: crypto.randomUUID(), role: "assistant", content: thinkingBuf, type: "thinking", createdAt: now });
|
|
1661
|
+
thinkingBuf = "";
|
|
1662
|
+
}
|
|
1663
|
+
function flushText() {
|
|
1664
|
+
if (!textBuf)
|
|
1665
|
+
return;
|
|
1666
|
+
pendingMessages.push({ id: crypto.randomUUID(), role: "assistant", content: textBuf, type: "text", createdAt: now });
|
|
1667
|
+
textBuf = "";
|
|
1668
|
+
}
|
|
1617
1669
|
try {
|
|
1618
1670
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
1671
|
+
if (chunk.type === "session_id" && chunk.sessionId) {
|
|
1672
|
+
this.chatSessions.set(chatId, chunk.sessionId);
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1619
1675
|
if (chunk.type === "text") {
|
|
1620
|
-
|
|
1676
|
+
flushThinking();
|
|
1677
|
+
textBuf += chunk.content;
|
|
1621
1678
|
this.broadcast({
|
|
1622
1679
|
type: "ai_stream",
|
|
1623
1680
|
content: chunk.content,
|
|
@@ -1625,6 +1682,7 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1625
1682
|
source: "chat"
|
|
1626
1683
|
});
|
|
1627
1684
|
} else if (chunk.type === "thinking") {
|
|
1685
|
+
thinkingBuf += chunk.content;
|
|
1628
1686
|
this.broadcast({
|
|
1629
1687
|
type: "ai_stream",
|
|
1630
1688
|
content: chunk.content,
|
|
@@ -1632,36 +1690,66 @@ ${sourceCode.substring(0, 3000)}
|
|
|
1632
1690
|
source: "chat"
|
|
1633
1691
|
});
|
|
1634
1692
|
} else if (chunk.type === "tool_use") {
|
|
1693
|
+
flushThinking();
|
|
1694
|
+
flushText();
|
|
1695
|
+
let toolName = "unknown";
|
|
1696
|
+
let toolParams = {};
|
|
1697
|
+
try {
|
|
1698
|
+
const parsed = JSON.parse(chunk.content);
|
|
1699
|
+
toolName = parsed.tool ?? "unknown";
|
|
1700
|
+
toolParams = parsed.input ?? {};
|
|
1701
|
+
} catch {}
|
|
1702
|
+
pendingMessages.push({
|
|
1703
|
+
id: crypto.randomUUID(),
|
|
1704
|
+
role: "assistant",
|
|
1705
|
+
content: chunk.content,
|
|
1706
|
+
type: "tool_start",
|
|
1707
|
+
toolName,
|
|
1708
|
+
toolParams,
|
|
1709
|
+
toolStatus: "running",
|
|
1710
|
+
createdAt: now
|
|
1711
|
+
});
|
|
1635
1712
|
this.broadcast({
|
|
1636
1713
|
type: "ai_stream",
|
|
1637
1714
|
content: chunk.content,
|
|
1638
1715
|
streamType: "tool_start",
|
|
1639
1716
|
source: "chat",
|
|
1640
|
-
toolName
|
|
1641
|
-
toolParams
|
|
1717
|
+
toolName,
|
|
1718
|
+
toolParams,
|
|
1642
1719
|
toolStatus: "running"
|
|
1643
1720
|
});
|
|
1644
1721
|
} else if (chunk.type === "tool_result") {
|
|
1722
|
+
let toolResult = chunk.content;
|
|
1723
|
+
let isError = false;
|
|
1724
|
+
try {
|
|
1725
|
+
const parsed = JSON.parse(chunk.content);
|
|
1726
|
+
toolResult = parsed.result ?? chunk.content;
|
|
1727
|
+
isError = !!parsed.is_error;
|
|
1728
|
+
} catch {}
|
|
1729
|
+
pendingMessages.push({
|
|
1730
|
+
id: crypto.randomUUID(),
|
|
1731
|
+
role: "assistant",
|
|
1732
|
+
content: chunk.content,
|
|
1733
|
+
type: "tool_end",
|
|
1734
|
+
toolStatus: isError ? "error" : "completed",
|
|
1735
|
+
toolResult: toolResult.substring(0, 300),
|
|
1736
|
+
createdAt: now
|
|
1737
|
+
});
|
|
1645
1738
|
this.broadcast({
|
|
1646
1739
|
type: "ai_stream",
|
|
1647
1740
|
content: chunk.content,
|
|
1648
1741
|
streamType: "tool_end",
|
|
1649
1742
|
source: "chat",
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
toolResult: chunk.content.substring(0, 300)
|
|
1743
|
+
toolStatus: isError ? "error" : "completed",
|
|
1744
|
+
toolResult: toolResult.substring(0, 300)
|
|
1653
1745
|
});
|
|
1654
1746
|
}
|
|
1655
1747
|
}
|
|
1656
1748
|
await aiProcess.process.exited;
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
content: fullResponse,
|
|
1662
|
-
type: "text",
|
|
1663
|
-
createdAt: new Date().toISOString()
|
|
1664
|
-
});
|
|
1749
|
+
flushThinking();
|
|
1750
|
+
flushText();
|
|
1751
|
+
for (const msg of pendingMessages) {
|
|
1752
|
+
this.persistence.saveChatMessage(projectId, chatId, msg);
|
|
1665
1753
|
}
|
|
1666
1754
|
} catch (err) {
|
|
1667
1755
|
this.broadcast({
|