@adhdev/daemon-core 0.9.76-rc.3 → 0.9.76-rc.30
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/cli-adapters/provider-cli-adapter.d.ts +2 -1
- package/dist/cli-adapters/provider-cli-runtime.d.ts +1 -0
- package/dist/commands/cli-manager.d.ts +17 -4
- package/dist/commands/mesh-coordinator.d.ts +2 -0
- package/dist/commands/router.d.ts +4 -0
- package/dist/config/mesh-config.d.ts +3 -0
- package/dist/git/git-types.d.ts +1 -1
- package/dist/git/git-worktree.d.ts +64 -0
- package/dist/git/index.d.ts +2 -0
- package/dist/index.js +1033 -375
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1060 -407
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +3 -0
- package/dist/providers/provider-instance-manager.d.ts +1 -0
- package/dist/providers/provider-instance.d.ts +2 -0
- package/dist/repo-mesh-types.d.ts +6 -0
- package/dist/shared-types.d.ts +22 -1
- package/package.json +3 -4
- package/src/cli-adapters/provider-cli-adapter.ts +6 -3
- package/src/cli-adapters/provider-cli-runtime.ts +3 -2
- package/src/commands/chat-commands.ts +50 -5
- package/src/commands/cli-manager.ts +78 -5
- package/src/commands/handler.ts +13 -4
- package/src/commands/mesh-coordinator.ts +149 -6
- package/src/commands/router.ts +319 -32
- package/src/config/mesh-config.ts +6 -0
- package/src/git/git-commands.ts +5 -1
- package/src/git/git-types.ts +1 -0
- package/src/git/git-worktree.ts +214 -0
- package/src/git/index.ts +14 -0
- package/src/mesh/coordinator-prompt.ts +25 -10
- package/src/providers/cli-provider-instance.d.ts +2 -0
- package/src/providers/cli-provider-instance.ts +55 -7
- package/src/providers/provider-instance-manager.ts +20 -1
- package/src/providers/provider-instance.ts +2 -0
- package/src/repo-mesh-types.ts +6 -0
- package/src/shared-types.ts +24 -1
- package/src/status/builders.ts +17 -12
- package/src/status/reporter.ts +6 -0
|
@@ -8,6 +8,7 @@ import { type ProviderModule } from './contracts.js';
|
|
|
8
8
|
import type { ProviderInstance, ProviderState, InstanceContext, HotChatSessionState, SessionModalState } from './provider-instance.js';
|
|
9
9
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
10
10
|
import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
11
|
+
import type { ChatMessage } from '../types.js';
|
|
11
12
|
type PersistableCliHistoryMessage = {
|
|
12
13
|
role: string;
|
|
13
14
|
content: string;
|
|
@@ -67,6 +68,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
67
68
|
constructor(provider: ProviderModule, workingDir: string, cliArgs?: string[], instanceId?: string, transportFactory?: PtyTransportFactory, options?: {
|
|
68
69
|
providerSessionId?: string;
|
|
69
70
|
launchMode?: 'new' | 'resume' | 'manual';
|
|
71
|
+
extraEnv?: Record<string, string>;
|
|
70
72
|
onProviderSessionResolved?: (info: {
|
|
71
73
|
instanceId: string;
|
|
72
74
|
providerType: string;
|
|
@@ -112,6 +114,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
112
114
|
private maybeAppendRuntimeRecoveryMessage;
|
|
113
115
|
private appendRuntimeSystemMessage;
|
|
114
116
|
private appendRuntimeMessage;
|
|
117
|
+
mergeRuntimeChatMessages(parsedMessages: ChatMessage[]): ChatMessage[];
|
|
115
118
|
private mergeConversationMessages;
|
|
116
119
|
private formatApprovalRequestMessage;
|
|
117
120
|
private promoteProviderSessionId;
|
|
@@ -67,6 +67,7 @@ export declare class ProviderInstanceManager {
|
|
|
67
67
|
onEvent(listener: (event: ProviderEvent & {
|
|
68
68
|
providerType: string;
|
|
69
69
|
}) => void): void;
|
|
70
|
+
emitProviderEvent(providerType: string, instanceId: string, event: ProviderEvent): void;
|
|
70
71
|
private emitPendingEvents;
|
|
71
72
|
/**
|
|
72
73
|
* Forward event to specific Instance
|
|
@@ -147,6 +147,8 @@ export interface InstanceContext {
|
|
|
147
147
|
onPtyData?: (data: string) => void;
|
|
148
148
|
/** Provider configvalue (resolved) */
|
|
149
149
|
settings: Record<string, any>;
|
|
150
|
+
/** Immediate provider-originated status/event emission. Used to avoid waiting for status polling. */
|
|
151
|
+
emitProviderEvent?: (event: ProviderEvent) => void;
|
|
150
152
|
}
|
|
151
153
|
export interface ProviderInstance {
|
|
152
154
|
/** Provider type */
|
|
@@ -53,6 +53,8 @@ export interface RepoMeshNodePolicy {
|
|
|
53
53
|
readOnly?: boolean;
|
|
54
54
|
canPush?: boolean;
|
|
55
55
|
maxConcurrentSessions?: number;
|
|
56
|
+
/** Ordered provider preference used when mesh_launch_session omits an explicit type. */
|
|
57
|
+
providerPriority?: string[];
|
|
56
58
|
}
|
|
57
59
|
export declare const DEFAULT_MESH_POLICY: RepoMeshPolicy;
|
|
58
60
|
export interface RepoMeshNodeCapabilities {
|
|
@@ -149,6 +151,10 @@ export interface LocalMeshNodeEntry {
|
|
|
149
151
|
policy: RepoMeshNodePolicy;
|
|
150
152
|
/** For single-machine mesh: same daemon, different worktree */
|
|
151
153
|
isLocalWorktree?: boolean;
|
|
154
|
+
/** Branch this worktree tracks (set when created via clone_mesh_node) */
|
|
155
|
+
worktreeBranch?: string;
|
|
156
|
+
/** Node ID this worktree was cloned from */
|
|
157
|
+
clonedFromNodeId?: string;
|
|
152
158
|
}
|
|
153
159
|
export interface RepoMeshStatus {
|
|
154
160
|
meshId: string;
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -328,6 +328,15 @@ export interface CompactSessionEntry {
|
|
|
328
328
|
settings?: Record<string, any>;
|
|
329
329
|
}
|
|
330
330
|
export type VersionUpdateReason = 'force_update_below' | 'major_minor_mismatch' | 'patch_mismatch' | 'daemon_ahead';
|
|
331
|
+
export type ReleaseChannel = 'stable' | 'preview';
|
|
332
|
+
export type NpmUpdateTag = 'latest' | 'next';
|
|
333
|
+
export interface VersionUpdatePolicy {
|
|
334
|
+
channel: ReleaseChannel;
|
|
335
|
+
npmTag: NpmUpdateTag;
|
|
336
|
+
targetVersion: string;
|
|
337
|
+
minVersion?: string;
|
|
338
|
+
updateCommand: string;
|
|
339
|
+
}
|
|
331
340
|
/** Available provider information */
|
|
332
341
|
export interface AvailableProviderInfo {
|
|
333
342
|
type: string;
|
|
@@ -469,6 +478,10 @@ export interface CompactDaemonEntry {
|
|
|
469
478
|
versionMismatch?: boolean;
|
|
470
479
|
versionUpdateRequired?: boolean;
|
|
471
480
|
versionUpdateReason?: VersionUpdateReason;
|
|
481
|
+
releaseChannel?: ReleaseChannel;
|
|
482
|
+
updateChannel?: ReleaseChannel;
|
|
483
|
+
updatePolicy?: VersionUpdatePolicy;
|
|
484
|
+
updateCommand?: string;
|
|
472
485
|
terminalBackend?: TerminalBackendStatus;
|
|
473
486
|
detectedIdes?: DetectedIdeInfo[];
|
|
474
487
|
availableProviders?: AvailableProviderInfo[];
|
|
@@ -490,10 +503,14 @@ export interface CloudDaemonSummaryEntry {
|
|
|
490
503
|
versionMismatch?: boolean;
|
|
491
504
|
versionUpdateRequired?: boolean;
|
|
492
505
|
versionUpdateReason?: VersionUpdateReason;
|
|
506
|
+
releaseChannel?: ReleaseChannel;
|
|
507
|
+
updateChannel?: ReleaseChannel;
|
|
508
|
+
updatePolicy?: VersionUpdatePolicy;
|
|
509
|
+
updateCommand?: string;
|
|
493
510
|
terminalBackend?: TerminalBackendStatus;
|
|
494
511
|
}
|
|
495
512
|
/** Minimal daemon bootstrap payload used by dashboard WS to initiate P2P. */
|
|
496
|
-
export interface DashboardBootstrapDaemonEntry {
|
|
513
|
+
export interface DashboardBootstrapDaemonEntry extends Partial<CloudDaemonSummaryEntry> {
|
|
497
514
|
id: string;
|
|
498
515
|
p2p?: StatusReportPayload['p2p'];
|
|
499
516
|
timestamp?: number;
|
|
@@ -505,6 +522,8 @@ export interface DaemonStatusEventPayload {
|
|
|
505
522
|
timestamp: number;
|
|
506
523
|
targetSessionId?: string;
|
|
507
524
|
providerType?: string;
|
|
525
|
+
providerSessionId?: string;
|
|
526
|
+
workspaceName?: string;
|
|
508
527
|
duration?: number;
|
|
509
528
|
elapsedSec?: number;
|
|
510
529
|
modalMessage?: string;
|
|
@@ -518,6 +537,8 @@ export interface DashboardStatusEventPayload {
|
|
|
518
537
|
daemonId?: string;
|
|
519
538
|
providerType?: string;
|
|
520
539
|
targetSessionId?: string;
|
|
540
|
+
providerSessionId?: string;
|
|
541
|
+
workspaceName?: string;
|
|
521
542
|
duration?: number;
|
|
522
543
|
elapsedSec?: number;
|
|
523
544
|
modalMessage?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.76-rc.
|
|
3
|
+
"version": "0.9.76-rc.30",
|
|
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",
|
|
@@ -52,16 +52,15 @@
|
|
|
52
52
|
"chalk": "^5.3.0",
|
|
53
53
|
"chokidar": "^5.0.0",
|
|
54
54
|
"conf": "^13.0.0",
|
|
55
|
+
"js-yaml": "^4.1.1",
|
|
55
56
|
"node-pty": "^1.2.0-beta.12",
|
|
56
57
|
"ws": "^8.19.0"
|
|
57
58
|
},
|
|
58
|
-
"bundleDependencies": [
|
|
59
|
-
"@adhdev/session-host-core"
|
|
60
|
-
],
|
|
61
59
|
"optionalDependencies": {
|
|
62
60
|
"@adhdev/ghostty-vt-node": "*"
|
|
63
61
|
},
|
|
64
62
|
"devDependencies": {
|
|
63
|
+
"@types/js-yaml": "^4.0.9",
|
|
65
64
|
"@types/node": "^22.0.0",
|
|
66
65
|
"@types/ws": "^8.18.1",
|
|
67
66
|
"tsup": "^8.2.0",
|
|
@@ -266,9 +266,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
266
266
|
const currentSnapshot = normalizeScreenSnapshot(screenText);
|
|
267
267
|
const lastSnapshot = this.lastScreenSnapshot;
|
|
268
268
|
if (!lastSnapshot || lastSnapshot === currentSnapshot) return screenText;
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
|
|
269
|
+
const activeScreenPattern = /\besc to (?:interrupt|stop)\b|Enter to interrupt, Ctrl\+C to cancel|Enter to confirm\s*[·•-]\s*Esc to cancel|\b(?:MCP servers?|tool calls?)\b[^\n\r]{0,160}\brequire approval\b/i;
|
|
270
|
+
const staleSnapshotLooksActive = activeScreenPattern.test(lastSnapshot);
|
|
271
|
+
const currentScreenLooksIdle = /(?:^|\n|\r)\s*[❯›>]\s*(?:Try\s+["“][^\n\r"”]+["”])?\s*(?:\n|\r|$)/.test(screenText)
|
|
272
|
+
&& !activeScreenPattern.test(screenText);
|
|
272
273
|
if (staleSnapshotLooksActive && currentScreenLooksIdle) return screenText;
|
|
273
274
|
if (currentSnapshot.length >= lastSnapshot.length) return screenText;
|
|
274
275
|
// Terminal screen reads can miss a just-rendered completed Hermes box while
|
|
@@ -421,6 +422,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
421
422
|
provider: CliProviderModule,
|
|
422
423
|
workingDir: string,
|
|
423
424
|
private extraArgs: string[] = [],
|
|
425
|
+
private extraEnv: Record<string, string> = {},
|
|
424
426
|
transportFactory: PtyTransportFactory = new NodePtyTransportFactory(),
|
|
425
427
|
) {
|
|
426
428
|
this.provider = provider;
|
|
@@ -522,6 +524,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
522
524
|
runtimeSettings: this.runtimeSettings,
|
|
523
525
|
workingDir: this.workingDir,
|
|
524
526
|
extraArgs: this.extraArgs,
|
|
527
|
+
extraEnv: this.extraEnv,
|
|
525
528
|
});
|
|
526
529
|
|
|
527
530
|
LOG.info('CLI', `[${this.cliType}] Spawning in ${this.workingDir}`);
|
|
@@ -27,8 +27,9 @@ export function resolveCliSpawnPlan(options: {
|
|
|
27
27
|
runtimeSettings: Record<string, any>;
|
|
28
28
|
workingDir: string;
|
|
29
29
|
extraArgs: string[];
|
|
30
|
+
extraEnv?: Record<string, string>;
|
|
30
31
|
}): CliSpawnPlan {
|
|
31
|
-
const { provider, runtimeSettings, workingDir, extraArgs } = options;
|
|
32
|
+
const { provider, runtimeSettings, workingDir, extraArgs, extraEnv } = options;
|
|
32
33
|
const { spawn: spawnConfig } = provider;
|
|
33
34
|
const configuredCommand = typeof runtimeSettings.executablePath === 'string' && runtimeSettings.executablePath.trim()
|
|
34
35
|
? runtimeSettings.executablePath.trim()
|
|
@@ -65,7 +66,7 @@ export function resolveCliSpawnPlan(options: {
|
|
|
65
66
|
shellArgs = allArgs;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
const env = buildCliSpawnEnv(process.env, spawnConfig.env);
|
|
69
|
+
const env = buildCliSpawnEnv(process.env, { ...(spawnConfig.env || {}), ...(extraEnv || {}) });
|
|
69
70
|
// Some CLI agents, notably Hermes, route their tools through TERMINAL_CWD
|
|
70
71
|
// rather than process.cwd(). Keep the generic ADHDev launch workspace as
|
|
71
72
|
// the single source of truth so PTY cwd and tool cwd cannot diverge.
|
|
@@ -28,6 +28,10 @@ interface ApprovalSelectableInstance extends ProviderInstance {
|
|
|
28
28
|
recordApprovalSelection?(buttonText: string): void;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
interface RuntimeChatMessageMerger extends ProviderInstance {
|
|
32
|
+
mergeRuntimeChatMessages?(messages: ChatMessage[]): ChatMessage[];
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
type LegacyStringScript = (params?: Record<string, unknown> | string) => string;
|
|
32
36
|
|
|
33
37
|
function getCurrentProviderType(h: CommandHelpers, fallback = ''): string {
|
|
@@ -250,6 +254,40 @@ function normalizeReadChatCommandStatus(status: unknown, activeModal: unknown):
|
|
|
250
254
|
}
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
function isGeneratingLikeStatus(status: unknown): boolean {
|
|
258
|
+
return status === 'generating' || status === 'streaming' || status === 'long_generating' || status === 'starting';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): boolean {
|
|
262
|
+
if (!isGeneratingLikeStatus(parsedStatus)) return false;
|
|
263
|
+
if (hasNonEmptyModalButtons(activeModal)) return false;
|
|
264
|
+
const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
|
|
265
|
+
if (adapterRawStatus !== 'idle') return false;
|
|
266
|
+
if (typeof adapter.isProcessing === 'function' && adapter.isProcessing()) return false;
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): string {
|
|
271
|
+
if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return 'idle';
|
|
272
|
+
return typeof parsedStatus === 'string' && parsedStatus.trim() ? parsedStatus : 'idle';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function finalizeStreamingMessagesWhenIdle(messages: ChatMessage[], status: string): ChatMessage[] {
|
|
276
|
+
if (status !== 'idle') return messages;
|
|
277
|
+
return messages.map((message) => {
|
|
278
|
+
const meta = message.meta && typeof message.meta === 'object'
|
|
279
|
+
? message.meta as Record<string, unknown>
|
|
280
|
+
: undefined;
|
|
281
|
+
const hasStreamingMeta = meta?.streaming === true;
|
|
282
|
+
if (message.bubbleState !== 'streaming' && !hasStreamingMeta) return message;
|
|
283
|
+
return {
|
|
284
|
+
...message,
|
|
285
|
+
...(message.bubbleState === 'streaming' ? { bubbleState: 'final' as const } : {}),
|
|
286
|
+
...(hasStreamingMeta ? { meta: { ...meta, streaming: false } } : {}),
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
253
291
|
function buildReadChatCommandResult(payload: Record<string, any>, args: any): CommandResult {
|
|
254
292
|
let validatedPayload: Record<string, any>;
|
|
255
293
|
const debugReadChat = payload?.debugReadChat && typeof payload.debugReadChat === 'object'
|
|
@@ -720,7 +758,7 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
|
|
|
720
758
|
}
|
|
721
759
|
|
|
722
760
|
export async function handleReadChat(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
723
|
-
const provider = h.getProvider(args?.agentType);
|
|
761
|
+
const provider = h.getProvider(args?.agentType || args?.providerType);
|
|
724
762
|
const transport = getTargetTransport(h, provider);
|
|
725
763
|
const historySessionId = getHistorySessionId(h, args);
|
|
726
764
|
|
|
@@ -760,10 +798,17 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
760
798
|
? parsedRecord.coverage
|
|
761
799
|
: undefined;
|
|
762
800
|
const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
|
|
763
|
-
const returnedStatus = parsedRecord.status
|
|
764
|
-
|
|
801
|
+
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus);
|
|
802
|
+
const runtimeMessageMerger = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
|
|
803
|
+
const parsedMessages = finalizeStreamingMessagesWhenIdle(parsedRecord.messages as ChatMessage[], returnedStatus);
|
|
804
|
+
const returnedMessages = runtimeMessageMerger?.category === 'cli'
|
|
805
|
+
&& runtimeMessageMerger.type === adapter.cliType
|
|
806
|
+
&& typeof runtimeMessageMerger.mergeRuntimeChatMessages === 'function'
|
|
807
|
+
? runtimeMessageMerger.mergeRuntimeChatMessages(parsedMessages)
|
|
808
|
+
: parsedMessages;
|
|
809
|
+
LOG.debug('Command', `[read_chat] cli-like parsed provider=${adapter.cliType} target=${String(args?.targetSessionId || '')} adapterStatus=${String(adapterStatus.status || '')} parsedStatus=${String(parsedRecord.status || '')} parsedMsgCount=${parsedRecord.messages.length} returnedMsgCount=${returnedMessages.length}`);
|
|
765
810
|
return buildReadChatCommandResult({
|
|
766
|
-
messages:
|
|
811
|
+
messages: returnedMessages,
|
|
767
812
|
status: returnedStatus,
|
|
768
813
|
activeModal,
|
|
769
814
|
debugReadChat: {
|
|
@@ -774,7 +819,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
774
819
|
returnedStatus: String(returnedStatus || ''),
|
|
775
820
|
shouldPreferAdapterMessages: false,
|
|
776
821
|
parsedMsgCount: parsedRecord.messages.length,
|
|
777
|
-
returnedMsgCount:
|
|
822
|
+
returnedMsgCount: returnedMessages.length,
|
|
778
823
|
},
|
|
779
824
|
...(title ? { title } : {}),
|
|
780
825
|
...(providerSessionId ? { providerSessionId } : {}),
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import * as os from 'os';
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import * as crypto from 'crypto';
|
|
11
|
-
import { existsSync } from 'fs';
|
|
11
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
12
12
|
import { execFileSync } from 'child_process';
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
@@ -132,6 +132,62 @@ type CliAdapterWithExtraArgs = CliAdapter & {
|
|
|
132
132
|
extraArgs?: string[];
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
+
type CliStartOptions = {
|
|
136
|
+
resumeSessionId?: string;
|
|
137
|
+
settingsOverride?: Record<string, any>;
|
|
138
|
+
extraEnv?: Record<string, string>;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const COORDINATOR_DELEGATED_ENV_UNSETS: Record<string, string> = {
|
|
142
|
+
ADHDEV_INLINE_MESH: '',
|
|
143
|
+
ADHDEV_MCP_TRANSPORT: '',
|
|
144
|
+
ADHDEV_MESH_ID: '',
|
|
145
|
+
HERMES_EPHEMERAL_SYSTEM_PROMPT: '',
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export interface CoordinatorDelegatedCliLaunchOptionsInput {
|
|
149
|
+
cliType: string;
|
|
150
|
+
workspace: string;
|
|
151
|
+
cliArgs?: string[];
|
|
152
|
+
env?: Record<string, string>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface CoordinatorDelegatedCliLaunchOptions {
|
|
156
|
+
cliArgs: string[];
|
|
157
|
+
env: Record<string, string>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function hasCliArg(args: string[], flag: string): boolean {
|
|
161
|
+
return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function ensureEmptyDelegatedMcpConfig(workspace: string): string {
|
|
165
|
+
const baseDir = path.join(os.tmpdir(), 'adhdev-delegated-agent-empty-mcp');
|
|
166
|
+
mkdirSync(baseDir, { recursive: true });
|
|
167
|
+
const workspaceHash = crypto.createHash('sha256').update(path.resolve(workspace || os.tmpdir())).digest('hex').slice(0, 16);
|
|
168
|
+
const filePath = path.join(baseDir, `${workspaceHash}.json`);
|
|
169
|
+
writeFileSync(filePath, JSON.stringify({ mcpServers: {} }, null, 2), 'utf-8');
|
|
170
|
+
return filePath;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function buildCoordinatorDelegatedCliLaunchOptions(
|
|
174
|
+
input: CoordinatorDelegatedCliLaunchOptionsInput,
|
|
175
|
+
): CoordinatorDelegatedCliLaunchOptions {
|
|
176
|
+
const cliType = String(input.cliType || '').trim();
|
|
177
|
+
const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
|
|
178
|
+
const env: Record<string, string> = { ...(input.env || {}), ...COORDINATOR_DELEGATED_ENV_UNSETS };
|
|
179
|
+
|
|
180
|
+
if (cliType === 'hermes-cli' && !hasCliArg(cliArgs, '--ignore-user-config')) {
|
|
181
|
+
cliArgs.unshift('--ignore-user-config');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (cliType === 'claude-cli' && !hasCliArg(cliArgs, '--mcp-config')) {
|
|
185
|
+
cliArgs.unshift('--mcp-config', ensureEmptyDelegatedMcpConfig(input.workspace));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { cliArgs, env };
|
|
189
|
+
}
|
|
190
|
+
|
|
135
191
|
function isUuid(value: string): boolean {
|
|
136
192
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
137
193
|
}
|
|
@@ -365,6 +421,7 @@ export class DaemonCliManager {
|
|
|
365
421
|
runtimeId: string,
|
|
366
422
|
providerSessionId?: string,
|
|
367
423
|
attachExisting = false,
|
|
424
|
+
extraEnv?: Record<string, string>,
|
|
368
425
|
): CliAdapter {
|
|
369
426
|
// cliType normalize (Resolve alias)
|
|
370
427
|
const normalizedType = this.providerLoader.resolveAlias(cliType);
|
|
@@ -382,7 +439,7 @@ export class DaemonCliManager {
|
|
|
382
439
|
providerSessionId,
|
|
383
440
|
attachExisting,
|
|
384
441
|
);
|
|
385
|
-
return new ProviderCliAdapter(resolvedProvider as CliProviderModule, workingDir, cliArgs, transportFactory);
|
|
442
|
+
return new ProviderCliAdapter(resolvedProvider as CliProviderModule, workingDir, cliArgs, extraEnv || {}, transportFactory);
|
|
386
443
|
}
|
|
387
444
|
|
|
388
445
|
throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
|
|
@@ -425,6 +482,7 @@ export class DaemonCliManager {
|
|
|
425
482
|
options?: {
|
|
426
483
|
providerSessionId?: string;
|
|
427
484
|
launchMode?: CliLaunchMode;
|
|
485
|
+
extraEnv?: Record<string, string>;
|
|
428
486
|
onProviderSessionResolved?: (info: {
|
|
429
487
|
instanceId: string;
|
|
430
488
|
providerType: string;
|
|
@@ -480,7 +538,7 @@ export class DaemonCliManager {
|
|
|
480
538
|
workingDir: string,
|
|
481
539
|
cliArgs?: string[],
|
|
482
540
|
initialModel?: string,
|
|
483
|
-
options?:
|
|
541
|
+
options?: CliStartOptions,
|
|
484
542
|
): Promise<{ runtimeSessionId: string; providerSessionId?: string }> {
|
|
485
543
|
const trimmed = (workingDir || '').trim();
|
|
486
544
|
if (!trimmed) throw new Error('working directory required');
|
|
@@ -629,6 +687,7 @@ export class DaemonCliManager {
|
|
|
629
687
|
{
|
|
630
688
|
providerSessionId: sessionBinding.providerSessionId,
|
|
631
689
|
launchMode: sessionBinding.launchMode,
|
|
690
|
+
extraEnv: options?.extraEnv,
|
|
632
691
|
onProviderSessionResolved: ({ providerSessionId, providerName, providerType, workspace }) => {
|
|
633
692
|
this.persistRecentActivity({
|
|
634
693
|
kind: 'cli',
|
|
@@ -651,6 +710,7 @@ export class DaemonCliManager {
|
|
|
651
710
|
key,
|
|
652
711
|
sessionBinding.providerSessionId,
|
|
653
712
|
false,
|
|
713
|
+
options?.extraEnv,
|
|
654
714
|
);
|
|
655
715
|
try {
|
|
656
716
|
await adapter.spawn();
|
|
@@ -899,12 +959,25 @@ export class DaemonCliManager {
|
|
|
899
959
|
const launchSource = resolved.source;
|
|
900
960
|
if (!cliType) throw new Error('cliType required');
|
|
901
961
|
|
|
962
|
+
const settingsOverride = args?.settings && typeof args.settings === 'object' ? args.settings : undefined;
|
|
963
|
+
const delegatedLaunch = settingsOverride?.launchedByCoordinator === true
|
|
964
|
+
? buildCoordinatorDelegatedCliLaunchOptions({
|
|
965
|
+
cliType,
|
|
966
|
+
workspace: dir,
|
|
967
|
+
cliArgs: args?.cliArgs,
|
|
968
|
+
env: args?.env,
|
|
969
|
+
})
|
|
970
|
+
: null;
|
|
902
971
|
const started = await this.startSession(
|
|
903
972
|
cliType,
|
|
904
973
|
dir,
|
|
905
|
-
args?.cliArgs,
|
|
974
|
+
delegatedLaunch ? delegatedLaunch.cliArgs : args?.cliArgs,
|
|
906
975
|
args?.initialModel,
|
|
907
|
-
{
|
|
976
|
+
{
|
|
977
|
+
resumeSessionId: args?.resumeSessionId,
|
|
978
|
+
settingsOverride,
|
|
979
|
+
extraEnv: delegatedLaunch ? delegatedLaunch.env : args?.env,
|
|
980
|
+
},
|
|
908
981
|
);
|
|
909
982
|
|
|
910
983
|
return {
|
package/src/commands/handler.ts
CHANGED
|
@@ -303,14 +303,15 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
303
303
|
const sessionLookupFailed = !!targetSessionId && !session;
|
|
304
304
|
|
|
305
305
|
const managerKey = this.extractIdeType(args, sessionLookupFailed);
|
|
306
|
-
let providerType: string | undefined;
|
|
306
|
+
let providerType: string | undefined = args?.agentType || args?.providerType;
|
|
307
307
|
|
|
308
308
|
if (!sessionLookupFailed) {
|
|
309
309
|
providerType =
|
|
310
310
|
session?.providerType
|
|
311
|
-
||
|
|
312
|
-
|| args?.providerType
|
|
311
|
+
|| providerType
|
|
313
312
|
|| this.inferProviderType(managerKey);
|
|
313
|
+
} else if (!providerType) {
|
|
314
|
+
providerType = this.inferProviderType(managerKey);
|
|
314
315
|
}
|
|
315
316
|
|
|
316
317
|
return { session, managerKey, providerType, sessionLookupFailed };
|
|
@@ -407,7 +408,15 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
407
408
|
'invoke_provider_script',
|
|
408
409
|
]);
|
|
409
410
|
|
|
410
|
-
|
|
411
|
+
const allowsInactiveReadChatFallback =
|
|
412
|
+
cmd === 'read_chat'
|
|
413
|
+
&& !!this._currentRoute.providerType
|
|
414
|
+
&& (
|
|
415
|
+
(typeof args?.providerSessionId === 'string' && args.providerSessionId.trim().length > 0)
|
|
416
|
+
|| (typeof args?.historySessionId === 'string' && args.historySessionId.trim().length > 0)
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd) && !allowsInactiveReadChatFallback) {
|
|
411
420
|
const result = {
|
|
412
421
|
success: false,
|
|
413
422
|
error: `Live session not found for targetSessionId: ${String(args?.targetSessionId || '').trim() || 'unknown'}`,
|