@adhdev/daemon-core 0.9.82-rc.230 → 0.9.82-rc.232

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.230",
3
+ "version": "0.9.82-rc.232",
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",
@@ -5866,12 +5866,15 @@ export class DaemonCommandRouter {
5866
5866
  nodeDaemonId = typeof node?.daemonId === 'string' ? node.daemonId.trim() : undefined;
5867
5867
  }
5868
5868
  // If the target node belongs to a remote daemon, forward the command there.
5869
+ // _meshDirectDispatch prevents re-forwarding (and P2P self-dial) when the stored
5870
+ // daemonId uses a legacy format that doesn't match the receiving daemon's identity.
5869
5871
  const selfDaemonId = this.deps.statusInstanceId;
5870
5872
  const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
5871
- if (isRemote && this.deps.dispatchMeshCommand) {
5873
+ if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
5872
5874
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId!, 'fast_forward_mesh_node', {
5873
5875
  ...(typeof args === 'object' && args !== null ? args as Record<string, unknown> : {}),
5874
5876
  workspace,
5877
+ _meshDirectDispatch: true,
5875
5878
  });
5876
5879
  return (forwarded ?? { success: false, error: 'no response from remote node' }) as CommandRouterResult;
5877
5880
  }
@@ -5916,11 +5919,14 @@ export class DaemonCommandRouter {
5916
5919
  let worktreeCleanup: Record<string, unknown> | undefined;
5917
5920
  if (node?.isLocalWorktree) {
5918
5921
  const nodeDaemonId = typeof node.daemonId === 'string' ? node.daemonId.trim() : undefined;
5919
- const isRemoteWorktree = nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand;
5922
+ const isRemoteWorktree = nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand
5923
+ && !args?._meshDirectDispatch;
5920
5924
  if (isRemoteWorktree) {
5921
5925
  // Worktree lives on a different machine — ask that daemon to clean it up.
5926
+ // _meshDirectDispatch prevents re-forwarding when stored daemonId uses legacy format.
5922
5927
  const forwarded = await this.deps.dispatchMeshCommand!(nodeDaemonId!, 'remove_mesh_node', {
5923
5928
  ...(typeof args === 'object' && args !== null ? args as Record<string, unknown> : {}),
5929
+ _meshDirectDispatch: true,
5924
5930
  });
5925
5931
  return (forwarded ?? { success: false, error: 'no response from remote node' }) as CommandRouterResult;
5926
5932
  }
@@ -6007,10 +6013,14 @@ export class DaemonCommandRouter {
6007
6013
  if (!sourceNode) return { success: false, error: `Source node '${sourceNodeId}' not found in mesh` };
6008
6014
 
6009
6015
  // Forward to the source node's daemon if it's on a different machine.
6016
+ // _meshDirectDispatch prevents infinite re-forwarding when the stored daemonId
6017
+ // uses a legacy format that doesn't match the receiving daemon's statusInstanceId.
6010
6018
  const sourceDaemonId = typeof sourceNode.daemonId === 'string' ? sourceNode.daemonId.trim() : undefined;
6011
- if (sourceDaemonId && sourceDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand) {
6019
+ if (sourceDaemonId && sourceDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand
6020
+ && !args?._meshDirectDispatch) {
6012
6021
  const forwarded = await this.deps.dispatchMeshCommand(sourceDaemonId, 'clone_mesh_node', {
6013
6022
  ...(typeof args === 'object' && args !== null ? args as Record<string, unknown> : {}),
6023
+ _meshDirectDispatch: true,
6014
6024
  });
6015
6025
  return (forwarded ?? { success: false, error: 'no response from remote node' }) as CommandRouterResult;
6016
6026
  }
@@ -6271,10 +6281,13 @@ export class DaemonCommandRouter {
6271
6281
  if (!node.isLocalWorktree) return { success: false, error: 'Node is not a local worktree node' };
6272
6282
 
6273
6283
  // Bootstrap runs scripts in the worktree path — forward to the node's daemon if remote.
6284
+ // _meshDirectDispatch prevents re-forwarding when stored daemonId uses legacy format.
6274
6285
  const nodeDaemonId = typeof node.daemonId === 'string' ? node.daemonId.trim() : undefined;
6275
- if (nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand) {
6286
+ if (nodeDaemonId && nodeDaemonId !== this.deps.statusInstanceId && this.deps.dispatchMeshCommand
6287
+ && !args?._meshDirectDispatch) {
6276
6288
  const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, 'retry_mesh_node_bootstrap', {
6277
6289
  ...(typeof args === 'object' && args !== null ? args as Record<string, unknown> : {}),
6290
+ _meshDirectDispatch: true,
6278
6291
  });
6279
6292
  return (forwarded ?? { success: false, error: 'no response from remote node' }) as CommandRouterResult;
6280
6293
  }
@@ -97,6 +97,15 @@ export function buildMeshSystemMessage(args: {
97
97
  if (args.event === 'monitor:long_generating') {
98
98
  return `[System] ${args.nodeLabel} is still reported as generating after a long interval${metadata}. Wait for pendingCoordinatorEvents or a completion/status event; if the user explicitly asks for status, make one bounded status check and then wait again.`;
99
99
  }
100
+ if (args.event === 'worktree_bootstrap_complete') {
101
+ const worktreePath = readNonEmptyString(args.metadataEvent.worktreePath);
102
+ const durationMs = typeof args.metadataEvent.durationMs === 'number' ? args.metadataEvent.durationMs : undefined;
103
+ return `[System] ${args.nodeLabel} worktree bootstrap completed${worktreePath ? ` at ${worktreePath}` : ''}${durationMs !== undefined ? ` in ${Math.round(durationMs / 1000)}s` : ''}. The worktree is ready — use \`mesh_launch_session\` to start an agent.`;
104
+ }
105
+ if (args.event === 'worktree_bootstrap_failed') {
106
+ const error = readNonEmptyString(args.metadataEvent.error);
107
+ return `[System] ${args.nodeLabel} worktree bootstrap failed${error ? `: ${error}` : '.'}. Use \`mesh_retry_node_bootstrap\` to retry or inspect the node state.`;
108
+ }
100
109
  if (args.event === 'refine:accepted') {
101
110
  const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
102
111
  return `[System] Refinery accepted async job${jobId ? ` ${jobId}` : ''} for ${args.nodeLabel}. Completion/failure will be delivered as a terminal refine event; do not poll repeatedly.`;
@@ -295,11 +295,9 @@ function expandPath(template: string, input: NativeHistoryInput): string | null
295
295
  // Re-expand ~ in case the fallback used it.
296
296
  if (out.startsWith('~/')) out = path.join(os.homedir(), out.slice(2));
297
297
  const now = new Date();
298
- // claude (and a couple of other Anthropic-CLI–style providers) writes
299
- // its per-cwd transcript under the *resolved* path
300
- // (/private/tmp/foo, not /tmp/foo on macOS where /tmp -> /private/tmp).
301
- // Without realpath, the spec template `~/.claude/projects/{cwd_dashed}/…`
302
- // builds `-tmp-foo` and never finds the actual `-private-tmp-foo` dir.
298
+ // Claude writes per-cwd transcripts under the resolved path and replaces
299
+ // every non-alphanumeric project-path character except `_` and `-` with
300
+ // `-`. Realpath also handles aliases such as /tmp -> /private/tmp.
303
301
  const workspaceRaw = input.workspace ?? '';
304
302
  let workspaceResolved = workspaceRaw;
305
303
  if (workspaceRaw) {
@@ -309,6 +307,7 @@ function expandPath(template: string, input: NativeHistoryInput): string | null
309
307
  const vars: Record<string, string> = {
310
308
  cwd: workspaceResolved,
311
309
  cwd_dashed: workspaceResolved.replace(/\//g, '-'),
310
+ cwd_claude_project: claudeProjectDirName(workspaceResolved),
312
311
  session_id: input.providerSessionId || input.sessionId || input.historySessionId || '',
313
312
  yyyy: String(now.getFullYear()),
314
313
  mm: String(now.getMonth() + 1).padStart(2, '0'),
@@ -329,6 +328,10 @@ function expandPath(template: string, input: NativeHistoryInput): string | null
329
328
  return out;
330
329
  }
331
330
 
331
+ function claudeProjectDirName(workspace: string): string {
332
+ return workspace.replace(/[^A-Za-z0-9_-]/g, '-');
333
+ }
334
+
332
335
  function globToRegex(pattern: string): RegExp {
333
336
  // Minimal glob: `*` → `[^/]*`, `?` → `[^/]`, `.` → `\.`. Anchored.
334
337
  const re = pattern
@@ -463,6 +466,7 @@ function expandPathForDate(template: string, input: NativeHistoryInput, day: Dat
463
466
  const vars: Record<string, string> = {
464
467
  cwd: workspaceResolved,
465
468
  cwd_dashed: workspaceResolved.replace(/\//g, '-'),
469
+ cwd_claude_project: claudeProjectDirName(workspaceResolved),
466
470
  session_id: input.providerSessionId || input.sessionId || input.historySessionId || '',
467
471
  yyyy: String(day.getFullYear()),
468
472
  mm: String(day.getMonth() + 1).padStart(2, '0'),