@adhdev/daemon-core 0.8.69 → 0.8.70
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/agent-stream/manager.d.ts +2 -1
- package/dist/agent-stream/provider-adapter.d.ts +6 -2
- package/dist/agent-stream/types.d.ts +5 -2
- package/dist/commands/stream-commands.d.ts +2 -1
- package/dist/config/recent-activity.d.ts +4 -3
- package/dist/index.js +286 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +286 -64
- package/dist/index.mjs.map +1 -1
- package/dist/providers/contracts.d.ts +10 -0
- package/dist/providers/extension-provider-instance.d.ts +1 -0
- package/dist/providers/open-panel-support.d.ts +6 -0
- package/dist/providers/provider-instance.d.ts +2 -1
- package/dist/shared-types.d.ts +1 -1
- package/dist/types.d.ts +1 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/agent-stream/forward.ts +1 -0
- package/src/agent-stream/manager.d.ts +2 -1
- package/src/agent-stream/manager.ts +20 -5
- package/src/agent-stream/provider-adapter.d.ts +3 -2
- package/src/agent-stream/provider-adapter.ts +123 -4
- package/src/agent-stream/types.d.ts +3 -2
- package/src/agent-stream/types.ts +5 -2
- package/src/cli-adapters/provider-cli-adapter.ts +3 -1
- package/src/commands/cli-manager.ts +12 -0
- package/src/commands/handler.ts +4 -2
- package/src/commands/router.ts +1 -0
- package/src/commands/stream-commands.d.ts +2 -1
- package/src/commands/stream-commands.ts +74 -2
- package/src/config/recent-activity.ts +25 -15
- package/src/daemon/dev-auto-implement.ts +2 -2
- package/src/daemon/dev-server.ts +2 -2
- package/src/daemon/scaffold-template.ts +12 -5
- package/src/providers/contracts.d.ts +10 -0
- package/src/providers/contracts.ts +14 -0
- package/src/providers/extension-provider-instance.ts +15 -2
- package/src/providers/ide-provider-instance.ts +2 -0
- package/src/providers/open-panel-support.ts +40 -0
- package/src/providers/provider-instance.d.ts +2 -1
- package/src/providers/provider-instance.ts +2 -1
- package/src/providers/provider-schema.ts +10 -0
- package/src/providers/read-chat-contract.ts +1 -0
- package/src/shared-types.d.ts +1 -1
- package/src/shared-types.ts +1 -0
- package/src/status/builders.ts +9 -23
- package/src/status/snapshot.ts +2 -2
- package/src/types.ts +1 -0
|
@@ -199,6 +199,16 @@ export interface SwitchSessionResult {
|
|
|
199
199
|
clickY?: number;
|
|
200
200
|
error?: string;
|
|
201
201
|
}
|
|
202
|
+
export interface FocusEditorResult {
|
|
203
|
+
focused: boolean;
|
|
204
|
+
error?: string;
|
|
205
|
+
}
|
|
206
|
+
export interface OpenPanelResult {
|
|
207
|
+
opened: boolean;
|
|
208
|
+
visible: boolean;
|
|
209
|
+
focused?: boolean;
|
|
210
|
+
error?: string;
|
|
211
|
+
}
|
|
202
212
|
/**
|
|
203
213
|
* Method 1: Script-Click — script calls el.click() directly
|
|
204
214
|
* Cursor Suitable for IDEs using div.cursor-pointer elements.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProviderModule } from './contracts.js';
|
|
2
|
+
import type { SessionCapability } from '../shared-types.js';
|
|
3
|
+
export declare const IDE_PROVIDER_SESSION_CAPABILITIES_BASE: SessionCapability[];
|
|
4
|
+
export declare const EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE: SessionCapability[];
|
|
5
|
+
export declare function providerHasOpenPanelSupport(provider: Pick<ProviderModule, 'category' | 'scripts'>): boolean;
|
|
6
|
+
export declare function getProviderSessionCapabilities(provider: Pick<ProviderModule, 'category' | 'scripts'>, baseCapabilities: SessionCapability[]): SessionCapability[];
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Each Instance manages its own status.
|
|
9
9
|
*/
|
|
10
10
|
import type { ProviderResumeCapability } from './contracts.js';
|
|
11
|
-
import type { AcpConfigOption, AcpMode, ProviderControlSchema, ProviderSummaryMetadata } from '../shared-types.js';
|
|
11
|
+
import type { AcpConfigOption, AcpMode, ProviderControlSchema, ProviderSummaryMetadata, SessionCapability } from '../shared-types.js';
|
|
12
12
|
import type { ChatMessage } from '../types.js';
|
|
13
13
|
export type ProviderStatus = 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting';
|
|
14
14
|
export interface ProviderRuntimeWriteOwner {
|
|
@@ -66,6 +66,7 @@ interface ProviderStateBase {
|
|
|
66
66
|
pendingEvents: ProviderEvent[];
|
|
67
67
|
runtime?: ProviderRuntimeInfo;
|
|
68
68
|
resume?: ProviderResumeCapability;
|
|
69
|
+
sessionCapabilities?: SessionCapability[];
|
|
69
70
|
/** Dynamic control current values */
|
|
70
71
|
controlValues?: Record<string, string | number | boolean>;
|
|
71
72
|
/** Provider-declared controls schema (from provider.controls) */
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -225,7 +225,7 @@ export type UnsubscribeRequest = {
|
|
|
225
225
|
export type StandaloneWsStatusPayload = StatusReportPayload;
|
|
226
226
|
export type SessionTransport = 'cdp-page' | 'cdp-webview' | 'pty' | 'acp';
|
|
227
227
|
export type SessionKind = 'workspace' | 'agent';
|
|
228
|
-
export type SessionCapability = 'read_chat' | 'send_message' | 'new_session' | 'list_sessions' | 'switch_session' | 'resolve_action' | 'terminal_io' | 'resize_terminal' | 'change_model' | 'set_mode' | 'set_thought_level';
|
|
228
|
+
export type SessionCapability = 'read_chat' | 'send_message' | 'new_session' | 'list_sessions' | 'switch_session' | 'resolve_action' | 'open_panel' | 'terminal_io' | 'resize_terminal' | 'change_model' | 'set_mode' | 'set_thought_level';
|
|
229
229
|
import type { RuntimeWriteOwner, RuntimeAttachedClient, SessionStatus } from './shared-types-extra.js';
|
|
230
230
|
export type { RuntimeWriteOwner, RuntimeAttachedClient, SessionStatus } from './shared-types-extra.js';
|
|
231
231
|
export interface SessionEntry {
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ export function forwardAgentStreamsToIdeInstance(
|
|
|
34
34
|
summaryMetadata: stream.summaryMetadata || undefined,
|
|
35
35
|
effects: stream.effects || undefined,
|
|
36
36
|
sessionId: stream.sessionId || stream.instanceId || undefined,
|
|
37
|
+
providerSessionId: stream.providerSessionId || stream.sessionId || undefined,
|
|
37
38
|
title: stream.title || stream.agentName || undefined,
|
|
38
39
|
agentType: stream.agentType || undefined,
|
|
39
40
|
agentName: stream.agentName || undefined,
|
|
@@ -52,7 +52,8 @@ export declare class DaemonAgentStreamManager {
|
|
|
52
52
|
newSession(cdp: DaemonCdpManager, sessionId: string): Promise<boolean>;
|
|
53
53
|
listSessionChats(cdp: DaemonCdpManager, sessionId: string): Promise<AgentChatListItem[]>;
|
|
54
54
|
switchConversation(cdp: DaemonCdpManager, sessionId: string, conversationId: string): Promise<boolean>;
|
|
55
|
-
|
|
55
|
+
selectSession(cdp: DaemonCdpManager, sessionId: string): Promise<boolean>;
|
|
56
|
+
openSessionPanel(cdp: DaemonCdpManager, sessionId: string): Promise<boolean>;
|
|
56
57
|
getConnectedSessions(parentSessionId?: string): string[];
|
|
57
58
|
getManagedSession(sessionId: string): ManagedAgent | undefined;
|
|
58
59
|
dispose(cdpManagers: Map<string, DaemonCdpManager>): Promise<void>;
|
|
@@ -322,20 +322,35 @@ export class DaemonAgentStreamManager {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
async
|
|
325
|
+
async selectSession(cdp: DaemonCdpManager, sessionId: string): Promise<boolean> {
|
|
326
326
|
const target = this.getSessionTarget(sessionId);
|
|
327
327
|
if (!target?.parentSessionId) return false;
|
|
328
328
|
await this.setActiveSession(cdp, target.parentSessionId, sessionId);
|
|
329
329
|
await this.syncActiveSession(cdp, target.parentSessionId);
|
|
330
|
+
return this.managedBySessionId.has(sessionId);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async openSessionPanel(cdp: DaemonCdpManager, sessionId: string): Promise<boolean> {
|
|
334
|
+
const selected = await this.selectSession(cdp, sessionId);
|
|
335
|
+
if (!selected) return false;
|
|
330
336
|
const agent = this.managedBySessionId.get(sessionId);
|
|
331
|
-
if (!agent
|
|
337
|
+
if (!agent) return false;
|
|
338
|
+
let panelVisible = false;
|
|
339
|
+
let editorFocused = false;
|
|
332
340
|
try {
|
|
333
341
|
const evaluate: AgentEvaluateFn = (expr, timeout) =>
|
|
334
342
|
cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
335
|
-
|
|
336
|
-
|
|
343
|
+
if (typeof agent.adapter.openPanel === 'function') {
|
|
344
|
+
const result = await agent.adapter.openPanel(evaluate);
|
|
345
|
+
panelVisible = result.visible;
|
|
346
|
+
}
|
|
347
|
+
if (typeof agent.adapter.focusEditor === 'function') {
|
|
348
|
+
const result = await agent.adapter.focusEditor(evaluate);
|
|
349
|
+
editorFocused = result.focused;
|
|
350
|
+
}
|
|
351
|
+
return panelVisible || editorFocused;
|
|
337
352
|
} catch (e) {
|
|
338
|
-
this.logFn(`[AgentStream]
|
|
353
|
+
this.logFn(`[AgentStream] openPanel(${sessionId}) error: ${(e as Error).message}`);
|
|
339
354
|
return false;
|
|
340
355
|
}
|
|
341
356
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Auto-configured using provider.js scripts + metadata.
|
|
6
6
|
*/
|
|
7
7
|
import type { IAgentStreamAdapter, AgentStreamState, AgentChatListItem, AgentEvaluateFn } from './types.js';
|
|
8
|
-
import type { ProviderModule } from '../providers/contracts.js';
|
|
8
|
+
import type { ProviderModule, FocusEditorResult, OpenPanelResult } from '../providers/contracts.js';
|
|
9
9
|
export declare class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
10
10
|
readonly agentType: string;
|
|
11
11
|
readonly agentName: string;
|
|
@@ -31,6 +31,7 @@ export declare class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
|
31
31
|
newSession(evaluate: AgentEvaluateFn): Promise<void>;
|
|
32
32
|
listChats(evaluate: AgentEvaluateFn): Promise<AgentChatListItem[]>;
|
|
33
33
|
switchSession(evaluate: AgentEvaluateFn, sessionId: string): Promise<boolean>;
|
|
34
|
-
focusEditor(evaluate: AgentEvaluateFn): Promise<
|
|
34
|
+
focusEditor(evaluate: AgentEvaluateFn): Promise<FocusEditorResult>;
|
|
35
|
+
openPanel(evaluate: AgentEvaluateFn): Promise<OpenPanelResult>;
|
|
35
36
|
private errorState;
|
|
36
37
|
}
|
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
AgentChatListItem,
|
|
12
12
|
AgentEvaluateFn,
|
|
13
13
|
} from './types.js';
|
|
14
|
-
import type { ProviderModule, ProviderScripts } from '../providers/contracts.js';
|
|
14
|
+
import type { ProviderModule, ProviderScripts, FocusEditorResult, OpenPanelResult } from '../providers/contracts.js';
|
|
15
15
|
import { extractProviderControlValues, normalizeProviderEffects } from '../providers/control-effects.js';
|
|
16
16
|
import { validateReadChatResultPayload } from '../providers/read-chat-contract.js';
|
|
17
17
|
import { resolveProviderStateSurface } from '../providers/provider-patch-state.js';
|
|
@@ -57,6 +57,110 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
private getOptionalError(raw: unknown): string | undefined {
|
|
61
|
+
return typeof raw === 'string' && raw.trim() ? raw.trim() : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private normalizeFocusEditorResult(raw: unknown): FocusEditorResult {
|
|
65
|
+
const data = this.parseMaybeJson(raw);
|
|
66
|
+
if (data === true) return { focused: true };
|
|
67
|
+
if (data === false || data == null) return { focused: false };
|
|
68
|
+
if (typeof data === 'string') {
|
|
69
|
+
const trimmed = data.trim();
|
|
70
|
+
const normalized = trimmed.toLowerCase();
|
|
71
|
+
if (
|
|
72
|
+
normalized === 'true'
|
|
73
|
+
|| normalized === 'ok'
|
|
74
|
+
|| normalized === 'success'
|
|
75
|
+
|| normalized === 'focused'
|
|
76
|
+
|| normalized === 'visible'
|
|
77
|
+
) {
|
|
78
|
+
return { focused: true };
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
normalized === 'false'
|
|
82
|
+
|| normalized === 'not_found'
|
|
83
|
+
|| normalized === 'not found'
|
|
84
|
+
|| normalized === 'missing'
|
|
85
|
+
|| normalized === 'panel_hidden'
|
|
86
|
+
|| normalized === 'hidden'
|
|
87
|
+
) {
|
|
88
|
+
return { focused: false };
|
|
89
|
+
}
|
|
90
|
+
return { focused: false, ...(trimmed ? { error: trimmed } : {}) };
|
|
91
|
+
}
|
|
92
|
+
if (data && typeof data === 'object') {
|
|
93
|
+
const error = this.getOptionalError(data.error);
|
|
94
|
+
if (data.focused === true || data.success === true || data.ok === true || data.visible === true) {
|
|
95
|
+
return { focused: true };
|
|
96
|
+
}
|
|
97
|
+
if (data.focused === false || data.success === false || data.ok === false || error) {
|
|
98
|
+
return { focused: false, ...(error ? { error } : {}) };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { focused: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private normalizeOpenPanelResult(raw: unknown): OpenPanelResult {
|
|
105
|
+
const data = this.parseMaybeJson(raw);
|
|
106
|
+
if (data === true) return { opened: true, visible: true };
|
|
107
|
+
if (data === false || data == null) return { opened: false, visible: false };
|
|
108
|
+
if (typeof data === 'string') {
|
|
109
|
+
const trimmed = data.trim();
|
|
110
|
+
const normalized = trimmed.toLowerCase();
|
|
111
|
+
if (
|
|
112
|
+
normalized === 'true'
|
|
113
|
+
|| normalized === 'ok'
|
|
114
|
+
|| normalized === 'opened'
|
|
115
|
+
|| normalized === 'open'
|
|
116
|
+
|| normalized === 'success'
|
|
117
|
+
) {
|
|
118
|
+
return { opened: true, visible: true };
|
|
119
|
+
}
|
|
120
|
+
if (normalized === 'visible') {
|
|
121
|
+
return { opened: false, visible: true };
|
|
122
|
+
}
|
|
123
|
+
if (normalized === 'focused') {
|
|
124
|
+
return { opened: false, visible: true, focused: true };
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
127
|
+
normalized === 'false'
|
|
128
|
+
|| normalized === 'panel_hidden'
|
|
129
|
+
|| normalized === 'hidden'
|
|
130
|
+
|| normalized === 'not_found'
|
|
131
|
+
|| normalized === 'not found'
|
|
132
|
+
|| normalized === 'missing'
|
|
133
|
+
) {
|
|
134
|
+
return { opened: false, visible: false };
|
|
135
|
+
}
|
|
136
|
+
return { opened: false, visible: false, ...(trimmed ? { error: trimmed } : {}) };
|
|
137
|
+
}
|
|
138
|
+
if (data && typeof data === 'object') {
|
|
139
|
+
const error = this.getOptionalError(data.error);
|
|
140
|
+
const focused = data.focused === true;
|
|
141
|
+
const visible = data.visible === true
|
|
142
|
+
|| data.opened === true
|
|
143
|
+
|| focused
|
|
144
|
+
|| data.success === true
|
|
145
|
+
|| data.ok === true;
|
|
146
|
+
if (visible) {
|
|
147
|
+
return {
|
|
148
|
+
opened: data.opened === true,
|
|
149
|
+
visible: true,
|
|
150
|
+
...(focused ? { focused: true } : {}),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (data.opened === false || data.visible === false || data.success === false || data.ok === false || error) {
|
|
154
|
+
return {
|
|
155
|
+
opened: false,
|
|
156
|
+
visible: false,
|
|
157
|
+
...(error ? { error } : {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { opened: false, visible: false };
|
|
162
|
+
}
|
|
163
|
+
|
|
60
164
|
private summarizeRaw(raw: unknown): string {
|
|
61
165
|
try {
|
|
62
166
|
if (typeof raw === 'string') return raw.replace(/\s+/g, ' ').trim().slice(0, 240);
|
|
@@ -167,6 +271,13 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
|
167
271
|
if (typeof validated.title === 'string' && validated.title.trim()) {
|
|
168
272
|
state.title = validated.title.trim();
|
|
169
273
|
}
|
|
274
|
+
const providerSessionId = typeof validated.providerSessionId === 'string' && validated.providerSessionId.trim()
|
|
275
|
+
? validated.providerSessionId.trim()
|
|
276
|
+
: '';
|
|
277
|
+
if (providerSessionId) {
|
|
278
|
+
state.sessionId = providerSessionId;
|
|
279
|
+
state.providerSessionId = providerSessionId;
|
|
280
|
+
}
|
|
170
281
|
const controlValues = extractProviderControlValues(this.provider.controls, validated);
|
|
171
282
|
const surface = resolveProviderStateSurface({
|
|
172
283
|
controlValues,
|
|
@@ -303,10 +414,18 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
|
303
414
|
return false;
|
|
304
415
|
}
|
|
305
416
|
|
|
306
|
-
async focusEditor(evaluate: AgentEvaluateFn): Promise<
|
|
417
|
+
async focusEditor(evaluate: AgentEvaluateFn): Promise<FocusEditorResult> {
|
|
307
418
|
const script = this.callScript('focusEditor');
|
|
308
|
-
if (!script) return;
|
|
309
|
-
await evaluate(script);
|
|
419
|
+
if (!script) return { focused: false };
|
|
420
|
+
const raw = await evaluate(script);
|
|
421
|
+
return this.normalizeFocusEditorResult(raw);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async openPanel(evaluate: AgentEvaluateFn): Promise<OpenPanelResult> {
|
|
425
|
+
const script = this.callScript('openPanel');
|
|
426
|
+
if (!script) return { opened: false, visible: false };
|
|
427
|
+
const raw = await evaluate(script);
|
|
428
|
+
return this.normalizeOpenPanelResult(raw);
|
|
310
429
|
}
|
|
311
430
|
|
|
312
431
|
private errorState(message: string): AgentStreamState {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Agent stream types.
|
|
5
5
|
* No vscode dependency — can be used as-is.
|
|
6
6
|
*/
|
|
7
|
-
import type { ProviderEffect } from '../providers/contracts.js';
|
|
7
|
+
import type { ProviderEffect, FocusEditorResult, OpenPanelResult } from '../providers/contracts.js';
|
|
8
8
|
import type { ProviderSummaryMetadata } from '../shared-types.js';
|
|
9
9
|
/** Agent chat message */
|
|
10
10
|
export interface AgentChatMessage {
|
|
@@ -61,7 +61,8 @@ export interface IAgentStreamAdapter {
|
|
|
61
61
|
newSession(evaluate: AgentEvaluateFn): Promise<void>;
|
|
62
62
|
listChats?(evaluate: AgentEvaluateFn): Promise<AgentChatListItem[]>;
|
|
63
63
|
switchSession?(evaluate: AgentEvaluateFn, sessionId: string): Promise<boolean>;
|
|
64
|
-
focusEditor?(evaluate: AgentEvaluateFn): Promise<
|
|
64
|
+
focusEditor?(evaluate: AgentEvaluateFn): Promise<FocusEditorResult>;
|
|
65
|
+
openPanel?(evaluate: AgentEvaluateFn): Promise<OpenPanelResult>;
|
|
65
66
|
setProvider?(provider: any): void;
|
|
66
67
|
}
|
|
67
68
|
export type AgentEvaluateFn = (expression: string, timeoutMs?: number) => Promise<unknown>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* No vscode dependency — can be used as-is.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { ProviderEffect } from '../providers/contracts.js';
|
|
8
|
+
import type { ProviderEffect, FocusEditorResult, OpenPanelResult } from '../providers/contracts.js';
|
|
9
9
|
import type { ProviderSummaryMetadata } from '../shared-types.js';
|
|
10
10
|
import type { ChatMessageKind } from '../providers/chat-message-normalization.js';
|
|
11
11
|
|
|
@@ -38,6 +38,8 @@ export interface AgentStreamState {
|
|
|
38
38
|
agentType: string;
|
|
39
39
|
agentName: string;
|
|
40
40
|
extensionId: string;
|
|
41
|
+
sessionId?: string;
|
|
42
|
+
providerSessionId?: string;
|
|
41
43
|
status: 'idle' | 'streaming' | 'waiting_approval' | 'error' | 'disconnected' | 'panel_hidden' | 'not_monitored';
|
|
42
44
|
messages: AgentChatMessage[];
|
|
43
45
|
inputContent: string;
|
|
@@ -74,7 +76,8 @@ export interface IAgentStreamAdapter {
|
|
|
74
76
|
newSession(evaluate: AgentEvaluateFn): Promise<void>;
|
|
75
77
|
listChats?(evaluate: AgentEvaluateFn): Promise<AgentChatListItem[]>;
|
|
76
78
|
switchSession?(evaluate: AgentEvaluateFn, sessionId: string): Promise<boolean>;
|
|
77
|
-
focusEditor?(evaluate: AgentEvaluateFn): Promise<
|
|
79
|
+
focusEditor?(evaluate: AgentEvaluateFn): Promise<FocusEditorResult>;
|
|
80
|
+
openPanel?(evaluate: AgentEvaluateFn): Promise<OpenPanelResult>;
|
|
78
81
|
setProvider?(provider: any): void;
|
|
79
82
|
}
|
|
80
83
|
|
|
@@ -1458,7 +1458,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1458
1458
|
}
|
|
1459
1459
|
}
|
|
1460
1460
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
1461
|
-
if (this.isWaitingForResponse)
|
|
1461
|
+
if (this.isWaitingForResponse) {
|
|
1462
|
+
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1463
|
+
}
|
|
1462
1464
|
const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || '');
|
|
1463
1465
|
if (blockingModal || this.currentStatus === 'waiting_approval') {
|
|
1464
1466
|
throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
|
|
@@ -883,6 +883,18 @@ export class DaemonCliManager {
|
|
|
883
883
|
// UUID session target based search priority
|
|
884
884
|
const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
|
|
885
885
|
if (found) {
|
|
886
|
+
// If we got here via fuzzy match (no targetSessionId, no dir), check for ambiguity.
|
|
887
|
+
// If multiple sessions of the same type exist, refuse to stop without a targetSessionId.
|
|
888
|
+
if (!args?.targetSessionId && !dir) {
|
|
889
|
+
const matchCount = [...this.adapters.values()].filter((a) => a.cliType === cliType).length;
|
|
890
|
+
if (matchCount > 1) {
|
|
891
|
+
return {
|
|
892
|
+
success: false,
|
|
893
|
+
error: `Multiple ${cliType} sessions running — provide targetSessionId to stop a specific session`,
|
|
894
|
+
code: 'AMBIGUOUS_SESSION',
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
}
|
|
886
898
|
await this.stopSessionWithMode(found.key, mode);
|
|
887
899
|
} else {
|
|
888
900
|
console.log(colorize('yellow', ` ⚠ No adapter found for ${cliType}`));
|
package/src/commands/handler.ts
CHANGED
|
@@ -413,7 +413,8 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
413
413
|
'change_model',
|
|
414
414
|
'set_thought_level',
|
|
415
415
|
'resolve_action',
|
|
416
|
-
'
|
|
416
|
+
'select_session',
|
|
417
|
+
'open_panel',
|
|
417
418
|
'pty_input',
|
|
418
419
|
'pty_resize',
|
|
419
420
|
'invoke_provider_script',
|
|
@@ -494,7 +495,8 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
494
495
|
case 'refresh_scripts': return this.handleRefreshScripts(args);
|
|
495
496
|
|
|
496
497
|
// ─── Stream commands (stream-commands.ts) ───────────
|
|
497
|
-
case '
|
|
498
|
+
case 'select_session': return Stream.handleSelectSession(this, args);
|
|
499
|
+
case 'open_panel': return Stream.handleOpenPanel(this, args);
|
|
498
500
|
|
|
499
501
|
// ─── PTY Raw I/O (stream-commands.ts) ─────────
|
|
500
502
|
case 'pty_input': return Stream.handlePtyInput(this, args);
|
package/src/commands/router.ts
CHANGED
|
@@ -718,6 +718,7 @@ export class DaemonCommandRouter {
|
|
|
718
718
|
sessionId,
|
|
719
719
|
typeof args?.seenAt === 'number' ? args.seenAt : Date.now(),
|
|
720
720
|
completionMarker,
|
|
721
|
+
targetSession?.providerSessionId,
|
|
721
722
|
);
|
|
722
723
|
if (READ_DEBUG_ENABLED) {
|
|
723
724
|
LOG.info('RecentRead', `mark_session_seen sessionId=${sessionId} seenAt=${String(args?.seenAt || '')} prevSeenAt=${String(prevSeenAt)} nextSeenAt=${String(next.sessionReads?.[sessionId] || 0)} marker=${completionMarker || '-'}`);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* IDE Extension Settings, Extension Script Execution
|
|
4
4
|
*/
|
|
5
5
|
import type { CommandResult, CommandHelpers } from './handler.js';
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function handleSelectSession(h: CommandHelpers, args: any): Promise<CommandResult>;
|
|
7
|
+
export declare function handleOpenPanel(h: CommandHelpers, args: any): Promise<CommandResult>;
|
|
7
8
|
export declare function handlePtyInput(h: CommandHelpers, args: any): CommandResult;
|
|
8
9
|
export declare function handlePtyResize(h: CommandHelpers, args: any): CommandResult;
|
|
9
10
|
export declare function handleGetProviderSettings(h: CommandHelpers, args: any): CommandResult;
|
|
@@ -28,14 +28,86 @@ function getCliPresentationMode(h: CommandHelpers, targetSessionId?: string): 't
|
|
|
28
28
|
return mode === 'chat' || mode === 'terminal' ? mode : null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
function normalizeOpenPanelCommandResult(result: CommandResult): { opened: boolean; visible: boolean; focused: boolean } {
|
|
32
|
+
const payload = Object.prototype.hasOwnProperty.call(result, 'result') ? result.result : result;
|
|
33
|
+
if (payload === true) return { opened: true, visible: true, focused: false };
|
|
34
|
+
if (!payload) return { opened: false, visible: false, focused: false };
|
|
35
|
+
if (typeof payload === 'string') {
|
|
36
|
+
const normalized = payload.trim().toLowerCase();
|
|
37
|
+
if (normalized === 'visible') return { opened: false, visible: true, focused: false };
|
|
38
|
+
if (normalized === 'focused') return { opened: false, visible: true, focused: true };
|
|
39
|
+
if (normalized === 'opened' || normalized === 'open' || normalized === 'true' || normalized === 'ok' || normalized === 'success') {
|
|
40
|
+
return { opened: true, visible: true, focused: false };
|
|
41
|
+
}
|
|
42
|
+
return { opened: false, visible: false, focused: false };
|
|
43
|
+
}
|
|
44
|
+
if (typeof payload === 'object') {
|
|
45
|
+
const record = payload as Record<string, unknown>;
|
|
46
|
+
return {
|
|
47
|
+
opened: record.opened === true,
|
|
48
|
+
visible: record.visible === true || record.opened === true || record.focused === true,
|
|
49
|
+
focused: record.focused === true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { opened: false, visible: false, focused: false };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeFocusEditorCommandResult(result: CommandResult): { focused: boolean } {
|
|
56
|
+
const payload = Object.prototype.hasOwnProperty.call(result, 'result') ? result.result : result;
|
|
57
|
+
if (payload === true) return { focused: true };
|
|
58
|
+
if (!payload) return { focused: false };
|
|
59
|
+
if (typeof payload === 'string') {
|
|
60
|
+
const normalized = payload.trim().toLowerCase();
|
|
61
|
+
return { focused: normalized === 'focused' || normalized === 'visible' || normalized === 'true' || normalized === 'ok' || normalized === 'success' };
|
|
62
|
+
}
|
|
63
|
+
if (typeof payload === 'object') {
|
|
64
|
+
const record = payload as Record<string, unknown>;
|
|
65
|
+
return { focused: record.focused === true || record.visible === true || record.success === true || record.ok === true };
|
|
66
|
+
}
|
|
67
|
+
return { focused: false };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function handleSelectSession(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
32
71
|
if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
|
|
33
72
|
const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
|
|
34
73
|
if (!sessionId) return { success: false, error: 'targetSessionId required' };
|
|
35
|
-
const ok = await h.agentStream.
|
|
74
|
+
const ok = await h.agentStream.selectSession(h.getCdp()!, sessionId);
|
|
36
75
|
return { success: ok };
|
|
37
76
|
}
|
|
38
77
|
|
|
78
|
+
export async function handleOpenPanel(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
79
|
+
const cdp = h.getCdp();
|
|
80
|
+
if (!cdp) return { success: false, error: 'AgentStream or CDP not available' };
|
|
81
|
+
const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
|
|
82
|
+
if (!sessionId) return { success: false, error: 'targetSessionId required' };
|
|
83
|
+
|
|
84
|
+
const currentTransport = h.currentSession?.transport;
|
|
85
|
+
const shouldUseAgentStream = !!h.agentStream
|
|
86
|
+
&& currentTransport !== 'cdp-page'
|
|
87
|
+
&& currentTransport !== 'pty'
|
|
88
|
+
&& currentTransport !== 'acp';
|
|
89
|
+
if (shouldUseAgentStream) {
|
|
90
|
+
const ok = await h.agentStream.openSessionPanel(cdp, sessionId);
|
|
91
|
+
return { success: ok };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const openResult = await executeProviderScript(h, args, 'openPanel');
|
|
95
|
+
if (!openResult.success) return openResult;
|
|
96
|
+
const revealState = normalizeOpenPanelCommandResult(openResult);
|
|
97
|
+
|
|
98
|
+
let focusState = { focused: false };
|
|
99
|
+
const focusResult = await executeProviderScript(h, args, 'focusEditor');
|
|
100
|
+
if (focusResult.success) {
|
|
101
|
+
focusState = normalizeFocusEditorCommandResult(focusResult);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
...openResult,
|
|
106
|
+
...(focusState.focused ? { focused: true } : {}),
|
|
107
|
+
success: revealState.visible || focusState.focused,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
39
111
|
// ─── PTY Raw I/O ──────────────────────────────────
|
|
40
112
|
|
|
41
113
|
export function handlePtyInput(h: CommandHelpers, args: any): CommandResult {
|
|
@@ -83,12 +83,20 @@ export function getRecentActivity(state: DaemonState, limit = 20): RecentActivit
|
|
|
83
83
|
.slice(0, limit);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
export function
|
|
87
|
-
|
|
86
|
+
export function buildSessionReadStateKey(sessionId: string, providerSessionId?: string | null): string {
|
|
87
|
+
const normalizedProviderSessionId = typeof providerSessionId === 'string' ? providerSessionId.trim() : '';
|
|
88
|
+
if (normalizedProviderSessionId) return `provider:${normalizedProviderSessionId}`;
|
|
89
|
+
return sessionId;
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
export function
|
|
91
|
-
|
|
92
|
+
export function getSessionSeenAt(state: DaemonState, sessionId: string, providerSessionId?: string | null): number {
|
|
93
|
+
const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
|
|
94
|
+
return state.sessionReads?.[providerKey] || state.sessionReads?.[sessionId] || 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getSessionSeenMarker(state: DaemonState, sessionId: string, providerSessionId?: string | null): string {
|
|
98
|
+
const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
|
|
99
|
+
return state.sessionReadMarkers?.[providerKey] || state.sessionReadMarkers?.[sessionId] || '';
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
export function markSessionSeen(
|
|
@@ -96,22 +104,24 @@ export function markSessionSeen(
|
|
|
96
104
|
sessionId: string,
|
|
97
105
|
seenAt = Date.now(),
|
|
98
106
|
completionMarker?: string | null,
|
|
107
|
+
providerSessionId?: string | null,
|
|
99
108
|
): DaemonState {
|
|
100
109
|
const prev = state.sessionReads || {};
|
|
101
|
-
const nextSeenAt = Math.max(prev[sessionId] || 0, seenAt);
|
|
102
110
|
const prevMarkers = state.sessionReadMarkers || {};
|
|
103
111
|
const nextMarker = typeof completionMarker === 'string' ? completionMarker : '';
|
|
112
|
+
const readKeys = Array.from(new Set([
|
|
113
|
+
sessionId,
|
|
114
|
+
buildSessionReadStateKey(sessionId, providerSessionId),
|
|
115
|
+
].filter(Boolean)));
|
|
116
|
+
const nextSessionReads = { ...prev };
|
|
117
|
+
const nextSessionReadMarkers = { ...prevMarkers };
|
|
118
|
+
for (const key of readKeys) {
|
|
119
|
+
nextSessionReads[key] = Math.max(prev[key] || 0, seenAt);
|
|
120
|
+
if (nextMarker) nextSessionReadMarkers[key] = nextMarker;
|
|
121
|
+
}
|
|
104
122
|
return {
|
|
105
123
|
...state,
|
|
106
|
-
sessionReads:
|
|
107
|
-
|
|
108
|
-
[sessionId]: nextSeenAt,
|
|
109
|
-
},
|
|
110
|
-
sessionReadMarkers: nextMarker
|
|
111
|
-
? {
|
|
112
|
-
...prevMarkers,
|
|
113
|
-
[sessionId]: nextMarker,
|
|
114
|
-
}
|
|
115
|
-
: prevMarkers,
|
|
124
|
+
sessionReads: nextSessionReads,
|
|
125
|
+
sessionReadMarkers: nextMarker ? nextSessionReadMarkers : prevMarkers,
|
|
116
126
|
};
|
|
117
127
|
}
|
|
@@ -898,8 +898,8 @@ export function buildAutoImplPrompt(ctx: DevServerContext,
|
|
|
898
898
|
lines.push('| setModel | `{ success: true/false }` |');
|
|
899
899
|
lines.push('| listModes | `{ modes: [{ name, id }], current }` |');
|
|
900
900
|
lines.push('| setMode | `{ success: true/false }` |');
|
|
901
|
-
lines.push('| focusEditor | `{ focused: true/false }` |');
|
|
902
|
-
lines.push('| openPanel | `{ opened: true/false }` |');
|
|
901
|
+
lines.push('| focusEditor | `{ focused: true/false, error? }` |');
|
|
902
|
+
lines.push('| openPanel | `{ opened: true/false, visible: true/false, focused?: true, error? }` |');
|
|
903
903
|
lines.push('');
|
|
904
904
|
|
|
905
905
|
// ── readChat.status lifecycle spec ──
|
package/src/daemon/dev-server.ts
CHANGED
|
@@ -1221,8 +1221,8 @@ export class DevServer implements DevServerContext {
|
|
|
1221
1221
|
lines.push('| setModel | `{ success: true/false }` |');
|
|
1222
1222
|
lines.push('| listModes | `{ modes: [{ name, id }], current }` |');
|
|
1223
1223
|
lines.push('| setMode | `{ success: true/false }` |');
|
|
1224
|
-
lines.push('| focusEditor | `{ focused: true/false }` |');
|
|
1225
|
-
lines.push('| openPanel | `{ opened: true/false }` |');
|
|
1224
|
+
lines.push('| focusEditor | `{ focused: true/false, error? }` |');
|
|
1225
|
+
lines.push('| openPanel | `{ opened: true/false, visible: true/false, focused?: true, error? }` |');
|
|
1226
1226
|
lines.push('');
|
|
1227
1227
|
|
|
1228
1228
|
// ── readChat.status lifecycle spec ──
|