@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.js CHANGED
@@ -1694,6 +1694,21 @@ var init_async_batch_writer = __esm({
1694
1694
  });
1695
1695
 
1696
1696
  // src/logging/logger.ts
1697
+ var logger_exports = {};
1698
+ __export(logger_exports, {
1699
+ LOG: () => LOG,
1700
+ LOG_DIR_PATH: () => LOG_DIR_PATH,
1701
+ LOG_PATH: () => LOG_PATH,
1702
+ daemonLog: () => daemonLog,
1703
+ getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
1704
+ getDaemonLogDir: () => getDaemonLogDir,
1705
+ getLogBufferSize: () => getLogBufferSize,
1706
+ getLogLevel: () => getLogLevel,
1707
+ getLogPath: () => getLogPath,
1708
+ getRecentLogs: () => getRecentLogs,
1709
+ installGlobalInterceptor: () => installGlobalInterceptor,
1710
+ setLogLevel: () => setLogLevel
1711
+ });
1697
1712
  function setLogLevel(level) {
1698
1713
  currentLevel = level;
1699
1714
  daemonLog("Logger", `Log level set to: ${level}`, "info");
@@ -1765,6 +1780,9 @@ function getRecentLogs(count = 50, minLevel = "info") {
1765
1780
  const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
1766
1781
  return filtered.slice(-count);
1767
1782
  }
1783
+ function getLogBufferSize() {
1784
+ return ringBuffer.length;
1785
+ }
1768
1786
  function ts() {
1769
1787
  return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
1770
1788
  }
@@ -1844,7 +1862,10 @@ function installGlobalInterceptor() {
1844
1862
  writeToFile(`Log file: ${currentLogFile}`);
1845
1863
  writeToFile(`Log level: ${currentLevel}`);
1846
1864
  }
1847
- var fs4, path9, os3, 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;
1865
+ function getLogPath() {
1866
+ return currentLogFile;
1867
+ }
1868
+ var fs4, path9, os3, 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;
1848
1869
  var init_logger = __esm({
1849
1870
  "src/logging/logger.ts"() {
1850
1871
  "use strict";
@@ -1905,6 +1926,7 @@ var init_logger = __esm({
1905
1926
  };
1906
1927
  interceptorInstalled = false;
1907
1928
  LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
1929
+ LOG_DIR_PATH = LOG_DIR;
1908
1930
  }
1909
1931
  });
1910
1932
 
@@ -5342,18 +5364,6 @@ function setupMeshEventForwarding(components) {
5342
5364
  } catch {
5343
5365
  }
5344
5366
  if (!meshIdFromDirectDispatch) return;
5345
- } else {
5346
- const workspaceMesh = getCachedMeshByWorkspace(workspace);
5347
- const workspaceMeshId = readNonEmptyString2(workspaceMesh?.id);
5348
- if (workspaceMeshId) {
5349
- try {
5350
- const activeDispatches = getActiveDirectDispatches(workspaceMeshId);
5351
- if (activeDispatches.some((d) => d.sessionId === instanceId) || hasUnterminalDirectDispatchLedgerEntry(workspaceMeshId, instanceId)) {
5352
- meshIdFromDirectDispatch = workspaceMeshId;
5353
- }
5354
- } catch {
5355
- }
5356
- }
5357
5367
  }
5358
5368
  const meshIdFromRuntime = readNonEmptyString2(settings.meshNodeFor) || meshIdFromDirectDispatch;
5359
5369
  const isMeshDelegate = Boolean(meshIdFromRuntime || settings.launchedByCoordinator);
@@ -13259,6 +13269,7 @@ function buildClaudeInteractiveToolResult(response) {
13259
13269
  }
13260
13270
  });
13261
13271
  }
13272
+ var CLAUDE_TUI_OPTION_PATTERN = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
13262
13273
  function claudeTuiQuestionHeaders(screenText) {
13263
13274
  const navLine = screenText.split(/\r?\n/).find((line) => line.includes("\u2714 Submit") && /[☐☒]/.test(line));
13264
13275
  if (!navLine) return [];
@@ -13270,6 +13281,88 @@ function claudeTuiQuestionHeaders(screenText) {
13270
13281
  }
13271
13282
  return headers;
13272
13283
  }
