@adhdev/daemon-core 0.9.82-rc.180 → 0.9.82-rc.182

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/dist/index.mjs CHANGED
@@ -1689,6 +1689,21 @@ var init_async_batch_writer = __esm({
1689
1689
  });
1690
1690
 
1691
1691
  // src/logging/logger.ts
1692
+ var logger_exports = {};
1693
+ __export(logger_exports, {
1694
+ LOG: () => LOG,
1695
+ LOG_DIR_PATH: () => LOG_DIR_PATH,
1696
+ LOG_PATH: () => LOG_PATH,
1697
+ daemonLog: () => daemonLog,
1698
+ getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
1699
+ getDaemonLogDir: () => getDaemonLogDir,
1700
+ getLogBufferSize: () => getLogBufferSize,
1701
+ getLogLevel: () => getLogLevel,
1702
+ getLogPath: () => getLogPath,
1703
+ getRecentLogs: () => getRecentLogs,
1704
+ installGlobalInterceptor: () => installGlobalInterceptor,
1705
+ setLogLevel: () => setLogLevel
1706
+ });
1692
1707
  import * as fs4 from "fs";
1693
1708
  import * as path9 from "path";
1694
1709
  import * as os3 from "os";
@@ -1763,6 +1778,9 @@ function getRecentLogs(count = 50, minLevel = "info") {
1763
1778
  const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
1764
1779
  return filtered.slice(-count);
1765
1780
  }
1781
+ function getLogBufferSize() {
1782
+ return ringBuffer.length;
1783
+ }
1766
1784
  function ts() {
1767
1785
  return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
1768
1786
  }
@@ -1842,7 +1860,10 @@ function installGlobalInterceptor() {
1842
1860
  writeToFile(`Log file: ${currentLogFile}`);
1843
1861
  writeToFile(`Log level: ${currentLevel}`);
1844
1862
  }
1845
- var LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
1863
+ function getLogPath() {
1864
+ return currentLogFile;
1865
+ }
1866
+ var LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
1846
1867
  var init_logger = __esm({
1847
1868
  "src/logging/logger.ts"() {
1848
1869
  "use strict";
@@ -1900,6 +1921,7 @@ var init_logger = __esm({
1900
1921
  };
1901
1922
  interceptorInstalled = false;
1902
1923
  LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
1924
+ LOG_DIR_PATH = LOG_DIR;
1903
1925
  }
1904
1926
  });
1905
1927
 
@@ -5337,18 +5359,6 @@ function setupMeshEventForwarding(components) {
5337
5359
  } catch {
5338
5360
  }
5339
5361
  if (!meshIdFromDirectDispatch) return;
5340
- } else {
5341
- const workspaceMesh = getCachedMeshByWorkspace(workspace);
5342
- const workspaceMeshId = readNonEmptyString2(workspaceMesh?.id);
5343
- if (workspaceMeshId) {
5344
- try {
5345
- const activeDispatches = getActiveDirectDispatches(workspaceMeshId);
5346
- if (activeDispatches.some((d) => d.sessionId === instanceId) || hasUnterminalDirectDispatchLedgerEntry(workspaceMeshId, instanceId)) {
5347
- meshIdFromDirectDispatch = workspaceMeshId;
5348
- }
5349
- } catch {
5350
- }
5351
- }
5352
5362
  }
5353
5363
  const meshIdFromRuntime = readNonEmptyString2(settings.meshNodeFor) || meshIdFromDirectDispatch;
5354
5364
  const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
@@ -12949,6 +12959,7 @@ function buildClaudeInteractiveToolResult(response) {
12949
12959
  }
12950
12960
  });
12951
12961
  }
12962
+ var CLAUDE_TUI_OPTION_PATTERN = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
12952
12963
  function claudeTuiQuestionHeaders(screenText) {
12953
12964
  const navLine = screenText.split(/\r?\n/).find((line) => line.includes("\u2714 Submit") && /[☐☒]/.test(line));
12954
12965
  if (!navLine) return [];
@@ -12960,6 +12971,88 @@ function claudeTuiQuestionHeaders(screenText) {
12960
12971
  }
12961
12972
  return headers;
12962
12973
  }
