Skip to content

Commit 4ea1c22

Browse files
committed
🤖 fix: emit metadata update after setting task state
Address Codex review comment: when a task workspace is created, workspaceService.create() emits metadata before taskState fields are set. Now we call workspaceService.emitMetadataUpdate() after setWorkspaceTaskState() so the renderer learns about parentWorkspaceId and agentType immediately, enabling nested sidebar styling without restart. Added emitMetadataUpdate() method to workspaceService. Change-Id: I7679051bf422554f0d0a3fa543d14587c163f233 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 85caf9f commit 4ea1c22

File tree

4 files changed

+62
-9
lines changed

4 files changed

+62
-9
lines changed

src/node/services/sessionTimingService.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ describe("SessionTimingService", () => {
7979
messageId,
8080
toolCallId: "t1",
8181
toolName: "bash",
82+
args: { cmd: "echo hi" },
8283
result: { ok: true },
8384
timestamp: startTime + 3000,
8485
});
@@ -166,6 +167,7 @@ describe("SessionTimingService", () => {
166167
messageId,
167168
toolCallId: "t1",
168169
toolName: "bash",
170+
args: { cmd: "sleep" },
169171
result: { ok: true },
170172
timestamp: startTime + 10_100,
171173
});

src/node/services/taskService.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ class FakeWorkspaceService {
276276
this.appendedMessages.push({ workspaceId, message: muxMessage });
277277
return Promise.resolve(Ok(undefined));
278278
}
279+
280+
emitMetadataUpdate(_workspaceId: string): Promise<void> {
281+
// No-op in tests - we don't need to verify metadata emission
282+
return Promise.resolve();
283+
}
279284
}
280285

