meshy-node 0.10.7 → 0.10.9

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", {
@@ -138742,11 +138761,12 @@ var DEFAULT_CONFIG = {
138742
138761
  dispatchTimeout: 1e4
138743
138762
  };
138744
138763
  var TaskDispatcher = class {
138745
- constructor(taskEngine, nodeRegistry, election, eventBus, config2, logger33, nodeMessageClient) {
138764
+ constructor(taskEngine, nodeRegistry, election, eventBus, config2, logger33, nodeMessageClient, localNodeMessageHandler) {
138746
138765
  this.taskEngine = taskEngine;
138747
138766
  this.nodeRegistry = nodeRegistry;
138748
138767
  this.election = election;
138749
138768
  this.eventBus = eventBus;
138769
+ this.localNodeMessageHandler = localNodeMessageHandler;
138750
138770
  this.config = { ...DEFAULT_CONFIG, ...config2 };
138751
138771
  this.log = logger33?.child("task-dispatcher") ?? { debug() {
138752
138772
  }, info() {
@@ -138756,10 +138776,7 @@ var TaskDispatcher = class {
138756
138776
  return this;
138757
138777
  } };
138758
138778
  this.messageClient = nodeMessageClient ?? new NodeMessageClient({ timeoutMs: this.config.dispatchTimeout, logger: logger33 });
138759
- this.onTaskAssigned = (data) => {
138760
- void this.dispatchTask(data.taskId, data.nodeId).catch(() => {
138761
- });
138762
- };
138779
+ this.onTaskAssigned = (data) => this.scheduleDispatch(data.taskId, data.nodeId);
138763
138780
  this.onElectionComplete = (data) => {
138764
138781
  const self2 = this.nodeRegistry.getSelf();
138765
138782
  if (data.leaderId === self2.id) {
@@ -138773,10 +138790,12 @@ var TaskDispatcher = class {
138773
138790
  nodeRegistry;
138774
138791
  election;
138775
138792
  eventBus;
138793
+ localNodeMessageHandler;
138776
138794
  config;
138777
138795
  log;
138778
138796
  messageClient;
138779
138797
  autoAssignTimer = null;
138798
+ pendingDispatchTimers = /* @__PURE__ */ new Set();
138780
138799
  running = false;
138781
138800
  onTaskAssigned;
138782
138801
  onElectionComplete;
@@ -138794,6 +138813,7 @@ var TaskDispatcher = class {
138794
138813
  this.eventBus.off("task.assigned", this.onTaskAssigned);
138795
138814
  this.eventBus.off("election.complete", this.onElectionComplete);
138796
138815
  this.stopAutoAssignLoop();
138816
+ this.clearPendingDispatchTimers();
138797
138817
  }
138798
138818
  // ── Dispatch ────────────────────────────────────────────────────────
138799
138819
  async dispatchTask(taskId, nodeId) {
@@ -138817,7 +138837,13 @@ var TaskDispatcher = class {
138817
138837
  endpoint: node2.endpoint,
138818
138838
  devtunnelEndpoint: node2.devtunnelEndpoint
138819
138839
  });
138820
- await this.messageClient.send(node2, createNodeMessage("task.execute", { task }));
138840
+ const message = createNodeMessage("task.execute", { task });
138841
+ if (node2.id === this.nodeRegistry.getSelf().id && this.localNodeMessageHandler) {
138842
+ await this.localNodeMessageHandler(message);
138843
+ this.log.info("task dispatch request accepted", { taskId, nodeId, local: true });
138844
+ return;
138845
+ }
138846
+ await this.messageClient.send(node2, message);
138821
138847
  this.log.info("task dispatch request accepted", { taskId, nodeId });
138822
138848
  } catch (err) {
138823
138849
  const message = err instanceof Error ? err.message : String(err);
@@ -138843,6 +138869,20 @@ var TaskDispatcher = class {
138843
138869
  this.autoAssignTimer = null;
138844
138870
  }
138845
138871
  }
138872
+ scheduleDispatch(taskId, nodeId) {
138873
+ const timer = setTimeout(() => {
138874
+ this.pendingDispatchTimers.delete(timer);
138875
+ void this.dispatchTask(taskId, nodeId).catch(() => {
138876
+ });
138877
+ }, 0);
138878
+ this.pendingDispatchTimers.add(timer);
138879
+ }
138880
+ clearPendingDispatchTimers() {
138881
+ for (const timer of this.pendingDispatchTimers) {
138882
+ clearTimeout(timer);
138883
+ }
138884
+ this.pendingDispatchTimers.clear();
138885
+ }
138846
138886
  };
138847
138887
 
138848
138888
  // ../../packages/core/src/cluster/heartbeat.ts
@@ -140021,6 +140061,31 @@ var ExecutionEngine = class {
140021
140061
  await Promise.all([...this.logWrites.values()]);
140022
140062
  }
140023
140063
  }
140064
+ async getLogLineCount(taskId) {
140065
+ const logPath = this.getLogPath(taskId);
140066
+ try {
140067
+ const content3 = await fs7.promises.readFile(logPath, "utf-8");
140068
+ const trimmed = content3.trim();
140069
+ return trimmed ? trimmed.split("\n").filter(Boolean).length : 0;
140070
+ } catch (error2) {
140071
+ if (error2.code === "ENOENT") return 0;
140072
+ throw error2;
140073
+ }
140074
+ }
140075
+ async flushLogsAndEmitReady(taskId) {
140076
+ try {
140077
+ await this.waitForLogWrites();
140078
+ const total = await this.getLogLineCount(taskId);
140079
+ this.eventBus.emit("task.logs.ready", { taskId, total });
140080
+ return total;
140081
+ } catch (error2) {
140082
+ this.logger.warn("failed to emit task log readiness checkpoint", {
140083
+ taskId,
140084
+ error: error2 instanceof Error ? error2.message : String(error2)
140085
+ });
140086
+ return 0;
140087
+ }
140088
+ }
140024
140089
  getLogPath(taskId) {
140025
140090
  return path7.join(this.logDir, `${taskId}.jsonl`);
140026
140091
  }
@@ -140886,9 +140951,59 @@ function nonEmptyString(value) {
140886
140951
  function getNativeHistorySessionId(payload) {
140887
140952
  return nonEmptyString(payload?.nativeSessionId) || nonEmptyString(payload?.sessionId);
140888
140953
  }
140889
- function buildPersistedSessionPayload(task, sessionId, cwd) {
140954
+ function isRecord5(value) {
140955
+ return typeof value === "object" && value !== null;
140956
+ }
140957
+ function getAgentSessions(payload) {
140958
+ const raw3 = payload.agentSessions;
140959
+ if (!isRecord5(raw3)) return {};
140960
+ const sessions = {};
140961
+ for (const [agent, value] of Object.entries(raw3)) {
140962
+ if (!isRecord5(value)) continue;
140963
+ const sessionId = nonEmptyString(value.sessionId);
140964
+ const cwd = nonEmptyString(value.cwd);
140965
+ if (!agent || !sessionId || !cwd) continue;
140966
+ sessions[agent] = { sessionId, cwd };
140967
+ }
140968
+ return sessions;
140969
+ }
140970
+ function getPersistedAgentSession(task, agent) {
140971
+ const payload = task?.payload ?? {};
140972
+ const sessions = getAgentSessions(payload);
140973
+ const session = sessions[agent];
140974
+ if (session) return session;
140975
+ if (Object.keys(sessions).length > 0 || task?.agent !== agent) {
140976
+ return null;
140977
+ }
140978
+ const legacySessionId = nonEmptyString(payload.sessionId);
140979
+ if (!legacySessionId) return null;
140980
+ return {
140981
+ sessionId: legacySessionId,
140982
+ cwd: nonEmptyString(payload.sessionCwd)
140983
+ };
140984
+ }
140985
+ function canUseSessionLogEventForAgent(task, agent, event) {
140986
+ const eventAgent = nonEmptyString(event.agent);
140987
+ if (eventAgent) return eventAgent === agent;
140988
+ const sessions = getAgentSessions(task?.payload ?? {});
140989
+ return Object.keys(sessions).length === 0 && task?.agent === agent;
140990
+ }
140991
+ function buildPersistedSessionPayload(task, sessionId, cwd, agent) {
140890
140992
  const payload = task?.payload ?? {};
140993
+ const activeAgent = nonEmptyString(agent);
140891
140994
  const nextPayload = { ...payload, sessionId, sessionCwd: cwd };
140995
+ if (activeAgent) {
140996
+ const agentSessions = getAgentSessions(payload);
140997
+ const taskAgent = nonEmptyString(task?.agent);
140998
+ const legacySessionId = nonEmptyString(payload.sessionId);
140999
+ const legacyCwd = nonEmptyString(payload.sessionCwd) || cwd;
141000
+ if (taskAgent && legacySessionId && !agentSessions[taskAgent]) {
141001
+ agentSessions[taskAgent] = { sessionId: legacySessionId, cwd: legacyCwd };
141002
+ }
141003
+ agentSessions[activeAgent] = { sessionId, cwd };
141004
+ nextPayload.activeAgent = activeAgent;
141005
+ nextPayload.agentSessions = agentSessions;
141006
+ }
140892
141007
  if (task?.conversationKind !== "nativeSession") {
140893
141008
  return nextPayload;
140894
141009
  }
@@ -141001,10 +141116,44 @@ function forwardTaskOutputToLeader(log2, nodeRegistry, taskId, event) {
141001
141116
  });
141002
141117
  });
141003
141118
  }
141119
+ function postLogsReadyToLeader(log2, leaderEndpoint, taskId, total) {
141120
+ fetch(`${leaderEndpoint}/api/worker/logs-ready`, applyRequestAuthHeaders({
141121
+ method: "POST",
141122
+ headers: { "Content-Type": "application/json" },
141123
+ body: JSON.stringify({ taskId, total })
141124
+ })).then((response) => {
141125
+ if (!response.ok) {
141126
+ log2.warn("leader rejected log readiness report", {
141127
+ taskId,
141128
+ total,
141129
+ leaderEndpoint,
141130
+ statusCode: response.status
141131
+ });
141132
+ return;
141133
+ }
141134
+ log2.debug("forwarded log readiness to leader", { taskId, total, leaderEndpoint, statusCode: response.status });
141135
+ }).catch((err) => {
141136
+ log2.warn("failed to forward log readiness", {
141137
+ taskId,
141138
+ total,
141139
+ leaderEndpoint,
141140
+ error: err instanceof Error ? err.message : String(err)
141141
+ });
141142
+ });
141143
+ }
141144
+ function forwardTaskLogsReadyToLeader(log2, nodeRegistry, taskId, total) {
141145
+ const leaderEndpoint = nodeRegistry.getLeaderEndpoint();
141146
+ if (!leaderEndpoint) {
141147
+ log2.warn("skipping log readiness forward because leader endpoint is missing", { taskId, total });
141148
+ return;
141149
+ }
141150
+ postLogsReadyToLeader(log2, leaderEndpoint, taskId, total);
141151
+ }
141004
141152
  function registerLeaderForwarding(eventBus, nodeRegistry, log2, taskId, agent, reporter) {
141005
141153
  const cleanup = () => {
141006
141154
  eventBus.off("task.status", onTaskStatus);
141007
141155
  eventBus.off("task.output", onTaskOutput);
141156
+ eventBus.off("task.logs.ready", onTaskLogsReady);
141008
141157
  };
141009
141158
  const onTaskOutput = (data) => {
141010
141159
  if (data.taskId !== taskId) return;
@@ -141016,6 +141165,16 @@ function registerLeaderForwarding(eventBus, nodeRegistry, log2, taskId, agent, r
141016
141165
  if (reporter) reporter.reportOutput(taskId, data.event);
141017
141166
  else forwardTaskOutputToLeader(log2, nodeRegistry, taskId, data.event);
141018
141167
  };
141168
+ const onTaskLogsReady = (data) => {
141169
+ if (data.taskId !== taskId) return;
141170
+ log2.debug("task.logs.ready event", {
141171
+ taskId,
141172
+ total: data.total,
141173
+ hasLeader: !!nodeRegistry.getLeaderEndpoint()
141174
+ });
141175
+ if (reporter) reporter.reportLogsReady(taskId, data.total);
141176
+ else forwardTaskLogsReadyToLeader(log2, nodeRegistry, taskId, data.total);
141177
+ };
141019
141178
  const onTaskStatus = (data) => {
141020
141179
  if (data.taskId !== taskId || data.status === "running") return;
141021
141180
  cleanup();
@@ -141041,11 +141200,175 @@ function registerLeaderForwarding(eventBus, nodeRegistry, log2, taskId, agent, r
141041
141200
  });
141042
141201
  };
141043
141202
  eventBus.on("task.output", onTaskOutput);
141203
+ eventBus.on("task.logs.ready", onTaskLogsReady);
141044
141204
  eventBus.on("task.status", onTaskStatus);
141045
141205
  log2.debug("registered leader forwarding listeners", { taskId, agent });
141046
141206
  return cleanup;
141047
141207
  }
141048
141208
 
141209
+ // ../../packages/core/src/tasks/agent-task-turn.ts
141210
+ init_cjs_shims();
141211
+ function startAgentTaskTurn(options) {
141212
+ const agent = resolveActiveAgent(options.task, options.targetAgent);
141213
+ if (!supportsFollowUpAgent(agent)) {
141214
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
141215
+ }
141216
+ const engine = options.engineRegistry.get(agent);
141217
+ if (!engine) {
141218
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${agent}`, 400);
141219
+ }
141220
+ const updates = {
141221
+ status: "running",
141222
+ assignedTo: options.assignedTo
141223
+ };
141224
+ if (options.targetAgent || options.task.payload.activeAgent !== void 0) {
141225
+ updates.payload = {
141226
+ ...options.task.payload,
141227
+ activeAgent: agent
141228
+ };
141229
+ }
141230
+ options.taskEngine.updateTask(options.task.id, updates);
141231
+ const handoffEvent = buildExplicitHandoffEvent(options.task, options.targetAgent, agent);
141232
+ if (handoffEvent) {
141233
+ recordAndEmit(options, engine, handoffEvent);
141234
+ }
141235
+ const handoffContent = handoffEvent ? buildAgentHandoffContent(options.task, engine, options.content, agent) : null;
141236
+ const sendContent = handoffContent ?? options.content;
141237
+ const sendOptions = handoffContent ? { logContent: options.content } : void 0;
141238
+ try {
141239
+ if (sendOptions) engine.sendMessage(options.task.id, sendContent, sendOptions);
141240
+ else engine.sendMessage(options.task.id, sendContent);
141241
+ return;
141242
+ } catch (err) {
141243
+ if (!shouldStartHandoff(options.targetAgent, err)) {
141244
+ throw err;
141245
+ }
141246
+ options.logger.info("starting cross-agent handoff turn", {
141247
+ taskId: options.task.id,
141248
+ fromAgent: resolveActiveAgent(options.task),
141249
+ toAgent: agent,
141250
+ ...options.metadata
141251
+ });
141252
+ startHandoffRun(options, engine, agent, !handoffEvent, handoffContent);
141253
+ }
141254
+ }
141255
+ function resolveActiveAgent(task, targetAgent) {
141256
+ const explicit = targetAgent?.trim();
141257
+ if (explicit) return explicit;
141258
+ const active = typeof task.payload.activeAgent === "string" ? task.payload.activeAgent.trim() : "";
141259
+ return active || task.agent;
141260
+ }
141261
+ function shouldStartHandoff(targetAgent, err) {
141262
+ return Boolean(targetAgent) && err instanceof MeshyError && err.code === "TASK_NOT_FOUND";
141263
+ }
141264
+ function buildExplicitHandoffEvent(task, targetAgent, agent) {
141265
+ if (!targetAgent?.trim()) return null;
141266
+ const fromAgent = resolveActiveAgent(task);
141267
+ return fromAgent === agent ? null : buildAgentHandoffLogEvent(fromAgent, agent);
141268
+ }
141269
+ function startHandoffRun(options, engine, agent, recordBoundary, prebuiltContent) {
141270
+ const fromAgent = resolveActiveAgent(options.task);
141271
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
141272
+ const boundary = buildAgentHandoffLogEvent(fromAgent, agent, timestamp);
141273
+ const userEvent = {
141274
+ ...buildTaskUserLogEvent(options.content, timestamp),
141275
+ agent
141276
+ };
141277
+ if (recordBoundary) {
141278
+ recordAndEmit(options, engine, boundary);
141279
+ }
141280
+ recordAndEmit(options, engine, userEvent);
141281
+ const handoffTask = {
141282
+ ...options.task,
141283
+ agent,
141284
+ status: "running",
141285
+ assignedTo: options.assignedTo,
141286
+ payload: {
141287
+ ...options.task.payload,
141288
+ activeAgent: agent,
141289
+ initialMessage: prebuiltContent ?? buildAgentHandoffContent(options.task, engine, options.content, agent)
141290
+ }
141291
+ };
141292
+ engine.run(handoffTask);
141293
+ }
141294
+ function recordAndEmit(options, engine, event) {
141295
+ engine.recordOutput(options.task.id, event);
141296
+ options.eventBus.emit("task.output", { taskId: options.task.id, event });
141297
+ }
141298
+ function buildAgentHandoffLogEvent(fromAgent, toAgent, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
141299
+ return {
141300
+ type: "agent.handoff",
141301
+ fromAgent,
141302
+ toAgent,
141303
+ timestamp
141304
+ };
141305
+ }
141306
+ function buildAgentHandoffContent(task, engine, currentContent, targetAgent) {
141307
+ const logs4 = readTaskLogs(engine, task.id);
141308
+ const historyLines = extractVisibleTranscriptLines(logs4);
141309
+ if (historyLines.length === 0) {
141310
+ const initialLines = contentToVisibleLines("User", normalizeTaskUserMessageContent(task.payload.initialMessage));
141311
+ historyLines.push(...initialLines);
141312
+ }
141313
+ const currentText = contentToPlainText(currentContent) || "[non-text user content]";
141314
+ const text10 = [
141315
+ `You are taking over this Meshy task as ${targetAgent}.`,
141316
+ "",
141317
+ "Previous visible conversation:",
141318
+ historyLines.length > 0 ? historyLines.join("\n") : "[No visible previous transcript found.]",
141319
+ "",
141320
+ "Current user request:",
141321
+ currentText
141322
+ ].join("\n");
141323
+ return [{ type: "text", text: text10 }, ...currentContent.filter((part) => part.type !== "text")];
141324
+ }
141325
+ function readTaskLogs(engine, taskId) {
141326
+ return readJsonLines(engine.getLogPath(taskId));
141327
+ }
141328
+ function extractVisibleTranscriptLines(events) {
141329
+ const lines = [];
141330
+ for (const event of events) {
141331
+ if (event.type === "user" && event.isMeta !== true && event.isSidechain !== true) {
141332
+ lines.push(...contentToVisibleLines("User", extractUserContent(event)));
141333
+ }
141334
+ if (event.type === "assistant") {
141335
+ const text10 = extractAssistantText(event);
141336
+ if (text10) lines.push(`Assistant: ${text10}`);
141337
+ }
141338
+ }
141339
+ return lines;
141340
+ }
141341
+ function extractUserContent(event) {
141342
+ const message = isRecord6(event.message) ? event.message : null;
141343
+ const messageContent = message?.content;
141344
+ const directContent = event.content;
141345
+ return normalizeTaskUserMessageContent(messageContent ?? directContent ?? event.message).filter((part) => part.type !== "tool_result");
141346
+ }
141347
+ function contentToVisibleLines(label, content3) {
141348
+ const text10 = contentToPlainText(content3);
141349
+ return text10 ? [`${label}: ${text10}`] : [];
141350
+ }
141351
+ function contentToPlainText(content3) {
141352
+ return content3.map((part) => {
141353
+ if (part.type === "text") return part.text;
141354
+ if (part.type === "image") return `[image: ${part.filename ?? part.mediaType}]`;
141355
+ return "";
141356
+ }).filter(Boolean).join("\n").trim();
141357
+ }
141358
+ function extractAssistantText(event) {
141359
+ if (typeof event.message === "string") return event.message.trim();
141360
+ const content3 = isRecord6(event.message) ? event.message.content : event.content;
141361
+ if (!Array.isArray(content3)) return "";
141362
+ return content3.flatMap((block) => {
141363
+ if (!isRecord6(block) || block.type !== "text" || typeof block.text !== "string") return [];
141364
+ const text10 = block.text.trim();
141365
+ return text10 ? [text10] : [];
141366
+ }).join("\n").trim();
141367
+ }
141368
+ function isRecord6(value) {
141369
+ return typeof value === "object" && value !== null;
141370
+ }
141371
+
141049
141372
  // ../../packages/core/src/tasks/leader-task-reporter.ts
141050
141373
  init_cjs_shims();
141051
141374
  var RestLeaderTaskReporter = class {
@@ -141089,6 +141412,14 @@ var RestLeaderTaskReporter = class {
141089
141412
  });
141090
141413
  });
141091
141414
  }
141415
+ reportLogsReady(taskId, total) {
141416
+ const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
141417
+ if (!leaderEndpoint) {
141418
+ this.logger.warn("skipping log readiness report because leader endpoint is missing", { taskId, total });
141419
+ return;
141420
+ }
141421
+ postLogsReadyToLeader(this.logger, leaderEndpoint, taskId, total);
141422
+ }
141092
141423
  patchTask(taskId, data, label, successMessage) {
141093
141424
  const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
141094
141425
  if (!leaderEndpoint) {
@@ -141540,14 +141871,14 @@ function getClaudeStreamError(event) {
141540
141871
  }
141541
141872
  return null;
141542
141873
  }
141543
- function isRecord5(value) {
141874
+ function isRecord7(value) {
141544
141875
  return typeof value === "object" && value !== null;
141545
141876
  }
141546
141877
  function getClaudeToolResultText(block) {
141547
141878
  const content3 = block.content;
141548
141879
  if (typeof content3 === "string") return content3;
141549
141880
  if (Array.isArray(content3)) {
141550
- return content3.filter(isRecord5).map((entry) => String(entry.text ?? "")).join("\n");
141881
+ return content3.filter(isRecord7).map((entry) => String(entry.text ?? "")).join("\n");
141551
141882
  }
141552
141883
  return "";
141553
141884
  }
@@ -141568,10 +141899,10 @@ function isEmptyClaudeMessageEvent(event) {
141568
141899
  return false;
141569
141900
  }
141570
141901
  const message = event.message;
141571
- if (!isRecord5(message) || !Array.isArray(message.content)) {
141902
+ if (!isRecord7(message) || !Array.isArray(message.content)) {
141572
141903
  return false;
141573
141904
  }
141574
- return !message.content.filter(isRecord5).some(hasVisibleClaudeContentBlock);
141905
+ return !message.content.filter(isRecord7).some(hasVisibleClaudeContentBlock);
141575
141906
  }
141576
141907
  function shouldSkipClaudeTranscriptEvent(event) {
141577
141908
  return event.isMeta === true || event.isSidechain === true || event.type === "attachment" || event.type === "last-prompt" || isEmptyClaudeMessageEvent(event);
@@ -141585,13 +141916,13 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141585
141916
  init() {
141586
141917
  const { tasks } = this.taskEngine.listTasks();
141587
141918
  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 });
141919
+ const session = getPersistedAgentSession(task, "claudecode");
141920
+ if (!session) continue;
141921
+ this.sessions.set(task.id, {
141922
+ sessionId: session.sessionId,
141923
+ cwd: path10.resolve(session.cwd || task.effectiveProjectPath || task.project || process.cwd()),
141924
+ title: task.title
141925
+ });
141595
141926
  }
141596
141927
  if (this.sessions.size > 0) {
141597
141928
  this.logger.info("Recovered sessions from store", { count: this.sessions.size });
@@ -141647,7 +141978,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141647
141978
  this.persistSession(task.id, event.session_id, cwd);
141648
141979
  this.logger.info("Session established", { taskId: task.id, sessionId: event.session_id });
141649
141980
  }
141650
- const outputEvent = timestampClaudeTranscriptEvent(event);
141981
+ const outputEvent = event.type === "system" && event.subtype === "init" && typeof event.session_id === "string" ? { ...timestampClaudeTranscriptEvent(event), agent: "claudecode" } : timestampClaudeTranscriptEvent(event);
141651
141982
  this.emitOutput(task.id, outputEvent);
141652
141983
  this.appendLog(task.id, outputEvent);
141653
141984
  } catch {
@@ -141664,7 +141995,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141664
141995
  proc.stderr.on("data", (chunk) => {
141665
141996
  stderrBuffer += chunk.toString();
141666
141997
  });
141667
- proc.on("close", (code4) => {
141998
+ proc.on("close", async (code4) => {
141668
141999
  if (buffer.trim()) {
141669
142000
  processLine(buffer);
141670
142001
  }
@@ -141673,6 +142004,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141673
142004
  return;
141674
142005
  }
141675
142006
  try {
142007
+ await this.flushLogsAndEmitReady(task.id);
141676
142008
  if (code4 === 0 && !streamError) {
141677
142009
  this.logger.info("Task completed successfully", { taskId: task.id, title: task.title });
141678
142010
  this.taskEngine.reportResult(task.id, { message: `Task "${task.title}" completed` });
@@ -141710,7 +142042,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141710
142042
  this.writeUserMessage(proc.stdin, initialContent);
141711
142043
  proc.unref();
141712
142044
  }
141713
- sendMessage(taskId, content3) {
142045
+ sendMessage(taskId, content3, options) {
141714
142046
  let session = this.sessions.get(taskId);
141715
142047
  if (!session) {
141716
142048
  const recovered = this.recoverSession(taskId);
@@ -141729,7 +142061,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141729
142061
  if (!session) {
141730
142062
  throw new MeshyError("TASK_NOT_FOUND", `No active session for task ${taskId}`, 404);
141731
142063
  }
141732
- const userEvent = buildTaskUserLogEvent(content3);
142064
+ const userEvent = buildTaskUserLogEvent(options?.logContent ?? content3);
141733
142065
  this.appendLog(taskId, userEvent);
141734
142066
  this.emitOutput(taskId, userEvent);
141735
142067
  this.logger.info("Sending follow-up message", { taskId, sessionId: session.sessionId });
@@ -141776,7 +142108,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141776
142108
  });
141777
142109
  this.persistSession(taskId, event.session_id, session.cwd);
141778
142110
  }
141779
- const outputEvent = timestampClaudeTranscriptEvent(event);
142111
+ const outputEvent = event.type === "system" && event.subtype === "init" && typeof event.session_id === "string" ? { ...timestampClaudeTranscriptEvent(event), agent: "claudecode" } : timestampClaudeTranscriptEvent(event);
141780
142112
  this.emitOutput(taskId, outputEvent);
141781
142113
  this.appendLog(taskId, outputEvent);
141782
142114
  } catch {
@@ -141793,7 +142125,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141793
142125
  proc.stderr.on("data", (chunk) => {
141794
142126
  stderrBuffer += chunk.toString();
141795
142127
  });
141796
- proc.on("close", (code4) => {
142128
+ proc.on("close", async (code4) => {
141797
142129
  if (buffer.trim()) {
141798
142130
  processLine(buffer);
141799
142131
  }
@@ -141802,6 +142134,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141802
142134
  return;
141803
142135
  }
141804
142136
  try {
142137
+ await this.flushLogsAndEmitReady(taskId);
141805
142138
  if (code4 === 0 && !streamError) {
141806
142139
  this.logger.info("Follow-up completed", { taskId, sessionId: session.sessionId });
141807
142140
  this.taskEngine.reportResult(taskId, { message: `Task "${session.title}" completed` });
@@ -141854,7 +142187,7 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141854
142187
  try {
141855
142188
  const current = this.taskEngine.getTask(taskId);
141856
142189
  this.taskEngine.updateTask(taskId, {
141857
- payload: buildPersistedSessionPayload(current, sessionId, cwd)
142190
+ payload: buildPersistedSessionPayload(current, sessionId, cwd, "claudecode")
141858
142191
  });
141859
142192
  } catch {
141860
142193
  }
@@ -141873,7 +142206,8 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141873
142206
  const event = JSON.parse(line);
141874
142207
  if (event.type === "system" && event.subtype === "init" && typeof event.session_id === "string") {
141875
142208
  const task = this.taskEngine.getTask(taskId);
141876
- const persistedCwd = typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0;
142209
+ if (!canUseSessionLogEventForAgent(task, "claudecode", event)) continue;
142210
+ const persistedCwd = getPersistedAgentSession(task, "claudecode")?.cwd ?? (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0);
141877
142211
  return {
141878
142212
  sessionId: event.session_id,
141879
142213
  cwd: path10.resolve(
@@ -141889,14 +142223,12 @@ var ClaudeCodeEngine = class extends ExecutionEngine {
141889
142223
  }
141890
142224
  recoverPersistedSession(taskId) {
141891
142225
  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
- }
142226
+ const persisted = getPersistedAgentSession(task, "claudecode");
142227
+ if (!persisted) return null;
141896
142228
  return {
141897
- sessionId,
142229
+ sessionId: persisted.sessionId,
141898
142230
  cwd: path10.resolve(
141899
- (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0) ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()
142231
+ persisted.cwd || task?.effectiveProjectPath || task?.project || process.cwd()
141900
142232
  ),
141901
142233
  title: task?.title ?? taskId
141902
142234
  };
@@ -141909,21 +142241,21 @@ var fs11 = __toESM(require("fs"), 1);
141909
142241
  var path11 = __toESM(require("path"), 1);
141910
142242
  var import_node_child_process5 = require("child_process");
141911
142243
  var CODEX_SESSION_MIRROR_INTERVAL_MS = 500;
141912
- function isRecord6(value) {
142244
+ function isRecord8(value) {
141913
142245
  return typeof value === "object" && value !== null;
141914
142246
  }
141915
142247
  function stableStringify(value) {
141916
142248
  if (Array.isArray(value)) {
141917
142249
  return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
141918
142250
  }
141919
- if (!isRecord6(value)) {
142251
+ if (!isRecord8(value)) {
141920
142252
  return JSON.stringify(value);
141921
142253
  }
141922
142254
  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
142255
  }
141924
142256
  function transcriptEventSignature(event) {
141925
142257
  const message = event.message;
141926
- if ((event.type === "assistant" || event.type === "user") && isRecord6(message) && Array.isArray(message.content)) {
142258
+ if ((event.type === "assistant" || event.type === "user") && isRecord8(message) && Array.isArray(message.content)) {
141927
142259
  return `${event.type}:${stableStringify(message.content)}`;
141928
142260
  }
141929
142261
  return stableStringify(event);
@@ -141962,9 +142294,6 @@ function splitPromptContent(content3) {
141962
142294
  images
141963
142295
  };
141964
142296
  }
141965
- function hasImageContent(content3) {
141966
- return content3.some((part) => part.type === "image");
141967
- }
141968
142297
  function buildCodexModeArgs(mode) {
141969
142298
  switch (mode) {
141970
142299
  case "plan":
@@ -141987,13 +142316,13 @@ var CodexEngine = class extends ExecutionEngine {
141987
142316
  init() {
141988
142317
  const { tasks } = this.taskEngine.listTasks();
141989
142318
  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 });
142319
+ const session = getPersistedAgentSession(task, "codex");
142320
+ if (!session) continue;
142321
+ this.sessions.set(task.id, {
142322
+ sessionId: session.sessionId,
142323
+ cwd: path11.resolve(session.cwd || task.effectiveProjectPath || task.project || process.cwd()),
142324
+ title: task.title
142325
+ });
141997
142326
  }
141998
142327
  if (this.sessions.size > 0) {
141999
142328
  this.logger.info("Recovered Codex sessions from store", { count: this.sessions.size });
@@ -142004,7 +142333,7 @@ var CodexEngine = class extends ExecutionEngine {
142004
142333
  const cwd = task.effectiveProjectPath ?? task.project ?? process.cwd();
142005
142334
  const initialContent = getTaskInitialMessageContent(task);
142006
142335
  const { prompt, imagePaths, promptEcho } = this.prepareAssets(task.id, initialContent);
142007
- this.markCodexPromptEchoEmitted(task.id, [promptEcho, prompt], hasImageContent(initialContent));
142336
+ this.markCodexPromptEchoEmitted(task.id, [promptEcho, prompt]);
142008
142337
  const args = this.buildArgs(imagePaths, { model: config2.model, mode: config2.mode ?? "bypass" });
142009
142338
  this.ensureLogDir();
142010
142339
  this.logger.info("Spawning codex CLI", { taskId: task.id, title: task.title, cwd });
@@ -142017,7 +142346,7 @@ var CodexEngine = class extends ExecutionEngine {
142017
142346
  this.writePrompt(proc.stdin, prompt);
142018
142347
  proc.unref();
142019
142348
  }
142020
- sendMessage(taskId, content3) {
142349
+ sendMessage(taskId, content3, options) {
142021
142350
  let session = this.sessions.get(taskId);
142022
142351
  if (!session) {
142023
142352
  const recovered = this.recoverSession(taskId);
@@ -142039,12 +142368,12 @@ var CodexEngine = class extends ExecutionEngine {
142039
142368
  const current = this.taskEngine.getTask(taskId);
142040
142369
  const model = typeof current?.payload.model === "string" ? current.payload.model : void 0;
142041
142370
  const mode = current?.payload.mode;
142042
- const userEvent = buildTaskUserLogEvent(content3);
142371
+ const userEvent = buildTaskUserLogEvent(options?.logContent ?? content3);
142043
142372
  this.markTranscriptEventEmitted(taskId, userEvent);
142044
142373
  this.appendLog(taskId, userEvent);
142045
142374
  this.emitOutput(taskId, userEvent);
142046
142375
  const { prompt, imagePaths, promptEcho } = this.prepareAssets(taskId, content3);
142047
- this.markCodexPromptEchoEmitted(taskId, [promptEcho, prompt], hasImageContent(content3));
142376
+ this.markCodexPromptEchoEmitted(taskId, [promptEcho, prompt]);
142048
142377
  const args = this.buildArgs(imagePaths, {
142049
142378
  model,
142050
142379
  sessionId: session.sessionId,
@@ -142103,7 +142432,7 @@ var CodexEngine = class extends ExecutionEngine {
142103
142432
  proc.stderr?.on("data", (chunk) => {
142104
142433
  stderrBuffer += chunk.toString();
142105
142434
  });
142106
- proc.on("close", (code4) => {
142435
+ proc.on("close", async (code4) => {
142107
142436
  if (buffer.trim()) {
142108
142437
  processLine(buffer);
142109
142438
  }
@@ -142116,6 +142445,7 @@ var CodexEngine = class extends ExecutionEngine {
142116
142445
  try {
142117
142446
  this.flushNativeSessionMirror(taskId);
142118
142447
  this.flushPendingAmbiguousAssistantTextEvents(taskId);
142448
+ await this.flushLogsAndEmitReady(taskId);
142119
142449
  if (code4 === 0) {
142120
142450
  this.logger.info(isFollowUp ? "Codex follow-up completed" : "Codex task completed successfully", {
142121
142451
  taskId,
@@ -142262,8 +142592,7 @@ var CodexEngine = class extends ExecutionEngine {
142262
142592
  markTranscriptEventEmitted(taskId, event) {
142263
142593
  this.getEmittedTranscriptSignatures(taskId).add(transcriptEventSignature(event));
142264
142594
  }
142265
- markCodexPromptEchoEmitted(taskId, prompts, hasImages) {
142266
- if (!hasImages) return;
142595
+ markCodexPromptEchoEmitted(taskId, prompts) {
142267
142596
  for (const prompt of new Set(prompts.map((candidate) => candidate.trim()).filter(Boolean))) {
142268
142597
  this.markTranscriptEventEmitted(taskId, buildTaskUserLogEvent([{ type: "text", text: prompt }]));
142269
142598
  }
@@ -142302,8 +142631,9 @@ var CodexEngine = class extends ExecutionEngine {
142302
142631
  title
142303
142632
  });
142304
142633
  this.persistSession(taskId, event.thread_id, cwd);
142305
- this.appendLog(taskId, event);
142306
- this.emitOutput(taskId, event);
142634
+ const sessionEvent = { ...event, agent: "codex" };
142635
+ this.appendLog(taskId, sessionEvent);
142636
+ this.emitOutput(taskId, sessionEvent);
142307
142637
  this.startNativeSessionMirror(taskId, event.thread_id);
142308
142638
  this.logger.info("Codex session established", { taskId, sessionId: event.thread_id });
142309
142639
  return [];
@@ -142348,7 +142678,7 @@ var CodexEngine = class extends ExecutionEngine {
142348
142678
  try {
142349
142679
  const current = this.taskEngine.getTask(taskId);
142350
142680
  this.taskEngine.updateTask(taskId, {
142351
- payload: buildPersistedSessionPayload(current, sessionId, cwd)
142681
+ payload: buildPersistedSessionPayload(current, sessionId, cwd, "codex")
142352
142682
  });
142353
142683
  } catch {
142354
142684
  }
@@ -142367,7 +142697,8 @@ var CodexEngine = class extends ExecutionEngine {
142367
142697
  const event = JSON.parse(line);
142368
142698
  if (event.type === "thread.started" && typeof event.thread_id === "string") {
142369
142699
  const task = this.taskEngine.getTask(taskId);
142370
- const persistedCwd = typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0;
142700
+ if (!canUseSessionLogEventForAgent(task, "codex", event)) continue;
142701
+ const persistedCwd = getPersistedAgentSession(task, "codex")?.cwd ?? (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0);
142371
142702
  return {
142372
142703
  sessionId: event.thread_id,
142373
142704
  cwd: path11.resolve(persistedCwd ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()),
@@ -142381,14 +142712,12 @@ var CodexEngine = class extends ExecutionEngine {
142381
142712
  }
142382
142713
  recoverPersistedSession(taskId) {
142383
142714
  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
- }
142715
+ const persisted = getPersistedAgentSession(task, "codex");
142716
+ if (!persisted) return null;
142388
142717
  return {
142389
- sessionId,
142718
+ sessionId: persisted.sessionId,
142390
142719
  cwd: path11.resolve(
142391
- (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0) ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()
142720
+ persisted.cwd || task?.effectiveProjectPath || task?.project || process.cwd()
142392
142721
  ),
142393
142722
  title: task?.title ?? taskId
142394
142723
  };
@@ -142400,7 +142729,7 @@ init_cjs_shims();
142400
142729
  var fs12 = __toESM(require("fs"), 1);
142401
142730
  var path12 = __toESM(require("path"), 1);
142402
142731
  var import_node_child_process6 = require("child_process");
142403
- function isRecord7(value) {
142732
+ function isRecord9(value) {
142404
142733
  return typeof value === "object" && value !== null;
142405
142734
  }
142406
142735
  function contentToPrompt(content3) {
@@ -142438,7 +142767,7 @@ function inferExtension2(mediaType) {
142438
142767
  return normalized.replace(/[^a-z0-9]/g, "") || "bin";
142439
142768
  }
142440
142769
  function extractSessionId(event) {
142441
- const data = isRecord7(event.data) ? event.data : void 0;
142770
+ const data = isRecord9(event.data) ? event.data : void 0;
142442
142771
  const direct = event.session_id ?? event.sessionId ?? event.conversation_id ?? event.conversationId ?? data?.sessionId ?? data?.session_id ?? data?.conversationId ?? data?.conversation_id;
142443
142772
  if (typeof direct === "string" && direct.trim()) return direct.trim();
142444
142773
  const type = typeof event.type === "string" ? event.type.toLowerCase() : "";
@@ -142452,13 +142781,13 @@ var CopilotCliEngine = class extends ExecutionEngine {
142452
142781
  init() {
142453
142782
  const { tasks } = this.taskEngine.listTasks();
142454
142783
  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 });
142784
+ const session = getPersistedAgentSession(task, "copilot");
142785
+ if (!session) continue;
142786
+ this.sessions.set(task.id, {
142787
+ sessionId: session.sessionId,
142788
+ cwd: path12.resolve(session.cwd || task.effectiveProjectPath || task.project || process.cwd()),
142789
+ title: task.title
142790
+ });
142462
142791
  }
142463
142792
  if (this.sessions.size > 0) {
142464
142793
  this.logger.info("Recovered Copilot CLI sessions from store", { count: this.sessions.size });
@@ -142487,7 +142816,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142487
142816
  this.attachProcess(task.id, task.title, cwd, proc, false);
142488
142817
  proc.unref();
142489
142818
  }
142490
- sendMessage(taskId, content3) {
142819
+ sendMessage(taskId, content3, options) {
142491
142820
  let session = this.sessions.get(taskId);
142492
142821
  if (!session) {
142493
142822
  const recovered = this.recoverSession(taskId) ?? this.recoverPersistedSession(taskId);
@@ -142504,7 +142833,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142504
142833
  const mode = current?.payload.mode;
142505
142834
  const systemPrompt = typeof current?.payload.systemPrompt === "string" ? current.payload.systemPrompt : void 0;
142506
142835
  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);
142836
+ const userEvent = buildTaskUserLogEvent(options?.logContent ?? content3);
142508
142837
  this.appendLog(taskId, userEvent);
142509
142838
  this.emitOutput(taskId, userEvent);
142510
142839
  const prompt = buildPrompt(content3, systemPrompt);
@@ -142597,7 +142926,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142597
142926
  proc.stderr?.on("data", (chunk) => {
142598
142927
  stderrBuffer += chunk.toString();
142599
142928
  });
142600
- proc.on("close", (code4) => {
142929
+ proc.on("close", async (code4) => {
142601
142930
  if (buffer.trim()) {
142602
142931
  processLine(buffer);
142603
142932
  }
@@ -142606,6 +142935,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142606
142935
  return;
142607
142936
  }
142608
142937
  try {
142938
+ await this.flushLogsAndEmitReady(taskId);
142609
142939
  if (code4 === 0) {
142610
142940
  this.logger.info(isFollowUp ? "Copilot CLI follow-up completed" : "Copilot CLI task completed successfully", {
142611
142941
  taskId,
@@ -142657,8 +142987,9 @@ var CopilotCliEngine = class extends ExecutionEngine {
142657
142987
  if (sessionId) {
142658
142988
  this.sessions.set(taskId, { sessionId, cwd, title });
142659
142989
  this.persistSession(taskId, sessionId, cwd);
142660
- this.appendLog(taskId, event);
142661
- this.emitOutput(taskId, event);
142990
+ const sessionEvent = { ...event, agent: "copilot" };
142991
+ this.appendLog(taskId, sessionEvent);
142992
+ this.emitOutput(taskId, sessionEvent);
142662
142993
  this.logger.info("Copilot CLI session established", { taskId, sessionId });
142663
142994
  return [];
142664
142995
  }
@@ -142668,7 +142999,7 @@ var CopilotCliEngine = class extends ExecutionEngine {
142668
142999
  try {
142669
143000
  const current = this.taskEngine.getTask(taskId);
142670
143001
  this.taskEngine.updateTask(taskId, {
142671
- payload: buildPersistedSessionPayload(current, sessionId, cwd)
143002
+ payload: buildPersistedSessionPayload(current, sessionId, cwd, "copilot")
142672
143003
  });
142673
143004
  } catch {
142674
143005
  }
@@ -142687,7 +143018,8 @@ var CopilotCliEngine = class extends ExecutionEngine {
142687
143018
  const sessionId = extractSessionId(event);
142688
143019
  if (sessionId) {
142689
143020
  const task = this.taskEngine.getTask(taskId);
142690
- const persistedCwd = typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0;
143021
+ if (!canUseSessionLogEventForAgent(task, "copilot", event)) continue;
143022
+ const persistedCwd = getPersistedAgentSession(task, "copilot")?.cwd ?? (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0);
142691
143023
  return {
142692
143024
  sessionId,
142693
143025
  cwd: path12.resolve(persistedCwd ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()),
@@ -142701,12 +143033,12 @@ var CopilotCliEngine = class extends ExecutionEngine {
142701
143033
  }
142702
143034
  recoverPersistedSession(taskId) {
142703
143035
  const task = this.taskEngine.getTask(taskId);
142704
- const sessionId = typeof task?.payload.sessionId === "string" ? task.payload.sessionId : void 0;
142705
- if (!sessionId) return null;
143036
+ const persisted = getPersistedAgentSession(task, "copilot");
143037
+ if (!persisted) return null;
142706
143038
  return {
142707
- sessionId,
143039
+ sessionId: persisted.sessionId,
142708
143040
  cwd: path12.resolve(
142709
- (typeof task?.payload.sessionCwd === "string" ? task.payload.sessionCwd : void 0) ?? task?.effectiveProjectPath ?? task?.project ?? process.cwd()
143041
+ persisted.cwd || task?.effectiveProjectPath || task?.project || process.cwd()
142710
143042
  ),
142711
143043
  title: task?.title ?? taskId
142712
143044
  };
@@ -144136,22 +144468,33 @@ async function handleLocalNodeMessage(deps, message) {
144136
144468
  const payload = message.payload;
144137
144469
  if (!payload.taskId || !payload.content) return;
144138
144470
  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" });
144471
+ const agent = task ? resolveActiveAgent(task, payload.targetAgent) : void 0;
144472
+ const engine = agent ? deps.engineRegistry.get(agent) : void 0;
144473
+ if (!task || !agent || !engine) return;
144142
144474
  if (!deps.nodeRegistry.isLeader()) {
144143
144475
  const reporter = new RestLeaderTaskReporter(deps.nodeRegistry, log2);
144144
144476
  reporter.reportRunning(payload.taskId, { branch: task.branch });
144145
- registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, task.agent, reporter);
144477
+ registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, agent, reporter);
144146
144478
  }
144147
144479
  try {
144148
- engine.sendMessage(payload.taskId, payload.content);
144149
- log2.info("started keepalive-delivered follow-up execution", { taskId: payload.taskId });
144480
+ startAgentTaskTurn({
144481
+ task,
144482
+ content: payload.content,
144483
+ targetAgent: payload.targetAgent,
144484
+ assignedTo: task.assignedTo,
144485
+ taskEngine: deps.taskEngine,
144486
+ engineRegistry: deps.engineRegistry,
144487
+ eventBus: deps.eventBus,
144488
+ logger: log2
144489
+ });
144490
+ log2.info("started keepalive-delivered follow-up execution", { taskId: payload.taskId, agent });
144150
144491
  } catch (error2) {
144151
- log2.warn("failed to start keepalive-delivered follow-up execution", {
144492
+ const messageText = error2 instanceof Error ? error2.message : String(error2);
144493
+ log2.error("failed to start keepalive-delivered follow-up execution", {
144152
144494
  taskId: payload.taskId,
144153
- error: error2 instanceof Error ? error2.message : String(error2)
144495
+ error: messageText
144154
144496
  });
144497
+ deps.taskEngine.reportFailure(payload.taskId, `Engine follow-up failed before execution started: ${messageText}`);
144155
144498
  }
144156
144499
  return;
144157
144500
  }
@@ -144308,14 +144651,15 @@ var MeshyNode = class {
144308
144651
  };
144309
144652
  this.nodeRegistry.setSelf(this.selfInfo);
144310
144653
  this.nodeRegistry.upsertNode(this.selfInfo);
144311
- this.heartbeat.setNodeMessageHandler((message) => handleLocalNodeMessage({
144654
+ const localNodeMessageHandler = (message) => handleLocalNodeMessage({
144312
144655
  taskEngine: this.taskEngine,
144313
144656
  engineRegistry: this.engineRegistry,
144314
144657
  nodeRegistry: this.nodeRegistry,
144315
144658
  eventBus: this.eventBus,
144316
144659
  logger: this.logger,
144317
144660
  getWorkDir: () => this.getWorkDir()
144318
- }, message));
144661
+ }, message);
144662
+ this.heartbeat.setNodeMessageHandler(localNodeMessageHandler);
144319
144663
  this.nodeMessageClient = new NodeMessageClient({ heartbeat: this.heartbeat, logger: this.logger });
144320
144664
  this.taskDispatcher = new TaskDispatcher(
144321
144665
  this.taskEngine,
@@ -144324,7 +144668,8 @@ var MeshyNode = class {
144324
144668
  this.eventBus,
144325
144669
  { autoAssignInterval: 5e3, dispatchTimeout: 1e4 },
144326
144670
  this.logger,
144327
- this.nodeMessageClient
144671
+ this.nodeMessageClient,
144672
+ localNodeMessageHandler
144328
144673
  );
144329
144674
  this.dataRouter = new DataRouter(this.nodeRegistry, this.election);
144330
144675
  const seeds = this.config.cluster.seeds;
@@ -145366,6 +145711,7 @@ var SystemAgentInfoSchema = external_exports.object({
145366
145711
  detail: external_exports.string().nullable().optional()
145367
145712
  }).optional()
145368
145713
  });
145714
+ var DefaultTaskAgentSchema = external_exports.enum(["codex", "claudecode", "copilot"]);
145369
145715
  var SystemRuntimeUpdateInfoSchema = external_exports.object({
145370
145716
  packageName: external_exports.string(),
145371
145717
  currentVersion: external_exports.string().nullable(),
@@ -145380,6 +145726,7 @@ var NodeSettingsSnapshotSchema = external_exports.object({
145380
145726
  version: external_exports.string(),
145381
145727
  packageName: external_exports.string().optional(),
145382
145728
  uptime: external_exports.number(),
145729
+ defaultTaskAgent: DefaultTaskAgentSchema.optional(),
145383
145730
  auth: SystemAuthInfoSchema.optional(),
145384
145731
  os: SystemOsInfoSchema.optional(),
145385
145732
  runtime: SystemRuntimeInfoSchema.optional(),
@@ -145721,6 +146068,10 @@ var QuickChatsResponse = external_exports.object({
145721
146068
  var ReplaceQuickChatsBody = external_exports.object({
145722
146069
  quickChats: external_exports.array(QuickChatItem).max(100)
145723
146070
  });
146071
+ var DefaultTaskAgentResponse = external_exports.object({
146072
+ defaultTaskAgent: DefaultTaskAgentSchema
146073
+ });
146074
+ var UpdateDefaultTaskAgentBody = DefaultTaskAgentResponse;
145724
146075
 
145725
146076
  // ../../packages/api/src/schemas/tasks.ts
145726
146077
  init_cjs_shims();
@@ -145772,6 +146123,7 @@ var TaskListResponse = external_exports.object({
145772
146123
  queuedMessages: external_exports.array(external_exports.object({
145773
146124
  id: external_exports.string(),
145774
146125
  content: external_exports.array(external_exports.record(external_exports.unknown())).min(1),
146126
+ targetAgent: external_exports.enum(NATIVE_SESSION_AGENT_OPTIONS).optional(),
145775
146127
  createdAt: external_exports.number()
145776
146128
  })).optional(),
145777
146129
  status: external_exports.enum(["pending", "assigned", "running", "completed", "failed", "cancelled", "archived"]),
@@ -145838,12 +146190,15 @@ var TaskMessageContentPart = external_exports.union([
145838
146190
  var TaskMessageContent = external_exports.array(TaskMessageContentPart).min(1);
145839
146191
  var SendMessageBody = external_exports.union([
145840
146192
  external_exports.object({
145841
- content: TaskMessageContent
146193
+ content: TaskMessageContent,
146194
+ targetAgent: external_exports.enum(NATIVE_SESSION_AGENT_OPTIONS).optional()
145842
146195
  }),
145843
146196
  external_exports.object({
145844
- message: external_exports.string().trim().min(1)
146197
+ message: external_exports.string().trim().min(1),
146198
+ targetAgent: external_exports.enum(NATIVE_SESSION_AGENT_OPTIONS).optional()
145845
146199
  }).transform((value) => ({
145846
- content: [{ type: "text", text: value.message }]
146200
+ content: [{ type: "text", text: value.message }],
146201
+ ...value.targetAgent ? { targetAgent: value.targetAgent } : {}
145847
146202
  }))
145848
146203
  ]);
145849
146204
  var AttachNativeSessionBody = external_exports.object({
@@ -145860,7 +146215,8 @@ var WorkerImportTaskBody = external_exports.object({
145860
146215
  logs: external_exports.array(TaskLogEvent).default([])
145861
146216
  });
145862
146217
  var TaskLogsQuery = external_exports.object({
145863
- after: external_exports.coerce.number().int().min(0).optional()
146218
+ after: external_exports.coerce.number().int().min(0).optional(),
146219
+ minTotal: external_exports.coerce.number().int().min(0).optional()
145864
146220
  });
145865
146221
  var CreateTaskShareBody = external_exports.object({
145866
146222
  tenant: external_exports.string().trim().min(1).max(128).default(DEFAULT_TASK_SHARE_TENANT),
@@ -146282,6 +146638,7 @@ function durationMs(startedAt) {
146282
146638
  }
146283
146639
  function createRequestLoggingMiddleware(rootLogger, options = {}) {
146284
146640
  const log2 = rootLogger.child("api/request");
146641
+ const lastLoggedAt = /* @__PURE__ */ new Map();
146285
146642
  return (req, res, next2) => {
146286
146643
  if (options.shouldLog && !options.shouldLog(req)) {
146287
146644
  next2();
@@ -146293,6 +146650,16 @@ function createRequestLoggingMiddleware(rootLogger, options = {}) {
146293
146650
  const logCompletion = (closed) => {
146294
146651
  if (logged) return;
146295
146652
  logged = true;
146653
+ const throttle = options.throttle;
146654
+ if (throttle && throttle.intervalMs > 0 && throttle.shouldThrottle(req, res, closed, path47)) {
146655
+ const now = throttle.now?.() ?? Date.now();
146656
+ const key2 = throttle.key?.(req, path47) ?? `${req.method.toUpperCase()} ${path47}`;
146657
+ const previousLoggedAt = lastLoggedAt.get(key2);
146658
+ if (previousLoggedAt !== void 0 && now - previousLoggedAt < throttle.intervalMs) {
146659
+ return;
146660
+ }
146661
+ lastLoggedAt.set(key2, now);
146662
+ }
146296
146663
  log2.info("request completed", {
146297
146664
  method: req.method,
146298
146665
  path: path47,
@@ -146413,8 +146780,21 @@ var sharedMcpControl;
146413
146780
  var DEFAULT_START_POLL_INTERVAL_MS = 1e4;
146414
146781
  var DEFAULT_START_TIMEOUT_MS = 15 * 6e4;
146415
146782
  var ALWAYS_ONLINE_STARTABLE_DEVBOX_STATUSES = /* @__PURE__ */ new Set(["hibernated", "stopped"]);
146783
+ var DEVBOX_MCP_NODE_VERSION = "22";
146416
146784
  var DEVBOX_MCP_COMMAND = "npx";
146417
- var DEVBOX_MCP_ARGS = ["-y", "-p", "node@22", "-p", "@microsoft/devbox-mcp@latest", "devbox-mcp-server"];
146785
+ var DEVBOX_MCP_ARGS = ["-y", "-p", `node@${DEVBOX_MCP_NODE_VERSION}`, "-p", "@microsoft/devbox-mcp@latest", "devbox-mcp-server"];
146786
+ var DEVBOX_MCP_DISPLAY_COMMAND = `${DEVBOX_MCP_COMMAND} ${DEVBOX_MCP_ARGS.join(" ")}`;
146787
+ function asDevBoxMcpError(err) {
146788
+ if (err instanceof MeshyError) return err;
146789
+ const cause = err instanceof Error ? err.message : String(err);
146790
+ if (!/connection closed/iu.test(cause)) return err;
146791
+ return new MeshyError(
146792
+ "DEVBOX_MCP_UNAVAILABLE",
146793
+ `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.`,
146794
+ 502,
146795
+ { cause, command: DEVBOX_MCP_DISPLAY_COMMAND }
146796
+ );
146797
+ }
146418
146798
  function getDevBoxControl(deps) {
146419
146799
  if (deps.devBoxControl) return deps.devBoxControl;
146420
146800
  sharedMcpControl ??= new McpDevBoxControl(deps.logger);
@@ -146557,7 +146937,7 @@ var McpDevBoxControl = class {
146557
146937
  return await client.callTool({ name: name2, arguments: args });
146558
146938
  } catch (err) {
146559
146939
  this.clientPromise = null;
146560
- throw err;
146940
+ throw asDevBoxMcpError(err);
146561
146941
  }
146562
146942
  }
146563
146943
  async getClient() {
@@ -146595,13 +146975,13 @@ function cleanDevBoxInfo(devbox) {
146595
146975
  }
146596
146976
  function normalizeMcpDevBoxList(value) {
146597
146977
  const normalized = normalizeMcpValue(value);
146598
- const record2 = isRecord8(normalized) ? normalized : {};
146978
+ const record2 = isRecord10(normalized) ? normalized : {};
146599
146979
  const list4 = Array.isArray(normalized) ? normalized : Array.isArray(record2.devboxes) ? record2.devboxes : Array.isArray(record2.value) ? record2.value : [];
146600
146980
  return list4.map((item) => normalizeDevBoxResource(item, void 0)).filter((item) => Boolean(item.name));
146601
146981
  }
146602
146982
  function normalizeDevBoxResource(value, fallback) {
146603
146983
  const record2 = normalizeRecord(value);
146604
- const additional = isRecord8(record2.additionalProperties) ? record2.additionalProperties : {};
146984
+ const additional = isRecord10(record2.additionalProperties) ? record2.additionalProperties : {};
146605
146985
  const fromUri = parseDevBoxUri(readString(record2, "uri"));
146606
146986
  const status = readString(record2, "status") ?? readString(record2, "powerState") ?? readString(record2, "lastStatus") ?? fallback?.lastStatus;
146607
146987
  const name2 = readString(record2, "name") ?? fromUri?.name ?? fallback?.name ?? "";
@@ -146655,7 +147035,7 @@ function parseDevCenterEndpoint(hostname6) {
146655
147035
  }
146656
147036
  function normalizeRecord(value) {
146657
147037
  const normalized = normalizeMcpValue(value);
146658
- return isRecord8(normalized) ? normalized : {};
147038
+ return isRecord10(normalized) ? normalized : {};
146659
147039
  }
146660
147040
  function normalizeMcpValue(value) {
146661
147041
  if (Array.isArray(value)) return value;
@@ -146690,7 +147070,7 @@ function tryParseJson(text10) {
146690
147070
  return null;
146691
147071
  }
146692
147072
  }
146693
- function isRecord8(value) {
147073
+ function isRecord10(value) {
146694
147074
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
146695
147075
  }
146696
147076
  function readString(record2, key2) {
@@ -147982,6 +148362,7 @@ function buildNodeSettingsSnapshot(options) {
147982
148362
  version: runtimeMetadata?.packageVersion ?? "0.1.0",
147983
148363
  packageName: runtimeMetadata?.packageName ?? "meshy",
147984
148364
  uptime,
148365
+ defaultTaskAgent: options.defaultTaskAgent,
147985
148366
  auth: options.auth,
147986
148367
  os: getOsSnapshot(),
147987
148368
  runtime: {
@@ -148014,6 +148395,7 @@ function buildSystemInfo(deps) {
148014
148395
  };
148015
148396
  const settingsSnapshot = buildNodeSettingsSnapshot({
148016
148397
  auth,
148398
+ defaultTaskAgent: deps.storagePath ? resolveNodeDefaultTaskAgent(deps.storagePath) : "codex",
148017
148399
  inspectRuntimeTools: deps.inspectRuntimeTools,
148018
148400
  localDashboardOrigin: deps.localDashboardOrigin,
148019
148401
  runtimeMetadata: deps.runtimeMetadata,
@@ -148027,6 +148409,7 @@ function buildSystemInfo(deps) {
148027
148409
  version: settingsSnapshot.version,
148028
148410
  packageName: settingsSnapshot.packageName,
148029
148411
  uptime: settingsSnapshot.uptime,
148412
+ defaultTaskAgent: settingsSnapshot.defaultTaskAgent,
148030
148413
  transportType: deps.getTransportType?.() ?? "direct",
148031
148414
  dashboardOrigin: deps.dashboardOrigin ?? self2.dashboardOrigin,
148032
148415
  localDashboardOrigin: deps.localDashboardOrigin,
@@ -148117,7 +148500,7 @@ function upgradeRuntimeAgentForDeps(deps, agent) {
148117
148500
  // ../../packages/api/src/node/runtime-restart-request.ts
148118
148501
  init_cjs_shims();
148119
148502
  function parseRuntimeRestartRequest(value) {
148120
- const body3 = isRecord9(value) ? value : {};
148503
+ const body3 = isRecord11(value) ? value : {};
148121
148504
  const startArgs = parseRuntimeRestartStartArgs(body3.startArgs);
148122
148505
  return {
148123
148506
  reason: body3.reason === "update" ? "update" : "restart",
@@ -148126,7 +148509,7 @@ function parseRuntimeRestartRequest(value) {
148126
148509
  }
148127
148510
  function parseRuntimeRestartStartArgs(value) {
148128
148511
  if (value === void 0) return void 0;
148129
- if (!isRecord9(value)) {
148512
+ if (!isRecord11(value)) {
148130
148513
  throw new MeshyError("VALIDATION_ERROR", "Runtime restart startArgs must be an object", 400);
148131
148514
  }
148132
148515
  const startArgs = {};
@@ -148175,7 +148558,7 @@ function parseOptionalString(value, key2) {
148175
148558
  const trimmed = value.trim();
148176
148559
  return trimmed || void 0;
148177
148560
  }
148178
- function isRecord9(value) {
148561
+ function isRecord11(value) {
148179
148562
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
148180
148563
  }
148181
148564
 
@@ -149196,7 +149579,7 @@ function toLegacyWorkerControl2(message) {
149196
149579
  }
149197
149580
 
149198
149581
  // ../../packages/api/src/tasks/task-route-utils.ts
149199
- function isRecord10(value) {
149582
+ function isRecord12(value) {
149200
149583
  return typeof value === "object" && value !== null;
149201
149584
  }
149202
149585
  function restoreTaskState(taskEngine, task) {
@@ -149263,7 +149646,7 @@ function normalizeStructuredValue(value) {
149263
149646
  if (Array.isArray(value)) {
149264
149647
  return value.map((entry) => normalizeStructuredValue(entry));
149265
149648
  }
149266
- if (!isRecord10(value)) {
149649
+ if (!isRecord12(value)) {
149267
149650
  return value;
149268
149651
  }
149269
149652
  return Object.fromEntries(
@@ -149272,20 +149655,20 @@ function normalizeStructuredValue(value) {
149272
149655
  }
149273
149656
  function getTranscriptEventSignature(event) {
149274
149657
  if (event.type === "user") {
149275
- const signature = getTaskUserMessageSignature(isRecord10(event.message) ? event.message.content : event.message) ?? getTaskUserMessageSignature(event.content);
149658
+ const signature = getTaskUserMessageSignature(isRecord12(event.message) ? event.message.content : event.message) ?? getTaskUserMessageSignature(event.content);
149276
149659
  return signature ? `user:${signature}` : null;
149277
149660
  }
149278
149661
  if (event.type === "assistant") {
149279
149662
  if (typeof event.message === "string") {
149280
149663
  return `assistant:${JSON.stringify(event.message)}`;
149281
149664
  }
149282
- if (isRecord10(event.message) && "content" in event.message) {
149665
+ if (isRecord12(event.message) && "content" in event.message) {
149283
149666
  return `assistant:${JSON.stringify(normalizeStructuredValue(event.message.content))}`;
149284
149667
  }
149285
149668
  if (Array.isArray(event.content)) {
149286
149669
  return `assistant:${JSON.stringify(normalizeStructuredValue(event.content))}`;
149287
149670
  }
149288
- if (isRecord10(event.message)) {
149671
+ if (isRecord12(event.message)) {
149289
149672
  return `assistant:${JSON.stringify(normalizeStructuredValue(event.message))}`;
149290
149673
  }
149291
149674
  }
@@ -177128,22 +177511,32 @@ async function handleTaskMessage(deps, message) {
177128
177511
  if (!task) {
177129
177512
  throw new MeshyError("TASK_NOT_FOUND", `Task ${payload.taskId} not found`, 404);
177130
177513
  }
177131
- if (!supportsFollowUpAgent(task.agent)) {
177132
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
177514
+ const targetAgent = typeof payload.targetAgent === "string" ? payload.targetAgent : void 0;
177515
+ const agent = resolveActiveAgent(task, targetAgent);
177516
+ if (!supportsFollowUpAgent(agent)) {
177517
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
177133
177518
  }
177134
- const engine = deps.engineRegistry.get(task.agent);
177519
+ const engine = deps.engineRegistry.get(agent);
177135
177520
  if (!engine) {
177136
- throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${task.agent}`, 400);
177521
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${agent}`, 400);
177137
177522
  }
177138
- deps.taskEngine.updateTask(payload.taskId, { status: "running" });
177139
177523
  if (!deps.nodeRegistry.isLeader()) {
177140
177524
  const reporter = new RestLeaderTaskReporter(deps.nodeRegistry, log2);
177141
177525
  reporter.reportRunning(payload.taskId, { branch: task.branch });
177142
- registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, task.agent, reporter);
177526
+ registerLeaderForwarding(deps.eventBus, deps.nodeRegistry, log2, payload.taskId, agent, reporter);
177143
177527
  }
177144
177528
  try {
177145
- engine.sendMessage(payload.taskId, payload.content);
177146
- log2.info("started follow-up execution", { taskId: payload.taskId });
177529
+ startAgentTaskTurn({
177530
+ task,
177531
+ content: payload.content,
177532
+ targetAgent,
177533
+ assignedTo: task.assignedTo,
177534
+ taskEngine: deps.taskEngine,
177535
+ engineRegistry: deps.engineRegistry,
177536
+ eventBus: deps.eventBus,
177537
+ logger: log2
177538
+ });
177539
+ log2.info("started follow-up execution", { taskId: payload.taskId, agent });
177147
177540
  } catch (error2) {
177148
177541
  const messageText = error2 instanceof Error ? error2.message : String(error2);
177149
177542
  log2.error("engine.sendMessage threw synchronously", { taskId: payload.taskId, error: messageText });
@@ -177394,6 +177787,7 @@ async function sendTaskLogsResponse(req, res, taskId) {
177394
177787
  const log2 = rootLogger.child("tasks/logs");
177395
177788
  const query = TaskLogsQuery.parse(req.query);
177396
177789
  const after = query.after ?? 0;
177790
+ const minTotal = query.minTotal ?? 0;
177397
177791
  const proxyRequest = getTaskLogsProxyRequestMetadata(req);
177398
177792
  let task = taskEngine.getTask(taskId);
177399
177793
  if (!task && proxyRequest.task?.id === taskId) {
@@ -177439,7 +177833,8 @@ async function sendTaskLogsResponse(req, res, taskId) {
177439
177833
  if (isLeader) {
177440
177834
  const local2 = readLocalTaskLogsWithNativeHistory(engineRegistry, task, taskId, after, task?.agent ?? "claudecode");
177441
177835
  leaderLocal = local2;
177442
- if ((local2.total > 0 || !needsProxy) && !shouldAskWorkerForNativeRepair) {
177836
+ const leaderLocalSatisfiesCheckpoint = minTotal === 0 || local2.total >= minTotal;
177837
+ if ((local2.total > 0 || !needsProxy) && leaderLocalSatisfiesCheckpoint && !shouldAskWorkerForNativeRepair) {
177443
177838
  log2.debug("serving leader-local task logs", {
177444
177839
  taskId,
177445
177840
  assignedTo: task?.assignedTo,
@@ -177450,7 +177845,16 @@ async function sendTaskLogsResponse(req, res, taskId) {
177450
177845
  res.json(local2);
177451
177846
  return;
177452
177847
  }
177453
- if (local2.total > 0 && shouldAskWorkerForNativeRepair) {
177848
+ if (local2.total > 0 && !leaderLocalSatisfiesCheckpoint) {
177849
+ log2.debug("leader-local task logs behind requested checkpoint, falling back to worker proxy", {
177850
+ taskId,
177851
+ assignedTo: task?.assignedTo,
177852
+ selfId,
177853
+ total: local2.total,
177854
+ minTotal,
177855
+ after
177856
+ });
177857
+ } else if (local2.total > 0 && shouldAskWorkerForNativeRepair) {
177454
177858
  log2.debug("leader-local native session logs may be partial, checking worker repair path", {
177455
177859
  taskId,
177456
177860
  assignedTo: task?.assignedTo,
@@ -177493,7 +177897,9 @@ async function sendTaskLogsResponse(req, res, taskId) {
177493
177897
  return;
177494
177898
  }
177495
177899
  }
177496
- const proxyPath = `/api/tasks/${taskId}/logs?after=${after}`;
177900
+ const proxyParams = new URLSearchParams({ after: String(after) });
177901
+ if (minTotal > 0) proxyParams.set("minTotal", String(minTotal));
177902
+ const proxyPath = `/api/tasks/${taskId}/logs?${proxyParams.toString()}`;
177497
177903
  try {
177498
177904
  const { endpoint, response: proxyRes } = await fetchNodeWithFallback(
177499
177905
  node2,
@@ -177548,17 +177954,24 @@ init_cjs_shims();
177548
177954
 
177549
177955
  // ../../packages/api/src/tasks/task-follow-up.ts
177550
177956
  init_cjs_shims();
177551
- function startLocalTaskFollowUp(deps, task, content3, assignedTo, metadata) {
177552
- const engine = deps.engineRegistry.get(task.agent);
177957
+ function startLocalTaskFollowUp(deps, task, content3, assignedTo, targetAgent, metadata) {
177958
+ const agent = resolveActiveAgent(task, targetAgent);
177959
+ const engine = deps.engineRegistry.get(agent);
177553
177960
  if (!engine) {
177554
- throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${task.agent}`, 400);
177961
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${agent}`, 400);
177555
177962
  }
177556
- deps.taskEngine.updateTask(task.id, {
177557
- status: "running",
177558
- assignedTo
177559
- });
177560
177963
  try {
177561
- engine.sendMessage(task.id, content3);
177964
+ startAgentTaskTurn({
177965
+ task,
177966
+ content: content3,
177967
+ targetAgent,
177968
+ assignedTo,
177969
+ taskEngine: deps.taskEngine,
177970
+ engineRegistry: deps.engineRegistry,
177971
+ eventBus: deps.eventBus,
177972
+ logger: deps.logger,
177973
+ metadata
177974
+ });
177562
177975
  } catch (err) {
177563
177976
  restoreTaskState(deps.taskEngine, task);
177564
177977
  deps.logger.warn("failed to start local follow-up execution", {
@@ -177581,9 +177994,10 @@ function restoreQueuedMessage(taskEngine, taskId, message) {
177581
177994
  queuedMessages: [message, ...current.queuedMessages ?? []]
177582
177995
  });
177583
177996
  }
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);
177997
+ async function sendTaskFollowUpMessage(deps, task, content3, targetAgent, metadata) {
177998
+ const agent = resolveActiveAgent(task, targetAgent);
177999
+ if (!supportsFollowUpAgent(agent)) {
178000
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
177587
178001
  }
177588
178002
  if (task.assignedTo && task.assignedTo !== deps.nodeRegistry.getSelf()?.id) {
177589
178003
  const node2 = deps.nodeRegistry.getNode(task.assignedTo);
@@ -177602,7 +178016,7 @@ async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177602
178016
  409
177603
178017
  );
177604
178018
  }
177605
- deps.taskEngine.updateTask(task.id, { status: "running" });
178019
+ deps.taskEngine.updateTask(task.id, targetAgent ? { status: "running", payload: { ...task.payload, activeAgent: agent } } : { status: "running" });
177606
178020
  log2.info("proxying follow-up message to worker", {
177607
178021
  taskId: task.id,
177608
178022
  assignedTo: task.assignedTo,
@@ -177612,7 +178026,8 @@ async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177612
178026
  const client = new NodeMessageClient({ heartbeat: deps.heartbeat, logger: deps.logger });
177613
178027
  const delivery = await client.send(node2, createNodeMessage("task.message", {
177614
178028
  taskId: task.id,
177615
- content: content3
178029
+ content: content3,
178030
+ ...targetAgent ? { targetAgent } : {}
177616
178031
  }));
177617
178032
  return delivery.queued ? { queued: true } : {};
177618
178033
  } catch (err) {
@@ -177630,31 +178045,34 @@ async function sendTaskFollowUpMessage(deps, task, content3, metadata) {
177630
178045
  startLocalTaskFollowUp({
177631
178046
  taskEngine: deps.taskEngine,
177632
178047
  engineRegistry: deps.engineRegistry,
178048
+ eventBus: deps.eventBus,
177633
178049
  logger: deps.logger.child("tasks/message")
177634
- }, task, content3, task.assignedTo, metadata);
178050
+ }, task, content3, task.assignedTo, targetAgent, metadata);
177635
178051
  return {};
177636
178052
  }
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);
178053
+ function queueRunningTaskMessage(deps, task, content3, targetAgent) {
178054
+ const agent = resolveActiveAgent(task, targetAgent);
178055
+ if (!supportsFollowUpAgent(agent)) {
178056
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
177640
178057
  }
177641
- const queued = deps.taskEngine.enqueueTaskMessage(task.id, content3);
178058
+ const queued = targetAgent ? deps.taskEngine.enqueueTaskMessage(task.id, content3, targetAgent) : deps.taskEngine.enqueueTaskMessage(task.id, content3);
177642
178059
  deps.logger.child("tasks/message").info("queued follow-up message for running task", {
177643
178060
  taskId: task.id,
178061
+ targetAgent,
177644
178062
  queuedCount: queued.queuedMessages?.length ?? 0
177645
178063
  });
177646
178064
  return queued;
177647
178065
  }
177648
178066
  var MESSAGEABLE_STATUSES = /* @__PURE__ */ new Set(["running", "completed", "cancelled", "failed"]);
177649
- async function applyTaskMessage(deps, task, content3) {
178067
+ async function applyTaskMessage(deps, task, content3, targetAgent) {
177650
178068
  if (!MESSAGEABLE_STATUSES.has(task.status)) {
177651
178069
  throw new MeshyError("VALIDATION_ERROR", `Cannot send message to task in ${task.status} status`, 400);
177652
178070
  }
177653
178071
  if (task.status === "running") {
177654
- const queued = queueRunningTaskMessage(deps, task, content3);
178072
+ const queued = queueRunningTaskMessage(deps, task, content3, targetAgent);
177655
178073
  return { ok: true, queued: true, queuedMessages: queued.queuedMessages ?? [] };
177656
178074
  }
177657
- const result = await sendTaskFollowUpMessage(deps, task, content3);
178075
+ const result = await sendTaskFollowUpMessage(deps, task, content3, targetAgent);
177658
178076
  return result.queued ? { ok: true, queued: true } : { ok: true };
177659
178077
  }
177660
178078
  function cancelQueuedTaskMessage(deps, taskId, messageId) {
@@ -177687,7 +178105,7 @@ async function drainNextQueuedTaskMessage(deps, taskId) {
177687
178105
  if (!dequeued) return false;
177688
178106
  drainingTaskIds.add(taskId);
177689
178107
  try {
177690
- await sendTaskFollowUpMessage(deps, dequeued.task, dequeued.message.content, {
178108
+ await sendTaskFollowUpMessage(deps, dequeued.task, dequeued.message.content, dequeued.message.targetAgent, {
177691
178109
  queuedMessageId: dequeued.message.id
177692
178110
  });
177693
178111
  deps.logger.child("tasks/message").info("started queued follow-up message", {
@@ -178671,7 +179089,7 @@ function createTaskRoutes() {
178671
179089
  res.json({ ok: true, queuedMessages: task.queuedMessages ?? [] });
178672
179090
  }));
178673
179091
  router.post("/:id/message", asyncHandler8(async (req, res) => {
178674
- const { taskEngine, engineRegistry, nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
179092
+ const { taskEngine, engineRegistry, nodeRegistry, heartbeat, eventBus, logger: rootLogger } = req.app.locals.deps;
178675
179093
  const body3 = SendMessageBody.parse(req.body);
178676
179094
  const taskId = req.params.id;
178677
179095
  const task = taskEngine.getTask(taskId);
@@ -178679,9 +179097,10 @@ function createTaskRoutes() {
178679
179097
  throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
178680
179098
  }
178681
179099
  res.json(await applyTaskMessage(
178682
- { taskEngine, engineRegistry, nodeRegistry, heartbeat, logger: rootLogger },
179100
+ { taskEngine, engineRegistry, nodeRegistry, heartbeat, eventBus, logger: rootLogger },
178683
179101
  task,
178684
- body3.content
179102
+ body3.content,
179103
+ body3.targetAgent
178685
179104
  ));
178686
179105
  }));
178687
179106
  router.use(createTaskOutputRoutes());
@@ -178855,6 +179274,10 @@ var WorkerMessageBody = external_exports.object({
178855
179274
  var WorkerTaskBody = external_exports.object({
178856
179275
  taskId: external_exports.string().min(1)
178857
179276
  });
179277
+ var WorkerLogsReadyBody = external_exports.object({
179278
+ taskId: external_exports.string().min(1),
179279
+ total: external_exports.number().int().min(0)
179280
+ });
178858
179281
  function asyncHandler10(fn) {
178859
179282
  return (req, res, next2) => fn(req, res, next2).catch(next2);
178860
179283
  }
@@ -178909,15 +179332,15 @@ function createWorkerRoutes() {
178909
179332
  if (!task) {
178910
179333
  throw new MeshyError("TASK_NOT_FOUND", `Task ${body3.taskId} not found`, 404);
178911
179334
  }
178912
- if (!supportsFollowUpAgent(task.agent)) {
178913
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
179335
+ const agent = resolveActiveAgent(task, body3.targetAgent);
179336
+ if (!supportsFollowUpAgent(agent)) {
179337
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${agent} does not support chat messages`, 400);
178914
179338
  }
178915
- const engine = engineRegistry.get(task.agent);
179339
+ const engine = engineRegistry.get(agent);
178916
179340
  if (!engine) {
178917
- res.status(400).json({ error: `Engine not registered for agent: ${task.agent}` });
179341
+ res.status(400).json({ error: `Engine not registered for agent: ${agent}` });
178918
179342
  return;
178919
179343
  }
178920
- taskEngine.updateTask(body3.taskId, { status: "running" });
178921
179344
  if (!nodeRegistry.isLeader()) {
178922
179345
  new RestLeaderTaskReporter(nodeRegistry, log2).reportRunning(body3.taskId, { branch: task.branch });
178923
179346
  }
@@ -178928,13 +179351,22 @@ function createWorkerRoutes() {
178928
179351
  nodeRegistry,
178929
179352
  log2,
178930
179353
  body3.taskId,
178931
- task.agent,
179354
+ agent,
178932
179355
  new RestLeaderTaskReporter(nodeRegistry, log2)
178933
179356
  );
178934
179357
  }