13284
+ function isClaudeTuiSelectFooter(text) {
13285
+ return /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
13286
+ }
13287
+ function readClaudeHeaderLine(lines, beforeIndex) {
13288
+ for (let i = beforeIndex; i >= 0; i -= 1) {
13289
+ const candidate = lines[i].trim();
13290
+ if (!candidate) continue;
13291
+ const match = candidate.match(/^[☐☒]\s+(.+?)\s*$/);
13292
+ if (match?.[1]) return readString(match[1]);
13293
+ if (/^─+$/.test(candidate)) break;
13294
+ }
13295
+ return void 0;
13296
+ }
13297
+ function readClaudeOptionDescription(lines, optionLineIndex) {
13298
+ const nextLine = lines[optionLineIndex + 1];
13299
+ const next = nextLine?.trim();
13300
+ if (!next || CLAUDE_TUI_OPTION_PATTERN.test(nextLine) || /^─+$/.test(next) || /^Enter to select\b/i.test(next) || /^[☐☒]\s+/.test(next)) {
13301
+ return void 0;
13302
+ }
13303
+ return next;
13304
+ }
13305
+ function parseClaudeHeaderlessInteractiveTuiQuestion(page, index) {
13306
+ if (!isClaudeTuiSelectFooter(page.screenText)) return null;
13307
+ if (!/Type something\.?|Chat about this/i.test(page.screenText)) return null;
13308
+ const lines = page.screenText.split(/\r?\n/);
13309
+ let footerIndex = -1;
13310
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
13311
+ if (/Enter to select/i.test(lines[i])) {
13312
+ footerIndex = i;
13313
+ break;
13314
+ }
13315
+ }
13316
+ if (footerIndex < 0) return null;
13317
+ let optionBlockEnd = footerIndex - 1;
13318
+ for (let i = footerIndex - 1; i >= 0; i -= 1) {
13319
+ if (/^─+$/.test(lines[i].trim())) {
13320
+ optionBlockEnd = i - 1;
13321
+ break;
13322
+ }
13323
+ }
13324
+ const optionLineIndexes = [];
13325
+ for (let i = optionBlockEnd; i >= 0; i -= 1) {
13326
+ const line = lines[i];
13327
+ if (CLAUDE_TUI_OPTION_PATTERN.test(line)) {
13328
+ optionLineIndexes.push(i);
13329
+ continue;
13330
+ }
13331
+ if (optionLineIndexes.length > 0 && (!line.trim() || /^─+$/.test(line.trim()))) break;
13332
+ }
13333
+ optionLineIndexes.reverse();
13334
+ if (optionLineIndexes.length === 0) return null;
13335
+ const firstOptionIndex = optionLineIndexes[0];
13336
+ let question = "";
13337
+ for (let i = firstOptionIndex - 1; i >= 0; i -= 1) {
13338
+ const candidate = lines[i].trim();
13339
+ if (!candidate || /^─+$/.test(candidate) || /^[☐☒]\s+/.test(candidate)) continue;
13340
+ question = candidate;
13341
+ break;
13342
+ }
13343
+ if (!question) return null;
13344
+ const options = [];
13345
+ let allowFreeform = false;
13346
+ for (const optionLineIndex of optionLineIndexes) {
13347
+ const match = lines[optionLineIndex].match(CLAUDE_TUI_OPTION_PATTERN);
13348
+ if (!match) continue;
13349
+ const label = match[2].trim();
13350
+ if (/^Chat about this$/i.test(label)) continue;
13351
+ if (/^Type something\.?$/i.test(label)) allowFreeform = true;
13352
+ const description = readClaudeOptionDescription(lines, optionLineIndex);
13353
+ options.push({ label, ...description ? { description } : {} });
13354
+ }
13355
+ if (options.length === 0) return null;
13356
+ const header = readString(page.header) || readClaudeHeaderLine(lines, firstOptionIndex - 1);
13357
+ return {
13358
+ questionId: `q${index + 1}`,
13359
+ question,
13360
+ ...header ? { header } : {},
13361
+ multiSelect: /Space to select|toggle selections/i.test(page.screenText),
13362
+ options,
13363
+ ...allowFreeform ? { allowFreeform: true } : {}
13364
+ };
13365
+ }
13273
13366
  function parseClaudeInteractiveTuiQuestion(page, index) {
13274
13367
  const lines = page.screenText.split(/\r?\n/);
13275
13368
  let navIndex = -1;
@@ -13279,7 +13372,8 @@ function parseClaudeInteractiveTuiQuestion(page, index) {
13279
13372
  break;
13280
13373
  }
13281
13374
  }