281286
function createWorkspaceMetadata(

src/node/services/taskService.ts

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from "@/common/types/message";
2626
import { isDynamicToolPart } from "@/common/types/toolParts";
2727
import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
28+
import type { ThinkingLevel } from "@/common/types/thinking";
2829
import { detectDefaultTrunkBranch, getCurrentBranch, listLocalBranches } from "@/node/git";
2930
import * as crypto from "crypto";
3031
import assert from "@/common/utils/assert";
@@ -227,6 +228,11 @@ export class TaskService extends EventEmitter {
227228

228229
await this.config.setWorkspaceTaskState(taskId, taskState);
229230

231+
// Emit metadata update so the renderer knows about parentWorkspaceId/agentType.
232+
// The initial create() call emits metadata before these fields are set,
233+
// so we need to re-emit after persisting the task state.
234+
await this.workspaceService.emitMetadataUpdate(taskId);
235+
230236
log.debug(`Created task workspace ${taskId} for parent ${parentWorkspaceId}`, {
231237
agentType,
232238
status: taskState.taskStatus,
@@ -270,7 +276,7 @@ export class TaskService extends EventEmitter {
270276
* Start a task that was previously created (either new or from queue).
271277
*/
272278
private async startTask(taskId: string, options: CreateTaskOptions): Promise<void> {
273-
const { prompt } = options;
279+
const { prompt, parentWorkspaceId } = options;
274280

275281
// Update task state to running
276282
const currentState = this.config.getWorkspaceTaskState(taskId);
@@ -282,10 +288,13 @@ export class TaskService extends EventEmitter {
282288
});
283289
}
284290

291+
// Inherit model/thinkingLevel from parent workspace to avoid forcing a specific provider.
292+
const { model, thinkingLevel } = await this.getTaskAISettings(parentWorkspaceId);
293+
285294
try {
286295
const result = await this.workspaceService.sendMessage(taskId, prompt, {
287-
model: "anthropic:claude-sonnet-4-20250514", // Default model for agents
288-
thinkingLevel: "medium",
296+
model,
297+
thinkingLevel,
289298
mode: "exec", // Agent tasks are always in exec mode
290299
});
291300

@@ -693,15 +702,18 @@ export class TaskService extends EventEmitter {
693702
taskStatus: "awaiting_report",
694703
});
695704

705+
// Get parent's AI settings for the reminder message
706+
const { model } = await this.getTaskAISettings(updatedState.parentWorkspaceId);
707+
696708
// Send reminder message
697709
try {
698710
const result = await this.workspaceService.sendMessage(
699711
workspaceId,
700712
"Your task is complete but you haven't called agent_report yet. " +
701713
"Please call agent_report now with your findings.",
702714
{
703-
model: "anthropic:claude-sonnet-4-20250514",
704-
thinkingLevel: "low",
715+
model,
716+
thinkingLevel: "low", // Low thinking for simple reminder
705717
// Require only agent_report
706718
toolPolicy: [{ regex_match: "agent_report", action: "require" }],
707719
mode: "exec",
@@ -950,13 +962,14 @@ export class TaskService extends EventEmitter {
950962
});
951963
} else if (taskState.taskStatus === "running") {
952964
// Send continuation message for running tasks
965+
const { model, thinkingLevel } = await this.getTaskAISettings(taskState.parentWorkspaceId);
953966
try {
954967
const result = await this.workspaceService.sendMessage(
955968
workspaceId,
956969
"Mux was restarted. Please continue your task and call agent_report when done.",
957970
{
958-
model: "anthropic:claude-sonnet-4-20250514",
959-
thinkingLevel: "medium",
971+
model,
972+
thinkingLevel,
960973
mode: "exec",
961974
}
962975
);
@@ -969,14 +982,15 @@ export class TaskService extends EventEmitter {
969982
}
970983
} else if (taskState.taskStatus === "awaiting_report") {
971984
// Send reminder message for tasks that finished without reporting
985+
const { model } = await this.getTaskAISettings(taskState.parentWorkspaceId);
972986
try {
973987
const result = await this.workspaceService.sendMessage(
974988
workspaceId,
975989
"Mux was restarted. Your task appears to have finished but agent_report was not called. " +
976990
"Please call agent_report now with your findings.",
977991
{
978-
model: "anthropic:claude-sonnet-4-20250514",
979-
thinkingLevel: "low",
992+
model,
993+
thinkingLevel: "low", // Low thinking for simple reminder
980994
// Require only agent_report
981995
toolPolicy: [{ regex_match: "agent_report", action: "require" }],
982996
mode: "exec",
@@ -1123,6 +1137,21 @@ export class TaskService extends EventEmitter {
11231137
return allMetadata.find((m) => m.id === workspaceId) ?? null;
11241138
}
11251139

1140+
/**
1141+
* Get AI settings (model, thinkingLevel) for a task by looking up its parent workspace.
1142+
* Falls back to defaults if parent metadata is unavailable.
1143+
*/
1144+
private async getTaskAISettings(
1145+
parentWorkspaceId: string
1146+
): Promise<{ model: string; thinkingLevel: ThinkingLevel }> {
1147+
const parentMetadata = await this.aiService.getWorkspaceMetadata(parentWorkspaceId);
1148+
const aiSettings = parentMetadata.success ? parentMetadata.data.aiSettings : undefined;
1149+
return {
1150+
model: aiSettings?.model ?? "anthropic:claude-sonnet-4-20250514",
1151+
thinkingLevel: aiSettings?.thinkingLevel ?? "medium",
1152+
};
1153+
}
1154+
11261155
/**
11271156
* Dispose of the service.
11281157
*/

src/node/services/workspaceService.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,23 @@ export class WorkspaceService extends EventEmitter {
747747
return allMetadata.find((m) => m.id === workspaceId) ?? null;
748748
}
749749

750+
/**
751+
* Emit a metadata update event for a workspace.
752+
* Used when workspace metadata changes outside of normal workspaceService flows
753+
* (e.g., when taskService sets parentWorkspaceId/agentType after workspace creation).
754+
*/
755+
async emitMetadataUpdate(workspaceId: string): Promise<void> {
756+
const allMetadata = await this.config.getAllWorkspaceMetadata();
757+
const metadata = allMetadata.find((m) => m.id === workspaceId) ?? null;
758+
759+
const session = this.sessions.get(workspaceId);
760+
if (session) {
761+
session.emitMetadata(metadata);
762+
} else {
763+
this.emit("metadata", { workspaceId, metadata });
764+
}
765+
}
766+
750767
async rename(workspaceId: string, newName: string): Promise<Result<{ newWorkspaceId: string }>> {
751768
try {
752769
if (this.aiService.isStreaming(workspaceId)) {

0 commit comments

Comments
 (0)