Skip to content

Commit 93ec83e

Browse files
committed
refactor: persist script executions to history
Change-Id: I4139072998a7b9d9130442e98ec968e960895e7b Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent f0cbd47 commit 93ec83e

File tree

13 files changed

+175
-194
lines changed

13 files changed

+175
-194
lines changed

.cmux/scripts/web_fetch

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env bash
2+
# Description: Fetch a URL via curl and print the response body
3+
set -euo pipefail
4+
5+
if [[ $# -ne 1 ]]; then
6+
echo "Usage: /script web_fetch <url>" >&2
7+
exit 1
8+
fi
9+
10+
url="$1"
11+
12+
echo "[web_fetch] Fetching: $url" >&2
13+
curl --fail --show-error --location "$url"

src/browser/components/ChatInput/index.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -722,14 +722,6 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
722722
const toolResult = result.data;
723723
const exitCode = toolResult.exitCode;
724724

725-
workspaceStore.recordScriptExecution(currentWorkspaceId, {
726-
rawCommand: messageText,
727-
scriptName: parsed.scriptName,
728-
args: parsed.args,
729-
result: toolResult,
730-
timestamp: Date.now(),
731-
});
732-
733725
// Use MUX_OUTPUT content if present, otherwise fall back to default message
734726
const toastMessage =
735727
toolResult.outputFile ??

src/browser/components/Messages/ScriptExecutionMessage.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const ScriptExecutionMessage: React.FC<ScriptExecutionMessageProps> = ({
5050
<span className="font-monospace max-w-96 truncate">
5151
{message.command || `/script ${message.scriptName}${argsPreview}`}
5252
</span>
53-
<span className="text-[10px] text-foreground-secondary ml-2 whitespace-nowrap">
53+
<span className="text-foreground-secondary ml-2 text-[10px] whitespace-nowrap">
5454
took {formatDuration(result.wall_duration_ms)}
5555
</span>
5656
<span className={exitBadgeClass}>exit {result.exitCode}</span>
@@ -66,11 +66,11 @@ export const ScriptExecutionMessage: React.FC<ScriptExecutionMessageProps> = ({
6666

6767
<DetailSection>
6868
<DetailLabel>Runtime info</DetailLabel>
69-
<div className="text-[11px] text-foreground-secondary">
69+
<div className="text-foreground-secondary text-[11px]">
7070
{new Date(message.timestamp).toLocaleString()}{" "}
7171
{formatDuration(result.wall_duration_ms)}
7272
</div>
73-
<div className="text-[11px] text-foreground-secondary">
73+
<div className="text-foreground-secondary text-[11px]">
7474
Visible only to you; never sent to the model.
7575
</div>
7676
</DetailSection>
@@ -108,7 +108,7 @@ export const ScriptExecutionMessage: React.FC<ScriptExecutionMessageProps> = ({
108108
{result.truncated && (
109109
<DetailSection>
110110
<DetailLabel>Truncation</DetailLabel>
111-
<div className="text-[11px] text-foreground-secondary">
111+
<div className="text-foreground-secondary text-[11px]">
112112
Output truncated: {result.truncated.reason} ({result.truncated.totalLines} lines
113113
preserved)
114114
</div>

src/browser/stores/WorkspaceStore.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import assert from "@/common/utils/assert";
2-
import type { MuxMessage, DisplayedMessage, QueuedMessage } from "@/common/types/message";
2+
import type {
3+
MuxMessage,
4+
DisplayedMessage,
5+
QueuedMessage,
6+
MuxFrontendMetadata,
7+
} from "@/common/types/message";
38
import { createMuxMessage } from "@/common/types/message";
49
import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
510
import type { WorkspaceChatMessage } from "@/common/types/ipc";
611
import type { TodoItem } from "@/common/types/tools";
7-
import {
8-
StreamingMessageAggregator,
9-
type ScriptExecutionEventInput,
10-
} from "@/browser/utils/messages/StreamingMessageAggregator";
12+
import { StreamingMessageAggregator } from "@/browser/utils/messages/StreamingMessageAggregator";
1113
import { updatePersistedState } from "@/browser/hooks/usePersistedState";
1214
import { getRetryStateKey } from "@/common/constants/storage";
1315
import { CUSTOM_EVENTS, createCustomEvent } from "@/common/constants/events";
@@ -433,17 +435,6 @@ export class WorkspaceStore {
433435
* Get current TODO list for a workspace.
434436
* Returns empty array if workspace doesn't exist or has no TODOs.
435437
*/
436-
recordScriptExecution(workspaceId: string, details: ScriptExecutionEventInput): void {
437-
const aggregator = this.aggregators.get(workspaceId);
438-
if (!aggregator) {
439-
return;
440-
}
441-
442-
aggregator.addScriptEvent(details);
443-
this.states.bump(workspaceId);
444-
this.checkAndBumpRecencyIfChanged();
445-
}
446-
447438
getTodos(workspaceId: string): TodoItem[] {
448439
const aggregator = this.aggregators.get(workspaceId);
449440
return aggregator ? aggregator.getCurrentTodos() : [];
@@ -714,7 +705,7 @@ export class WorkspaceStore {
714705
// Store continueMessage in summary so it survives history replacement
715706
muxMetadata: continueMessage
716707
? { type: "compaction-result", continueMessage, requestId: compactRequestMsg?.id }
717-
: { type: "normal" },
708+
: ({ type: "normal" } as unknown as MuxFrontendMetadata),
718709
}
719710
);
720711

src/browser/utils/messages/StreamingMessageAggregator.test.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -392,48 +392,57 @@ describe("StreamingMessageAggregator", () => {
392392
describe("script execution events", () => {
393393
test("adds script logs to displayed messages", () => {
394394
const aggregator = new StreamingMessageAggregator(TEST_CREATED_AT);
395-
const userMessage = createMuxMessage("user-1", "user", "run script", {
396-
historySequence: 1,
397-
timestamp: Date.now(),
398-
});
399-
aggregator.addMessage(userMessage);
400395

396+
// Create a persisted message with script metadata
401397
const timestamp = Date.now();
402-
aggregator.addScriptEvent({
403-
rawCommand: "/script demo",
404-
scriptName: "demo",
405-
args: ["--flag"],
406-
result: BASE_SCRIPT_RESULT,
398+
const scriptMessage = createMuxMessage("script-1", "user", "Run script", {
399+
historySequence: 1,
407400
timestamp,
401+
muxMetadata: {
402+
type: "script-execution",
403+
id: "script-exec-1",
404+
historySequence: 1,
405+
timestamp,
406+
command: "/script demo",
407+
scriptName: "demo",
408+
args: ["--flag"],
409+
result: BASE_SCRIPT_RESULT,
410+
}
408411
});
412+
413+
aggregator.addMessage(scriptMessage);
409414

410415
const displayed = aggregator.getDisplayedMessages();
411416
const scriptMsg = displayed.find((msg) => msg.type === "script-execution");
412417
expect(scriptMsg).toBeDefined();
413418
if (scriptMsg?.type === "script-execution") {
414-
expect(scriptMsg.historySequence).toBeGreaterThan(1);
419+
expect(scriptMsg.historySequence).toBe(1);
415420
expect(scriptMsg.timestamp).toBe(timestamp);
416421
expect(scriptMsg.result).toBe(BASE_SCRIPT_RESULT);
417422
}
418423
});
419424

420425
test("removes script logs when history is truncated", () => {
421426
const aggregator = new StreamingMessageAggregator(TEST_CREATED_AT);
422-
aggregator.addMessage(
423-
createMuxMessage("user-1", "user", "first", {
427+
428+
const timestamp = Date.now();
429+
const scriptMessage = createMuxMessage("script-1", "user", "Run script", {
430+
historySequence: 1,
431+
timestamp,
432+
muxMetadata: {
433+
type: "script-execution",
434+
id: "script-exec-1",
424435
historySequence: 1,
425-
timestamp: Date.now(),
426-
})
427-
);
428-
429-
aggregator.addScriptEvent({
430-
rawCommand: "/script cleanup",
431-
scriptName: "cleanup",
432-
args: [],
433-
result: BASE_SCRIPT_RESULT,
434-
timestamp: Date.now(),
436+
timestamp,
437+
command: "/script cleanup",
438+
scriptName: "cleanup",
439+
args: [],
440+
result: BASE_SCRIPT_RESULT,
441+
}
435442
});
436443

444+
aggregator.addMessage(scriptMessage);
445+
437446
const deleteEvent: DeleteMessage = { type: "delete", historySequences: [1] };
438447
aggregator.handleDeleteMessage(deleteEvent);
439448

0 commit comments

Comments
 (0)