@adhdev/daemon-core 0.9.77-rc.2 → 0.9.77-rc.21

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.
@@ -77,6 +77,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
77
77
  private resizeSuppressUntil;
78
78
  private statusHistory;
79
79
  private cliScripts;
80
+ /** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
81
+ private scriptState;
80
82
  private runtimeSettings;
81
83
  /** Full accumulated rendered PTY transcript for parser/readback use */
82
84
  private accumulatedBuffer;
@@ -63,17 +63,27 @@ export interface ParsedSession {
63
63
  coverage?: 'full' | 'tail' | 'current-turn';
64
64
  }
65
65
  export interface CliScripts {
66
- parseSession?: (input: CliScriptInput & {
66
+ /**
67
+ * Optional state factory. Called once per CLI session start (or script reload).
68
+ * The returned object is passed as the first argument to detectStatus, parseApproval,
69
+ * and parseSession on every invocation, allowing scripts to maintain per-session state
70
+ * (e.g. last-seen status, approval fingerprints, stability counters).
71
+ *
72
+ * Scripts that don't define createState() receive null as the state argument,
73
+ * making this change fully backward compatible.
74
+ */
75
+ createState?: () => unknown;
76
+ parseSession?: (state: unknown, input: CliScriptInput & {
67
77
  tail?: string;
68
78
  tailScreen?: CliScreenSnapshot;
69
79
  }) => ParsedSession | null;
70
- detectStatus?: (input: CliStatusInput) => string | null;
71
- parseApproval?: (input: CliApprovalInput) => {
80
+ detectStatus?: (state: unknown, input: CliStatusInput) => string | null;
81
+ parseApproval?: (state: unknown, input: CliApprovalInput) => {
72
82
  message: string;
73
83
  buttons: string[];
74
84
  } | null;
75
85
  resolveAction?: (data: any) => string;
76
- [name: string]: ((input: any) => any) | undefined;
86
+ [name: string]: ((state: unknown, input: any) => any) | ((data: any) => any) | (() => unknown) | undefined;
77
87
  }
78
88
  export interface CliScreenLine {
79
89
  index: number;
@@ -17,6 +17,14 @@ export type MeshCoordinatorSetup = {
17
17
  requiresRestart: boolean;
18
18
  instructions: string;
19
19
  template: string;
20
+ } | {
21
+ /** Provider registers MCP via its own CLI command (e.g. `codex mcp add` / `gemini mcp add`). */
22
+ kind: 'cli_command';
23
+ serverName: string;
24
+ /** The rendered shell command to execute before launching the coordinator session. */
25
+ command: string;
26
+ requiresRestart: boolean;
27
+ instructions: string;
20
28
  } | {
21
29
  kind: 'unsupported';
22
30
  reason: string;
package/dist/index.js CHANGED
@@ -1317,13 +1317,15 @@ function formatCompletionMetadata(event) {
1317
1317
  }
1318
1318
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
1319
1319
  const task = claimNextTask(meshId, nodeId, sessionId);
1320
- if (!task) return false;
1320
+ if (!task) {
1321
+ return false;
1322
+ }
1321
1323
  LOG.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
1322
1324
  components.cliManager.handleCliCommand("agent_command", {
1323
1325
  targetSessionId: sessionId,
1324
1326
  cliType: providerType,
1325
1327
  action: "send_chat",
1326
- input: task.message
1328
+ message: task.message
1327
1329
  }).catch((e) => {
1328
1330
  LOG.error("MeshQueue", `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
1329
1331
  });
@@ -1395,7 +1397,7 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
1395
1397
  function injectMeshSystemMessage(components, args) {
1396
1398
  if (args.event === "agent:generating_completed") {
1397
1399
  const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1398
- const nodeId = readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1400
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1399
1401
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
1400
1402
  if (sessionId) {
1401
1403
  updateSessionTaskStatus(args.meshId, sessionId, "completed");
@@ -1405,6 +1407,15 @@ function injectMeshSystemMessage(components, args) {
1405
1407
  }, 500);
1406
1408
  }
1407
1409
  }
1410
+ } else if (args.event === "agent:ready") {
1411
+ const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1412
+ const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
1413
+ const providerType = readNonEmptyString(args.metadataEvent.providerType);
1414
+ if (sessionId && nodeId && providerType) {
1415
+ setTimeout(() => {
1416
+ tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
1417
+ }, 500);
1418
+ }
1408
1419
  } else if (args.event === "agent:stopped") {
1409
1420
  const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
1410
1421
  if (sessionId) {
@@ -1416,7 +1427,7 @@ function injectMeshSystemMessage(components, args) {
1416
1427
  try {
1417
1428
  appendLedgerEntry(args.meshId, {
1418
1429
  kind: ledgerKind,
1419
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1430
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1420
1431
  sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1421
1432
  providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
1422
1433
  payload: {
@@ -1436,7 +1447,7 @@ function injectMeshSystemMessage(components, args) {
1436
1447
  const maxRetries = mesh?.policy?.maxTaskRetries ?? 1;
1437
1448
  recoveryContext = getSessionRecoveryContext(args.meshId, {
1438
1449
  sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || void 0,
1439
- nodeId: readNonEmptyString(args.metadataEvent.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1450
+ nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
1440
1451
  maxRetries
1441
1452
  });
1442
1453
  recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
@@ -1531,6 +1542,7 @@ function handleMeshForwardEvent(components, payload) {
1531
1542
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
1532
1543
  return injectMeshSystemMessage(components, {
1533
1544
  meshId,
1545
+ nodeId,
1534
1546
  nodeLabel,
1535
1547
  event: eventName,
1536
1548
  metadataEvent: {
@@ -1560,10 +1572,12 @@ function setupMeshEventForwarding(components) {
1560
1572
  if (!meshId) return;
1561
1573
  const targetNode = mesh?.nodes?.find((n) => n.workspace === workspace);
1562
1574
  const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
1575
+ const resolvedNodeId = targetNode?.id || runtimeNodeId;
1563
1576
  const nodeLabel = targetNode ? `Node '${targetNode.id}'` : runtimeNodeId ? `Node '${runtimeNodeId}'` : `Agent at ${workspace}`;
1564
1577
  injectMeshSystemMessage(components, {
1565
1578
  meshId,
1566
1579
  sourceInstanceId: instanceId,
1580
+ nodeId: resolvedNodeId,
1567
1581
  nodeLabel,
1568
1582
  event: event.event,
1569
1583
  metadataEvent: event
@@ -1584,6 +1598,7 @@ var init_mesh_events = __esm({
1584
1598
  "agent:generating_completed",
1585
1599
  "agent:waiting_approval",
1586
1600
  "agent:stopped",
1601
+ "agent:ready",
1587
1602
  "monitor:long_generating"
1588
1603
  ]);
1589
1604
  EVENT_TO_LEDGER_KIND = {
@@ -2699,6 +2714,8 @@ var init_provider_cli_adapter = __esm({
2699
2714
  statusHistory = [];
2700
2715
  // ─── CLI Scripts (script-based parsing) ───
2701
2716
  cliScripts;
2717
+ /** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
2718
+ scriptState = null;
2702
2719
  runtimeSettings = {};
2703
2720
  /** Full accumulated rendered PTY transcript for parser/readback use */
2704
2721
  accumulatedBuffer = "";
@@ -2880,6 +2897,7 @@ ${lastSnapshot}`;
2880
2897
  this.cliScripts = scripts;
2881
2898
  this.parsedStatusCache = null;
2882
2899
  this.parseErrorMessage = null;
2900
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
2883
2901
  const scriptNames = listCliScriptNames(scripts);
2884
2902
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
2885
2903
  }
@@ -2997,6 +3015,7 @@ ${lastSnapshot}`;
2997
3015
  this.ready = false;
2998
3016
  this.startupParseGate = false;
2999
3017
  this.spawnAt = 0;
3018
+ this.scriptState = null;
3000
3019
  this.onStatusChange?.();
3001
3020
  });
3002
3021
  this.spawnAt = Date.now();
@@ -3770,7 +3789,7 @@ ${lastSnapshot}`;
3770
3789
  scope: this.currentTurnScope,
3771
3790
  runtimeSettings: this.runtimeSettings
3772
3791
  });
3773
- const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3792
+ const session = this.cliScripts.parseSession(this.scriptState, { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3774
3793
  this.parseErrorMessage = null;
3775
3794
  return session && typeof session === "object" ? session : null;
3776
3795
  } catch (e) {
@@ -3784,7 +3803,7 @@ ${lastSnapshot}`;
3784
3803
  if (!this.cliScripts?.detectStatus) return null;
3785
3804
  try {
3786
3805
  const screenText = this.terminalScreen.getText();
3787
- const status = this.cliScripts.detectStatus({
3806
+ const status = this.cliScripts.detectStatus(this.scriptState, {
3788
3807
  tail: text.slice(-500),
3789
3808
  screenText,
3790
3809
  rawBuffer: this.accumulatedRawBuffer,
@@ -3803,7 +3822,7 @@ ${lastSnapshot}`;
3803
3822
  try {
3804
3823
  const screenText = this.terminalScreen.getText();
3805
3824
  const buffer = screenText || this.accumulatedBuffer;
3806
- return this.cliScripts.parseApproval({
3825
+ return this.cliScripts.parseApproval(this.scriptState, {
3807
3826
  buffer,
3808
3827
  screenText,
3809
3828
  rawBuffer: this.accumulatedRawBuffer,
@@ -3911,7 +3930,7 @@ ${lastSnapshot}`;
3911
3930
  scope: this.currentTurnScope,
3912
3931
  runtimeSettings: this.runtimeSettings
3913
3932
  });
3914
- return await Promise.resolve(fn({
3933
+ return await Promise.resolve(fn(this.scriptState, {
3915
3934
  ...input,
3916
3935
  args: args && typeof args === "object" ? { ...args } : {}
3917
3936
  }));
@@ -15205,11 +15224,13 @@ async function handleOpenPanel(h, args) {
15205
15224
  async function handlePtyInput(h, args) {
15206
15225
  const { cliType, data, targetSessionId } = args || {};
15207
15226
  if (!data) return { success: false, error: "data required" };
15227
+ const cleanData = typeof data === "string" ? data.replace(/\x1b\[[?>][0-9;]*c/g, "") : data;
15228
+ if (!cleanData) return { success: true };
15208
15229
  const adapter = h.getCliAdapter(targetSessionId || cliType);
15209
15230
  if (!adapter || typeof adapter.writeRaw !== "function") {
15210
15231
  return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
15211
15232
  }
15212
- await adapter.writeRaw(data);
15233
+ await adapter.writeRaw(cleanData);
15213
15234
  return { success: true };
15214
15235
  }
15215
15236
  function handlePtyResize(_h, args) {
@@ -16839,6 +16860,8 @@ var CliProviderInstance = class {
16839
16860
  this.completedDebounceTimer = null;
16840
16861
  }, 3e3);
16841
16862
  }
16863
+ } else if (newStatus === "idle" && this.lastStatus === "starting") {
16864
+ this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
16842
16865
  } else if (newStatus === "stopped") {
16843
16866
  if (this.generatingDebounceTimer) {
16844
16867
  clearTimeout(this.generatingDebounceTimer);
@@ -18582,9 +18605,6 @@ function buildCoordinatorDelegatedCliLaunchOptions(input) {
18582
18605
  const cliType = String(input.cliType || "").trim();
18583
18606
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
18584
18607
  const env = { ...input.env || {}, ...COORDINATOR_DELEGATED_ENV_UNSETS };
18585
- if (cliType === "hermes-cli" && !hasCliArg(cliArgs, "--ignore-user-config")) {
18586
- cliArgs.unshift("--ignore-user-config");
18587
- }
18588
18608
  if (cliType === "claude-cli" && !hasCliArg(cliArgs, "--mcp-config")) {
18589
18609
  cliArgs.unshift("--mcp-config", ensureEmptyDelegatedMcpConfig(input.workspace));
18590
18610
  }
@@ -21919,6 +21939,22 @@ function resolveMeshCoordinatorSetup(options) {
21919
21939
  if (!instructions || !template?.trim()) {
21920
21940
  return { kind: "unsupported", reason: "Provider manual MCP setup is missing instructions or template" };
21921
21941
  }
21942
+ const renderedTemplate = renderMeshCoordinatorTemplate(template, {
21943
+ meshId,
21944
+ workspace,
21945
+ serverName,
21946
+ adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
21947
+ });
21948
+ const isCliCommand = !renderedTemplate.trim().includes("\n") && !renderedTemplate.trim().startsWith("{");
21949
+ if (isCliCommand) {
21950
+ return {
21951
+ kind: "cli_command",
21952
+ serverName,
21953
+ command: renderedTemplate.trim(),
21954
+ requiresRestart: mcpConfig.requiresRestart === true,
21955
+ instructions
21956
+ };
21957
+ }
21922
21958
  return {
21923
21959
  kind: "manual",
21924
21960
  serverName,
@@ -21926,12 +21962,7 @@ function resolveMeshCoordinatorSetup(options) {
21926
21962
  configPathCommand: mcpConfig.configPathCommand,
21927
21963
  requiresRestart: mcpConfig.requiresRestart === true,
21928
21964
  instructions,
21929
- template: renderMeshCoordinatorTemplate(template, {
21930
- meshId,
21931
- workspace,
21932
- serverName,
21933
- adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND
21934
- })
21965
+ template: renderedTemplate
21935
21966
  };
21936
21967
  }
21937
21968
  return {
@@ -24113,6 +24144,93 @@ var DaemonCommandRouter = class {
24113
24144
  meshCoordinatorSetup: coordinatorSetup
24114
24145
  };
24115
24146
  }
24147
+ if (coordinatorSetup.kind === "cli_command") {
24148
+ let cliCmdSystemPrompt = "";
24149
+ try {
24150
+ cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType });
24151
+ } catch (error) {
24152
+ const message = error?.message || String(error);
24153
+ LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
24154
+ return {
24155
+ success: false,
24156
+ code: "mesh_coordinator_prompt_failed",
24157
+ error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
24158
+ meshId,
24159
+ cliType,
24160
+ workspace
24161
+ };
24162
+ }
24163
+ try {
24164
+ const { execFileSync: execCmdSync } = await import("child_process");
24165
+ const cmdParts = coordinatorSetup.command.trim().split(/\s+/);
24166
+ const [regCmd, ...regArgs] = cmdParts;
24167
+ LOG.info("MeshCoordinator", `Running MCP registration: ${coordinatorSetup.command}`);
24168
+ execCmdSync(regCmd, regArgs, { stdio: "pipe", timeout: 15e3 });
24169
+ } catch (error) {
24170
+ LOG.warn("MeshCoordinator", `MCP registration command failed (may be pre-registered): ${error?.message || error}`);
24171
+ }
24172
+ const cliCmdArgs = [];
24173
+ const cliCmdEnv = {};
24174
+ if (cliCmdSystemPrompt) {
24175
+ if (cliType === "codex-cli") {
24176
+ cliCmdArgs.push("-c", `developer_instructions=${JSON.stringify(cliCmdSystemPrompt)}`);
24177
+ } else if (cliType === "gemini-cli") {
24178
+ try {
24179
+ const { writeFileSync: wfs, existsSync: efs, readFileSync: rfs } = await import("fs");
24180
+ const geminiMdPath = `${workspace}/GEMINI.md`;
24181
+ const marker = "<!-- adhdev-mesh-coordinator-prompt -->";
24182
+ const markerEnd = "<!-- /adhdev-mesh-coordinator-prompt -->";
24183
+ const block = `${marker}
24184
+ ${cliCmdSystemPrompt}
24185
+ ${markerEnd}`;
24186
+ if (efs(geminiMdPath)) {
24187
+ const existing = rfs(geminiMdPath, "utf-8");
24188
+ const replaced = existing.replace(
24189
+ new RegExp(`${marker}[\\s\\S]*?${markerEnd}`, "g"),
24190
+ block
24191
+ );
24192
+ wfs(geminiMdPath, replaced.includes(marker) ? replaced : `${existing}
24193
+
24194
+ ${block}`);
24195
+ } else {
24196
+ wfs(geminiMdPath, block);
24197
+ }
24198
+ LOG.info("MeshCoordinator", `Wrote coordinator prompt to ${workspace}/GEMINI.md`);
24199
+ } catch (e) {
24200
+ LOG.warn("MeshCoordinator", `Could not write GEMINI.md: ${e?.message || e}`);
24201
+ }
24202
+ }
24203
+ }
24204
+ const cliCmdLaunch = await this.deps.cliManager.handleCliCommand("launch_cli", {
24205
+ cliType,
24206
+ dir: workspace,
24207
+ cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : void 0,
24208
+ env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : void 0,
24209
+ settings: { meshCoordinatorFor: meshId }
24210
+ });
24211
+ if (!cliCmdLaunch?.success) {
24212
+ return { success: false, error: cliCmdLaunch?.error || "Failed to launch CLI session" };
24213
+ }
24214
+ LOG.info("MeshCoordinator", `Launched ${cliType} coordinator (cli_command) for mesh ${meshId}`);
24215
+ try {
24216
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
24217
+ appendLedgerEntry2(meshId, {
24218
+ kind: "coordinator_started",
24219
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
24220
+ providerType: cliType,
24221
+ payload: { workspace }
24222
+ });
24223
+ } catch {
24224
+ }
24225
+ return {
24226
+ success: true,
24227
+ meshId,
24228
+ cliType,
24229
+ workspace,
24230
+ sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
24231
+ mcpRegistered: true
24232
+ };
24233
+ }
24116
24234
  const configFormat = coordinatorSetup.configFormat;
24117
24235
  if (configFormat !== "claude_mcp_json" && configFormat !== "hermes_config_yaml") {
24118
24236
  return {