@adhdev/daemon-core 0.8.68 → 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.
Files changed (48) hide show
  1. package/dist/agent-stream/manager.d.ts +2 -1
  2. package/dist/agent-stream/provider-adapter.d.ts +6 -2
  3. package/dist/agent-stream/types.d.ts +5 -2
  4. package/dist/commands/stream-commands.d.ts +2 -1
  5. package/dist/config/recent-activity.d.ts +4 -3
  6. package/dist/index.js +286 -64
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +286 -64
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/providers/contracts.d.ts +10 -0
  11. package/dist/providers/extension-provider-instance.d.ts +1 -0
  12. package/dist/providers/open-panel-support.d.ts +6 -0
  13. package/dist/providers/provider-instance.d.ts +2 -1
  14. package/dist/shared-types.d.ts +1 -1
  15. package/dist/types.d.ts +1 -0
  16. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  17. package/package.json +1 -1
  18. package/src/agent-stream/forward.ts +1 -0
  19. package/src/agent-stream/manager.d.ts +2 -1
  20. package/src/agent-stream/manager.ts +20 -5
  21. package/src/agent-stream/provider-adapter.d.ts +3 -2
  22. package/src/agent-stream/provider-adapter.ts +123 -4
  23. package/src/agent-stream/types.d.ts +3 -2
  24. package/src/agent-stream/types.ts +5 -2
  25. package/src/cli-adapters/provider-cli-adapter.ts +3 -1
  26. package/src/commands/cli-manager.ts +12 -0
  27. package/src/commands/handler.ts +4 -2
  28. package/src/commands/router.ts +1 -0
  29. package/src/commands/stream-commands.d.ts +2 -1
  30. package/src/commands/stream-commands.ts +74 -2
  31. package/src/config/recent-activity.ts +25 -15
  32. package/src/daemon/dev-auto-implement.ts +2 -2
  33. package/src/daemon/dev-server.ts +2 -2
  34. package/src/daemon/scaffold-template.ts +12 -5
  35. package/src/providers/contracts.d.ts +10 -0
  36. package/src/providers/contracts.ts +14 -0
  37. package/src/providers/extension-provider-instance.ts +15 -2
  38. package/src/providers/ide-provider-instance.ts +2 -0
  39. package/src/providers/open-panel-support.ts +40 -0
  40. package/src/providers/provider-instance.d.ts +2 -1
  41. package/src/providers/provider-instance.ts +2 -1
  42. package/src/providers/provider-schema.ts +10 -0
  43. package/src/providers/read-chat-contract.ts +1 -0
  44. package/src/shared-types.d.ts +1 -1
  45. package/src/shared-types.ts +1 -0
  46. package/src/status/builders.ts +9 -23
  47. package/src/status/snapshot.ts +2 -2
  48. package/src/types.ts +1 -0