13282
- if (navIndex < 0 || !page.screenText.includes("Enter to select")) return null;
13375
+ if (navIndex < 0) return parseClaudeHeaderlessInteractiveTuiQuestion(page, index);
13376
+ if (!page.screenText.includes("Enter to select")) return null;
13283
13377
  let question = "";
13284
13378
  let questionLineIndex = -1;
13285
13379
  for (let i = navIndex + 1; i < lines.length; i += 1) {
@@ -13293,9 +13387,8 @@ function parseClaudeInteractiveTuiQuestion(page, index) {
13293
13387
  if (!question) return null;
13294
13388
  const options = [];
13295
13389
  let allowFreeform = false;
13296
- const optionPattern = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
13297
13390
  for (let i = questionLineIndex + 1; i < lines.length; i += 1) {
13298
- const match = lines[i].match(optionPattern);
13391
+ const match = lines[i].match(CLAUDE_TUI_OPTION_PATTERN);
13299
13392
  if (!match) continue;
13300
13393
  const label = match[2].trim();
13301
13394
  if (/^Type something\.?$/i.test(label)) {
@@ -13305,7 +13398,7 @@ function parseClaudeInteractiveTuiQuestion(page, index) {
13305
13398
  if (/^Chat about this$/i.test(label)) continue;
13306
13399
  let description;
13307
13400
  const nextLine = lines[i + 1]?.trim();
13308
- if (nextLine && !optionPattern.test(lines[i + 1]) && !/^─+$/.test(nextLine) && !/^Enter to select\b/.test(nextLine)) {
13401
+ if (nextLine && !CLAUDE_TUI_OPTION_PATTERN.test(lines[i + 1]) && !/^─+$/.test(nextLine) && !/^Enter to select\b/.test(nextLine)) {
13309
13402
  description = nextLine;
13310
13403
  }
13311
13404
  options.push({ label, ...description ? { description } : {} });
@@ -28189,7 +28282,11 @@ var SpecCliAdapter = class {
28189
28282
  maybeRefreshNativeHistory() {
28190
28283
  }
28191
28284
  getScriptParsedStatus() {
28192
- return this.getStatus();
28285
+ const status = this.getStatus();
28286
+ return {
28287
+ ...status,
28288
+ messages: this.readClaudeScreenAssistantMessages()
28289
+ };
28193
28290
  }
28194
28291
  getPartialResponse() {
28195
28292
  return this.partialResponse;
@@ -28416,11 +28513,58 @@ var SpecCliAdapter = class {
28416
28513
  }
28417
28514
  }
28418
28515
  }
28516
+ readCurrentScreenSections(screenText) {
28517
+ try {
28518
+ const ev = evaluate(this.spec, screenText);
28519
+ return Object.fromEntries(ev.sections.map((section) => [section.id, section.text]));
28520
+ } catch {
28521
+ return {};
28522
+ }
28523
+ }
28524
+ readClaudeScreenAssistantMessages() {
28525
+ if (this.cliType !== "claude-cli") return [];
28526
+ let screenText = "";
28527
+ try {
28528
+ screenText = this.driver.snapshot();
28529
+ } catch {
28530
+ return [];
28531
+ }
28532
+ const sections = this.readCurrentScreenSections(screenText);
28533
+ const body = sections.body || screenText;
28534
+ const messages = [];
28535
+ const seen = /* @__PURE__ */ new Set();
28536
+ for (const line of body.split(/\r?\n/)) {
28537
+ const match = line.match(/^\s*⏺\s+(.+?)\s*$/);
28538
+ const content = match?.[1]?.trim();
28539
+ if (!content || seen.has(content)) continue;
28540
+ seen.add(content);
28541
+ messages.push({
28542
+ role: "assistant",
28543
+ kind: "standard",
28544
+ content,
28545
+ source: "assistant_text",
28546
+ userFacing: true,
28547
+ bubbleState: "final"
28548
+ });
28549
+ }
28550
+ return messages;
28551
+ }
28419
28552
  maybeCaptureClaudeTuiPrompt() {
28420
28553
  if (this.cliType !== "claude-cli" || this.activeInteractivePrompt || this.claudeTuiPromptCaptureInFlight) return;
28421
28554
  const screenText = this.driver.snapshot();
28422
28555
  const headers = this.readClaudeTuiHeaders(screenText);
28423
- if (headers.length === 0 || !screenText.includes("Enter to select")) return;
28556
+ if (!screenText.includes("Enter to select")) return;
28557
+ if (headers.length === 0) {
28558
+ const prompt = detectClaudeAskUserQuestionPromptFromTuiPages([{ screenText }], {
28559
+ promptId: `ask-user-${this.providerSessionId || "claude"}-${Date.now()}`,
28560
+ providerType: this.cliType
28561
+ });
28562
+ if (!prompt) return;
28563
+ this.activeInteractivePrompt = prompt;
28564
+ this.interactivePromptTransport = "tui";
28565
+ this.statusCallback?.();
28566
+ return;
28567
+ }
28424
28568
  this.claudeTuiPromptCaptureInFlight = true;
28425
28569
  void this.captureClaudeTuiPrompt(screenText, headers).finally(() => {
28426
28570
  this.claudeTuiPromptCaptureInFlight = false;
@@ -28512,6 +28656,11 @@ function getMessageTime(message) {
28512
28656
  }
28513
28657
  var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
28514
28658
  var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
28659
+ var TERMINAL_MESH_EVENTS = /* @__PURE__ */ new Set([
28660
+ "agent:generating_completed",
28661
+ "agent:stopped",
28662
+ "agent:ready"
28663
+ ]);
28515
28664
  var IMAGE_MIME_EXTENSIONS = {
28516
28665
  "image/png": ".png",
28517
28666
  "image/jpeg": ".jpg",
@@ -29032,6 +29181,39 @@ var CliProviderInstance = class {
29032
29181
  longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
29033
29182
  });
29034
29183
  }
29184
+ /**
29185
+ * Stamp a direct-dispatch mesh assignment on this instance.
29186
+ * setupMeshEventForwarding reads settings.meshNodeFor + meshActiveTaskId to
29187
+ * route generating_completed back to the originating coordinator. Without
29188
+ * this stamp, mesh_send_task --direct targets a plain CLI session whose
29189
+ * completion events silently drop because the forwarder has nothing to
29190
+ * match against.
29191
+ */
29192
+ attachMeshAssignment(assignment) {
29193
+ if (!assignment?.meshId) return;
29194
+ this.settings = {
29195
+ ...this.settings,
29196
+ meshNodeFor: assignment.meshId,
29197
+ ...assignment.nodeId ? { meshNodeId: assignment.nodeId } : {},
29198
+ ...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {}
29199
+ };
29200
+ this.adapter.updateRuntimeSettings?.(this.settings);
29201
+ }
29202
+ /**
29203
+ * Clear a previously-attached mesh assignment after the task reaches a
29204
+ * terminal state. Leaving meshNodeFor pinned would route this session's
29205
+ * subsequent unrelated turns (e.g. ad-hoc dashboard chats) to the
29206
+ * coordinator as if they were task completions.
29207
+ */
29208
+ detachMeshAssignment() {
29209
+ if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
29210
+ const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
29211
+ void meshNodeFor;
29212
+ void meshNodeId;
29213
+ void meshActiveTaskId;
29214
+ this.settings = rest;
29215
+ this.adapter.updateRuntimeSettings?.(this.settings);
29216
+ }
29035
29217
  onEvent(event, data) {
29036
29218
  if (event === "send_message") {
29037
29219
  const input = normalizeInputEnvelope(data);
@@ -29664,9 +29846,15 @@ var CliProviderInstance = class {
29664
29846
  };
29665
29847
  if (this.context?.emitProviderEvent) {
29666
29848
  this.context.emitProviderEvent(enrichedEvent);
29667
- return;
29849
+ } else {
29850
+ this.events.push(enrichedEvent);
29851
+ }
29852
+ if (TERMINAL_MESH_EVENTS.has(event.event) && this.settings.meshActiveTaskId) {
29853
+ try {
29854
+ this.detachMeshAssignment();
29855
+ } catch {
29856
+ }
29668
29857
  }
29669
- this.events.push(enrichedEvent);
29670
29858
  }
29671
29859
  flushEvents() {
29672
29860
  const events = [...this.events];
@@ -32321,6 +32509,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
32321
32509
  } else if (currentStatus === "starting") {
32322
32510
  currentStatus = getEffectiveAgentSendStatus(adapter);
32323
32511
  }
32512
+ const meshContext = args?.meshContext;
32513
+ if (meshContext && typeof meshContext === "object" && typeof meshContext.meshId === "string" && meshContext.meshId) {
32514
+ const targetInstanceId = key;
32515
+ try {
32516
+ this.deps.getInstanceManager()?.attachMeshAssignmentToInstance(targetInstanceId, {
32517
+ meshId: meshContext.meshId,
32518
+ ...typeof meshContext.nodeId === "string" && meshContext.nodeId ? { nodeId: meshContext.nodeId } : {},
32519
+ ...typeof meshContext.taskId === "string" && meshContext.taskId ? { taskId: meshContext.taskId } : {}
32520
+ });
32521
+ } catch {
32522
+ }
32523
+ }
32324
32524
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
32325
32525
  const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
32326
32526
  if (provider?.category === "acp") {
@@ -42709,6 +42909,36 @@ var ProviderInstanceManager = class {
42709
42909
  }
42710
42910
  return updated;
42711
42911
  }
42912
+ /** Stamp a mesh assignment on a single instance (used by mesh_send_task
42913
+ * --direct so the worker's completion event has a coordinator routing
42914
+ * marker in state.settings). Returns true if the instance existed and
42915
+ * the stamp was applied. */
42916
+ attachMeshAssignmentToInstance(instanceId, assignment) {
42917
+ const inst = this.instances.get(instanceId);
42918
+ if (!inst || typeof inst.attachMeshAssignment !== "function") {
42919
+ try {
42920
+ const { LOG: LOG2 } = (init_logger(), __toCommonJS(logger_exports));
42921
+ LOG2.warn?.("MeshDispatch", `attachMeshAssignment skipped: instance ${instanceId} ${inst ? "has no attach method" : "not found"}`);
42922
+ } catch {
42923
+ }
42924
+ return false;
42925
+ }
42926
+ inst.attachMeshAssignment(assignment);
42927
+ try {
42928
+ const { LOG: LOG2 } = (init_logger(), __toCommonJS(logger_exports));
42929
+ LOG2.info?.("MeshDispatch", `stamped mesh assignment on ${instanceId}: mesh=${assignment.meshId} node=${assignment.nodeId || ""} task=${assignment.taskId || ""}`);
42930
+ } catch {
42931
+ }
42932
+ return true;
42933
+ }
42934
+ /** Clear a mesh assignment after the dispatched task reaches a terminal
42935
+ * state (generating_completed / stopped / failed). */
42936
+ detachMeshAssignmentFromInstance(instanceId) {
42937
+ const inst = this.instances.get(instanceId);
42938
+ if (!inst || typeof inst.detachMeshAssignment !== "function") return false;
42939
+ inst.detachMeshAssignment();
42940
+ return true;
42941
+ }
42712
42942
  refreshProviderDefinitions(resolveProvider) {
42713
42943
  let refreshed = 0;
42714
42944
  for (const instance of this.instances.values()) {