12974
+ function isClaudeTuiSelectFooter(text) {
12975
+ return /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
12976
+ }
12977
+ function readClaudeHeaderLine(lines, beforeIndex) {
12978
+ for (let i = beforeIndex; i >= 0; i -= 1) {
12979
+ const candidate = lines[i].trim();
12980
+ if (!candidate) continue;
12981
+ const match = candidate.match(/^[☐☒]\s+(.+?)\s*$/);
12982
+ if (match?.[1]) return readString(match[1]);
12983
+ if (/^─+$/.test(candidate)) break;
12984
+ }
12985
+ return void 0;
12986
+ }
12987
+ function readClaudeOptionDescription(lines, optionLineIndex) {
12988
+ const nextLine = lines[optionLineIndex + 1];
12989
+ const next = nextLine?.trim();
12990
+ if (!next || CLAUDE_TUI_OPTION_PATTERN.test(nextLine) || /^─+$/.test(next) || /^Enter to select\b/i.test(next) || /^[☐☒]\s+/.test(next)) {
12991
+ return void 0;
12992
+ }
12993
+ return next;
12994
+ }
12995
+ function parseClaudeHeaderlessInteractiveTuiQuestion(page, index) {
12996
+ if (!isClaudeTuiSelectFooter(page.screenText)) return null;
12997
+ if (!/Type something\.?|Chat about this/i.test(page.screenText)) return null;
12998
+ const lines = page.screenText.split(/\r?\n/);
12999
+ let footerIndex = -1;
13000
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
13001
+ if (/Enter to select/i.test(lines[i])) {
13002
+ footerIndex = i;
13003
+ break;
13004
+ }
13005
+ }
13006
+ if (footerIndex < 0) return null;
13007
+ let optionBlockEnd = footerIndex - 1;
13008
+ for (let i = footerIndex - 1; i >= 0; i -= 1) {
13009
+ if (/^─+$/.test(lines[i].trim())) {
13010
+ optionBlockEnd = i - 1;
13011
+ break;
13012
+ }
13013
+ }
13014
+ const optionLineIndexes = [];
13015
+ for (let i = optionBlockEnd; i >= 0; i -= 1) {
13016
+ const line = lines[i];
13017
+ if (CLAUDE_TUI_OPTION_PATTERN.test(line)) {
13018
+ optionLineIndexes.push(i);
13019
+ continue;
13020
+ }
13021
+ if (optionLineIndexes.length > 0 && (!line.trim() || /^─+$/.test(line.trim()))) break;
13022
+ }
13023
+ optionLineIndexes.reverse();
13024
+ if (optionLineIndexes.length === 0) return null;
13025
+ const firstOptionIndex = optionLineIndexes[0];
13026
+ let question = "";
13027
+ for (let i = firstOptionIndex - 1; i >= 0; i -= 1) {
13028
+ const candidate = lines[i].trim();
13029
+ if (!candidate || /^─+$/.test(candidate) || /^[☐☒]\s+/.test(candidate)) continue;
13030
+ question = candidate;
13031
+ break;
13032
+ }
13033
+ if (!question) return null;
13034
+ const options = [];
13035
+ let allowFreeform = false;
13036
+ for (const optionLineIndex of optionLineIndexes) {
13037
+ const match = lines[optionLineIndex].match(CLAUDE_TUI_OPTION_PATTERN);
13038
+ if (!match) continue;
13039
+ const label = match[2].trim();
13040
+ if (/^Chat about this$/i.test(label)) continue;
13041
+ if (/^Type something\.?$/i.test(label)) allowFreeform = true;
13042
+ const description = readClaudeOptionDescription(lines, optionLineIndex);
13043
+ options.push({ label, ...description ? { description } : {} });
13044
+ }
13045
+ if (options.length === 0) return null;
13046
+ const header = readString(page.header) || readClaudeHeaderLine(lines, firstOptionIndex - 1);
13047
+ return {
13048
+ questionId: `q${index + 1}`,
13049
+ question,
13050
+ ...header ? { header } : {},
13051
+ multiSelect: /Space to select|toggle selections/i.test(page.screenText),
13052
+ options,
13053
+ ...allowFreeform ? { allowFreeform: true } : {}
13054
+ };
13055
+ }
12963
13056
  function parseClaudeInteractiveTuiQuestion(page, index) {
12964
13057
  const lines = page.screenText.split(/\r?\n/);
12965
13058
  let navIndex = -1;
@@ -12969,7 +13062,8 @@ function parseClaudeInteractiveTuiQuestion(page, index) {
12969
13062
  break;
12970
13063
  }
12971
13064
  }
