@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.
- package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +14 -4
- package/dist/commands/mesh-coordinator.d.ts +8 -0
- package/dist/index.js +137 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +137 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +10 -4
- package/src/cli-adapters/provider-cli-shared.ts +14 -4
- package/src/commands/cli-manager.ts +0 -4
- package/src/commands/mesh-coordinator.ts +28 -6
- package/src/commands/router.ts +99 -0
- package/src/commands/stream-commands.ts +8 -1
- package/src/mesh/mesh-events.ts +22 -5
- package/src/providers/cli-provider-instance.ts +2 -0
package/package.json
CHANGED
|
@@ -195,6 +195,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
195
195
|
|
|
196
196
|
// ─── CLI Scripts (script-based parsing) ───
|
|
197
197
|
private cliScripts: CliScripts;
|
|
198
|
+
/** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
|
|
199
|
+
private scriptState: unknown = null;
|
|
198
200
|
private runtimeSettings: Record<string, any> = {};
|
|
199
201
|
/** Full accumulated rendered PTY transcript for parser/readback use */
|
|
200
202
|
private accumulatedBuffer: string = '';
|
|
@@ -477,6 +479,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
477
479
|
this.cliScripts = scripts;
|
|
478
480
|
this.parsedStatusCache = null;
|
|
479
481
|
this.parseErrorMessage = null;
|
|
482
|
+
// Initialize per-session state: createState() is called once here and on script reload.
|
|
483
|
+
// The returned object lives until the PTY exits (scriptState = null on exit).
|
|
484
|
+
this.scriptState = typeof scripts.createState === 'function' ? scripts.createState() : null;
|
|
480
485
|
const scriptNames = listCliScriptNames(scripts);
|
|
481
486
|
LOG.info('CLI', `[${this.cliType}] CLI scripts injected: [${scriptNames.join(', ')}]`);
|
|
482
487
|
}
|
|
@@ -610,6 +615,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
610
615
|
this.ready = false;
|
|
611
616
|
this.startupParseGate = false;
|
|
612
617
|
this.spawnAt = 0;
|
|
618
|
+
this.scriptState = null;
|
|
613
619
|
this.onStatusChange?.();
|
|
614
620
|
});
|
|
615
621
|
|
|
@@ -1470,7 +1476,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1470
1476
|
scope: this.currentTurnScope,
|
|
1471
1477
|
runtimeSettings: this.runtimeSettings,
|
|
1472
1478
|
});
|
|
1473
|
-
const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
|
|
1479
|
+
const session = this.cliScripts.parseSession(this.scriptState, { ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
|
|
1474
1480
|
this.parseErrorMessage = null;
|
|
1475
1481
|
return session && typeof session === 'object' ? session : null;
|
|
1476
1482
|
} catch (e: any) {
|
|
@@ -1485,7 +1491,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1485
1491
|
if (!this.cliScripts?.detectStatus) return null;
|
|
1486
1492
|
try {
|
|
1487
1493
|
const screenText = this.terminalScreen.getText();
|
|
1488
|
-
const status = this.cliScripts.detectStatus({
|
|
1494
|
+
const status = this.cliScripts.detectStatus(this.scriptState, {
|
|
1489
1495
|
tail: text.slice(-500),
|
|
1490
1496
|
screenText,
|
|
1491
1497
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -1505,7 +1511,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1505
1511
|
try {
|
|
1506
1512
|
const screenText = this.terminalScreen.getText();
|
|
1507
1513
|
const buffer = screenText || this.accumulatedBuffer;
|
|
1508
|
-
return this.cliScripts.parseApproval({
|
|
1514
|
+
return this.cliScripts.parseApproval(this.scriptState, {
|
|
1509
1515
|
buffer,
|
|
1510
1516
|
screenText,
|
|
1511
1517
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -1640,7 +1646,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1640
1646
|
scope: this.currentTurnScope,
|
|
1641
1647
|
runtimeSettings: this.runtimeSettings,
|
|
1642
1648
|
});
|
|
1643
|
-
return await Promise.resolve(fn({
|
|
1649
|
+
return await Promise.resolve(fn(this.scriptState, {
|
|
1644
1650
|
...input,
|
|
1645
1651
|
args: args && typeof args === 'object' ? { ...args } : {},
|
|
1646
1652
|
}));
|
|
@@ -48,11 +48,21 @@ export interface ParsedSession {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export interface CliScripts {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Optional state factory. Called once per CLI session start (or script reload).
|
|
53
|
+
* The returned object is passed as the first argument to detectStatus, parseApproval,
|
|
54
|
+
* and parseSession on every invocation, allowing scripts to maintain per-session state
|
|
55
|
+
* (e.g. last-seen status, approval fingerprints, stability counters).
|
|
56
|
+
*
|
|
57
|
+
* Scripts that don't define createState() receive null as the state argument,
|
|
58
|
+
* making this change fully backward compatible.
|
|
59
|
+
*/
|
|
60
|
+
createState?: () => unknown;
|
|
61
|
+
parseSession?: (state: unknown, input: CliScriptInput & { tail?: string; tailScreen?: CliScreenSnapshot }) => ParsedSession | null;
|
|
62
|
+
detectStatus?: (state: unknown, input: CliStatusInput) => string | null;
|
|
63
|
+
parseApproval?: (state: unknown, input: CliApprovalInput) => { message: string; buttons: string[] } | null;
|
|
54
64
|
resolveAction?: (data: any) => string;
|
|
55
|
-
[name: string]: ((input: any) => any) | undefined;
|
|
65
|
+
[name: string]: ((state: unknown, input: any) => any) | ((data: any) => any) | (() => unknown) | undefined;
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
export interface CliScreenLine {
|
|
@@ -177,10 +177,6 @@ export function buildCoordinatorDelegatedCliLaunchOptions(
|
|
|
177
177
|
const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
|
|
178
178
|
const env: Record<string, string> = { ...(input.env || {}), ...COORDINATOR_DELEGATED_ENV_UNSETS };
|
|
179
179
|
|
|
180
|
-
if (cliType === 'hermes-cli' && !hasCliArg(cliArgs, '--ignore-user-config')) {
|
|
181
|
-
cliArgs.unshift('--ignore-user-config');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
180
|
if (cliType === 'claude-cli' && !hasCliArg(cliArgs, '--mcp-config')) {
|
|
185
181
|
cliArgs.unshift('--mcp-config', ensureEmptyDelegatedMcpConfig(input.workspace));
|
|
186
182
|
}
|
|
@@ -28,6 +28,15 @@ export type MeshCoordinatorSetup =
|
|
|
28
28
|
instructions: string
|
|
29
29
|
template: string
|
|
30
30
|
}
|
|
31
|
+
| {
|
|
32
|
+
/** Provider registers MCP via its own CLI command (e.g. `codex mcp add` / `gemini mcp add`). */
|
|
33
|
+
kind: 'cli_command'
|
|
34
|
+
serverName: string
|
|
35
|
+
/** The rendered shell command to execute before launching the coordinator session. */
|
|
36
|
+
command: string
|
|
37
|
+
requiresRestart: boolean
|
|
38
|
+
instructions: string
|
|
39
|
+
}
|
|
31
40
|
| {
|
|
32
41
|
kind: 'unsupported'
|
|
33
42
|
reason: string
|
|
@@ -152,6 +161,24 @@ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetup
|
|
|
152
161
|
if (!instructions || !template?.trim()) {
|
|
153
162
|
return { kind: 'unsupported', reason: 'Provider manual MCP setup is missing instructions or template' }
|
|
154
163
|
}
|
|
164
|
+
const renderedTemplate = renderMeshCoordinatorTemplate(template, {
|
|
165
|
+
meshId,
|
|
166
|
+
workspace,
|
|
167
|
+
serverName,
|
|
168
|
+
adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND,
|
|
169
|
+
})
|
|
170
|
+
// Detect if the template is a runnable CLI command (single line, no YAML/JSON structure).
|
|
171
|
+
// If so, use cli_command kind so the daemon can execute it automatically.
|
|
172
|
+
const isCliCommand = !renderedTemplate.trim().includes('\n') && !renderedTemplate.trim().startsWith('{')
|
|
173
|
+
if (isCliCommand) {
|
|
174
|
+
return {
|
|
175
|
+
kind: 'cli_command',
|
|
176
|
+
serverName,
|
|
177
|
+
command: renderedTemplate.trim(),
|
|
178
|
+
requiresRestart: mcpConfig.requiresRestart === true,
|
|
179
|
+
instructions: instructions,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
155
182
|
return {
|
|
156
183
|
kind: 'manual',
|
|
157
184
|
serverName,
|
|
@@ -159,12 +186,7 @@ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetup
|
|
|
159
186
|
configPathCommand: mcpConfig.configPathCommand,
|
|
160
187
|
requiresRestart: mcpConfig.requiresRestart === true,
|
|
161
188
|
instructions,
|
|
162
|
-
template:
|
|
163
|
-
meshId,
|
|
164
|
-
workspace,
|
|
165
|
-
serverName,
|
|
166
|
-
adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND,
|
|
167
|
-
}),
|
|
189
|
+
template: renderedTemplate,
|
|
168
190
|
}
|
|
169
191
|
}
|
|
170
192
|
|
package/src/commands/router.ts
CHANGED
|
@@ -1711,6 +1711,105 @@ export class DaemonCommandRouter {
|
|
|
1711
1711
|
};
|
|
1712
1712
|
}
|
|
1713
1713
|
|
|
1714
|
+
// ─── CLI-command MCP registration (Codex, Gemini CLI) ───────────
|
|
1715
|
+
if (coordinatorSetup.kind === 'cli_command') {
|
|
1716
|
+
// Build coordinator prompt first — fail closed on errors.
|
|
1717
|
+
let cliCmdSystemPrompt = '';
|
|
1718
|
+
try {
|
|
1719
|
+
cliCmdSystemPrompt = buildCoordinatorSystemPrompt({ mesh, coordinatorCliType: cliType });
|
|
1720
|
+
} catch (error: any) {
|
|
1721
|
+
const message = error?.message || String(error);
|
|
1722
|
+
LOG.error('MeshCoordinator', `Failed to build coordinator prompt: ${message}`);
|
|
1723
|
+
return {
|
|
1724
|
+
success: false,
|
|
1725
|
+
code: 'mesh_coordinator_prompt_failed',
|
|
1726
|
+
error: `Failed to build Repo Mesh coordinator prompt: ${message}`,
|
|
1727
|
+
meshId, cliType, workspace,
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// Run the provider's MCP registration command.
|
|
1732
|
+
try {
|
|
1733
|
+
const { execFileSync: execCmdSync } = await import('node:child_process');
|
|
1734
|
+
const cmdParts = coordinatorSetup.command.trim().split(/\s+/);
|
|
1735
|
+
const [regCmd, ...regArgs] = cmdParts;
|
|
1736
|
+
LOG.info('MeshCoordinator', `Running MCP registration: ${coordinatorSetup.command}`);
|
|
1737
|
+
execCmdSync(regCmd, regArgs, { stdio: 'pipe', timeout: 15_000 });
|
|
1738
|
+
} catch (error: any) {
|
|
1739
|
+
// Non-fatal — server may already be registered (providers return exit 1 on duplicate).
|
|
1740
|
+
LOG.warn('MeshCoordinator', `MCP registration command failed (may be pre-registered): ${error?.message || error}`);
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
// Inject system prompt using provider-native methods.
|
|
1744
|
+
// Codex: -c 'instructions="..."' CLI config override
|
|
1745
|
+
// Gemini: write GEMINI.md to workspace (auto-loaded as context)
|
|
1746
|
+
const cliCmdArgs: string[] = [];
|
|
1747
|
+
const cliCmdEnv: Record<string, string> = {};
|
|
1748
|
+
if (cliCmdSystemPrompt) {
|
|
1749
|
+
if (cliType === 'codex-cli') {
|
|
1750
|
+
// Codex reads `developer_instructions` from config.toml as system instructions.
|
|
1751
|
+
// The -c flag overrides a config key for this session only.
|
|
1752
|
+
cliCmdArgs.push('-c', `developer_instructions=${JSON.stringify(cliCmdSystemPrompt)}`);
|
|
1753
|
+
} else if (cliType === 'gemini-cli') {
|
|
1754
|
+
// Gemini CLI auto-loads GEMINI.md from CWD as project context.
|
|
1755
|
+
// Write a temporary GEMINI.md to the workspace before launch.
|
|
1756
|
+
try {
|
|
1757
|
+
const { writeFileSync: wfs, existsSync: efs, readFileSync: rfs } = await import('node:fs');
|
|
1758
|
+
const geminiMdPath = `${workspace}/GEMINI.md`;
|
|
1759
|
+
const marker = '<!-- adhdev-mesh-coordinator-prompt -->';
|
|
1760
|
+
const markerEnd = '<!-- /adhdev-mesh-coordinator-prompt -->';
|
|
1761
|
+
const block = `${marker}\n${cliCmdSystemPrompt}\n${markerEnd}`;
|
|
1762
|
+
if (efs(geminiMdPath)) {
|
|
1763
|
+
const existing = rfs(geminiMdPath, 'utf-8');
|
|
1764
|
+
// Replace existing block or append
|
|
1765
|
+
const replaced = existing.replace(
|
|
1766
|
+
new RegExp(`${marker}[\\s\\S]*?${markerEnd}`, 'g'),
|
|
1767
|
+
block,
|
|
1768
|
+
);
|
|
1769
|
+
wfs(geminiMdPath, replaced.includes(marker) ? replaced : `${existing}\n\n${block}`);
|
|
1770
|
+
} else {
|
|
1771
|
+
wfs(geminiMdPath, block);
|
|
1772
|
+
}
|
|
1773
|
+
LOG.info('MeshCoordinator', `Wrote coordinator prompt to ${workspace}/GEMINI.md`);
|
|
1774
|
+
} catch (e: any) {
|
|
1775
|
+
LOG.warn('MeshCoordinator', `Could not write GEMINI.md: ${e?.message || e}`);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
const cliCmdLaunch: any = await this.deps.cliManager.handleCliCommand('launch_cli', {
|
|
1781
|
+
cliType,
|
|
1782
|
+
dir: workspace,
|
|
1783
|
+
cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : undefined,
|
|
1784
|
+
env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : undefined,
|
|
1785
|
+
settings: { meshCoordinatorFor: meshId },
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1788
|
+
if (!cliCmdLaunch?.success) {
|
|
1789
|
+
return { success: false, error: cliCmdLaunch?.error || 'Failed to launch CLI session' };
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
LOG.info('MeshCoordinator', `Launched ${cliType} coordinator (cli_command) for mesh ${meshId}`);
|
|
1793
|
+
try {
|
|
1794
|
+
const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
|
|
1795
|
+
appendLedgerEntry(meshId, {
|
|
1796
|
+
kind: 'coordinator_started',
|
|
1797
|
+
sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
|
|
1798
|
+
providerType: cliType,
|
|
1799
|
+
payload: { workspace },
|
|
1800
|
+
});
|
|
1801
|
+
} catch { /* best-effort */ }
|
|
1802
|
+
|
|
1803
|
+
return {
|
|
1804
|
+
success: true,
|
|
1805
|
+
meshId,
|
|
1806
|
+
cliType,
|
|
1807
|
+
workspace,
|
|
1808
|
+
sessionId: cliCmdLaunch.sessionId || cliCmdLaunch.id,
|
|
1809
|
+
mcpRegistered: true,
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1714
1813
|
const configFormat = coordinatorSetup.configFormat as MeshCoordinatorConfigFormat;
|
|
1715
1814
|
if (configFormat !== 'claude_mcp_json' && configFormat !== 'hermes_config_yaml') {
|
|
1716
1815
|
return {
|
|
@@ -113,11 +113,18 @@ export async function handleOpenPanel(h: CommandHelpers, args: any): Promise<Com
|
|
|
113
113
|
export async function handlePtyInput(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
114
114
|
const { cliType, data, targetSessionId } = args || {};
|
|
115
115
|
if (!data) return { success: false, error: 'data required' };
|
|
116
|
+
|
|
117
|
+
// Filter out VT100/VT420 Device Attributes responses (e.g. \x1b[?1;2c or \x1b[>0;276;0c)
|
|
118
|
+
// These are echoed by xterm.js in the dashboard in response to \x1b[c queries
|
|
119
|
+
// and pollute the CLI input buffer.
|
|
120
|
+
const cleanData = typeof data === 'string' ? data.replace(/\x1b\[[?>][0-9;]*c/g, '') : data;
|
|
121
|
+
if (!cleanData) return { success: true };
|
|
122
|
+
|
|
116
123
|
const adapter = h.getCliAdapter(targetSessionId || cliType);
|
|
117
124
|
if (!adapter || typeof adapter.writeRaw !== 'function') {
|
|
118
125
|
return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
|
|
119
126
|
}
|
|
120
|
-
await adapter.writeRaw(
|
|
127
|
+
await adapter.writeRaw(cleanData);
|
|
121
128
|
return { success: true };
|
|
122
129
|
}
|
|
123
130
|
|
package/src/mesh/mesh-events.ts
CHANGED
|
@@ -37,6 +37,7 @@ const MESH_COORDINATOR_EVENTS = new Set([
|
|
|
37
37
|
'agent:generating_completed',
|
|
38
38
|
'agent:waiting_approval',
|
|
39
39
|
'agent:stopped',
|
|
40
|
+
'agent:ready',
|
|
40
41
|
'monitor:long_generating',
|
|
41
42
|
]);
|
|
42
43
|
|
|
@@ -68,7 +69,9 @@ export function tryAssignQueueTask(
|
|
|
68
69
|
providerType: string
|
|
69
70
|
): boolean {
|
|
70
71
|
const task = claimNextTask(meshId, nodeId, sessionId);
|
|
71
|
-
if (!task)
|
|
72
|
+
if (!task) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
72
75
|
|
|
73
76
|
LOG.info('MeshQueue', `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
|
|
74
77
|
|
|
@@ -76,7 +79,7 @@ export function tryAssignQueueTask(
|
|
|
76
79
|
targetSessionId: sessionId,
|
|
77
80
|
cliType: providerType,
|
|
78
81
|
action: 'send_chat',
|
|
79
|
-
|
|
82
|
+
message: task.message,
|
|
80
83
|
}).catch((e: any) => {
|
|
81
84
|
LOG.error('MeshQueue', `Failed to dispatch task to node ${nodeId}: ${e?.message}`);
|
|
82
85
|
});
|
|
@@ -164,6 +167,7 @@ function buildMeshSystemMessage(args: {
|
|
|
164
167
|
function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
165
168
|
meshId: string;
|
|
166
169
|
sourceInstanceId?: string;
|
|
170
|
+
nodeId?: string;
|
|
167
171
|
nodeLabel: string;
|
|
168
172
|
event: string;
|
|
169
173
|
metadataEvent: Record<string, unknown>;
|
|
@@ -171,7 +175,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
171
175
|
// ── Task Queue & Ledger ──
|
|
172
176
|
if (args.event === 'agent:generating_completed') {
|
|
173
177
|
const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
|
|
174
|
-
const nodeId = readNonEmptyString(args.
|
|
178
|
+
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
175
179
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
176
180
|
|
|
177
181
|
if (sessionId) {
|
|
@@ -183,6 +187,16 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
183
187
|
}, 500);
|
|
184
188
|
}
|
|
185
189
|
}
|
|
190
|
+
} else if (args.event === 'agent:ready') {
|
|
191
|
+
const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
|
|
192
|
+
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
193
|
+
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
194
|
+
|
|
195
|
+
if (sessionId && nodeId && providerType) {
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
198
|
+
}, 500);
|
|
199
|
+
}
|
|
186
200
|
} else if (args.event === 'agent:stopped') {
|
|
187
201
|
const sessionId = readNonEmptyString(args.metadataEvent.targetSessionId);
|
|
188
202
|
if (sessionId) {
|
|
@@ -195,7 +209,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
195
209
|
try {
|
|
196
210
|
appendLedgerEntry(args.meshId, {
|
|
197
211
|
kind: ledgerKind,
|
|
198
|
-
nodeId: readNonEmptyString(args.
|
|
212
|
+
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined,
|
|
199
213
|
sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || undefined,
|
|
200
214
|
providerType: readNonEmptyString(args.metadataEvent.providerType) || undefined,
|
|
201
215
|
payload: {
|
|
@@ -219,7 +233,7 @@ function injectMeshSystemMessage(components: DaemonComponents, args: {
|
|
|
219
233
|
|
|
220
234
|
recoveryContext = getSessionRecoveryContext(args.meshId, {
|
|
221
235
|
sessionId: readNonEmptyString(args.metadataEvent.targetSessionId) || undefined,
|
|
222
|
-
nodeId: readNonEmptyString(args.
|
|
236
|
+
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || undefined,
|
|
223
237
|
maxRetries,
|
|
224
238
|
});
|
|
225
239
|
recoveryContext.failedProviderType = readNonEmptyString(args.metadataEvent.providerType) || null;
|
|
@@ -327,6 +341,7 @@ export function handleMeshForwardEvent(components: DaemonComponents, payload: Re
|
|
|
327
341
|
const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : 'Remote agent';
|
|
328
342
|
return injectMeshSystemMessage(components, {
|
|
329
343
|
meshId,
|
|
344
|
+
nodeId,
|
|
330
345
|
nodeLabel,
|
|
331
346
|
event: eventName,
|
|
332
347
|
metadataEvent: {
|
|
@@ -374,6 +389,7 @@ export function setupMeshEventForwarding(components: DaemonComponents) {
|
|
|
374
389
|
// Determine node label. Inline/cloud meshes may be unavailable here, so preserve runtime node id.
|
|
375
390
|
const targetNode = mesh?.nodes?.find((n: any) => n.workspace === workspace);
|
|
376
391
|
const runtimeNodeId = readNonEmptyString(settings.meshNodeId);
|
|
392
|
+
const resolvedNodeId = targetNode?.id || runtimeNodeId;
|
|
377
393
|
const nodeLabel = targetNode
|
|
378
394
|
? `Node '${targetNode.id}'`
|
|
379
395
|
: runtimeNodeId
|
|
@@ -383,6 +399,7 @@ export function setupMeshEventForwarding(components: DaemonComponents) {
|
|
|
383
399
|
injectMeshSystemMessage(components, {
|
|
384
400
|
meshId,
|
|
385
401
|
sourceInstanceId: instanceId,
|
|
402
|
+
nodeId: resolvedNodeId,
|
|
386
403
|
nodeLabel,
|
|
387
404
|
event: event.event,
|
|
388
405
|
metadataEvent: event,
|
|
@@ -833,6 +833,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
833
833
|
this.completedDebounceTimer = null;
|
|
834
834
|
}, 3000);
|
|
835
835
|
}
|
|
836
|
+
} else if (newStatus === 'idle' && this.lastStatus === 'starting') {
|
|
837
|
+
this.pushEvent({ event: 'agent:ready', chatTitle, timestamp: now });
|
|
836
838
|
} else if (newStatus === 'stopped') {
|
|
837
839
|
// Cancel any pending debounce
|
|
838
840
|
if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
|