meshy-node 0.10.7 → 0.10.8

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/main.cjs CHANGED
@@ -136339,6 +136339,8 @@ var fs3 = __toESM(require("fs"), 1);
136339
136339
  var os3 = __toESM(require("os"), 1);
136340
136340
  var path3 = __toESM(require("path"), 1);
136341
136341
  var STARTUP_TRANSPORTS = /* @__PURE__ */ new Set(["direct", "devtunnel", "tailscale"]);
136342
+ var DEFAULT_TASK_AGENTS = /* @__PURE__ */ new Set(["codex", "claudecode", "copilot"]);
136343
+ var DEFAULT_NODE_TASK_AGENT = "codex";
136342
136344
  function getDeviceNodeName() {
136343
136345
  return os3.hostname();
136344
136346
  }
@@ -136408,6 +136410,9 @@ function normalizeString(value) {
136408
136410
  function normalizeStartupTransport(value) {
136409
136411
  return typeof value === "string" && STARTUP_TRANSPORTS.has(value) ? value : void 0;
136410
136412
  }
136413
+ function normalizeDefaultTaskAgent(value) {
136414
+ return typeof value === "string" && DEFAULT_TASK_AGENTS.has(value) ? value : void 0;
136415
+ }
136411
136416
  function normalizeStartupMetadata(startup) {
136412
136417
  return {
136413
136418
  port: normalizePositiveInteger(startup?.port),
@@ -136445,6 +136450,9 @@ function resolveDefaultNodeName(storagePath, deviceName = getDeviceNodeName()) {
136445
136450
  const preferredName = resolvePersistedDefaultNodeName(storagePath);
136446
136451
  return preferredName && preferredName.length > 0 ? preferredName : deviceName;
136447
136452
  }
136453
+ function resolveNodeDefaultTaskAgent(storagePath) {
136454
+ return normalizeDefaultTaskAgent(readNodeMetadata(storagePath).defaultTaskAgent) ?? DEFAULT_NODE_TASK_AGENT;
136455
+ }
136448
136456
  function persistDefaultNodeName(storagePath, name2, deviceName = getDeviceNodeName()) {
136449
136457
  const metadata = readNodeMetadata(storagePath);
136450
136458
  const normalizedName = name2.trim();
@@ -136455,6 +136463,11 @@ function persistDefaultNodeName(storagePath, name2, deviceName = getDeviceNodeNa
136455
136463
  }
136456
136464
  writeNodeMetadata(storagePath, metadata);
136457
136465
  }
136466
+ function persistNodeDefaultTaskAgent(storagePath, agent) {
136467
+ const metadata = readNodeMetadata(storagePath);
136468
+ metadata.defaultTaskAgent = agent;
136469
+ writeNodeMetadata(storagePath, metadata);
136470
+ }
136458
136471
  function resolveNodeAuthMetadata(storagePath) {
136459
136472
  return normalizeAuthMetadata(readNodeMetadata(storagePath).auth);
136460
136473
  }
@@ -136958,6 +136971,7 @@ function parseTaskQueuedMessages(value) {
136958
136971
  messages.push({
136959
136972
  id: entry.id,
136960
136973
  content: clone(entry.content),
136974
+ ...typeof entry.targetAgent === "string" ? { targetAgent: entry.targetAgent } : {},
136961
136975
  createdAt: entry.createdAt
136962
136976
  });
136963
136977
  }
@@ -138439,7 +138453,7 @@ var TaskEngine = class {
138439
138453
  });
138440
138454
  return true;
138441
138455
  }
138442
- enqueueTaskMessage(taskId, content3) {
138456
+ enqueueTaskMessage(taskId, content3, targetAgent) {
138443
138457
  const task = this.store.getTask(taskId);
138444
138458
  if (!task) {
138445
138459
  throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
@@ -138454,7 +138468,12 @@ var TaskEngine = class {
138454
138468
  const now = Date.now();
138455
138469
  const queuedMessages = [
138456
138470
  ...task.queuedMessages ?? [],
138457
- { id: (0, import_node_crypto5.randomUUID)(), content: content3, createdAt: now }
138471
+ {
138472
+ id: (0, import_node_crypto5.randomUUID)(),
138473
+ content: content3,
138474
+ ...targetAgent ? { targetAgent } : {},
138475
+ createdAt: now
138476
+ }
138458
138477
  ];
138459
138478
  const updated = this.store.updateTask(taskId, { queuedMessages, updatedAt: now });
138460
138479
  this.eventBus.emit("task.updated", {
@@ -140021,6 +140040,31 @@ var ExecutionEngine = class {
140021
140040
  await Promise.all([...this.logWrites.values()]);
140022
140041
  }
140023
140042
  }
140043
+ async getLogLineCount(taskId) {
140044
+ const logPath = this.getLogPath(taskId);
140045
+ try {
140046
+ const content3 = await fs7.promises.readFile(logPath, "utf-8");
140047
+ const trimmed = content3.trim();
140048
+ return trimmed ? trimmed.split("\n").filter(Boolean).length : 0;
140049
+ } catch (error2) {
140050
+ if (error2.code === "ENOENT") return 0;
140051
+ throw error2;
140052
+ }
140053
+ }
140054
+ async flushLogsAndEmitReady(taskId) {
140055
+ try {
140056
+ await this.waitForLogWrites();
140057
+ const total = await this.getLogLineCount(taskId);
140058
+ this.eventBus.emit("task.logs.ready", { taskId, total });
140059
+ return total;
140060
+ } catch (error2) {
140061
+ this.logger.warn("failed to emit task log readiness checkpoint", {
140062
+ taskId,
140063
+ error: error2 instanceof Error ? error2.message : String(error2)
140064
+ });
140065
+ return 0;
140066
+ }
140067
+ }
140024
140068
  getLogPath(taskId) {
140025
140069
  return path7.join(this.logDir, `${taskId}.jsonl`);
140026
140070
  }
@@ -140886,9 +140930,59 @@ function nonEmptyString(value) {
140886
140930
  function getNativeHistorySessionId(payload) {
140887
140931
  return nonEmptyString(payload?.nativeSessionId) || nonEmptyString(payload?.sessionId);
140888
140932
  }
140889
- function buildPersistedSessionPayload(task, sessionId, cwd) {
140933
+ function isRecord5(value) {
140934
+ return typeof value === "object" && value !== null;
140935
+ }
140936
+ function getAgentSessions(payload) {
140937
+ const raw3 = payload.agentSessions;
140938
+ if (!isRecord5(raw3)) return {};
140939
+ const sessions = {};
140940
+ for (const [agent, value] of Object.entries(raw3)) {
140941
+ if (!isRecord5(value)) continue;
140942
+ const sessionId = nonEmptyString(value.sessionId);
140943
+ const cwd = nonEmptyString(value.cwd);
140944
+ if (!agent || !sessionId || !cwd) continue;
140945
+ sessions[agent] = { sessionId, cwd };
140946
+ }
140947
+ return sessions;
140948
+ }
140949
+ function getPersistedAgentSession(task, agent) {
140890
140950
  const payload = task?.payload ?? {};
140951
+ const sessions = getAgentSessions(payload);
140952
+ const session = sessions[agent];
140953
+ if (session) return session;
140954
+ if (Object.keys(sessions).length > 0 || task?.agent !== agent) {
140955
+ return null;
140956
+ }
140957
+ const legacySessionId = nonEmptyString(payload.sessionId);
140958
+ if (!legacySessionId) return null;
140959
+ return {
140960
+ sessionId: legacySessionId,
140961
+ cwd: nonEmptyString(payload.sessionCwd)
140962
+ };
140963
+ }
140964
+ function canUseSessionLogEventForAgent(task, agent, event) {
140965
+ const eventAgent = nonEmptyString(event.agent);
140966
+ if (eventAgent) return eventAgent === agent;
140967
+ const sessions = getAgentSessions(task?.payload ?? {});
140968
+ return Object.keys(sessions).length === 0 && task?.agent === agent;
140969
+ }
140970
+ function buildPersistedSessionPayload(task, sessionId, cwd, agent) {
140971
+ const payload = task?.payload ?? {};
140972
+ const activeAgent = nonEmptyString(agent);
140891
140973
  const nextPayload = { ...payload, sessionId, sessionCwd: cwd };
140974
+ if (activeAgent) {
140975
+ const agentSessions = getAgentSessions(payload);
140976
+ const taskAgent = nonEmptyString(task?.agent);
140977
+ const legacySessionId = nonEmptyString(payload.sessionId);
140978
+ const legacyCwd = nonEmptyString(payload.sessionCwd) || cwd;
140979
+ if (taskAgent && legacySessionId && !agentSessions[taskAgent]) {
140980
+ agentSessions[taskAgent] = { sessionId: legacySessionId, cwd: legacyCwd };
140981
+ }
140982
+ agentSessions[activeAgent] = { sessionId, cwd };
140983
+ nextPayload.activeAgent = activeAgent;
140984
+ nextPayload.agentSessions = agentSessions;
140985
+ }
140892
140986
  if (task?.conversationKind !== "nativeSession") {
140893
140987
  return nextPayload;
140894
140988
  }
@@ -141001,10 +141095,44 @@ function forwardTaskOutputToLeader(log2, nodeRegistry, taskId, event) {
141001
141095
  });
141002
141096
  });
141003
141097
  }
141098
+ function postLogsReadyToLeader(log2, leaderEndpoint, taskId, total) {
141099
+ fetch(`${leaderEndpoint}/api/worker/logs-ready`, applyRequestAuthHeaders({
141100
+ method: "POST",
141101
+ headers: { "Content-Type": "application/json" },
141102
+ body: JSON.stringify({ taskId, total })
141103
+ })).then((response) => {
141104
+ if (!response.ok) {
141105
+ log2.warn("leader rejected log readiness report", {
141106
+ taskId,
141107
+ total,
141108
+ leaderEndpoint,
141109
+ statusCode: response.status
141110
+ });
141111
+ return;
141112
+ }
141113
+ log2.debug("forwarded log readiness to leader", { taskId, total, leaderEndpoint, statusCode: response.status });
141114
+ }).catch((err) => {
141115
+ log2.warn("failed to forward log readiness", {
141116
+ taskId,
141117
+ total,
141118
+ leaderEndpoint,
141119
+ error: err instanceof Error ? err.message : String(err)
141120
+ });
141121
+ });
141122
+ }
141123
+ function forwardTaskLogsReadyToLeader(log2, nodeRegistry, taskId, total) {
141124
+ const leaderEndpoint = nodeRegistry.getLeaderEndpoint();
141125
+ if (!leaderEndpoint) {
141126
+ log2.warn("skipping log readiness forward because leader endpoint is missing", { taskId, total });
141127
+ return;
141128
+ }
141129
+ postLogsReadyToLeader(log2, leaderEndpoint, taskId, total);
141130
+ }
141004
141131
  function registerLeaderForwarding(eventBus, nodeRegistry, log2, taskId, agent, reporter) {
141005
141132
  const cleanup = () => {
141006
141133
  eventBus.off("task.status", onTaskStatus);
141007
141134
  eventBus.off("task.output", onTaskOutput);
141135
+ eventBus.off("task.logs.ready", onTaskLogsReady);
141008
141136
  };
141009
141137
  const onTaskOutput = (data) => {
141010
141138
  if (data.taskId !== taskId) return;
@@ -141016,6 +141144,16 @@ function registerLeaderForwarding(eventBus, nodeRegistry, log2, taskId, agent, r
141016
141144
  if (reporter) reporter.reportOutput(taskId, data.event);
141017
141145
  else forwardTaskOutputToLeader(log2, nodeRegistry, taskId, data.event);
141018
141146
  };
141147
+ const onTaskLogsReady = (data) => {
141148
+ if (data.taskId !== taskId) return;
141149
+ log2.debug("task.logs.ready event", {
141150
+ taskId,
141151
+ total: data.total,
141152
+ hasLeader: !!nodeRegistry.getLeaderEndpoint()
141153
+ });
141154
+ if (reporter) reporter.reportLogsReady(taskId, data.total);
141155
+ else forwardTaskLogsReadyToLeader(log2, nodeRegistry, taskId, data.total);
141156
+ };
141019
141157
  const onTaskStatus = (data) => {
141020
141158
  if (data.taskId !== taskId || data.status === "running") return;
141021
141159
  cleanup();
@@ -141041,11 +141179,175 @@ function registerLeaderForwarding(eventBus, nodeRegistry, log2, taskId, agent, r
141041
141179
  });
141042
141180
  };
141043
141181
  eventBus.on("task.output", onTaskOutput);
141182
+ eventBus.on("task.logs.ready", onTaskLogsReady);
141044
141183
  eventBus.on("task.status", onTaskStatus);
141045
141184
  log2.debug("registered leader forwarding listeners", { taskId, agent });
141046
141185
  return cleanup;
141047
141186
  }
141048
141187
 
141188
+ // ../../packages/core/src/tasks/agent-task-turn.ts
141189
+ init_cjs_shims();
141190
+ function startAgentTaskTurn(options) {
141191
+ const agent = resolveActiveAgent(options.task, options.targetAgent);
141192
+ if (!supportsFollowUpAgent(agent)) {
141193
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
141194
+ }
141195
+ const engine = options.engineRegistry.get(agent);
141196
+ if (!engine) {
141197
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${agent}`, 400);
141198
+ }
141199
+ const updates = {
141200
+ status: "running",
141201
+ assignedTo: options.assignedTo
141202
+ };
141203
+ if (options.targetAgent || options.task.payload.activeAgent !== void 0) {
141204
+ updates.payload = {
141205
+ ...options.task.payload,
141206
+ activeAgent: agent
141207
+ };
141208
+ }
141209
+ options.taskEngine.updateTask(options.task.id, updates);
141210
+ const handoffEvent = buildExplicitHandoffEvent(options.task, options.targetAgent, agent);
141211
+ if (handoffEvent) {
141212
+ recordAndEmit(options, engine, handoffEvent);
141213
+ }
141214
+ const handoffContent = handoffEvent ? buildAgentHandoffContent(options.task, engine, options.content, agent) : null;
141215
+ const sendContent = handoffContent ?? options.content;
141216
+ const sendOptions = handoffContent ? { logContent: options.content } : void 0;
141217
+ try {
141218
+ if (sendOptions) engine.sendMessage(options.task.id, sendContent, sendOptions);
141219
+ else engine.sendMessage(options.task.id, sendContent);
141220
+ return;
141221
+ } catch (err) {
141222
+ if (!shouldStartHandoff(options.targetAgent, err)) {
141223
+ throw err;
141224
+ }
141225
+ options.logger.info("starting cross-agent handoff turn", {
141226
+ taskId: options.task.id,
141227
+ fromAgent: resolveActiveAgent(options.task),
141228
+ toAgent: agent,
141229
+ ...options.metadata
141230
+ });
141231
+ startHandoffRun(options, engine, agent, !handoffEvent, handoffContent);
141232
+ }
141233
+ }
141234
+ function resolveActiveAgent(task, targetAgent) {
141235
+ const explicit = targetAgent?.trim();
141236
+ if (explicit) return explicit;
141237
+ const active = typeof task.payload.activeAgent === "string" ? task.payload.activeAgent.trim() : "";
141238
+ return active || task.agent;
141239
+ }
141240
+ function shouldStartHandoff(targetAgent, err) {
141241
+ return Boolean(targetAgent) && err instanceof MeshyError && err.code === "TASK_NOT_FOUND";
141242
+ }
141243
+ function buildExplicitHandoffEvent(task, targetAgent, agent) {
141244
+ if (!targetAgent?.trim()) return null;
141245
+ const fromAgent = resolveActiveAgent(task);
141246
+ return fromAgent === agent ? null : buildAgentHandoffLogEvent(fromAgent, agent);
141247
+ }
141248
+ function startHandoffRun(options, engine, agent, recordBoundary, prebuiltContent) {
141249
+ const fromAgent = resolveActiveAgent(options.task);
141250
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
141251
+ const boundary = buildAgentHandoffLogEvent(fromAgent, agent, timestamp);
141252
+ const userEvent = {
141253
+ ...buildTaskUserLogEvent(options.content, timestamp),
141254
+ agent
141255
+ };
141256
+ if (recordBoundary) {
141257
+ recordAndEmit(options, engine, boundary);
141258
+ }
141259
+ recordAndEmit(options, engine, userEvent);
141260
+ const handoffTask = {
141261
+ ...options.task,
141262
+ agent,
141263
+ status: "running",
141264
+ assignedTo: options.assignedTo,
141265
+ payload: {
141266
+ ...options.task.payload,
141267
+ activeAgent: agent,
141268
+ initialMessage: prebuiltContent ?? buildAgentHandoffContent(options.task, engine, options.content, agent)
141269
+ }
141270
+ };
141271
+ engine.run(handoffTask);
141272
+ }
141273
+ function recordAndEmit(options, engine, event) {
141274
+ engine.recordOutput(options.task.id, event);
141275
+ options.eventBus.emit("task.output", { taskId: options.task.id, event });
141276
+ }
141277
+ function buildAgentHandoffLogEvent(fromAgent, toAgent, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
141278
+ return {
141279
+ type: "agent.handoff",
141280
+ fromAgent,
141281
+ toAgent,
141282
+ timestamp
141283
+ };
141284
+ }
141285
+ function buildAgentHandoffContent(task, engine, currentContent, targetAgent) {
141286
+ const logs4 = readTaskLogs(engine, task.id);
141287
+ const historyLines = extractVisibleTranscriptLines(logs4);
141288
+ if (historyLines.length === 0) {
141289
+ const initialLines = contentToVisibleLines("User", normalizeTaskUserMessageContent(task.payload.initialMessage));
141290
+ historyLines.push(...initialLines);
141291
+ }
141292
+ const currentText = contentToPlainText(currentContent) || "[non-text user content]";
141293
+ const text10 = [
141294
+ `You are taking over this Meshy task as ${targetAgent}.`,
141295
+ "",
141296
+ "Previous visible conversation:",
141297
+ historyLines.length > 0 ? historyLines.join("\n") : "[No visible previous transcript found.]",
141298
+ "",
141299
+ "Current user request:",
141300
+ currentText
141301
+ ].join("\n");
141302
+ return [{ type: "text", text: text10 }, ...currentContent.filter((part) => part.type !== "text")];
141303
+ }
141304
+ function readTaskLogs(engine, taskId) {
141305
+ return readJsonLines(engine.getLogPath(taskId));
141306
+ }
141307
+ function extractVisibleTranscriptLines(events) {
141308
+ const lines = [];
141309
+ for (const event of events) {
141310
+ if (event.type === "user" && event.isMeta !== true && event.isSidechain !== true) {
141311
+ lines.push(...contentToVisibleLines("User", extractUserContent(event)));
141312
+ }
141313
+ if (event.type === "assistant") {
141314
+ const text10 = extractAssistantText(event);
141315
+ if (text10) lines.push(`Assistant: ${text10}`);
141316
+ }
141317
+ }
141318
+ return lines;
141319
+ }
141320
+ function extractUserContent(event) {
141321
+ const message = isRecord6(event.message) ? event.message : null;
141322
+ const messageContent = message?.content;
141323
+ const directContent = event.content;
141324
+ return normalizeTaskUserMessageContent(messageContent ?? directContent ?? event.message).filter((part) => part.type !== "tool_result");
141325
+ }
141326
+ function contentToVisibleLines(label, content3) {
141327
+ const text10 = contentToPlainText(content3);
141328
+ return text10 ? [`${label}: ${text10}`] : [];
141329
+ }
141330
+ function contentToPlainText(content3) {
141331
+ return content3.map((part) => {
141332
+ if (part.type === "text") return part.text;
141333
+ if (part.type === "image") return `[image: ${part.filename ?? part.mediaType}]`;
141334
+ return "";
141335
+ }).filter(Boolean).join("\n").trim();
141336
+ }
141337
+ function extractAssistantText(event) {
141338
+ if (typeof event.message === "string") return event.message.trim();
141339
+ const content3 = isRecord6(event.message) ? event.message.content : event.content;
141340
+ if (!Array.isArray(content3)) return "";
141341
+ return content3.flatMap((block) => {
141342
+ if (!isRecord6(block) || block.type !== "text" || typeof block.text !== "string") return [];
141343
+ const text10 = block.text.trim();
141344
+ return text10 ? [text10] : [];
141345
+ }).join("\n").trim();
141346
+ }
141347
+ function isRecord6(value) {
141348
+ return typeof value === "object" && value !== null;
141349
+ }
141350
+
141049
141351
  // ../../packages/core/src/tasks/leader-task-reporter.ts
141050
141352
  init_cjs_shims();
141051
141353
  var RestLeaderTaskReporter = class {
@@ -141089,6 +141391,14 @@ var RestLeaderTaskReporter = class {
141089
141391
  });
141090
141392
  });
141091
141393
  }
141394
+ reportLogsReady(taskId, total) {
141395
+ const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
141396
+ if (!leaderEndpoint) {
141397
+ this.logger.warn("skipping log readiness report because leader endpoint is missing", { taskId, total });
141398
+ return;
141399
+ }
141400
+ postLogsReadyToLeader(this.logger, leaderEndpoint, taskId, total);
141401
+ }
141092
141402
  patchTask(taskId, data, label, successMessage) {
141093
141403
  const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
141094
141404
  if (!leaderEndpoint) {
@@ -141540,14 +141850,14 @@ function getClaudeStreamError(event) {
141540
141850
  }
141541
141851
  return null;
141542
141852
  }
141543
- function isRecord5(value) {
141853
+ function isRecord7(value) {
141544
141854
  return typeof value === "object" && value !== null;
141545
141855
  }
141546
141856
  function getClaudeToolResultText(block) {
141547
141857
  const content3 = block.content;
141548
141858
  if (typeof content3 === "string") return content3;
141549
141859
  if (Array.isArray(content3)) {
141550
- return content3.filter(isRecord5).map((entry) => String(entry.text ?? "")).join("\n");
141860
+ return content3.filter(isRecord7).map((entry) => String(entry.text ?? "")).join("\n");
141551
141861
  }
141552
141862
  return "";
141553
141863
  }
@@ -141568,10 +141878,10 @@ function isEmptyClaudeMessageEvent(event) {
141568
141878
  return false;
141569
141879
  }
141570
141880
  const message = event.message;
141571
- if (!isRecord5(message) || !Array.isArray(message.content)) {
141881
+ if (!isRecord7(message) || !Array.isArray(message.content)) {
141572
141882
  return false;
141573
141883
  }
141574
- return !message.content.filter(isRecord5).some(hasVisibleClaudeContentBlock);
141884
+ return !message.content.filter(isRecord7).some(hasVisibleClaudeContentBlock);
141575
141885
  }
141576
141886
  function shouldSkipClaudeTranscriptEvent(event) {
141577
141887
  return event.isMeta === true || event.isSidechain === true || event.type === "attachment" || event.type === "last-prompt" || isEmptyClaudeMessageEvent(event);
@@ -141585,13 +141895,13 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141585
141895
  init() {
141586
141896
  const { tasks } = this.taskEngine.listTasks();
141587
141897
  for (const task of tasks) {
141588
- if (task.agent !== "claudecode") continue;
141589
- const sessionId = task.payload.sessionId;
141590
- if (!sessionId) continue;
141591
- const cwd = path10.resolve(
141592
- task.payload.sessionCwd ?? task.effectiveProjectPath ?? task.project ?? process.cwd()
141593
- );
141594
- this.sessions.set(task.id, { sessionId, cwd, title: task.title });
141898
+ const session = getPersistedAgentSession(task, "claudecode");
141899
+ if (!session) continue;
141900
+ this.sessions.set(task.id, {
141901
+ sessionId: session.sessionId,
141902
+ cwd: path10.resolve(session.cwd || task.effectiveProjectPath || task.project || process.cwd()),
141903
+ title: task.title
141904
+ });
141595
141905
  }
141596
141906
  if (this.sessions.size > 0) {
141597
141907
  this.logger.info("Recovered sessions from store", { count: this.sessions.size });
@@ -141647,7 +141957,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141647
141957
  this.persistSession(task.id, event.session_id, cwd);
141648
141958
  this.logger.info("Session established", { taskId: task.id, sessionId: event.session_id });
141649
141959
  }
141650
- const outputEvent = timestampClaudeTranscriptEvent(event);
141960
+ const outputEvent = event.type === "system" && event.subtype === "init" && typeof event.session_id === "string" ? { ...timestampClaudeTranscriptEvent(event), agent: "claudecode" } : timestampClaudeTranscriptEvent(event);
141651
141961
  this.emitOutput(task.id, outputEvent);
141652
141962
  this.appendLog(task.id, outputEvent);
141653
141963
  } catch {
@@ -141664,7 +141974,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141664
141974
  proc.stderr.on("data", (chunk) => {
141665
141975
  stderrBuffer += chunk.toString();
141666
141976
  });
141667
- proc.on("close", (code4) => {
141977
+ proc.on("close", async (code4) => {
141668
141978
  if (buffer.trim()) {
141669
141979
  processLine(buffer);
141670
141980
  }
@@ -141673,6 +141983,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141673
141983
  return;
141674
141984
  }
141675
141985
  try {
141986
+ await this.flushLogsAndEmitReady(task.id);
141676
141987
  if (code4 === 0 && !streamError) {
141677
141988
  this.logger.info("Task completed successfully", { taskId: task.id, title: task.title });
141678
141989
  this.taskEngine.reportResult(task.id, { message: `Task "${task.title}" completed` });
@@ -141710,7 +142021,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141710
142021
  this.writeUserMessage(proc.stdin, initialContent);
141711
142022
  proc.unref();
141712
142023
  }
141713
- sendMessage(taskId, content3) {
142024
+ sendMessage(taskId, content3, options) {
141714
142025
  let session = this.sessions.get(taskId);
141715
142026
  if (!session) {
141716
142027
  const recovered = this.recoverSession(taskId);
@@ -141729,7 +142040,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141729
142040
  if (!session) {
141730
142041
  throw new MeshyError("TASK_NOT_FOUND", `No active session for task ${taskId}`, 404);
141731
142042
  }
141732
- const userEvent = buildTaskUserLogEvent(content3);
142043
+ const userEvent = buildTaskUserLogEvent(options?.logContent ?? content3);
141733
142044
  this.appendLog(taskId, userEvent);
141734
142045
  this.emitOutput(taskId, userEvent);
141735
142046
  this.logger.info("Sending follow-up message", { taskId, sessionId: session.sessionId });
@@ -141776,7 +142087,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141776
142087
  });
141777
142088
  this.persistSession(taskId, event.session_id, session.cwd);
141778
142089
  }
141779
- const outputEvent = timestampClaudeTranscriptEvent(event);
142090
+ const outputEvent = event.type === "system" && event.subtype === "init" && typeof event.session_id === "string" ? { ...timestampClaudeTranscriptEvent(event), agent: "claudecode" } : timestampClaudeTranscriptEvent(event);
141780
142091
  this.emitOutput(taskId, outputEvent);
141781
142092
  this.appendLog(taskId, outputEvent);
141782
142093
  } catch {
@@ -141793,7 +142104,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141793
142104
  proc.stderr.on("data", (chunk) => {
141794
142105
  stderrBuffer += chunk.toString();
141795
142106
  });
141796
- proc.on("close", (code4) => {
142107
+ proc.on("close", async (code4) => {
141797
142108
  if (buffer.trim()) {
141798
142109
  processLine(buffer);
141799
142110
  }
@@ -141802,6 +142113,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141802
142113
  return;
141803
142114
  }
141804
142115
  try {
142116
+ await this.flushLogsAndEmitReady(taskId);
141805
142117
  if (code4 === 0 && !streamError) {
141806
142118
  this.logger.info("Follow-up completed", { taskId, sessionId: session.sessionId });
141807
142119
  this.taskEngine.reportResult(taskId, { message: `Task "${session.title}" completed` });
@@ -141854,7 +142166,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141854
142166
  try {
141855
142167
  const current = this.taskEngine.getTask(taskId);
141856
142168
  this.taskEngine.updateTask(taskId, {
141857
- payload: buildPersistedSessionPayload(current, sessionId, cwd)
142169
+ payload: buildPersistedSessionPayload(current, sessionId, cwd, "claudecode")
141858
142170
  });
141859
142171
  } catch {
141860
142172
  }
@@ -141873,7 +142185,8 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141873
142185
  const event = JSON.parse(line);
141874
142186
  if (event.type === "system" && event.subtype === "init" && typeof event.session_id === "string") {
141875
142187
  const task = this.taskEngine.getTask(taskId);
141876
- const persistedCwd = typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0;
142188
+ if (!canUseSessionLogEventForAgent(task, "claudecode", event)) continue;
142189
+ const persistedCwd = getPersistedAgentSession(task, "claudecode")?.cwd ?? (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0);
141877
142190
  return {
141878
142191
  sessionId: event.session_id,
141879
142192
  cwd: path10.resolve(
@@ -141889,14 +142202,12 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141889
142202
  }
141890
142203
  recoverPersistedSession(taskId) {
141891
142204
  const task = this.taskEngine.getTask(taskId);
141892
- const sessionId = typeof task?.payload.sessionId === "string" ? task.payload.sessionId : void 0;
141893
- if (!sessionId) {
141894
- return null;
141895
- }
142205
+ const persisted = getPersistedAgentSession(task, "claudecode");
142206
+ if (!persisted) return null;
141896
142207
  return {
141897
- sessionId,
142208
+ sessionId: persisted.sessionId,
141898
142209
  cwd: path10.resolve(
141899
- (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0) ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()
142210
+ persisted.cwd || task?.effectiveProjectPath || task?.project || process.cwd()
141900
142211
  ),
141901
142212
  title: task?.title ?? taskId
141902
142213
  };
@@ -141909,21 +142220,21 @@ var fs11 = __toESM(require("fs"), 1);
141909
142220
  var path11 = __toESM(require("path"), 1);
141910
142221
  var import_node_child_process5 = require("child_process");
141911
142222
  var CODEX_SESSION_MIRROR_INTERVAL_MS = 500;
141912
- function isRecord6(value) {
142223
+ function isRecord8(value) {
141913
142224
  return typeof value === "object" && value !== null;
141914
142225
  }
141915
142226
  function stableStringify(value) {
141916
142227
  if (Array.isArray(value)) {
141917
142228
  return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
141918
142229
  }
141919
- if (!isRecord6(value)) {
142230
+ if (!isRecord8(value)) {
141920
142231
  return JSON.stringify(value);
141921
142232
  }
141922
142233
  return `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key2, entryValue]) => `${JSON.stringify(key2)}:${stableStringify(entryValue)}`).join(",")}}`;
141923
142234
  }
141924
142235
  function transcriptEventSignature(event) {
141925
142236
  const message = event.message;
141926
- if ((event.type === "assistant" || event.type === "user") && isRecord6(message) && Array.isArray(message.content)) {
142237
+ if ((event.type === "assistant" || event.type === "user") && isRecord8(message) && Array.isArray(message.content)) {
141927
142238
  return `${event.type}:${stableStringify(message.content)}`;
141928
142239
  }
141929
142240
  return stableStringify(event);
@@ -141962,9 +142273,6 @@ function splitPromptContent(content3) {
141962
142273
  images
141963
142274
  };
141964
142275
  }
141965
- function hasImageContent(content3) {
141966
- return content3.some((part) => part.type === "image");
141967
- }
141968
142276
  function buildCodexModeArgs(mode) {
141969
142277
  switch (mode) {
141970
142278
  case "plan":
@@ -141987,13 +142295,13 @@ var CodexEngine = class extends ExecutionEngine {
141987
142295
  init() {
141988
142296
  const { tasks } = this.taskEngine.listTasks();
141989
142297
  for (const task of tasks) {
141990
- if (task.agent !== "codex") continue;
141991
- const sessionId = task.payload.sessionId;
141992
- if (!sessionId) continue;
141993
- const cwd = path11.resolve(
141994
- task.payload.sessionCwd ?? task.effectiveProjectPath ?? task.project ?? process.cwd()
141995
- );
141996
- this.sessions.set(task.id, { sessionId, cwd, title: task.title });
142298
+ const session = getPersistedAgentSession(task, "codex");
142299
+ if (!session) continue;
142300
+ this.sessions.set(task.id, {
142301
+ sessionId: session.sessionId,
142302
+ cwd: path11.resolve(session.cwd || task.effectiveProjectPath || task.project || process.cwd()),
142303
+ title: task.title
142304
+ });
141997
142305
  }
141998
142306
  if (this.sessions.size > 0) {
141999
142307
  this.logger.info("Recovered Codex sessions from store", { count: this.sessions.size });
@@ -142004,7 +142312,7 @@ var CodexEngine = class extends ExecutionEngine {
142004
142312
  const cwd = task.effectiveProjectPath ?? task.project ?? process.cwd();
142005
142313
  const initialContent = getTaskInitialMessageContent(task);
142006
142314
  const { prompt, imagePaths, promptEcho } = this.prepareAssets(task.id, initialContent);
142007
- this.markCodexPromptEchoEmitted(task.id, [promptEcho, prompt], hasImageContent(initialContent));
142315
+ this.markCodexPromptEchoEmitted(task.id, [promptEcho, prompt]);
142008
142316
  const args = this.buildArgs(imagePaths, { model: config2.model, mode: config2.mode ?? "bypass" });
142009
142317
  this.ensureLogDir();
142010
142318
  this.logger.info("Spawning codex CLI", { taskId: task.id, title: task.title, cwd });
@@ -142017,7 +142325,7 @@ var CodexEngine = class extends ExecutionEngine {
142017
142325
  this.writePrompt(proc.stdin, prompt);
142018
142326
  proc.unref();
142019
142327
  }
142020
- sendMessage(taskId, content3) {
142328
+ sendMessage(taskId, content3, options) {
142021
142329
  let session = this.sessions.get(taskId);
142022
142330
  if (!session) {
142023
142331
  const recovered = this.recoverSession(taskId);
@@ -142039,12 +142347,12 @@ var CodexEngine = class extends ExecutionEngine {
142039
142347
  const current = this.taskEngine.getTask(taskId);
142040
142348
  const model = typeof current?.payload.model === "string" ? current.payload.model : void 0;
142041
142349
  const mode = current?.payload.mode;
142042
- const userEvent = buildTaskUserLogEvent(content3);
142350
+ const userEvent = buildTaskUserLogEvent(options?.logContent ?? content3);
142043
142351
  this.markTranscriptEventEmitted(taskId, userEvent);
142044
142352
  this.appendLog(taskId, userEvent);
142045
142353
  this.emitOutput(taskId, userEvent);
142046
142354
  const { prompt, imagePaths, promptEcho } = this.prepareAssets(taskId, content3);
142047
- this.markCodexPromptEchoEmitted(taskId, [promptEcho, prompt], hasImageContent(content3));
142355
+ this.markCodexPromptEchoEmitted(taskId, [promptEcho, prompt]);
142048
142356
  const args = this.buildArgs(imagePaths, {
142049
142357
  model,
142050
142358
  sessionId: session.sessionId,
@@ -142103,7 +142411,7 @@ var CodexEngine = class extends ExecutionEngine {
142103
142411
  proc.stderr?.on("data", (chunk) => {
142104
142412
  stderrBuffer += chunk.toString();
142105
142413
  });
142106
- proc.on("close", (code4) => {
142414
+ proc.on("close", async (code4) => {
142107
142415
  if (buffer.trim()) {
142108
142416
  processLine(buffer);
142109
142417
  }
@@ -142116,6 +142424,7 @@ var CodexEngine = class extends ExecutionEngine {
142116
142424
  try {
142117
142425
  this.flushNativeSessionMirror(taskId);
142118
142426
  this.flushPendingAmbiguousAssistantTextEvents(taskId);
142427
+ await this.flushLogsAndEmitReady(taskId);
142119
142428
  if (code4 === 0) {
142120
142429
  this.logger.info(isFollowUp ? "Codex follow-up completed" : "Codex task completed successfully", {
142121
142430
  taskId,
@@ -142262,8 +142571,7 @@ var CodexEngine = class extends ExecutionEngine {
142262
142571
  markTranscriptEventEmitted(taskId, event) {
142263
142572
  this.getEmittedTranscriptSignatures(taskId).add(transcriptEventSignature(event));
142264
142573
  }
142265
- markCodexPromptEchoEmitted(taskId, prompts, hasImages) {
142266
- if (!hasImages) return;
142574
+ markCodexPromptEchoEmitted(taskId, prompts) {
142267
142575
  for (const prompt of new Set(prompts.map((candidate) => candidate.trim()).filter(Boolean))) {
142268
142576
  this.markTranscriptEventEmitted(taskId, buildTaskUserLogEvent([{ type: "text", text: prompt }]));
142269
142577
  }
@@ -142302,8 +142610,9 @@ var CodexEngine = class extends ExecutionEngine {
142302
142610
  title
142303
142611
  });
142304
142612
  this.persistSession(taskId, event.thread_id, cwd);
142305
- this.appendLog(taskId, event);
142306
- this.emitOutput(taskId, event);
142613
+ const sessionEvent = { ...event, agent: "codex" };
142614
+ this.appendLog(taskId, sessionEvent);
142615
+ this.emitOutput(taskId, sessionEvent);
142307
142616
  this.startNativeSessionMirror(taskId, event.thread_id);
142308
142617
  this.logger.info("Codex session established", { taskId, sessionId: event.thread_id });
142309
142618
  return [];
@@ -142348,7 +142657,7 @@ var CodexEngine = class extends ExecutionEngine {
142348
142657
  try {
142349
142658
  const current = this.taskEngine.getTask(taskId);
142350
142659
  this.taskEngine.updateTask(taskId, {
142351
- payload: buildPersistedSessionPayload(current, sessionId, cwd)
142660
+ payload: buildPersistedSessionPayload(current, sessionId, cwd, "codex")
142352
142661
  });
142353
142662
  } catch {
142354
142663
  }
@@ -142367,7 +142676,8 @@ var CodexEngine = class extends ExecutionEngine {
142367
142676
  const event = JSON.parse(line);
142368
142677
  if (event.type === "thread.started" && typeof event.thread_id === "string") {
142369
142678
  const task = this.taskEngine.getTask(taskId);
142370
- const persistedCwd = typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0;
142679
+ if (!canUseSessionLogEventForAgent(task, "codex", event)) continue;
142680
+ const persistedCwd = getPersistedAgentSession(task, "codex")?.cwd ?? (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0);
142371
142681
  return {
142372
142682
  sessionId: event.thread_id,
142373
142683
  cwd: path11.resolve(persistedCwd ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()),
@@ -142381,14 +142691,12 @@ var CodexEngine = class extends ExecutionEngine {
142381
142691
  }
142382
142692
  recoverPersistedSession(taskId) {
142383
142693
  const task = this.taskEngine.getTask(taskId);
142384
- const sessionId = typeof task?.payload.sessionId === "string" ? task.payload.sessionId : void 0;
142385
- if (!sessionId) {
142386
- return null;
142387
- }
142694
+ const persisted = getPersistedAgentSession(task, "codex");
142695
+ if (!persisted) return null;
142388
142696
  return {
142389
- sessionId,
142697
+ sessionId: persisted.sessionId,
142390
142698
  cwd: path11.resolve(
142391
- (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0) ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()
142699
+ persisted.cwd || task?.effectiveProjectPath || task?.project || process.cwd()
142392
142700
  ),
142393
142701
  title: task?.title ?? taskId
142394
142702
  };
@@ -142400,7 +142708,7 @@ init_cjs_shims();
142400
142708
  var fs12 = __toESM(require("fs"), 1);
142401
142709
  var path12 = __toESM(require("path"), 1);
142402
142710
  var import_node_child_process6 = require("child_process");
142403
- function isRecord7(value) {
142711
+ function isRecord9(value) {
142404
142712
  return typeof value === "object" && value !== null;
142405
142713
  }
142406
142714
  function contentToPrompt(content3) {
@@ -142438,7 +142746,7 @@ function inferExtension2(mediaType) {
142438
142746
  return normalized.replace(/[^a-z0-9]/g, "") || "bin";
142439
142747
  }
142440
142748
  function extractSessionId(event) {
142441
- const data = isRecord7(event.data) ? event.data : void 0;
142749
+ const data = isRecord9(event.data) ? event.data : void 0;
142442
142750
  const direct = event.session_id ?? event.sessionId ?? event.conversation_id ?? event.conversationId ?? data?.sessionId ?? data?.session_id ?? data?.conversationId ?? data?.conversation_id;
142443
142751
  if (typeof direct === "string" && direct.trim()) return direct.trim();
142444
142752
  const type = typeof event.type === "string" ? event.type.toLowerCase() : "";
@@ -142452,13 +142760,13 @@ var CopilotCliEngine = class extends ExecutionEngine {
142452
142760
  init() {
142453
142761
  const { tasks } = this.taskEngine.listTasks();
142454
142762
  for (const task of tasks) {
142455
- if (task.agent !== "copilot") continue;
142456
- const sessionId = task.payload.sessionId;
142457
- if (!sessionId) continue;
142458
- const cwd = path12.resolve(
142459
- task.payload.sessionCwd ?? task.effectiveProjectPath ?? task.project ?? process.cwd()
142460
- );
142461
- this.sessions.set(task.id, { sessionId, cwd, title: task.title });
142763
+ const session = getPersistedAgentSession(task, "copilot");
142764
+ if (!session) continue;
142765
+ this.sessions.set(task.id, {
142766
+ sessionId: session.sessionId,
142767
+ cwd: path12.resolve(session.cwd || task.effectiveProjectPath || task.project || process.cwd()),
142768
+ title: task.title
142769
+ });
142462
142770
  }
142463
142771
  if (this.sessions.size > 0) {
142464
142772
  this.logger.info("Recovered Copilot CLI sessions from store", { count: this.sessions.size });
@@ -142487,7 +142795,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142487
142795
  this.attachProcess(task.id, task.title, cwd, proc, false);
142488
142796
  proc.unref();
142489
142797
  }
142490
- sendMessage(taskId, content3) {
142798
+ sendMessage(taskId, content3, options) {
142491
142799
  let session = this.sessions.get(taskId);
142492
142800
  if (!session) {
142493
142801
  const recovered = this.recoverSession(taskId) ?? this.recoverPersistedSession(taskId);
@@ -142504,7 +142812,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142504
142812
  const mode = current?.payload.mode;
142505
142813
  const systemPrompt = typeof current?.payload.systemPrompt === "string" ? current.payload.systemPrompt : void 0;
142506
142814
  const allowedTools = Array.isArray(current?.payload.allowedTools) && current.payload.allowedTools.every((tool) => typeof tool === "string") ? current.payload.allowedTools : void 0;
142507
- const userEvent = buildTaskUserLogEvent(content3);
142815
+ const userEvent = buildTaskUserLogEvent(options?.logContent ?? content3);
142508
142816
  this.appendLog(taskId, userEvent);
142509
142817
  this.emitOutput(taskId, userEvent);
142510
142818
  const prompt = buildPrompt(content3, systemPrompt);
@@ -142597,7 +142905,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142597
142905
  proc.stderr?.on("data", (chunk) => {
142598
142906
  stderrBuffer += chunk.toString();
142599
142907
  });
142600
- proc.on("close", (code4) => {
142908
+ proc.on("close", async (code4) => {
142601
142909
  if (buffer.trim()) {
142602
142910
  processLine(buffer);
142603
142911
  }
@@ -142606,6 +142914,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142606
142914
  return;
142607
142915
  }
142608
142916
  try {
142917
+ await this.flushLogsAndEmitReady(taskId);
142609
142918
  if (code4 === 0) {
142610
142919
  this.logger.info(isFollowUp ? "Copilot CLI follow-up completed" : "Copilot CLI task completed successfully", {
142611
142920
  taskId,
@@ -142657,8 +142966,9 @@ var CopilotCliEngine = class extends ExecutionEngine {
142657
142966
  if (sessionId) {
142658
142967
  this.sessions.set(taskId, { sessionId, cwd, title });
142659
142968
  this.persistSession(taskId, sessionId, cwd);
142660
- this.appendLog(taskId, event);
142661
- this.emitOutput(taskId, event);
142969
+ const sessionEvent = { ...event, agent: "copilot" };
142970
+ this.appendLog(taskId, sessionEvent);
142971
+ this.emitOutput(taskId, sessionEvent);
142662
142972
  this.logger.info("Copilot CLI session established", { taskId, sessionId });
142663
142973
  return [];
142664
142974
  }
@@ -142668,7 +142978,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142668
142978
  try {
142669
142979
  const current = this.taskEngine.getTask(taskId);
142670
142980
  this.taskEngine.updateTask(taskId, {
142671
- payload: buildPersistedSessionPayload(current, sessionId, cwd)
142981
+ payload: buildPersistedSessionPayload(current, sessionId, cwd, "copilot")
142672
142982
  });
142673
142983
  } catch {
142674
142984
  }
@@ -142687,7 +142997,8 @@ var CopilotCliEngine = class extends ExecutionEngine {
142687
142997
  const sessionId = extractSessionId(event);
142688
142998
  if (sessionId) {
142689
142999
  const task = this.taskEngine.getTask(taskId);
142690
- const persistedCwd = typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0;
143000
+ if (!canUseSessionLogEventForAgent(task, "copilot", event)) continue;
143001
+ const persistedCwd = getPersistedAgentSession(task, "copilot")?.cwd ?? (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0);
142691
143002
  return {
142692
143003
  sessionId,
142693
143004
  cwd: path12.resolve(persistedCwd ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()),
@@ -142701,12 +143012,12 @@ var CopilotCliEngine = class extends ExecutionEngine {
142701
143012
  }
142702
143013
  recoverPersistedSession(taskId) {
142703
143014
  const task = this.taskEngine.getTask(taskId);
142704
- const sessionId = typeof task?.payload.sessionId === "string" ? task.payload.sessionId : void 0;
142705
- if (!sessionId) return null;
143015
+ const persisted = getPersistedAgentSession(task, "copilot");
143016
+ if (!persisted) return null;
142706
143017
  return {
142707
- sessionId,
143018
+ sessionId: persisted.sessionId,
142708
143019
  cwd: path12.resolve(
142709
- (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0) ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()
143020
+ persisted.cwd || task?.effectiveProjectPath || task?.project || process.cwd()
142710
143021
  ),
142711
143022
  title: task?.title ?? taskId
142712
143023
  };
@@ -144136,22 +144447,33 @@ async function handleLocalNodeMessage(deps, message) {
144136
144447
  const payload = message.payload;
144137
144448
  if (!payload.taskId || !payload.content) return;
144138
144449
  const task = deps.taskEngine.getTask(payload.taskId);
144139
- const engine = task ? deps.engineRegistry.get(task.agent) : void 0;
144140
- if (!task || !engine) return;
144141
- deps.taskEngine.updateTask(payload.taskId, { status: "running" });
144450
+ const agent = task ? resolveActiveAgent(task, payload.targetAgent) : void 0;
144451
+ const engine = agent ? deps.engineRegistry.get(agent) : void 0;
144452
+ if (!task || !agent || !engine) return;
144142
144453
  if (!deps.nodeRegistry.isLeader()) {
144143
144454
  const reporter = new RestLeaderTaskReporter(deps.nodeRegistry, log2);
144144
144455
  reporter.reportRunning(payload.taskId, { branch: task.branch });
144145
- registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, task.agent, reporter);
144456
+ registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, agent, reporter);
144146
144457
  }
144147
144458
  try {
144148
- engine.sendMessage(payload.taskId, payload.content);
144149
- log2.info("started keepalive-delivered follow-up execution", { taskId: payload.taskId });
144459
+ startAgentTaskTurn({
144460
+ task,
144461
+ content: payload.content,
144462
+ targetAgent: payload.targetAgent,
144463
+ assignedTo: task.assignedTo,
144464
+ taskEngine: deps.taskEngine,
144465
+ engineRegistry: deps.engineRegistry,
144466
+ eventBus: deps.eventBus,
144467
+ logger: log2
144468
+ });
144469
+ log2.info("started keepalive-delivered follow-up execution", { taskId: payload.taskId, agent });
144150
144470
  } catch (error2) {
144151
- log2.warn("failed to start keepalive-delivered follow-up execution", {
144471
+ const messageText = error2 instanceof Error ? error2.message : String(error2);
144472
+ log2.error("failed to start keepalive-delivered follow-up execution", {
144152
144473
  taskId: payload.taskId,
144153
- error: error2 instanceof Error ? error2.message : String(error2)
144474
+ error: messageText
144154
144475
  });
144476
+ deps.taskEngine.reportFailure(payload.taskId, `Engine follow-up failed before execution started: ${messageText}`);
144155
144477
  }
144156
144478
  return;
144157
144479
  }
@@ -145366,6 +145688,7 @@ var SystemAgentInfoSchema = external_exports.object({
145366
145688
  detail: external_exports.string().nullable().optional()
145367
145689
  }).optional()
145368
145690
  });
145691
+ var DefaultTaskAgentSchema = external_exports.enum(["codex", "claudecode", "copilot"]);
145369
145692
  var SystemRuntimeUpdateInfoSchema = external_exports.object({
145370
145693
  packageName: external_exports.string(),
145371
145694
  currentVersion: external_exports.string().nullable(),
@@ -145380,6 +145703,7 @@ var NodeSettingsSnapshotSchema = external_exports.object({
145380
145703
  version: external_exports.string(),
145381
145704
  packageName: external_exports.string().optional(),
145382
145705
  uptime: external_exports.number(),
145706
+ defaultTaskAgent: DefaultTaskAgentSchema.optional(),
145383
145707
  auth: SystemAuthInfoSchema.optional(),
145384
145708
  os: SystemOsInfoSchema.optional(),
145385
145709
  runtime: SystemRuntimeInfoSchema.optional(),
@@ -145721,6 +146045,10 @@ var QuickChatsResponse = external_exports.object({
145721
146045
  var ReplaceQuickChatsBody = external_exports.object({
145722
146046
  quickChats: external_exports.array(QuickChatItem).max(100)
145723
146047
  });
146048
+ var DefaultTaskAgentResponse = external_exports.object({
146049
+ defaultTaskAgent: DefaultTaskAgentSchema
146050
+ });
146051
+ var UpdateDefaultTaskAgentBody = DefaultTaskAgentResponse;
145724
146052
 
145725
146053
  // ../../packages/api/src/schemas/tasks.ts
145726
146054
  init_cjs_shims();
@@ -145772,6 +146100,7 @@ var TaskListResponse = external_exports.object({
145772
146100
  queuedMessages: external_exports.array(external_exports.object({
145773
146101
  id: external_exports.string(),
145774
146102
  content: external_exports.array(external_exports.record(external_exports.unknown())).min(1),
146103
+ targetAgent: external_exports.enum(NATIVE_SESSION_AGENT_OPTIONS).optional(),
145775
146104
  createdAt: external_exports.number()
145776
146105
  })).optional(),
145777
146106
  status: external_exports.enum(["pending", "assigned", "running", "completed", "failed", "cancelled", "archived"]),
@@ -145838,12 +146167,15 @@ var TaskMessageContentPart = external_exports.union([
145838
146167
  var TaskMessageContent = external_exports.array(TaskMessageContentPart).min(1);
145839
146168
  var SendMessageBody = external_exports.union([
145840
146169
  external_exports.object({
145841
- content: TaskMessageContent
146170
+ content: TaskMessageContent,
146171
+ targetAgent: external_exports.enum(NATIVE_SESSION_AGENT_OPTIONS).optional()
145842
146172
  }),
145843
146173
  external_exports.object({
145844
- message: external_exports.string().trim().min(1)
146174
+ message: external_exports.string().trim().min(1),
146175
+ targetAgent: external_exports.enum(NATIVE_SESSION_AGENT_OPTIONS).optional()
145845
146176
  }).transform((value) => ({
145846
- content: [{ type: "text", text: value.message }]
146177
+ content: [{ type: "text", text: value.message }],
146178
+ ...value.targetAgent ? { targetAgent: value.targetAgent } : {}
145847
146179
  }))
145848
146180
  ]);
145849
146181
  var AttachNativeSessionBody = external_exports.object({
@@ -145860,7 +146192,8 @@ var WorkerImportTaskBody = external_exports.object({
145860
146192
  logs: external_exports.array(TaskLogEvent).default([])
145861
146193
  });
145862
146194
  var TaskLogsQuery = external_exports.object({
145863
- after: external_exports.coerce.number().int().min(0).optional()
146195
+ after: external_exports.coerce.number().int().min(0).optional(),
146196
+ minTotal: external_exports.coerce.number().int().min(0).optional()
145864
146197
  });
145865
146198
  var CreateTaskShareBody = external_exports.object({
145866
146199
  tenant: external_exports.string().trim().min(1).max(128).default(DEFAULT_TASK_SHARE_TENANT),
@@ -146282,6 +146615,7 @@ function durationMs(startedAt) {
146282
146615
  }
146283
146616
  function createRequestLoggingMiddleware(rootLogger, options = {}) {
146284
146617
  const log2 = rootLogger.child("api/request");
146618
+ const lastLoggedAt = /* @__PURE__ */ new Map();
146285
146619
  return (req, res, next2) => {
146286
146620
  if (options.shouldLog && !options.shouldLog(req)) {
146287
146621
  next2();
@@ -146293,6 +146627,16 @@ function createRequestLoggingMiddleware(rootLogger, options = {}) {
146293
146627
  const logCompletion = (closed) => {
146294
146628
  if (logged) return;
146295
146629
  logged = true;
146630
+ const throttle = options.throttle;
146631
+ if (throttle && throttle.intervalMs > 0 && throttle.shouldThrottle(req, res, closed, path47)) {
146632
+ const now = throttle.now?.() ?? Date.now();
146633
+ const key2 = throttle.key?.(req, path47) ?? `${req.method.toUpperCase()} ${path47}`;
146634
+ const previousLoggedAt = lastLoggedAt.get(key2);
146635
+ if (previousLoggedAt !== void 0 && now - previousLoggedAt < throttle.intervalMs) {
146636
+ return;
146637
+ }
146638
+ lastLoggedAt.set(key2, now);
146639
+ }
146296
146640
  log2.info("request completed", {
146297
146641
  method: req.method,
146298
146642
  path: path47,
@@ -146413,8 +146757,21 @@ var sharedMcpControl;
146413
146757
  var DEFAULT_START_POLL_INTERVAL_MS = 1e4;
146414
146758
  var DEFAULT_START_TIMEOUT_MS = 15 * 6e4;
146415
146759
  var ALWAYS_ONLINE_STARTABLE_DEVBOX_STATUSES = /* @__PURE__ */ new Set(["hibernated", "stopped"]);
146760
+ var DEVBOX_MCP_NODE_VERSION = "22";
146416
146761
  var DEVBOX_MCP_COMMAND = "npx";
146417
- var DEVBOX_MCP_ARGS = ["-y", "-p", "node@22", "-p", "@microsoft/devbox-mcp@latest", "devbox-mcp-server"];
146762
+ var DEVBOX_MCP_ARGS = ["-y", "-p", `node@${DEVBOX_MCP_NODE_VERSION}`, "-p", "@microsoft/devbox-mcp@latest", "devbox-mcp-server"];
146763
+ var DEVBOX_MCP_DISPLAY_COMMAND = `${DEVBOX_MCP_COMMAND} ${DEVBOX_MCP_ARGS.join(" ")}`;
146764
+ function asDevBoxMcpError(err) {
146765
+ if (err instanceof MeshyError) return err;
146766
+ const cause = err instanceof Error ? err.message : String(err);
146767
+ if (!/connection closed/iu.test(cause)) return err;
146768
+ return new MeshyError(
146769
+ "DEVBOX_MCP_UNAVAILABLE",
146770
+ `Unable to reach the DevBox MCP server: it closed the connection. Meshy runs @microsoft/devbox-mcp with Node.js ${DEVBOX_MCP_NODE_VERSION} via npx; check that npx can install packages and Azure Dev Box authentication is available, then try again.`,
146771
+ 502,
146772
+ { cause, command: DEVBOX_MCP_DISPLAY_COMMAND }
146773
+ );
146774
+ }
146418
146775
  function getDevBoxControl(deps) {
146419
146776
  if (deps.devBoxControl) return deps.devBoxControl;
146420
146777
  sharedMcpControl ??= new McpDevBoxControl(deps.logger);
@@ -146557,7 +146914,7 @@ var McpDevBoxControl = class {
146557
146914
  return await client.callTool({ name: name2, arguments: args });
146558
146915
  } catch (err) {
146559
146916
  this.clientPromise = null;
146560
- throw err;
146917
+ throw asDevBoxMcpError(err);
146561
146918
  }
146562
146919
  }
146563
146920
  async getClient() {
@@ -146595,13 +146952,13 @@ function cleanDevBoxInfo(devbox) {
146595
146952
  }
146596
146953
  function normalizeMcpDevBoxList(value) {
146597
146954
  const normalized = normalizeMcpValue(value);
146598
- const record2 = isRecord8(normalized) ? normalized : {};
146955
+ const record2 = isRecord10(normalized) ? normalized : {};
146599
146956
  const list4 = Array.isArray(normalized) ? normalized : Array.isArray(record2.devboxes) ? record2.devboxes : Array.isArray(record2.value) ? record2.value : [];
146600
146957
  return list4.map((item) => normalizeDevBoxResource(item, void 0)).filter((item) => Boolean(item.name));
146601
146958
  }
146602
146959
  function normalizeDevBoxResource(value, fallback) {
146603
146960
  const record2 = normalizeRecord(value);
146604
- const additional = isRecord8(record2.additionalProperties) ? record2.additionalProperties : {};
146961
+ const additional = isRecord10(record2.additionalProperties) ? record2.additionalProperties : {};
146605
146962
  const fromUri = parseDevBoxUri(readString(record2, "uri"));
146606
146963
  const status = readString(record2, "status") ?? readString(record2, "powerState") ?? readString(record2, "lastStatus") ?? fallback?.lastStatus;
146607
146964
  const name2 = readString(record2, "name") ?? fromUri?.name ?? fallback?.name ?? "";
@@ -146655,7 +147012,7 @@ function parseDevCenterEndpoint(hostname6) {
146655
147012
  }
146656
147013
  function normalizeRecord(value) {
146657
147014
  const normalized = normalizeMcpValue(value);
146658
- return isRecord8(normalized) ? normalized : {};
147015
+ return isRecord10(normalized) ? normalized : {};
146659
147016
  }
146660
147017
  function normalizeMcpValue(value) {
146661
147018
  if (Array.isArray(value)) return value;
@@ -146690,7 +147047,7 @@ function tryParseJson(text10) {
146690
147047
  return null;
146691
147048
  }
146692
147049
  }
146693
- function isRecord8(value) {
147050
+ function isRecord10(value) {
146694
147051
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
146695
147052
  }
146696
147053
  function readString(record2, key2) {
@@ -147982,6 +148339,7 @@ function buildNodeSettingsSnapshot(options) {
147982
148339
  version: runtimeMetadata?.packageVersion ?? "0.1.0",
147983
148340
  packageName: runtimeMetadata?.packageName ?? "meshy",
147984
148341
  uptime,
148342
+ defaultTaskAgent: options.defaultTaskAgent,
147985
148343
  auth: options.auth,
147986
148344
  os: getOsSnapshot(),
147987
148345
  runtime: {
@@ -148014,6 +148372,7 @@ function buildSystemInfo(deps) {
148014
148372
  };
148015
148373
  const settingsSnapshot = buildNodeSettingsSnapshot({
148016
148374
  auth,
148375
+ defaultTaskAgent: deps.storagePath ? resolveNodeDefaultTaskAgent(deps.storagePath) : "codex",
148017
148376
  inspectRuntimeTools: deps.inspectRuntimeTools,
148018
148377
  localDashboardOrigin: deps.localDashboardOrigin,
148019
148378
  runtimeMetadata: deps.runtimeMetadata,
@@ -148027,6 +148386,7 @@ function buildSystemInfo(deps) {
148027
148386
  version: settingsSnapshot.version,
148028
148387
  packageName: settingsSnapshot.packageName,
148029
148388
  uptime: settingsSnapshot.uptime,
148389
+ defaultTaskAgent: settingsSnapshot.defaultTaskAgent,
148030
148390
  transportType: deps.getTransportType?.() ?? "direct",
148031
148391
  dashboardOrigin: deps.dashboardOrigin ?? self2.dashboardOrigin,
148032
148392
  localDashboardOrigin: deps.localDashboardOrigin,
@@ -148117,7 +148477,7 @@ function upgradeRuntimeAgentForDeps(deps, agent) {
148117
148477
  // ../../packages/api/src/node/runtime-restart-request.ts
148118
148478
  init_cjs_shims();
148119
148479
  function parseRuntimeRestartRequest(value) {
148120
- const body3 = isRecord9(value) ? value : {};
148480
+ const body3 = isRecord11(value) ? value : {};
148121
148481
  const startArgs = parseRuntimeRestartStartArgs(body3.startArgs);
148122
148482
  return {
148123
148483
  reason: body3.reason === "update" ? "update" : "restart",
@@ -148126,7 +148486,7 @@ function parseRuntimeRestartRequest(value) {
148126
148486
  }
148127
148487
  function parseRuntimeRestartStartArgs(value) {
148128
148488
  if (value === void 0) return void 0;
148129
- if (!isRecord9(value)) {
148489
+ if (!isRecord11(value)) {
148130
148490
  throw new MeshyError("VALIDATION_ERROR", "Runtime restart startArgs must be an object", 400);
148131
148491
  }
148132
148492
  const startArgs = {};
@@ -148175,7 +148535,7 @@ function parseOptionalString(value, key2) {
148175
148535
  const trimmed = value.trim();
148176
148536
  return trimmed || void 0;
148177
148537
  }
148178
- function isRecord9(value) {
148538
+ function isRecord11(value) {
148179
148539
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
148180
148540
  }
148181
148541
 
@@ -149196,7 +149556,7 @@ function toLegacyWorkerControl2(message) {
149196
149556
  }
149197
149557
 
149198
149558
  // ../../packages/api/src/tasks/task-route-utils.ts
149199
- function isRecord10(value) {
149559
+ function isRecord12(value) {
149200
149560
  return typeof value === "object" && value !== null;
149201
149561
  }
149202
149562
  function restoreTaskState(taskEngine, task) {
@@ -149263,7 +149623,7 @@ function normalizeStructuredValue(value) {
149263
149623
  if (Array.isArray(value)) {
149264
149624
  return value.map((entry) => normalizeStructuredValue(entry));
149265
149625
  }
149266
- if (!isRecord10(value)) {
149626
+ if (!isRecord12(value)) {
149267
149627
  return value;
149268
149628
  }
149269
149629
  return Object.fromEntries(
@@ -149272,20 +149632,20 @@ function normalizeStructuredValue(value) {
149272
149632
  }
149273
149633
  function getTranscriptEventSignature(event) {
149274
149634
  if (event.type === "user") {
149275
- const signature = getTaskUserMessageSignature(isRecord10(event.message) ? event.message.content : event.message) ?? getTaskUserMessageSignature(event.content);
149635
+ const signature = getTaskUserMessageSignature(isRecord12(event.message) ? event.message.content : event.message) ?? getTaskUserMessageSignature(event.content);
149276
149636
  return signature ? `user:${signature}` : null;
149277
149637
  }
149278
149638
  if (event.type === "assistant") {
149279
149639
  if (typeof event.message === "string") {
149280
149640
  return `assistant:${JSON.stringify(event.message)}`;
149281
149641
  }
149282
- if (isRecord10(event.message) && "content" in event.message) {
149642
+ if (isRecord12(event.message) && "content" in event.message) {
149283
149643
  return `assistant:${JSON.stringify(normalizeStructuredValue(event.message.content))}`;
149284
149644
  }
149285
149645
  if (Array.isArray(event.content)) {
149286
149646
  return `assistant:${JSON.stringify(normalizeStructuredValue(event.content))}`;
149287
149647
  }
149288
- if (isRecord10(event.message)) {
149648
+ if (isRecord12(event.message)) {
149289
149649
  return `assistant:${JSON.stringify(normalizeStructuredValue(event.message))}`;
149290
149650
  }
149291
149651
  }
@@ -177128,22 +177488,32 @@ async function handleTaskMessage(deps, message) {
177128
177488
  if (!task) {
177129
177489
  throw new MeshyError("TASK_NOT_FOUND", `Task ${payload.taskId} not found`, 404);
177130
177490
  }
177131
- if (!supportsFollowUpAgent(task.agent)) {
177132
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
177491
+ const targetAgent = typeof payload.targetAgent === "string" ? payload.targetAgent : void 0;
177492
+ const agent = resolveActiveAgent(task, targetAgent);
177493
+ if (!supportsFollowUpAgent(agent)) {
177494
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
177133
177495
  }
177134
- const engine = deps.engineRegistry.get(task.agent);
177496
+ const engine = deps.engineRegistry.get(agent);
177135
177497
  if (!engine) {
177136
- throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${task.agent}`, 400);
177498
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${agent}`, 400);
177137
177499
  }
177138
- deps.taskEngine.updateTask(payload.taskId, { status: "running" });
177139
177500
  if (!deps.nodeRegistry.isLeader()) {
177140
177501
  const reporter = new RestLeaderTaskReporter(deps.nodeRegistry, log2);
177141
177502
  reporter.reportRunning(payload.taskId, { branch: task.branch });
177142
- registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, task.agent, reporter);
177503
+ registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, agent, reporter);
177143
177504
  }
177144
177505
  try {
177145
- engine.sendMessage(payload.taskId, payload.content);
177146
- log2.info("started follow-up execution", { taskId: payload.taskId });
177506
+ startAgentTaskTurn({
177507
+ task,
177508
+ content: payload.content,
177509
+ targetAgent,
177510
+ assignedTo: task.assignedTo,
177511
+ taskEngine: deps.taskEngine,
177512
+ engineRegistry: deps.engineRegistry,
177513
+ eventBus: deps.eventBus,
177514
+ logger: log2
177515
+ });
177516
+ log2.info("started follow-up execution", { taskId: payload.taskId, agent });
177147
177517
  } catch (error2) {
177148
177518
  const messageText = error2 instanceof Error ? error2.message : String(error2);
177149
177519
  log2.error("engine.sendMessage threw synchronously", { taskId: payload.taskId, error: messageText });
@@ -177394,6 +177764,7 @@ async function sendTaskLogsResponse(req, res, taskId) {
177394
177764
  const log2 = rootLogger.child("tasks/logs");
177395
177765
  const query = TaskLogsQuery.parse(req.query);
177396
177766
  const after = query.after ?? 0;
177767
+ const minTotal = query.minTotal ?? 0;
177397
177768
  const proxyRequest = getTaskLogsProxyRequestMetadata(req);
177398
177769
  let task = taskEngine.getTask(taskId);
177399
177770
  if (!task && proxyRequest.task?.id === taskId) {
@@ -177439,7 +177810,8 @@ async function sendTaskLogsResponse(req, res, taskId) {
177439
177810
  if (isLeader) {
177440
177811
  const local2 = readLocalTaskLogsWithNativeHistory(engineRegistry, task, taskId, after, task?.agent ?? "claudecode");
177441
177812
  leaderLocal = local2;
177442
- if ((local2.total > 0 || !needsProxy) && !shouldAskWorkerForNativeRepair) {
177813
+ const leaderLocalSatisfiesCheckpoint = minTotal === 0 || local2.total >= minTotal;
177814
+ if ((local2.total > 0 || !needsProxy) && leaderLocalSatisfiesCheckpoint && !shouldAskWorkerForNativeRepair) {
177443
177815
  log2.debug("serving leader-local task logs", {
177444
177816
  taskId,
177445
177817
  assignedTo: task?.assignedTo,
@@ -177450,7 +177822,16 @@ async function sendTaskLogsResponse(req, res, taskId) {
177450
177822
  res.json(local2);
177451
177823
  return;
177452
177824
  }
177453
- if (local2.total > 0 && shouldAskWorkerForNativeRepair) {
177825
+ if (local2.total > 0 && !leaderLocalSatisfiesCheckpoint) {
177826
+ log2.debug("leader-local task logs behind requested checkpoint, falling back to worker proxy", {
177827
+ taskId,
177828
+ assignedTo: task?.assignedTo,
177829
+ selfId,
177830
+ total: local2.total,
177831
+ minTotal,
177832
+ after
177833
+ });
177834
+ } else if (local2.total > 0 && shouldAskWorkerForNativeRepair) {
177454
177835
  log2.debug("leader-local native session logs may be partial, checking worker repair path", {
177455
177836
  taskId,
177456
177837
  assignedTo: task?.assignedTo,
@@ -177493,7 +177874,9 @@ async function sendTaskLogsResponse(req, res, taskId) {
177493
177874
  return;
177494
177875
  }
177495
177876
  }
177496
- const proxyPath = `/api/tasks/${taskId}/logs?after=${after}`;
177877
+ const proxyParams = new URLSearchParams({ after: String(after) });
177878
+ if (minTotal > 0) proxyParams.set("minTotal", String(minTotal));
177879
+ const proxyPath = `/api/tasks/${taskId}/logs?${proxyParams.toString()}`;
177497
177880
  try {
177498
177881
  const { endpoint, response: proxyRes } = await fetchNodeWithFallback(
177499
177882
  node2,
@@ -177548,17 +177931,24 @@ init_cjs_shims();
177548
177931
 
177549
177932
  // ../../packages/api/src/tasks/task-follow-up.ts
177550
177933
  init_cjs_shims();
177551
- function startLocalTaskFollowUp(deps, task, content3, assignedTo, metadata) {
177552
- const engine = deps.engineRegistry.get(task.agent);
177934
+ function startLocalTaskFollowUp(deps, task, content3, assignedTo, targetAgent, metadata) {
177935
+ const agent = resolveActiveAgent(task, targetAgent);
177936
+ const engine = deps.engineRegistry.get(agent);
177553
177937
  if (!engine) {
177554
- throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${task.agent}`, 400);
177938
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${agent}`, 400);
177555
177939
  }
177556
- deps.taskEngine.updateTask(task.id, {
177557
- status: "running",
177558
- assignedTo
177559
- });
177560
177940
  try {
177561
- engine.sendMessage(task.id, content3);
177941
+ startAgentTaskTurn({
177942
+ task,
177943
+ content: content3,
177944
+ targetAgent,
177945
+ assignedTo,
177946
+ taskEngine: deps.taskEngine,
177947
+ engineRegistry: deps.engineRegistry,
177948
+ eventBus: deps.eventBus,
177949
+ logger: deps.logger,
177950
+ metadata
177951
+ });
177562
177952
  } catch (err) {
177563
177953
  restoreTaskState(deps.taskEngine, task);
177564
177954
  deps.logger.warn("failed to start local follow-up execution", {
@@ -177581,9 +177971,10 @@ function restoreQueuedMessage(taskEngine, taskId, message) {
177581
177971
  queuedMessages: [message, ...current.queuedMessages ?? []]
177582
177972
  });
177583
177973
  }
177584
- async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177585
- if (!supportsFollowUpAgent(task.agent)) {
177586
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
177974
+ async function sendTaskFollowUpMessage(deps, task, content3, targetAgent, metadata) {
177975
+ const agent = resolveActiveAgent(task, targetAgent);
177976
+ if (!supportsFollowUpAgent(agent)) {
177977
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
177587
177978
  }
177588
177979
  if (task.assignedTo && task.assignedTo !== deps.nodeRegistry.getSelf()?.id) {
177589
177980
  const node2 = deps.nodeRegistry.getNode(task.assignedTo);
@@ -177602,7 +177993,7 @@ async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177602
177993
  409
177603
177994
  );
177604
177995
  }
177605
- deps.taskEngine.updateTask(task.id, { status: "running" });
177996
+ deps.taskEngine.updateTask(task.id, targetAgent ? { status: "running", payload: { ...task.payload, activeAgent: agent } } : { status: "running" });
177606
177997
  log2.info("proxying follow-up message to worker", {
177607
177998
  taskId: task.id,
177608
177999
  assignedTo: task.assignedTo,
@@ -177612,7 +178003,8 @@ async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177612
178003
  const client = new NodeMessageClient({ heartbeat: deps.heartbeat, logger: deps.logger });
177613
178004
  const delivery = await client.send(node2, createNodeMessage("task.message", {
177614
178005
  taskId: task.id,
177615
- content: content3
178006
+ content: content3,
178007
+ ...targetAgent ? { targetAgent } : {}
177616
178008
  }));
177617
178009
  return delivery.queued ? { queued: true } : {};
177618
178010
  } catch (err) {
@@ -177630,31 +178022,34 @@ async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177630
178022
  startLocalTaskFollowUp({
177631
178023
  taskEngine: deps.taskEngine,
177632
178024
  engineRegistry: deps.engineRegistry,
178025
+ eventBus: deps.eventBus,
177633
178026
  logger: deps.logger.child("tasks/message")
177634
- }, task, content3, task.assignedTo, metadata);
178027
+ }, task, content3, task.assignedTo, targetAgent, metadata);
177635
178028
  return {};
177636
178029
  }
177637
- function queueRunningTaskMessage(deps, task, content3) {
177638
- if (!supportsFollowUpAgent(task.agent)) {
177639
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
178030
+ function queueRunningTaskMessage(deps, task, content3, targetAgent) {
178031
+ const agent = resolveActiveAgent(task, targetAgent);
178032
+ if (!supportsFollowUpAgent(agent)) {
178033
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
177640
178034
  }
177641
- const queued = deps.taskEngine.enqueueTaskMessage(task.id, content3);
178035
+ const queued = targetAgent ? deps.taskEngine.enqueueTaskMessage(task.id, content3, targetAgent) : deps.taskEngine.enqueueTaskMessage(task.id, content3);
177642
178036
  deps.logger.child("tasks/message").info("queued follow-up message for running task", {
177643
178037
  taskId: task.id,
178038
+ targetAgent,
177644
178039
  queuedCount: queued.queuedMessages?.length ?? 0
177645
178040
  });
177646
178041
  return queued;
177647
178042
  }
177648
178043
  var MESSAGEABLE_STATUSES = /* @__PURE__ */ new Set(["running", "completed", "cancelled", "failed"]);
177649
- async function applyTaskMessage(deps, task, content3) {
178044
+ async function applyTaskMessage(deps, task, content3, targetAgent) {
177650
178045
  if (!MESSAGEABLE_STATUSES.has(task.status)) {
177651
178046
  throw new MeshyError("VALIDATION_ERROR", `Cannot send message to task in ${task.status} status`, 400);
177652
178047
  }
177653
178048
  if (task.status === "running") {
177654
- const queued = queueRunningTaskMessage(deps, task, content3);
178049
+ const queued = queueRunningTaskMessage(deps, task, content3, targetAgent);
177655
178050
  return { ok: true, queued: true, queuedMessages: queued.queuedMessages ?? [] };
177656
178051
  }
177657
- const result = await sendTaskFollowUpMessage(deps, task, content3);
178052
+ const result = await sendTaskFollowUpMessage(deps, task, content3, targetAgent);
177658
178053
  return result.queued ? { ok: true, queued: true } : { ok: true };
177659
178054
  }
177660
178055
  function cancelQueuedTaskMessage(deps, taskId, messageId) {
@@ -177687,7 +178082,7 @@ async function drainNextQueuedTaskMessage(deps, taskId) {
177687
178082
  if (!dequeued) return false;
177688
178083
  drainingTaskIds.add(taskId);
177689
178084
  try {
177690
- await sendTaskFollowUpMessage(deps, dequeued.task, dequeued.message.content, {
178085
+ await sendTaskFollowUpMessage(deps, dequeued.task, dequeued.message.content, dequeued.message.targetAgent, {
177691
178086
  queuedMessageId: dequeued.message.id
177692
178087
  });
177693
178088
  deps.logger.child("tasks/message").info("started queued follow-up message", {
@@ -178671,7 +179066,7 @@ function createTaskRoutes() {
178671
179066
  res.json({ ok: true, queuedMessages: task.queuedMessages ?? [] });
178672
179067
  }));
178673
179068
  router.post("/:id/message", asyncHandler8(async (req, res) => {
178674
- const { taskEngine, engineRegistry, nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
179069
+ const { taskEngine, engineRegistry, nodeRegistry, heartbeat, eventBus, logger: rootLogger } = req.app.locals.deps;
178675
179070
  const body3 = SendMessageBody.parse(req.body);
178676
179071
  const taskId = req.params.id;
178677
179072
  const task = taskEngine.getTask(taskId);
@@ -178679,9 +179074,10 @@ function createTaskRoutes() {
178679
179074
  throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
178680
179075
  }
178681
179076
  res.json(await applyTaskMessage(
178682
- { taskEngine, engineRegistry, nodeRegistry, heartbeat, logger: rootLogger },
179077
+ { taskEngine, engineRegistry, nodeRegistry, heartbeat, eventBus, logger: rootLogger },
178683
179078
  task,
178684
- body3.content
179079
+ body3.content,
179080
+ body3.targetAgent
178685
179081
  ));
178686
179082
  }));
178687
179083
  router.use(createTaskOutputRoutes());
@@ -178855,6 +179251,10 @@ var WorkerMessageBody = external_exports.object({
178855
179251
  var WorkerTaskBody = external_exports.object({
178856
179252
  taskId: external_exports.string().min(1)
178857
179253
  });
179254
+ var WorkerLogsReadyBody = external_exports.object({
179255
+ taskId: external_exports.string().min(1),
179256
+ total: external_exports.number().int().min(0)
179257
+ });
178858
179258
  function asyncHandler10(fn) {
178859
179259
  return (req, res, next2) => fn(req, res, next2).catch(next2);
178860
179260
  }
@@ -178909,15 +179309,15 @@ function createWorkerRoutes() {
178909
179309
  if (!task) {
178910
179310
  throw new MeshyError("TASK_NOT_FOUND", `Task ${body3.taskId} not found`, 404);
178911
179311
  }
178912
- if (!supportsFollowUpAgent(task.agent)) {
178913
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
179312
+ const agent = resolveActiveAgent(task, body3.targetAgent);
179313
+ if (!supportsFollowUpAgent(agent)) {
179314
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
178914
179315
  }
178915
- const engine = engineRegistry.get(task.agent);
179316
+ const engine = engineRegistry.get(agent);
178916
179317
  if (!engine) {
178917
- res.status(400).json({ error: `Engine not registered for agent: ${task.agent}` });
179318
+ res.status(400).json({ error: `Engine not registered for agent: ${agent}` });
178918
179319
  return;
178919
179320
  }
178920
- taskEngine.updateTask(body3.taskId, { status: "running" });
178921
179321
  if (!nodeRegistry.isLeader()) {
178922
179322
  new RestLeaderTaskReporter(nodeRegistry, log2).reportRunning(body3.taskId, { branch: task.branch });
178923
179323
  }
@@ -178928,13 +179328,22 @@ function createWorkerRoutes() {
178928
179328
  nodeRegistry,
178929
179329
  log2,
178930
179330
  body3.taskId,
178931
- task.agent,
179331
+ agent,
178932
179332
  new RestLeaderTaskReporter(nodeRegistry, log2)
178933
179333
  );
178934
179334
  }
178935
179335
  try {
178936
- engine.sendMessage(body3.taskId, body3.content);
178937
- log2.info("started follow-up execution", { taskId: body3.taskId });
179336
+ startAgentTaskTurn({
179337
+ task,
179338
+ content: body3.content,
179339
+ targetAgent: body3.targetAgent,
179340
+ assignedTo: task.assignedTo,
179341
+ taskEngine,
179342
+ engineRegistry,
179343
+ eventBus,
179344
+ logger: log2
179345
+ });
179346
+ log2.info("started follow-up execution", { taskId: body3.taskId, agent });
178938
179347
  } catch (err) {
178939
179348
  const errorMessage = err instanceof Error ? err.message : String(err);
178940
179349
  log2.error("engine.sendMessage threw synchronously", { taskId: body3.taskId, error: errorMessage });
@@ -178986,6 +179395,12 @@ function createWorkerRoutes() {
178986
179395
  }
178987
179396
  res.json({ ok: true });
178988
179397
  }));
179398
+ router.post("/logs-ready", asyncHandler10(async (req, res) => {
179399
+ const { eventBus } = req.app.locals.deps;
179400
+ const body3 = WorkerLogsReadyBody.parse(req.body);
179401
+ eventBus.emit("task.logs.ready", { taskId: body3.taskId, total: body3.total });
179402
+ res.json({ ok: true });
179403
+ }));
178989
179404
  router.post("/heartbeat", asyncHandler10(async (req, res) => {
178990
179405
  const { heartbeat, logger: rootLogger } = req.app.locals.deps;
178991
179406
  const log2 = rootLogger.child("worker/heartbeat");
@@ -179156,6 +179571,10 @@ function getQuickChatStore(deps) {
179156
179571
  if (deps.storagePath) return createQuickChatStore(deps.storagePath);
179157
179572
  throw new MeshyError("VALIDATION_ERROR", "Quick chats are not available for this node", 501);
179158
179573
  }
179574
+ function getStoragePath2(deps) {
179575
+ if (deps.storagePath) return deps.storagePath;
179576
+ throw new MeshyError("VALIDATION_ERROR", "Node metadata is not available for this node", 501);
179577
+ }
179159
179578
  function validateQuickChatAliases(quickChats) {
179160
179579
  const seen = /* @__PURE__ */ new Set();
179161
179580
  for (const quickChat of quickChats) {
@@ -179248,6 +179667,21 @@ function createSystemRoutes() {
179248
179667
  const metrics2 = taskEngine.getMetrics();
179249
179668
  res.json(metrics2);
179250
179669
  }));
179670
+ router.get("/default-task-agent", asyncHandler11(async (req, res) => {
179671
+ const storagePath = getStoragePath2(req.app.locals.deps);
179672
+ res.json({ defaultTaskAgent: resolveNodeDefaultTaskAgent(storagePath) });
179673
+ }));
179674
+ router.put("/default-task-agent", asyncHandler11(async (req, res) => {
179675
+ const body3 = UpdateDefaultTaskAgentBody.parse(req.body);
179676
+ const deps = req.app.locals.deps;
179677
+ const storagePath = getStoragePath2(deps);
179678
+ persistNodeDefaultTaskAgent(storagePath, body3.defaultTaskAgent);
179679
+ const settingsSnapshot = deps.refreshSettingsSnapshot?.();
179680
+ if (settingsSnapshot) {
179681
+ deps.nodeRegistry.updateSettingsSnapshot(deps.nodeRegistry.getSelf().id, settingsSnapshot);
179682
+ }
179683
+ res.json({ defaultTaskAgent: resolveNodeDefaultTaskAgent(storagePath) });
179684
+ }));
179251
179685
  router.get("/quick-chats", asyncHandler11(async (req, res) => {
179252
179686
  const store = getQuickChatStore(req.app.locals.deps);
179253
179687
  res.json({ quickChats: store.list() });
@@ -179284,6 +179718,7 @@ var ALL_EVENT_NAMES = [
179284
179718
  "task.updated",
179285
179719
  "task.deleted",
179286
179720
  "task.output",
179721
+ "task.logs.ready",
179287
179722
  "election.started",
179288
179723
  "election.complete",
179289
179724
  "cluster.state",
@@ -179590,12 +180025,22 @@ var REQUEST_TELEMETRY_SUPPRESSED_ROUTES = /* @__PURE__ */ new Set([
179590
180025
  "POST /api/cluster-control/heartbeat",
179591
180026
  "POST /api/cluster-control/keepalive"
179592
180027
  ]);
180028
+ var REQUEST_LOGGING_THROTTLED_ROUTES = /* @__PURE__ */ new Set([
180029
+ "POST /api/worker/keepalive",
180030
+ "POST /api/cluster-control/keepalive"
180031
+ ]);
180032
+ var REQUEST_LOGGING_THROTTLE_MS = 6e4;
179593
180033
  function normalizeRequestTelemetryPath(pathname) {
179594
180034
  return pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
179595
180035
  }
180036
+ function requestRouteKey(method, pathname) {
180037
+ return `${method.toUpperCase()} ${normalizeRequestTelemetryPath(pathname)}`;
180038
+ }
179596
180039
  function isSuppressedRequestTelemetryRoute(req) {
179597
- const key2 = `${req.method.toUpperCase()} ${normalizeRequestTelemetryPath(req.path)}`;
179598
- return REQUEST_TELEMETRY_SUPPRESSED_ROUTES.has(key2);
180040
+ return REQUEST_TELEMETRY_SUPPRESSED_ROUTES.has(requestRouteKey(req.method, req.path));
180041
+ }
180042
+ function isThrottledRequestLoggingRoute(method, pathname) {
180043
+ return REQUEST_LOGGING_THROTTLED_ROUTES.has(requestRouteKey(method, pathname));
179599
180044
  }
179600
180045
  function usesLargeJsonBodyLimit(pathname) {
179601
180046
  return pathname === "/api/cluster/join" || pathname === "/api/node" || pathname.startsWith("/api/node/") || pathname === "/api/tasks" || pathname.startsWith("/api/tasks/") || pathname === "/api/worker" || pathname.startsWith("/api/worker/") || pathname.startsWith("/api/mobile/");
@@ -179767,7 +180212,11 @@ function createServer2(deps) {
179767
180212
  const canServeDashboard = (req) => !deps.config.validateBearerToken || isTrustedDashboardRequest(req, authConfig);
179768
180213
  const canServePreview = (req) => canServeDashboard(req) || hasAuthorizationHeader(req);
179769
180214
  app.use(createRequestLoggingMiddleware(deps.logger, {
179770
- shouldLog: (req) => isApiRequest(req) || isPreviewRequest(req)
180215
+ shouldLog: (req) => isApiRequest(req) || isPreviewRequest(req),
180216
+ throttle: {
180217
+ intervalMs: REQUEST_LOGGING_THROTTLE_MS,
180218
+ shouldThrottle: (req, res, closed, path47) => !closed && res.statusCode < 400 && isThrottledRequestLoggingRoute(req.method, path47)
180219
+ }
179771
180220
  }));
179772
180221
  app.use(createRequestTelemetryMiddleware(deps.telemetry ?? createNoopTelemetry(), {
179773
180222
  shouldTrack: (req) => !isSuppressedRequestTelemetryRoute(req) && (isApiRequest(req) || isPreviewRequest(req))
@@ -181756,6 +182205,7 @@ function createSettingsSnapshotProvider(options) {
181756
182205
  function buildSnapshot() {
181757
182206
  cachedSnapshot = buildNodeSettingsSnapshot({
181758
182207
  auth: options.authMetadata,
182208
+ defaultTaskAgent: resolveNodeDefaultTaskAgent(options.storagePath),
181759
182209
  localDashboardOrigin: options.localDashboardOrigin,
181760
182210
  runtimeMetadata: options.runtimeMetadata,
181761
182211
  runtimeUpdate: createRuntimeUpdateSnapshot({