12972
- if (navIndex < 0 || !page.screenText.includes("Enter to select")) return null;
13065
+ if (navIndex < 0) return parseClaudeHeaderlessInteractiveTuiQuestion(page, index);
13066
+ if (!page.screenText.includes("Enter to select")) return null;
12973
13067
  let question = "";
12974
13068
  let questionLineIndex = -1;
12975
13069
  for (let i = navIndex + 1; i < lines.length; i += 1) {
@@ -12983,9 +13077,8 @@ function parseClaudeInteractiveTuiQuestion(page, index) {
12983
13077
  if (!question) return null;
12984
13078
  const options = [];
12985
13079
  let allowFreeform = false;
12986
- const optionPattern = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
12987
13080
  for (let i = questionLineIndex + 1; i < lines.length; i += 1) {
12988
- const match = lines[i].match(optionPattern);
13081
+ const match = lines[i].match(CLAUDE_TUI_OPTION_PATTERN);
12989
13082
  if (!match) continue;
12990
13083
  const label = match[2].trim();
12991
13084
  if (/^Type something\.?$/i.test(label)) {
@@ -12995,7 +13088,7 @@ function parseClaudeInteractiveTuiQuestion(page, index) {
12995
13088
  if (/^Chat about this$/i.test(label)) continue;
12996
13089
  let description;
12997
13090
  const nextLine = lines[i + 1]?.trim();
12998
- if (nextLine && !optionPattern.test(lines[i + 1]) && !/^─+$/.test(nextLine) && !/^Enter to select\b/.test(nextLine)) {
13091
+ if (nextLine && !CLAUDE_TUI_OPTION_PATTERN.test(lines[i + 1]) && !/^─+$/.test(nextLine) && !/^Enter to select\b/.test(nextLine)) {
12999
13092
  description = nextLine;
13000
13093
  }
13001
13094
  options.push({ label, ...description ? { description } : {} });
@@ -27879,7 +27972,11 @@ var SpecCliAdapter = class {
27879
27972
  maybeRefreshNativeHistory() {
27880
27973
  }
27881
27974
  getScriptParsedStatus() {
27882
- return this.getStatus();
27975
+ const status = this.getStatus();
27976
+ return {
27977
+ ...status,
27978
+ messages: this.readClaudeScreenAssistantMessages()
27979
+ };
27883
27980
  }
27884
27981
  getPartialResponse() {
27885
27982
  return this.partialResponse;
@@ -28106,11 +28203,58 @@ var SpecCliAdapter = class {
28106
28203
  }
28107
28204
  }
28108
28205
  }
28206
+ readCurrentScreenSections(screenText) {
28207
+ try {
28208
+ const ev = evaluate(this.spec, screenText);
28209
+ return Object.fromEntries(ev.sections.map((section) => [section.id, section.text]));
28210
+ } catch {
28211
+ return {};
28212
+ }
28213
+ }
28214
+ readClaudeScreenAssistantMessages() {
28215
+ if (this.cliType !== "claude-cli") return [];
28216
+ let screenText = "";
28217
+ try {
28218
+ screenText = this.driver.snapshot();
28219
+ } catch {
28220
+ return [];
28221
+ }
28222
+ const sections = this.readCurrentScreenSections(screenText);
28223
+ const body = sections.body || screenText;
28224
+ const messages = [];
28225
+ const seen = /* @__PURE__ */ new Set();
28226
+ for (const line of body.split(/\r?\n/)) {
28227
+ const match = line.match(/^\s*⏺\s+(.+?)\s*$/);
28228
+ const content = match?.[1]?.trim();
28229
+ if (!content || seen.has(content)) continue;
28230
+ seen.add(content);
28231
+ messages.push({
28232
+ role: "assistant",
28233
+ kind: "standard",
28234
+ content,
28235
+ source: "assistant_text",
28236
+ userFacing: true,
28237
+ bubbleState: "final"
28238
+ });
28239
+ }
28240
+ return messages;
28241
+ }
28109
28242
  maybeCaptureClaudeTuiPrompt() {
28110
28243
  if (this.cliType !== "claude-cli" || this.activeInteractivePrompt || this.claudeTuiPromptCaptureInFlight) return;
28111
28244
  const screenText = this.driver.snapshot();
28112
28245
  const headers = this.readClaudeTuiHeaders(screenText);
28113
- if (headers.length === 0 || !screenText.includes("Enter to select")) return;
28246
+ if (!screenText.includes("Enter to select")) return;
28247
+ if (headers.length === 0) {
28248
+ const prompt = detectClaudeAskUserQuestionPromptFromTuiPages([{ screenText }], {
28249
+ promptId: `ask-user-${this.providerSessionId || "claude"}-${Date.now()}`,
28250
+ providerType: this.cliType
28251
+ });
28252
+ if (!prompt) return;
28253
+ this.activeInteractivePrompt = prompt;
28254
+ this.interactivePromptTransport = "tui";
28255
+ this.statusCallback?.();
28256
+ return;
28257
+ }
28114
28258
  this.claudeTuiPromptCaptureInFlight = true;
28115
28259
  void this.captureClaudeTuiPrompt(screenText, headers).finally(() => {
28116
28260
  this.claudeTuiPromptCaptureInFlight = false;
@@ -28202,6 +28346,11 @@ function getMessageTime(message) {
28202
28346
  }
28203
28347
  var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
28204
28348
  var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
28349
+ var TERMINAL_MESH_EVENTS = /* @__PURE__ */ new Set([
28350
+ "agent:generating_completed",
28351
+ "agent:stopped",
28352
+ "agent:ready"
28353
+ ]);
28205
28354
  var IMAGE_MIME_EXTENSIONS = {
28206
28355
  "image/png": ".png",
28207
28356
  "image/jpeg": ".jpg",
@@ -28722,6 +28871,39 @@ var CliProviderInstance = class {
28722
28871
  longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
28723
28872
  });
28724
28873
  }
28874
+ /**
28875
+ * Stamp a direct-dispatch mesh assignment on this instance.
28876
+ * setupMeshEventForwarding reads settings.meshNodeFor + meshActiveTaskId to
28877
+ * route generating_completed back to the originating coordinator. Without
28878
+ * this stamp, mesh_send_task --direct targets a plain CLI session whose
28879
+ * completion events silently drop because the forwarder has nothing to
28880
+ * match against.
28881
+ */
28882
+ attachMeshAssignment(assignment) {
28883
+ if (!assignment?.meshId) return;
28884
+ this.settings = {
28885
+ ...this.settings,
28886
+ meshNodeFor: assignment.meshId,
28887
+ ...assignment.nodeId ? { meshNodeId: assignment.nodeId } : {},
28888
+ ...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {}
28889
+ };
28890
+ this.adapter.updateRuntimeSettings?.(this.settings);
28891
+ }
28892
+ /**
28893
+ * Clear a previously-attached mesh assignment after the task reaches a
28894
+ * terminal state. Leaving meshNodeFor pinned would route this session's
28895
+ * subsequent unrelated turns (e.g. ad-hoc dashboard chats) to the
28896
+ * coordinator as if they were task completions.
28897
+ */
28898
+ detachMeshAssignment() {
28899
+ if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
28900
+ const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
28901
+ void meshNodeFor;
28902
+ void meshNodeId;
28903
+ void meshActiveTaskId;
28904
+ this.settings = rest;
28905
+ this.adapter.updateRuntimeSettings?.(this.settings);
28906
+ }
28725
28907
  onEvent(event, data) {
28726
28908
  if (event === "send_message") {
28727
28909
  const input = normalizeInputEnvelope(data);
@@ -29354,9 +29536,15 @@ var CliProviderInstance = class {
29354
29536
  };
29355
29537
  if (this.context?.emitProviderEvent) {
29356
29538
  this.context.emitProviderEvent(enrichedEvent);
29357
- return;
29539
+ } else {
29540
+ this.events.push(enrichedEvent);
29541
+ }
29542
+ if (TERMINAL_MESH_EVENTS.has(event.event) && this.settings.meshActiveTaskId) {
29543
+ try {
29544
+ this.detachMeshAssignment();
29545
+ } catch {
29546
+ }
29358
29547
  }
29359
- this.events.push(enrichedEvent);
29360
29548
  }
29361
29549
  flushEvents() {
29362
29550
  const events = [...this.events];
@@ -32016,6 +32204,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
32016
32204
  } else if (currentStatus === "starting") {
32017
32205
  currentStatus = getEffectiveAgentSendStatus(adapter);
32018
32206
  }
32207
+ const meshContext = args?.meshContext;
32208
+ if (meshContext && typeof meshContext === "object" && typeof meshContext.meshId === "string" && meshContext.meshId) {
32209
+ const targetInstanceId = key;
32210
+ try {
32211
+ this.deps.getInstanceManager()?.attachMeshAssignmentToInstance(targetInstanceId, {
32212
+ meshId: meshContext.meshId,
32213
+ ...typeof meshContext.nodeId === "string" && meshContext.nodeId ? { nodeId: meshContext.nodeId } : {},
32214
+ ...typeof meshContext.taskId === "string" && meshContext.taskId ? { taskId: meshContext.taskId } : {}
32215
+ });
32216
+ } catch {
32217
+ }
32218
+ }
32019
32219
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
32020
32220
  const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
32021
32221
  if (provider?.category === "acp") {
@@ -42404,6 +42604,36 @@ var ProviderInstanceManager = class {
42404
42604
  }
42405
42605
  return updated;
42406
42606
  }
42607
+ /** Stamp a mesh assignment on a single instance (used by mesh_send_task
42608
+ * --direct so the worker's completion event has a coordinator routing
42609
+ * marker in state.settings). Returns true if the instance existed and
42610
+ * the stamp was applied. */
42611
+ attachMeshAssignmentToInstance(instanceId, assignment) {
42612
+ const inst = this.instances.get(instanceId);
42613
+ if (!inst || typeof inst.attachMeshAssignment !== "function") {
42614
+ try {
42615
+ const { LOG: LOG2 } = (init_logger(), __toCommonJS(logger_exports));
42616
+ LOG2.warn?.("MeshDispatch", `attachMeshAssignment skipped: instance ${instanceId} ${inst ? "has no attach method" : "not found"}`);
42617
+ } catch {
42618
+ }
42619
+ return false;
42620
+ }
42621
+ inst.attachMeshAssignment(assignment);
42622
+ try {
42623
+ const { LOG: LOG2 } = (init_logger(), __toCommonJS(logger_exports));
42624
+ LOG2.info?.("MeshDispatch", `stamped mesh assignment on ${instanceId}: mesh=${assignment.meshId} node=${assignment.nodeId || ""} task=${assignment.taskId || ""}`);
42625
+ } catch {
42626
+ }
42627
+ return true;
42628
+ }
42629
+ /** Clear a mesh assignment after the dispatched task reaches a terminal
42630
+ * state (generating_completed / stopped / failed). */
42631
+ detachMeshAssignmentFromInstance(instanceId) {
42632
+ const inst = this.instances.get(instanceId);
42633
+ if (!inst || typeof inst.detachMeshAssignment !== "function") return false;
42634
+ inst.detachMeshAssignment();
42635
+ return true;
42636
+ }
42407
42637
  refreshProviderDefinitions(resolveProvider) {
42408
42638
  let refreshed = 0;
42409
42639
  for (const instance of this.instances.values()) {