MCPcopy
hub / github.com/coder/mux / updateHistory

Method updateHistory

src/node/services/historyService.ts:1533–1604  ·  view source on GitHub ↗

* Update an existing message in history by historySequence * Reads the active chat.jsonl, replaces the matching message, and rewrites the file. * * This runs on every stream end, so it must stay O(active epoch): targets are * always in the active epoch (stream placeholders, compaction su

(workspaceId: string, message: MuxMessage)

Source from the content-addressed store, hash-verified

1531 * never in the sealed archive.
1532 */
1533 async updateHistory(workspaceId: string, message: MuxMessage): Promise<Result<void>> {
1534 return this.fileLocks.withLock(workspaceId, async () => {
1535 try {
1536 const historyPath = this.getChatHistoryPath(workspaceId);
1537
1538 // Read the active epoch — structural rewrite requires full file content
1539 const messages = await this.readChatHistory(workspaceId);
1540 const targetSequence = message.metadata?.historySequence;
1541
1542 if (targetSequence === undefined) {
1543 return Err("Cannot update message without historySequence");
1544 }
1545
1546 assert(
1547 isNonNegativeInteger(targetSequence),
1548 "updateHistory requires historySequence to be a non-negative integer"
1549 );
1550
1551 // Find and replace the message with matching historySequence
1552 let found = false;
1553 let persistedMessage: MuxMessage | undefined;
1554 for (let i = 0; i < messages.length; i++) {
1555 if (messages[i].metadata?.historySequence === targetSequence) {
1556 const existingMessage = messages[i];
1557 assert(existingMessage, "updateHistory matched message must exist");
1558
1559 // Preserve compaction boundary metadata during late in-place rewrites.
1560 // Compaction may update an assistant row first, then a late stream rewrite can
1561 // update that same historySequence and accidentally drop compaction markers.
1562 const preservedCompactionMetadata = getCompactionMetadataToPreserve(
1563 workspaceId,
1564 existingMessage,
1565 message
1566 );
1567
1568 // Preserve the historySequence, update everything else.
1569 messages[i] = {
1570 ...message,
1571 metadata: {
1572 ...message.metadata,
1573 ...(preservedCompactionMetadata ?? {}),
1574 historySequence: targetSequence,
1575 },
1576 };
1577 persistedMessage = messages[i];
1578 found = true;
1579 break;
1580 }
1581 }
1582
1583 if (!found || !persistedMessage) {
1584 return Err(`No message found with historySequence ${targetSequence}`);
1585 }
1586
1587 // Rewrite entire file
1588 const historyEntries = this.serializeHistoryEntries(messages, workspaceId);
1589
1590 // Atomic write prevents corruption if app crashes mid-write

Callers 10

commitPartialMethod · 0.95
performCompactionMethod · 0.80
answerAskUserQuestionMethod · 0.80
simulateToolPolicyNoopFunction · 0.80
askSideQuestionFunction · 0.80
dispatchEventMethod · 0.80

Calls 11

getChatHistoryPathMethod · 0.95
readChatHistoryMethod · 0.95
ErrFunction · 0.90
isNonNegativeIntegerFunction · 0.90
OkFunction · 0.90
getErrorMessageFunction · 0.90
withLockMethod · 0.80
assertFunction · 0.50

Tested by

no test coverage detected