@@ -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
- focusSession(cdp: DaemonCdpManager, sessionId: string): Promise<boolean>;
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>;
@@ -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;
@@ -18,6 +18,9 @@ export declare class ProviderStreamAdapter implements IAgentStreamAdapter {
18
18
  private hasScript;
19
19
  private getStateTitle;
20
20
  private parseMaybeJson;
21
+ private getOptionalError;
22
+ private normalizeFocusEditorResult;
23
+ private normalizeOpenPanelResult;
21
24
  private summarizeRaw;
22
25
  private isTransportError;
23
26
  private titlesMatch;
@@ -31,6 +34,7 @@ export declare class ProviderStreamAdapter implements IAgentStreamAdapter {
31
34
  newSession(evaluate: AgentEvaluateFn): Promise<void>;
32
35
  listChats(evaluate: AgentEvaluateFn): Promise<AgentChatListItem[]>;
33
36
  switchSession(evaluate: AgentEvaluateFn, sessionId: string): Promise<boolean>;
34
- focusEditor(evaluate: AgentEvaluateFn): Promise<void>;
37
+ focusEditor(evaluate: AgentEvaluateFn): Promise<FocusEditorResult>;
38
+ openPanel(evaluate: AgentEvaluateFn): Promise<OpenPanelResult>;
35
39
  private errorState;
36
40
  }
@@ -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
  import type { ChatMessageKind } from '../providers/chat-message-normalization.js';
10
10
  /** Agent chat message */
@@ -34,6 +34,8 @@ export interface AgentStreamState {
34
34
  agentType: string;
35
35
  agentName: string;
36
36
  extensionId: string;
37
+ sessionId?: string;
38
+ providerSessionId?: string;
37
39
  status: 'idle' | 'streaming' | 'waiting_approval' | 'error' | 'disconnected' | 'panel_hidden' | 'not_monitored';
38
40
  messages: AgentChatMessage[];
39
41
  inputContent: string;
@@ -70,7 +72,8 @@ export interface IAgentStreamAdapter {
70
72
  newSession(evaluate: AgentEvaluateFn): Promise<void>;
71
73
  listChats?(evaluate: AgentEvaluateFn): Promise<AgentChatListItem[]>;
72
74
  switchSession?(evaluate: AgentEvaluateFn, sessionId: string): Promise<boolean>;
73
- focusEditor?(evaluate: AgentEvaluateFn): Promise<void>;
75
+ focusEditor?(evaluate: AgentEvaluateFn): Promise<FocusEditorResult>;
76
+ openPanel?(evaluate: AgentEvaluateFn): Promise<OpenPanelResult>;
74
77
  setProvider?(provider: any): void;
75
78
  }
76
79
  export type AgentEvaluateFn = (expression: string, timeoutMs?: number) => Promise<unknown>;
@@ -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 handleFocusSession(h: CommandHelpers, args: any): Promise<CommandResult>;
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;
@@ -25,6 +25,7 @@ export declare function appendRecentActivity(state: DaemonState, entry: Omit<Rec
25
25
  lastUsedAt?: number;
26
26
  }): DaemonState;
27
27
  export declare function getRecentActivity(state: DaemonState, limit?: number): RecentActivityEntry[];
28
- export declare function getSessionSeenAt(state: DaemonState, sessionId: string): number;
29
- export declare function getSessionSeenMarker(state: DaemonState, sessionId: string): string;
30
- export declare function markSessionSeen(state: DaemonState, sessionId: string, seenAt?: number, completionMarker?: string | null): DaemonState;
28
+ export declare function buildSessionReadStateKey(sessionId: string, providerSessionId?: string | null): string;
29
+ export declare function getSessionSeenAt(state: DaemonState, sessionId: string, providerSessionId?: string | null): number;
30
+ export declare function getSessionSeenMarker(state: DaemonState, sessionId: string, providerSessionId?: string | null): string;
31
+ export declare function markSessionSeen(state: DaemonState, sessionId: string, seenAt?: number, completionMarker?: string | null, providerSessionId?: string | null): DaemonState;
package/dist/index.js CHANGED
@@ -870,6 +870,7 @@ function validateMessage(message, source, index) {
870
870
  if (isFiniteNumber(message.index)) normalized.index = message.index;
871
871
  if (isFiniteNumber(message.timestamp)) normalized.timestamp = message.timestamp;
872
872
  if (isFiniteNumber(message.receivedAt)) normalized.receivedAt = message.receivedAt;
873
+ if (typeof message._turnKey === "string") normalized._turnKey = message._turnKey;
873
874
  if (Array.isArray(message.toolCalls)) normalized.toolCalls = message.toolCalls;
874
875
  if (isPlainObject3(message.meta)) normalized.meta = message.meta;
875
876
  if (typeof message.senderName === "string") normalized.senderName = message.senderName;
@@ -3024,7 +3025,9 @@ ${data.message || ""}`.trim();
3024
3025
  }
3025
3026
  }
3026
3027
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
3027
- if (this.isWaitingForResponse) return;
3028
+ if (this.isWaitingForResponse) {
3029
+ throw new Error(`${this.cliName} is still processing the previous prompt`);
3030
+ }
3028
3031
  const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
3029
3032
  if (blockingModal || this.currentStatus === "waiting_approval") {
3030
3033
  throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
@@ -3943,27 +3946,37 @@ function getRecentActivity(state, limit = 20) {
3943
3946
  })
3944
3947
  })).sort((a, b) => b.lastUsedAt - a.lastUsedAt).slice(0, limit);
3945
3948
  }
3946
- function getSessionSeenAt(state, sessionId) {
3947
- return state.sessionReads?.[sessionId] || 0;
3949
+ function buildSessionReadStateKey(sessionId, providerSessionId) {
3950
+ const normalizedProviderSessionId = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
3951
+ if (normalizedProviderSessionId) return `provider:${normalizedProviderSessionId}`;
3952
+ return sessionId;
3953
+ }
3954
+ function getSessionSeenAt(state, sessionId, providerSessionId) {
3955
+ const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
3956
+ return state.sessionReads?.[providerKey] || state.sessionReads?.[sessionId] || 0;
3948
3957
  }
3949
- function getSessionSeenMarker(state, sessionId) {
3950
- return state.sessionReadMarkers?.[sessionId] || "";
3958
+ function getSessionSeenMarker(state, sessionId, providerSessionId) {
3959
+ const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
3960
+ return state.sessionReadMarkers?.[providerKey] || state.sessionReadMarkers?.[sessionId] || "";
3951
3961
  }
3952
- function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker) {
3962
+ function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker, providerSessionId) {
3953
3963
  const prev = state.sessionReads || {};
3954
- const nextSeenAt = Math.max(prev[sessionId] || 0, seenAt);
3955
3964
  const prevMarkers = state.sessionReadMarkers || {};
3956
3965
  const nextMarker = typeof completionMarker === "string" ? completionMarker : "";
3966
+ const readKeys = Array.from(new Set([
3967
+ sessionId,
3968
+ buildSessionReadStateKey(sessionId, providerSessionId)
3969
+ ].filter(Boolean)));
3970
+ const nextSessionReads = { ...prev };
3971
+ const nextSessionReadMarkers = { ...prevMarkers };
3972
+ for (const key of readKeys) {
3973
+ nextSessionReads[key] = Math.max(prev[key] || 0, seenAt);
3974
+ if (nextMarker) nextSessionReadMarkers[key] = nextMarker;
3975
+ }
3957
3976
  return {
3958
3977
  ...state,
3959
- sessionReads: {
3960
- ...prev,
3961
- [sessionId]: nextSeenAt
3962
- },
3963
- sessionReadMarkers: nextMarker ? {
3964
- ...prevMarkers,
3965
- [sessionId]: nextMarker
3966
- } : prevMarkers
3978
+ sessionReads: nextSessionReads,
3979
+ sessionReadMarkers: nextMarker ? nextSessionReadMarkers : prevMarkers
3967
3980
  };
3968
3981
  }
3969
3982
 
@@ -6563,6 +6576,39 @@ function resolveProviderStateSurface(params) {
6563
6576
 
6564
6577
  // src/providers/extension-provider-instance.ts
6565
6578
  init_chat_message_normalization();
6579
+
6580
+ // src/providers/open-panel-support.ts
6581
+ var IDE_PROVIDER_SESSION_CAPABILITIES_BASE = [
6582
+ "read_chat",
6583
+ "send_message",
6584
+ "new_session",
6585
+ "list_sessions",
6586
+ "switch_session",
6587
+ "resolve_action",
6588
+ "change_model",
6589
+ "set_mode",
6590
+ "set_thought_level"
6591
+ ];
6592
+ var EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE = [
6593
+ "read_chat",
6594
+ "send_message",
6595
+ "new_session",
6596
+ "list_sessions",
6597
+ "switch_session",
6598
+ "resolve_action",
6599
+ "change_model",
6600
+ "set_mode"
6601
+ ];
6602
+ function providerHasOpenPanelSupport(provider) {
6603
+ if (typeof provider.scripts?.openPanel === "function") return true;
6604
+ if (provider.category === "ide" && typeof provider.scripts?.webviewOpenPanel === "function") return true;
6605
+ return false;
6606
+ }
6607
+ function getProviderSessionCapabilities(provider, baseCapabilities) {
6608
+ return providerHasOpenPanelSupport(provider) ? [...baseCapabilities, "open_panel"] : [...baseCapabilities];
6609
+ }
6610
+
6611
+ // src/providers/extension-provider-instance.ts
6566
6612
  var ExtensionProviderInstance = class {
6567
6613
  type;
6568
6614
  category = "extension";
@@ -6588,6 +6634,7 @@ var ExtensionProviderInstance = class {
6588
6634
  instanceId;
6589
6635
  ideType = "";
6590
6636
  chatId = null;
6637
+ providerSessionId = null;
6591
6638
  chatTitle = null;
6592
6639
  agentName = "";
6593
6640
  extensionId = "";
@@ -6621,8 +6668,10 @@ var ExtensionProviderInstance = class {
6621
6668
  name: this.provider.name,
6622
6669
  category: "extension",
6623
6670
  status: this.currentStatus,
6671
+ providerSessionId: this.providerSessionId || this.chatId || void 0,
6672
+ sessionCapabilities: getProviderSessionCapabilities(this.provider, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE),
6624
6673
  activeChat: this.messages.length > 0 || this.runtimeMessages.length > 0 ? {
6625
- id: this.chatId || this.instanceId,
6674
+ id: this.providerSessionId || this.chatId || this.instanceId,
6626
6675
  title: this.chatTitle || this.agentName || this.provider.name,
6627
6676
  status: this.currentStatus,
6628
6677
  messages: this.mergeConversationMessages(this.messages),
@@ -6652,7 +6701,11 @@ var ExtensionProviderInstance = class {
6652
6701
  });
6653
6702
  this.controlValues = patchedState.controlValues;
6654
6703
  this.summaryMetadata = patchedState.summaryMetadata;
6655
- if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
6704
+ const nextProviderSessionId = typeof data?.providerSessionId === "string" && data.providerSessionId.trim() ? data.providerSessionId.trim() : typeof data?.sessionId === "string" && data.sessionId.trim() ? data.sessionId.trim() : "";
6705
+ if (nextProviderSessionId) {
6706
+ this.providerSessionId = nextProviderSessionId;
6707
+ this.chatId = nextProviderSessionId;
6708
+ }
6656
6709
  if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
6657
6710
  if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
6658
6711
  if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
@@ -6938,6 +6991,7 @@ ${effect.notification.body || ""}`.trim();
6938
6991
  this.controlValues = {};
6939
6992
  this.currentStatus = "idle";
6940
6993
  this.chatId = null;
6994
+ this.providerSessionId = null;
6941
6995
  this.chatTitle = null;
6942
6996
  this.agentName = "";
6943
6997
  this.extensionId = "";
@@ -7092,6 +7146,7 @@ var IdeProviderInstance = class {
7092
7146
  workspace: this.workspace || null,
7093
7147
  extensions: extensionStates,
7094
7148
  cdpConnected: cdp?.isConnected || false,
7149
+ sessionCapabilities: getProviderSessionCapabilities(this.provider, IDE_PROVIDER_SESSION_CAPABILITIES_BASE),
7095
7150
  controlValues: surface.controlValues,
7096
7151
  providerControls: this.provider.controls,
7097
7152
  summaryMetadata: surface.summaryMetadata,
@@ -8118,27 +8173,8 @@ function isCdpConnected(cdpManagers, key) {
8118
8173
  }
8119
8174
  return false;
8120
8175
  }
8121
- var IDE_SESSION_CAPABILITIES = [
8122
- "read_chat",
8123
- "send_message",
8124
- "new_session",
8125
- "list_sessions",
8126
- "switch_session",
8127
- "resolve_action",
8128
- "change_model",
8129
- "set_mode",
8130
- "set_thought_level"
8131
- ];
8132
- var EXTENSION_SESSION_CAPABILITIES = [
8133
- "read_chat",
8134
- "send_message",
8135
- "new_session",
8136
- "list_sessions",
8137
- "switch_session",
8138
- "resolve_action",
8139
- "change_model",
8140
- "set_mode"
8141
- ];
8176
+ var IDE_SESSION_CAPABILITIES = [...IDE_PROVIDER_SESSION_CAPABILITIES_BASE];
8177
+ var EXTENSION_SESSION_CAPABILITIES = [...EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE];
8142
8178
  var PTY_SESSION_CAPABILITIES = [
8143
8179
  "read_chat",
8144
8180
  "send_message",
@@ -8182,7 +8218,7 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
8182
8218
  ...includeSessionMetadata && { workspace: state.workspace || null },
8183
8219
  activeChat,
8184
8220
  ...summaryMetadata && { summaryMetadata },
8185
- ...includeSessionMetadata && { capabilities: IDE_SESSION_CAPABILITIES },
8221
+ ...includeSessionMetadata && { capabilities: state.sessionCapabilities || IDE_SESSION_CAPABILITIES },
8186
8222
  cdpConnected: state.cdpConnected ?? isCdpConnected(cdpManagers, state.type),
8187
8223
  ...includeSessionControls && {
8188
8224
  ...controlValues && { controlValues },
@@ -8205,6 +8241,7 @@ function buildExtensionAgentSession(parent, ext, options) {
8205
8241
  parentId: parent.instanceId || parent.type,
8206
8242
  providerType: ext.type,
8207
8243
  ...includeSessionMetadata && { providerName: ext.name },
8244
+ providerSessionId: ext.providerSessionId,
8208
8245
  kind: "agent",
8209
8246
  transport: "cdp-webview",
8210
8247
  status: normalizeManagedStatus(activeChat?.status || ext.status, {
@@ -8214,7 +8251,7 @@ function buildExtensionAgentSession(parent, ext, options) {
8214
8251
  ...includeSessionMetadata && { workspace: parent.workspace || null },
8215
8252
  activeChat,
8216
8253
  ...summaryMetadata && { summaryMetadata },
8217
- ...includeSessionMetadata && { capabilities: EXTENSION_SESSION_CAPABILITIES },
8254
+ ...includeSessionMetadata && { capabilities: ext.sessionCapabilities || EXTENSION_SESSION_CAPABILITIES },
8218
8255
  ...includeSessionControls && {
8219
8256
  ...controlValues && { controlValues },
8220
8257
  providerControls: ext.providerControls
@@ -10132,13 +10169,75 @@ function getCliPresentationMode(h, targetSessionId) {
10132
10169
  const mode = instance.getPresentationMode?.();
10133
10170
  return mode === "chat" || mode === "terminal" ? mode : null;
10134
10171
  }
10135
- async function handleFocusSession(h, args) {
10172
+ function normalizeOpenPanelCommandResult(result) {
10173
+ const payload = Object.prototype.hasOwnProperty.call(result, "result") ? result.result : result;
10174
+ if (payload === true) return { opened: true, visible: true, focused: false };
10175
+ if (!payload) return { opened: false, visible: false, focused: false };
10176
+ if (typeof payload === "string") {
10177
+ const normalized = payload.trim().toLowerCase();
10178
+ if (normalized === "visible") return { opened: false, visible: true, focused: false };
10179
+ if (normalized === "focused") return { opened: false, visible: true, focused: true };
10180
+ if (normalized === "opened" || normalized === "open" || normalized === "true" || normalized === "ok" || normalized === "success") {
10181
+ return { opened: true, visible: true, focused: false };
10182
+ }
10183
+ return { opened: false, visible: false, focused: false };
10184
+ }
10185
+ if (typeof payload === "object") {
10186
+ const record = payload;
10187
+ return {
10188
+ opened: record.opened === true,
10189
+ visible: record.visible === true || record.opened === true || record.focused === true,
10190
+ focused: record.focused === true
10191
+ };
10192
+ }
10193
+ return { opened: false, visible: false, focused: false };
10194
+ }
10195
+ function normalizeFocusEditorCommandResult(result) {
10196
+ const payload = Object.prototype.hasOwnProperty.call(result, "result") ? result.result : result;
10197
+ if (payload === true) return { focused: true };
10198
+ if (!payload) return { focused: false };
10199
+ if (typeof payload === "string") {
10200
+ const normalized = payload.trim().toLowerCase();
10201
+ return { focused: normalized === "focused" || normalized === "visible" || normalized === "true" || normalized === "ok" || normalized === "success" };
10202
+ }
10203
+ if (typeof payload === "object") {
10204
+ const record = payload;
10205
+ return { focused: record.focused === true || record.visible === true || record.success === true || record.ok === true };
10206
+ }
10207
+ return { focused: false };
10208
+ }
10209
+ async function handleSelectSession(h, args) {
10136
10210
  if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
10137
10211
  const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
10138
10212
  if (!sessionId) return { success: false, error: "targetSessionId required" };
10139
- const ok = await h.agentStream.focusSession(h.getCdp(), sessionId);
10213
+ const ok = await h.agentStream.selectSession(h.getCdp(), sessionId);
10140
10214
  return { success: ok };
10141
10215
  }
10216
+ async function handleOpenPanel(h, args) {
10217
+ const cdp = h.getCdp();
10218
+ if (!cdp) return { success: false, error: "AgentStream or CDP not available" };
10219
+ const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
10220
+ if (!sessionId) return { success: false, error: "targetSessionId required" };
10221
+ const currentTransport = h.currentSession?.transport;
10222
+ const shouldUseAgentStream = !!h.agentStream && currentTransport !== "cdp-page" && currentTransport !== "pty" && currentTransport !== "acp";
10223
+ if (shouldUseAgentStream) {
10224
+ const ok = await h.agentStream.openSessionPanel(cdp, sessionId);
10225
+ return { success: ok };
10226
+ }
10227
+ const openResult = await executeProviderScript(h, args, "openPanel");
10228
+ if (!openResult.success) return openResult;
10229
+ const revealState = normalizeOpenPanelCommandResult(openResult);
10230
+ let focusState = { focused: false };
10231
+ const focusResult = await executeProviderScript(h, args, "focusEditor");
10232
+ if (focusResult.success) {
10233
+ focusState = normalizeFocusEditorCommandResult(focusResult);
10234
+ }
10235
+ return {
10236
+ ...openResult,
10237
+ ...focusState.focused ? { focused: true } : {},
10238
+ success: revealState.visible || focusState.focused
10239
+ };
10240
+ }
10142
10241
  function handlePtyInput(h, args) {
10143
10242
  const { cliType, data, targetSessionId } = args || {};
10144
10243
  if (!data) return { success: false, error: "data required" };
@@ -10828,7 +10927,8 @@ var DaemonCommandHandler = class {
10828
10927
  "change_model",
10829
10928
  "set_thought_level",
10830
10929
  "resolve_action",
10831
- "focus_session",
10930
+ "select_session",
10931
+ "open_panel",
10832
10932
  "pty_input",
10833
10933
  "pty_resize",
10834
10934
  "invoke_provider_script"
@@ -10926,8 +11026,10 @@ var DaemonCommandHandler = class {
10926
11026
  case "refresh_scripts":
10927
11027
  return this.handleRefreshScripts(args);
10928
11028
  // ─── Stream commands (stream-commands.ts) ───────────
10929
- case "focus_session":
10930
- return handleFocusSession(this, args);
11029
+ case "select_session":
11030
+ return handleSelectSession(this, args);
11031
+ case "open_panel":
11032
+ return handleOpenPanel(this, args);
10931
11033
  // ─── PTY Raw I/O (stream-commands.ts) ─────────
10932
11034
  case "pty_input":
10933
11035
  return handlePtyInput(this, args);
@@ -13624,6 +13726,16 @@ Run 'adhdev doctor' for detailed diagnostics.`
13624
13726
  if (!cliType) throw new Error("cliType required");
13625
13727
  const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
13626
13728
  if (found) {
13729
+ if (!args?.targetSessionId && !dir) {
13730
+ const matchCount = [...this.adapters.values()].filter((a) => a.cliType === cliType).length;
13731
+ if (matchCount > 1) {
13732
+ return {
13733
+ success: false,
13734
+ error: `Multiple ${cliType} sessions running \u2014 provide targetSessionId to stop a specific session`,
13735
+ code: "AMBIGUOUS_SESSION"
13736
+ };
13737
+ }
13738
+ }
13627
13739
  await this.stopSessionWithMode(found.key, mode);
13628
13740
  } else {
13629
13741
  console.log(colorize("yellow", ` \u26A0 No adapter found for ${cliType}`));
@@ -13805,6 +13917,7 @@ function validateProviderDefinition(raw) {
13805
13917
  warnings.push("disableUpstream is deprecated in provider definitions; use machine-level provider source policy instead");
13806
13918
  }
13807
13919
  const category = provider.category;
13920
+ const typedProvider = provider;
13808
13921
  const controls = Array.isArray(provider.controls) ? provider.controls : [];
13809
13922
  if (category === "cli" || category === "acp") {
13810
13923
  const spawn4 = provider.spawn;
@@ -13827,6 +13940,9 @@ function validateProviderDefinition(raw) {
13827
13940
  for (const control of controls) {
13828
13941
  validateControl(control, errors);
13829
13942
  }
13943
+ if ((category === "ide" || category === "extension") && typeof typedProvider.scripts?.focusEditor === "function" && !providerHasOpenPanelSupport(typedProvider)) {
13944
+ warnings.push("scripts.focusEditor is present without scripts.openPanel/webviewOpenPanel; open_panel capability will remain disabled");
13945
+ }
13830
13946
  return { errors, warnings };
13831
13947
  }
13832
13948
  function validateCapabilities(provider, controls, errors) {
@@ -15902,8 +16018,8 @@ function buildStatusSnapshot(options) {
15902
16018
  for (const sourceSession of unreadSourceSessions) {
15903
16019
  const session = sessionsById.get(sourceSession.id);
15904
16020
  if (!session) continue;
15905
- const lastSeenAt = getSessionSeenAt(state, sourceSession.id);
15906
- const seenCompletionMarker = getSessionSeenMarker(state, sourceSession.id);
16021
+ const lastSeenAt = getSessionSeenAt(state, sourceSession.id, sourceSession.providerSessionId);
16022
+ const seenCompletionMarker = getSessionSeenMarker(state, sourceSession.id, sourceSession.providerSessionId);
15907
16023
  const lastUsedAt = getSessionLastUsedAt(sourceSession);
15908
16024
  const completionMarker = getSessionCompletionMarker(sourceSession);
15909
16025
  const { unread, inboxBucket } = sourceSession.surfaceHidden ? { unread: false, inboxBucket: "idle" } : getUnreadState(
@@ -16719,7 +16835,8 @@ var DaemonCommandRouter = class {
16719
16835
  currentState,
16720
16836
  sessionId,
16721
16837
  typeof args?.seenAt === "number" ? args.seenAt : Date.now(),
16722
- completionMarker
16838
+ completionMarker,
16839
+ targetSession?.providerSessionId
16723
16840
  );
16724
16841
  if (READ_DEBUG_ENABLED2) {
16725
16842
  LOG.info("RecentRead", `mark_session_seen sessionId=${sessionId} seenAt=${String(args?.seenAt || "")} prevSeenAt=${String(prevSeenAt)} nextSeenAt=${String(next.sessionReads?.[sessionId] || 0)} marker=${completionMarker || "-"}`);
@@ -17152,6 +17269,77 @@ var ProviderStreamAdapter = class {
17152
17269
  return raw;
17153
17270
  }
17154
17271
  }
17272
+ getOptionalError(raw) {
17273
+ return typeof raw === "string" && raw.trim() ? raw.trim() : void 0;
17274
+ }
17275
+ normalizeFocusEditorResult(raw) {
17276
+ const data = this.parseMaybeJson(raw);
17277
+ if (data === true) return { focused: true };
17278
+ if (data === false || data == null) return { focused: false };
17279
+ if (typeof data === "string") {
17280
+ const trimmed = data.trim();
17281
+ const normalized = trimmed.toLowerCase();
17282
+ if (normalized === "true" || normalized === "ok" || normalized === "success" || normalized === "focused" || normalized === "visible") {
17283
+ return { focused: true };
17284
+ }
17285
+ if (normalized === "false" || normalized === "not_found" || normalized === "not found" || normalized === "missing" || normalized === "panel_hidden" || normalized === "hidden") {
17286
+ return { focused: false };
17287
+ }
17288
+ return { focused: false, ...trimmed ? { error: trimmed } : {} };
17289
+ }
17290
+ if (data && typeof data === "object") {
17291
+ const error = this.getOptionalError(data.error);
17292
+ if (data.focused === true || data.success === true || data.ok === true || data.visible === true) {
17293
+ return { focused: true };
17294
+ }
17295
+ if (data.focused === false || data.success === false || data.ok === false || error) {
17296
+ return { focused: false, ...error ? { error } : {} };
17297
+ }
17298
+ }
17299
+ return { focused: false };
17300
+ }
17301
+ normalizeOpenPanelResult(raw) {
17302
+ const data = this.parseMaybeJson(raw);
17303
+ if (data === true) return { opened: true, visible: true };
17304
+ if (data === false || data == null) return { opened: false, visible: false };
17305
+ if (typeof data === "string") {
17306
+ const trimmed = data.trim();
17307
+ const normalized = trimmed.toLowerCase();
17308
+ if (normalized === "true" || normalized === "ok" || normalized === "opened" || normalized === "open" || normalized === "success") {
17309
+ return { opened: true, visible: true };
17310
+ }
17311
+ if (normalized === "visible") {
17312
+ return { opened: false, visible: true };
17313
+ }
17314
+ if (normalized === "focused") {
17315
+ return { opened: false, visible: true, focused: true };
17316
+ }
17317
+ if (normalized === "false" || normalized === "panel_hidden" || normalized === "hidden" || normalized === "not_found" || normalized === "not found" || normalized === "missing") {
17318
+ return { opened: false, visible: false };
17319
+ }
17320
+ return { opened: false, visible: false, ...trimmed ? { error: trimmed } : {} };
17321
+ }
17322
+ if (data && typeof data === "object") {
17323
+ const error = this.getOptionalError(data.error);
17324
+ const focused = data.focused === true;
17325
+ const visible = data.visible === true || data.opened === true || focused || data.success === true || data.ok === true;
17326
+ if (visible) {
17327
+ return {
17328
+ opened: data.opened === true,
17329
+ visible: true,
17330
+ ...focused ? { focused: true } : {}
17331
+ };
17332
+ }
17333
+ if (data.opened === false || data.visible === false || data.success === false || data.ok === false || error) {
17334
+ return {
17335
+ opened: false,
17336
+ visible: false,
17337
+ ...error ? { error } : {}
17338
+ };
17339
+ }
17340
+ }
17341
+ return { opened: false, visible: false };
17342
+ }
17155
17343
  summarizeRaw(raw) {
17156
17344
  try {
17157
17345
  if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
@@ -17238,6 +17426,11 @@ var ProviderStreamAdapter = class {
17238
17426
  if (typeof validated.title === "string" && validated.title.trim()) {
17239
17427
  state.title = validated.title.trim();
17240
17428
  }
17429
+ const providerSessionId = typeof validated.providerSessionId === "string" && validated.providerSessionId.trim() ? validated.providerSessionId.trim() : "";
17430
+ if (providerSessionId) {
17431
+ state.sessionId = providerSessionId;
17432
+ state.providerSessionId = providerSessionId;
17433
+ }
17241
17434
  const controlValues = extractProviderControlValues(this.provider.controls, validated);
17242
17435
  const surface = resolveProviderStateSurface({
17243
17436
  controlValues,
@@ -17360,8 +17553,15 @@ var ProviderStreamAdapter = class {
17360
17553
  }
17361
17554
  async focusEditor(evaluate) {
17362
17555
  const script = this.callScript("focusEditor");
17363
- if (!script) return;
17364
- await evaluate(script);
17556
+ if (!script) return { focused: false };
17557
+ const raw = await evaluate(script);
17558
+ return this.normalizeFocusEditorResult(raw);
17559
+ }
17560
+ async openPanel(evaluate) {
17561
+ const script = this.callScript("openPanel");
17562
+ if (!script) return { opened: false, visible: false };
17563
+ const raw = await evaluate(script);
17564
+ return this.normalizeOpenPanelResult(raw);
17365
17565
  }
17366
17566
  errorState(message) {
17367
17567
  return {
@@ -17631,19 +17831,33 @@ var DaemonAgentStreamManager = class {
17631
17831
  return false;
17632
17832
  }
17633
17833
  }
17634
- async focusSession(cdp, sessionId) {
17834
+ async selectSession(cdp, sessionId) {
17635
17835
  const target = this.getSessionTarget(sessionId);
17636
17836
  if (!target?.parentSessionId) return false;
17637
17837
  await this.setActiveSession(cdp, target.parentSessionId, sessionId);
17638
17838
  await this.syncActiveSession(cdp, target.parentSessionId);
17839
+ return this.managedBySessionId.has(sessionId);
17840
+ }
17841
+ async openSessionPanel(cdp, sessionId) {
17842
+ const selected = await this.selectSession(cdp, sessionId);
17843
+ if (!selected) return false;
17639
17844
  const agent = this.managedBySessionId.get(sessionId);
17640
- if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
17845
+ if (!agent) return false;
17846
+ let panelVisible = false;
17847
+ let editorFocused = false;
17641
17848
  try {
17642
17849
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
17643
- await agent.adapter.focusEditor(evaluate);
17644
- return true;
17850
+ if (typeof agent.adapter.openPanel === "function") {
17851
+ const result = await agent.adapter.openPanel(evaluate);
17852
+ panelVisible = result.visible;
17853
+ }
17854
+ if (typeof agent.adapter.focusEditor === "function") {
17855
+ const result = await agent.adapter.focusEditor(evaluate);
17856
+ editorFocused = result.focused;
17857
+ }
17858
+ return panelVisible || editorFocused;
17645
17859
  } catch (e) {
17646
- this.logFn(`[AgentStream] focusEditor(${sessionId}) error: ${e.message}`);
17860
+ this.logFn(`[AgentStream] openPanel(${sessionId}) error: ${e.message}`);
17647
17861
  return false;
17648
17862
  }
17649
17863
  }
@@ -17869,6 +18083,7 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
17869
18083
  summaryMetadata: stream.summaryMetadata || void 0,
17870
18084
  effects: stream.effects || void 0,
17871
18085
  sessionId: stream.sessionId || stream.instanceId || void 0,
18086
+ providerSessionId: stream.providerSessionId || stream.sessionId || void 0,
17872
18087
  title: stream.title || stream.agentName || void 0,
17873
18088
  agentType: stream.agentType || void 0,
17874
18089
  agentName: stream.agentName || void 0,
@@ -18521,9 +18736,14 @@ module.exports.setMode = (params) => {
18521
18736
  (() => {
18522
18737
  try {
18523
18738
  const input = document.querySelector('${meta.inputSelector || '[contenteditable="true"]'}');
18524
- if (input) { input.focus(); return 'focused'; }
18525
- return 'not_found';
18526
- } catch(e) { return 'error'; }
18739
+ if (input) {
18740
+ input.focus();
18741
+ return JSON.stringify({ focused: true });
18742
+ }
18743
+ return JSON.stringify({ focused: false, error: 'not_found' });
18744
+ } catch(e) {
18745
+ return JSON.stringify({ focused: false, error: e.message });
18746
+ }
18527
18747
  })()
18528
18748
  `;
18529
18749
  files[`${scriptDir}/open_panel.js`] = `/**
@@ -18534,8 +18754,10 @@ module.exports.setMode = (params) => {
18534
18754
  (() => {
18535
18755
  try {
18536
18756
  // TODO: Check if panel visible, if not find toggle button
18537
- return 'not_found';
18538
- } catch(e) { return 'error'; }
18757
+ return JSON.stringify({ opened: false, visible: false, error: 'not_found' });
18758
+ } catch(e) {
18759
+ return JSON.stringify({ opened: false, visible: false, error: e.message });
18760
+ }
18539
18761
  })()
18540
18762
  `;
18541
18763
  files[`${scriptDir}/resolve_action.js`] = `/**
@@ -21347,8 +21569,8 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
21347
21569
  lines.push("| setModel | `{ success: true/false }` |");
21348
21570
  lines.push("| listModes | `{ modes: [{ name, id }], current }` |");
21349
21571
  lines.push("| setMode | `{ success: true/false }` |");
21350
- lines.push("| focusEditor | `{ focused: true/false }` |");
21351
- lines.push("| openPanel | `{ opened: true/false }` |");
21572
+ lines.push("| focusEditor | `{ focused: true/false, error? }` |");
21573
+ lines.push("| openPanel | `{ opened: true/false, visible: true/false, focused?: true, error? }` |");
21352
21574
  lines.push("");
21353
21575
  lines.push("## \u{1F534} CRITICAL: readChat `status` Lifecycle");
21354
21576
  lines.push("The `status` field in readChat controls how the dashboard and daemon auto-approve-loop behave.");
@@ -22982,8 +23204,8 @@ var DevServer = class _DevServer {
22982
23204
  lines.push("| setModel | `{ success: true/false }` |");
22983
23205
  lines.push("| listModes | `{ modes: [{ name, id }], current }` |");
22984
23206
  lines.push("| setMode | `{ success: true/false }` |");
22985
- lines.push("| focusEditor | `{ focused: true/false }` |");
22986
- lines.push("| openPanel | `{ opened: true/false }` |");
23207
+ lines.push("| focusEditor | `{ focused: true/false, error? }` |");
23208
+ lines.push("| openPanel | `{ opened: true/false, visible: true/false, focused?: true, error? }` |");
22987
23209
  lines.push("");
22988
23210
  lines.push("## \u{1F534} CRITICAL: readChat `status` Lifecycle");
22989
23211
  lines.push("The `status` field in readChat controls how the dashboard and daemon auto-approve-loop behave.");