@adhdev/daemon-core 0.9.76-rc.31 → 0.9.76-rc.33

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.
@@ -1,2 +1,11 @@
1
1
  import type { DaemonComponents } from '../boot/daemon-lifecycle.js';
2
+ export declare function handleMeshForwardEvent(components: DaemonComponents, payload: Record<string, unknown>): {
3
+ success: boolean;
4
+ forwarded: number;
5
+ error?: undefined;
6
+ } | {
7
+ success: boolean;
8
+ error: string;
9
+ forwarded?: undefined;
10
+ };
2
11
  export declare function setupMeshEventForwarding(components: DaemonComponents): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.76-rc.31",
3
+ "version": "0.9.76-rc.33",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1857,9 +1857,13 @@ export class ProviderCliAdapter implements CliAdapter {
1857
1857
  };
1858
1858
  this.recordTrace('submit_echo_missing', diagnostic);
1859
1859
  if (this.requirePromptEchoBeforeSubmit) {
1860
- const message = `${this.cliName} prompt echo was not observed on the PTY screen before submit`;
1861
- LOG.warn('CLI', `[${this.cliType}] ${message} elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
1862
- completion.rejectOnce(new Error(message));
1860
+ // At this point the prompt text write already completed. Rejecting without
1861
+ // a submit key can leave the delegated CLI with an unsent prompt sitting at
1862
+ // the input line, which makes later coordinator sends appear stuck. Prefer a
1863
+ // guarded submit after the full echo wait; the existing stuck-submit retry
1864
+ // will send a delayed follow-up Enter if the prompt remains visible.
1865
+ LOG.warn('CLI', `[${this.cliType}] prompt echo was not observed before submit; sending guarded submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
1866
+ this.submitSendKey(state, completion);
1863
1867
  return;
1864
1868
  }
1865
1869
  LOG.warn('CLI', `[${this.cliType}] prompt echo was not observed before submit; sending submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs}`);
@@ -36,6 +36,7 @@ import { createInteractionId, getRecentDebugTrace, recordDebugTrace } from '../l
36
36
  import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../session-host/runtime-surface.js';
37
37
  import { createHermesManualMeshCoordinatorSetup, resolveMeshCoordinatorSetup } from './mesh-coordinator.js';
38
38
  import { buildSessionEntries } from '../status/builders.js';
39
+ import { handleMeshForwardEvent } from '../mesh/mesh-events.js';
39
40
  import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
40
41
  import { getSessionCompletionMarker } from '../status/snapshot.js';
41
42
  import { execNpmCommandSync, resolveCurrentGlobalInstallSurface, spawnDetachedDaemonUpgradeHelper } from './upgrade-helper.js';
@@ -471,6 +472,10 @@ export class DaemonCommandRouter {
471
472
  private async executeDaemonCommand(cmd: string, args: any): Promise<CommandRouterResult | null> {
472
473
  switch (cmd) {
473
474
  // ─── CLI / ACP commands ───
475
+ case 'mesh_forward_event': {
476
+ return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager } as any, args as Record<string, unknown>);
477
+ }
478
+
474
479
  case 'launch_cli':
475
480
  case 'stop_cli':
476
481
  case 'set_cli_view_mode':
@@ -15,6 +15,75 @@ function formatCompletionMetadata(event: Record<string, unknown>): string {
15
15
  return parts.length > 0 ? ` (${parts.join('; ')})` : '';
16
16
  }
17
17
 
18
+ function buildMeshSystemMessage(args: {
19
+ event: string;
20
+ nodeLabel: string;
21
+ metadataEvent: Record<string, unknown>;
22
+ }): string {
23
+ const metadata = formatCompletionMetadata(args.metadataEvent);
24
+ if (args.event === 'agent:generating_completed') {
25
+ return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. You may use mesh_read_chat to review its progress.`;
26
+ }
27
+ if (args.event === 'agent:waiting_approval') {
28
+ return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
29
+ }
30
+ return '';
31
+ }
32
+
33
+ function injectMeshSystemMessage(components: DaemonComponents, args: {
34
+ meshId: string;
35
+ sourceInstanceId?: string;
36
+ nodeLabel: string;
37
+ event: string;
38
+ metadataEvent: Record<string, unknown>;
39
+ }) {
40
+ const coordinatorInstances = components.instanceManager.getByCategory('cli').filter((inst) => {
41
+ const instState = inst.getState();
42
+ if (instState.settings?.meshCoordinatorFor !== args.meshId) return false;
43
+ if (args.sourceInstanceId && instState.instanceId === args.sourceInstanceId) return false;
44
+ return true;
45
+ });
46
+
47
+ if (coordinatorInstances.length === 0) return { success: true, forwarded: 0 };
48
+
49
+ const messageText = buildMeshSystemMessage({
50
+ event: args.event,
51
+ nodeLabel: args.nodeLabel,
52
+ metadataEvent: args.metadataEvent,
53
+ });
54
+ if (!messageText) return { success: false, error: 'unsupported mesh event' };
55
+
56
+ for (const coord of coordinatorInstances) {
57
+ const coordState = coord.getState();
58
+ LOG.info('MeshEvents', `Forwarding mesh event to coordinator ${coordState.instanceId}`);
59
+ coord.onEvent('send_message', { input: { text: messageText, textFallback: messageText } });
60
+ }
61
+ return { success: true, forwarded: coordinatorInstances.length };
62
+ }
63
+
64
+ export function handleMeshForwardEvent(components: DaemonComponents, payload: Record<string, unknown>) {
65
+ const eventName = readNonEmptyString(payload.event);
66
+ if (eventName !== 'agent:generating_completed' && eventName !== 'agent:waiting_approval') {
67
+ return { success: false, error: 'unsupported mesh event' };
68
+ }
69
+ const meshId = readNonEmptyString(payload.meshId);
70
+ if (!meshId) return { success: false, error: 'meshId required' };
71
+
72
+ const nodeId = readNonEmptyString(payload.nodeId);
73
+ const workspace = readNonEmptyString(payload.workspace);
74
+ const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : 'Remote agent';
75
+ return injectMeshSystemMessage(components, {
76
+ meshId,
77
+ nodeLabel,
78
+ event: eventName,
79
+ metadataEvent: {
80
+ targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId),
81
+ providerType: readNonEmptyString(payload.providerType),
82
+ providerSessionId: readNonEmptyString(payload.providerSessionId),
83
+ },
84
+ });
85
+ }
86
+
18
87
  export function setupMeshEventForwarding(components: DaemonComponents) {
19
88
  components.instanceManager.onEvent((event) => {
20
89
  // We only care about agent sub-session completion or waiting approval
@@ -38,22 +107,6 @@ export function setupMeshEventForwarding(components: DaemonComponents) {
38
107
  const meshId = meshIdFromRuntime || readNonEmptyString(mesh?.id);
39
108
  if (!meshId) return;
40
109
 
41
- // Find the coordinator session(s)
42
- const allInstances = components.instanceManager.getByCategory('cli');
43
- const coordinatorInstances = allInstances.filter((inst) => {
44
- const instState = inst.getState();
45
-
46
- // The coordinator session was launched with meshCoordinatorFor setting
47
- if (instState.settings?.meshCoordinatorFor !== meshId) return false;
48
-
49
- // Exclude the source instance itself (just in case)
50
- if (instState.instanceId === instanceId) return false;
51
-
52
- return true;
53
- });
54
-
55
- if (coordinatorInstances.length === 0) return;
56
-
57
110
  // Determine node label. Inline/cloud meshes may be unavailable here, so preserve runtime node id.
58
111
  const targetNode = mesh?.nodes?.find((n: any) => n.workspace === workspace);
59
112
  const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
@@ -62,23 +115,13 @@ export function setupMeshEventForwarding(components: DaemonComponents) {
62
115
  : runtimeNodeId
63
116
  ? `Node '${runtimeNodeId}'`
64
117
  : `Agent at ${workspace}`;
65
- const metadata = formatCompletionMetadata(event);
66
-
67
- // Construct a system message in English
68
- let messageText = '';
69
- if (event.event === 'agent:generating_completed') {
70
- messageText = `[System] ${nodeLabel} has completed its task and is now idle${metadata}. You may use mesh_read_chat to review its progress.`;
71
- } else if (event.event === 'agent:waiting_approval') {
72
- messageText = `[System] ${nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
73
- }
74
118
 
75
- if (!messageText) return;
76
-
77
- // Inject the message into the coordinator sessions
78
- for (const coord of coordinatorInstances) {
79
- const coordState = coord.getState();
80
- LOG.info('MeshEvents', `Forwarding event from ${workspace} to coordinator ${coordState.instanceId}`);
81
- coord.onEvent('send_message', { input: { text: messageText, textFallback: messageText } });
82
- }
119
+ injectMeshSystemMessage(components, {
120
+ meshId,
121
+ sourceInstanceId: instanceId,
122
+ nodeLabel,
123
+ event: event.event,
124
+ metadataEvent: event,
125
+ });
83
126
  });
84
127
  }