178935
179358
  try {
178936
- engine.sendMessage(body3.taskId, body3.content);
178937
- log2.info("started follow-up execution", { taskId: body3.taskId });
179359
+ startAgentTaskTurn({
179360
+ task,
179361
+ content: body3.content,
179362
+ targetAgent: body3.targetAgent,
179363
+ assignedTo: task.assignedTo,
179364
+ taskEngine,
179365
+ engineRegistry,
179366
+ eventBus,
179367
+ logger: log2
179368
+ });
179369
+ log2.info("started follow-up execution", { taskId: body3.taskId, agent });
178938
179370
  } catch (err) {
178939
179371
  const errorMessage = err instanceof Error ? err.message : String(err);
178940
179372
  log2.error("engine.sendMessage threw synchronously", { taskId: body3.taskId, error: errorMessage });
@@ -178986,6 +179418,12 @@ function createWorkerRoutes() {
178986
179418
  }
178987
179419
  res.json({ ok: true });
178988
179420
  }));
179421
+ router.post("/logs-ready", asyncHandler10(async (req, res) => {
179422
+ const { eventBus } = req.app.locals.deps;
179423
+ const body3 = WorkerLogsReadyBody.parse(req.body);
179424
+ eventBus.emit("task.logs.ready", { taskId: body3.taskId, total: body3.total });
179425
+ res.json({ ok: true });
179426
+ }));
178989
179427
  router.post("/heartbeat", asyncHandler10(async (req, res) => {
178990
179428
  const { heartbeat, logger: rootLogger } = req.app.locals.deps;
178991
179429
  const log2 = rootLogger.child("worker/heartbeat");
@@ -179156,6 +179594,10 @@ function getQuickChatStore(deps) {
179156
179594
  if (deps.storagePath) return createQuickChatStore(deps.storagePath);
179157
179595
  throw new MeshyError("VALIDATION_ERROR", "Quick chats are not available for this node", 501);
179158
179596
  }
179597
+ function getStoragePath2(deps) {
179598
+ if (deps.storagePath) return deps.storagePath;
179599
+ throw new MeshyError("VALIDATION_ERROR", "Node metadata is not available for this node", 501);
179600
+ }
179159
179601
  function validateQuickChatAliases(quickChats) {
179160
179602
  const seen = /* @__PURE__ */ new Set();
179161
179603
  for (const quickChat of quickChats) {
@@ -179248,6 +179690,21 @@ function createSystemRoutes() {
179248
179690
  const metrics2 = taskEngine.getMetrics();
179249
179691
  res.json(metrics2);
179250
179692
  }));
179693
+ router.get("/default-task-agent", asyncHandler11(async (req, res) => {
179694
+ const storagePath = getStoragePath2(req.app.locals.deps);
179695
+ res.json({ defaultTaskAgent: resolveNodeDefaultTaskAgent(storagePath) });
179696
+ }));
179697
+ router.put("/default-task-agent", asyncHandler11(async (req, res) => {
179698
+ const body3 = UpdateDefaultTaskAgentBody.parse(req.body);
179699
+ const deps = req.app.locals.deps;
179700
+ const storagePath = getStoragePath2(deps);
179701
+ persistNodeDefaultTaskAgent(storagePath, body3.defaultTaskAgent);
179702
+ const settingsSnapshot = deps.refreshSettingsSnapshot?.();
179703
+ if (settingsSnapshot) {
179704
+ deps.nodeRegistry.updateSettingsSnapshot(deps.nodeRegistry.getSelf().id, settingsSnapshot);
179705
+ }
179706
+ res.json({ defaultTaskAgent: resolveNodeDefaultTaskAgent(storagePath) });
179707
+ }));
179251
179708
  router.get("/quick-chats", asyncHandler11(async (req, res) => {
179252
179709
  const store = getQuickChatStore(req.app.locals.deps);
179253
179710
  res.json({ quickChats: store.list() });
@@ -179284,6 +179741,7 @@ var ALL_EVENT_NAMES = [
179284
179741
  "task.updated",
179285
179742
  "task.deleted",
179286
179743
  "task.output",
179744
+ "task.logs.ready",
179287
179745
  "election.started",
179288
179746
  "election.complete",
179289
179747
  "cluster.state",
@@ -179590,12 +180048,22 @@ var REQUEST_TELEMETRY_SUPPRESSED_ROUTES = /* @__PURE__ */ new Set([
179590
180048
  "POST /api/cluster-control/heartbeat",
179591
180049
  "POST /api/cluster-control/keepalive"
179592
180050
  ]);
180051
+ var REQUEST_LOGGING_THROTTLED_ROUTES = /* @__PURE__ */ new Set([
180052
+ "POST /api/worker/keepalive",
180053
+ "POST /api/cluster-control/keepalive"
180054
+ ]);
180055
+ var REQUEST_LOGGING_THROTTLE_MS = 6e4;
179593
180056
  function normalizeRequestTelemetryPath(pathname) {
179594
180057
  return pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
179595
180058
  }
180059
+ function requestRouteKey(method, pathname) {
180060
+ return `${method.toUpperCase()} ${normalizeRequestTelemetryPath(pathname)}`;
180061
+ }
179596
180062
  function isSuppressedRequestTelemetryRoute(req) {
179597
- const key2 = `${req.method.toUpperCase()} ${normalizeRequestTelemetryPath(req.path)}`;
179598
- return REQUEST_TELEMETRY_SUPPRESSED_ROUTES.has(key2);
180063
+ return REQUEST_TELEMETRY_SUPPRESSED_ROUTES.has(requestRouteKey(req.method, req.path));
180064
+ }
180065
+ function isThrottledRequestLoggingRoute(method, pathname) {
180066
+ return REQUEST_LOGGING_THROTTLED_ROUTES.has(requestRouteKey(method, pathname));
179599
180067
  }
179600
180068
  function usesLargeJsonBodyLimit(pathname) {
179601
180069
  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 +180235,11 @@ function createServer2(deps) {
179767
180235
  const canServeDashboard = (req) => !deps.config.validateBearerToken || isTrustedDashboardRequest(req, authConfig);
179768
180236
  const canServePreview = (req) => canServeDashboard(req) || hasAuthorizationHeader(req);
179769
180237
  app.use(createRequestLoggingMiddleware(deps.logger, {
179770
- shouldLog: (req) => isApiRequest(req) || isPreviewRequest(req)
180238
+ shouldLog: (req) => isApiRequest(req) || isPreviewRequest(req),
180239
+ throttle: {
180240
+ intervalMs: REQUEST_LOGGING_THROTTLE_MS,
180241
+ shouldThrottle: (req, res, closed, path47) => !closed && res.statusCode < 400 && isThrottledRequestLoggingRoute(req.method, path47)
180242
+ }
179771
180243
  }));
179772
180244
  app.use(createRequestTelemetryMiddleware(deps.telemetry ?? createNoopTelemetry(), {
179773
180245
  shouldTrack: (req) => !isSuppressedRequestTelemetryRoute(req) && (isApiRequest(req) || isPreviewRequest(req))
@@ -181756,6 +182228,7 @@ function createSettingsSnapshotProvider(options) {
181756
182228
  function buildSnapshot() {
181757
182229
  cachedSnapshot = buildNodeSettingsSnapshot({
181758
182230
  auth: options.authMetadata,
182231
+ defaultTaskAgent: resolveNodeDefaultTaskAgent(options.storagePath),
181759
182232
  localDashboardOrigin: options.localDashboardOrigin,
181760
182233
  runtimeMetadata: options.runtimeMetadata,
181761
182234
  runtimeUpdate: createRuntimeUpdateSnapshot({