@adhdev/daemon-core 0.7.33 → 0.7.36

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 (36) hide show
  1. package/dist/index.d.mts +29 -3
  2. package/dist/index.d.ts +29 -3
  3. package/dist/index.js +637 -253
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +637 -255
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/{normalize-auJAPmKy.d.mts → normalize-DVI4Lo5I.d.mts} +63 -1
  8. package/dist/{normalize-auJAPmKy.d.ts → normalize-DVI4Lo5I.d.ts} +63 -1
  9. package/dist/status/normalize.d.mts +1 -1
  10. package/dist/status/normalize.d.ts +1 -1
  11. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  12. package/package.json +1 -1
  13. package/src/agent-stream/manager.ts +14 -1
  14. package/src/agent-stream/provider-adapter.ts +45 -3
  15. package/src/agent-stream/types.ts +4 -0
  16. package/src/cli-adapters/terminal-screen.ts +15 -0
  17. package/src/commands/cli-manager.ts +35 -0
  18. package/src/commands/router.ts +44 -1
  19. package/src/commands/workspace-commands.ts +2 -1
  20. package/src/config/config.d.ts +4 -0
  21. package/src/config/config.ts +9 -1
  22. package/src/config/recent-activity.ts +83 -0
  23. package/src/config/workspaces.d.ts +3 -1
  24. package/src/config/workspaces.ts +15 -1
  25. package/src/index.ts +16 -0
  26. package/src/providers/acp-provider-instance.ts +5 -0
  27. package/src/providers/cli-provider-instance.ts +2 -0
  28. package/src/providers/contracts.ts +94 -0
  29. package/src/providers/extension-provider-instance.ts +4 -0
  30. package/src/providers/ide-provider-instance.ts +2 -0
  31. package/src/providers/provider-instance.ts +5 -1
  32. package/src/shared-types.d.ts +35 -0
  33. package/src/shared-types.ts +70 -0
  34. package/src/status/builders.ts +90 -1
  35. package/src/status/reporter.ts +8 -0
  36. package/src/status/snapshot.ts +162 -0
@@ -469,6 +469,31 @@ interface WorkspaceActivityEntry {
469
469
  }
470
470
  declare function getWorkspaceActivity(config: ADHDevConfig, limit?: number): WorkspaceActivityEntry[];
471
471
 
472
+ /**
473
+ * Unified recent activity — machine-facing "pick up where you left off".
474
+ *
475
+ * Unlike cliHistory or workspaceActivity, this is task/session oriented:
476
+ * - one normalized row shape for IDE / CLI / ACP
477
+ * - deduped by kind + providerType + workspace
478
+ * - optionally linked to a live sessionId when known
479
+ */
480
+
481
+ interface RecentActivityEntry {
482
+ id: string;
483
+ kind: 'ide' | 'cli' | 'acp';
484
+ providerType: string;
485
+ providerName: string;
486
+ workspace?: string | null;
487
+ currentModel?: string;
488
+ sessionId?: string | null;
489
+ title?: string;
490
+ lastUsedAt: number;
491
+ }
492
+ declare function appendRecentActivity(config: ADHDevConfig, entry: Omit<RecentActivityEntry, 'id' | 'lastUsedAt'> & {
493
+ lastUsedAt?: number;
494
+ }): ADHDevConfig;
495
+ declare function getRecentActivity(config: ADHDevConfig, limit?: number): RecentActivityEntry[];
496
+
472
497
  /**
473
498
  * ADHDev Launcher — Configuration
474
499
  *
@@ -503,6 +528,8 @@ interface ADHDevConfig {
503
528
  defaultWorkspaceId?: string | null;
504
529
  /** Recently used workspaces (IDE / CLI / ACP / default) for quick resume */
505
530
  recentWorkspaceActivity?: WorkspaceActivityEntry[];
531
+ /** Unified recent activity across IDE / CLI / ACP launch flows */
532
+ recentActivity?: RecentActivityEntry[];
506
533
  machineNickname: string | null;
507
534
  /**
508
535
  * Stable local machine ID (prefix: `mach_`) — generated locally on first run.
@@ -651,6 +678,8 @@ interface SessionEntry {
651
678
  currentAutoApprove?: string;
652
679
  acpConfigOptions?: AcpConfigOption[];
653
680
  acpModes?: AcpMode[];
681
+ controlValues?: Record<string, string | number | boolean>;
682
+ providerControls?: ProviderControlSchema[];
654
683
  errorMessage?: string;
655
684
  errorReason?: ProviderErrorReason;
656
685
  }
@@ -680,6 +709,26 @@ interface AcpMode {
680
709
  name: string;
681
710
  description?: string;
682
711
  }
712
+ /** Provider control schema (daemon → frontend) */
713
+ interface ProviderControlSchema {
714
+ id: string;
715
+ type: 'select' | 'toggle' | 'cycle' | 'slider' | 'action';
716
+ label: string;
717
+ icon?: string;
718
+ placement: 'bar' | 'header' | 'menu';
719
+ options?: { value: string; label: string; description?: string; group?: string }[];
720
+ dynamic?: boolean;
721
+ listScript?: string;
722
+ setScript?: string;
723
+ readFrom?: string;
724
+ defaultValue?: string | number | boolean;
725
+ invokeScript?: string;
726
+ resultDisplay?: 'toast' | 'inline' | 'none';
727
+ min?: number;
728
+ max?: number;
729
+ step?: number;
730
+ order?: number;
731
+ }
683
732
  /** Machine hardware/OS info (reported by daemon, displayed by web) */
684
733
  interface MachineInfo {
685
734
  hostname: string;
@@ -709,6 +758,18 @@ interface WorkspaceActivity {
709
758
  kind?: string;
710
759
  agentType?: string;
711
760
  }
761
+ interface RecentSessionEntry {
762
+ id: string;
763
+ sessionId?: string | null;
764
+ providerType: string;
765
+ providerName: string;
766
+ kind: 'ide' | 'cli' | 'acp';
767
+ title: string;
768
+ workspace?: string | null;
769
+ currentModel?: string;
770
+ status?: SessionEntry['status'];
771
+ lastUsedAt: number;
772
+ }
712
773
  interface StatusReportPayload {
713
774
  /** Daemon instance ID */
714
775
  instanceId: string;
@@ -738,6 +799,7 @@ interface StatusReportPayload {
738
799
  defaultWorkspaceId?: string | null;
739
800
  defaultWorkspacePath?: string | null;
740
801
  workspaceActivity?: WorkspaceActivity[];
802
+ recentSessions?: RecentSessionEntry[];
741
803
  }
742
804
 
743
805
  /**
@@ -882,4 +944,4 @@ declare function isManagedStatusWaiting(status?: string | null, opts?: {
882
944
  }): boolean;
883
945
  declare function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(activeChat: T): T;
884
946
 
885
- export { saveConfig as $, type AvailableProviderInfo as A, type ProviderInfo as B, type CommandResult as C, type DaemonEvent as D, type ExtensionInfo as E, type ProviderStatus as F, type SessionCapability as G, type SessionKind as H, type InstanceContext as I, type SystemInfo as J, type WorkspaceEntry as K, addCliHistory as L, type MachineInfo as M, getWorkspaceActivity as N, getWorkspaceState as O, type ProviderCategory as P, isManagedStatusWaiting as Q, type ResolvedProvider as R, type StatusResponse as S, isManagedStatusWorking as T, isSetupComplete as U, loadConfig as V, type WorkspaceActivity as W, markSetupComplete as X, normalizeActiveChatData as Y, normalizeManagedStatus as Z, resetConfig as _, type SessionEntry as a, updateConfig as a0, type ProviderModule as b, type ProviderSettingSchema as c, type CdpTargetFilter as d, type ProviderInstance as e, type ProviderState as f, type ProviderEvent as g, type SessionTransport as h, type StatusReportPayload as i, type ProviderResumeCapability as j, type AcpProviderState as k, type ContentBlock as l, type AcpConfigOption as m, type AcpMode as n, type ActiveChatData as o, type AgentEntry as p, type AgentSessionStream as q, type ChatMessage as r, type CliProviderState as s, type DetectedIde as t, type DetectedIdeInfo as u, type ExtensionProviderState as v, type IdeProviderState as w, type ManagedStatus as x, type ProviderConfig as y, type ProviderErrorReason as z };
947
+ export { markSetupComplete as $, type AvailableProviderInfo as A, type ProviderErrorReason as B, type CommandResult as C, type DaemonEvent as D, type ExtensionInfo as E, type ProviderInfo as F, type ProviderStatus as G, type RecentActivityEntry as H, type InstanceContext as I, type SessionCapability as J, type SessionKind as K, type SystemInfo as L, type MachineInfo as M, type WorkspaceEntry as N, addCliHistory as O, type ProviderCategory as P, appendRecentActivity as Q, type ResolvedProvider as R, type StatusResponse as S, getRecentActivity as T, getWorkspaceActivity as U, getWorkspaceState as V, type WorkspaceActivity as W, isManagedStatusWaiting as X, isManagedStatusWorking as Y, isSetupComplete as Z, loadConfig as _, type SessionEntry as a, normalizeActiveChatData as a0, normalizeManagedStatus as a1, resetConfig as a2, saveConfig as a3, updateConfig as a4, type ProviderModule as b, type ProviderSettingSchema as c, type CdpTargetFilter as d, type ProviderInstance as e, type ProviderState as f, type ProviderEvent as g, type SessionTransport as h, type StatusReportPayload as i, type ProviderResumeCapability as j, type AcpProviderState as k, type ContentBlock as l, type AcpConfigOption as m, type AcpMode as n, type ActiveChatData as o, type AgentEntry as p, type AgentSessionStream as q, type ChatMessage as r, type CliProviderState as s, type DetectedIde as t, type DetectedIdeInfo as u, type ExtensionProviderState as v, type IdeProviderState as w, type ManagedStatus as x, type ProviderConfig as y, type ProviderControlSchema as z };
@@ -469,6 +469,31 @@ interface WorkspaceActivityEntry {
469
469
  }
470
470
  declare function getWorkspaceActivity(config: ADHDevConfig, limit?: number): WorkspaceActivityEntry[];
471
471
 
472
+ /**
473
+ * Unified recent activity — machine-facing "pick up where you left off".
474
+ *
475
+ * Unlike cliHistory or workspaceActivity, this is task/session oriented:
476
+ * - one normalized row shape for IDE / CLI / ACP
477
+ * - deduped by kind + providerType + workspace
478
+ * - optionally linked to a live sessionId when known
479
+ */
480
+
481
+ interface RecentActivityEntry {
482
+ id: string;
483
+ kind: 'ide' | 'cli' | 'acp';
484
+ providerType: string;
485
+ providerName: string;
486
+ workspace?: string | null;
487
+ currentModel?: string;
488
+ sessionId?: string | null;
489
+ title?: string;
490
+ lastUsedAt: number;
491
+ }
492
+ declare function appendRecentActivity(config: ADHDevConfig, entry: Omit<RecentActivityEntry, 'id' | 'lastUsedAt'> & {
493
+ lastUsedAt?: number;
494
+ }): ADHDevConfig;
495
+ declare function getRecentActivity(config: ADHDevConfig, limit?: number): RecentActivityEntry[];
496
+
472
497
  /**
473
498
  * ADHDev Launcher — Configuration
474
499
  *
@@ -503,6 +528,8 @@ interface ADHDevConfig {
503
528
  defaultWorkspaceId?: string | null;
504
529
  /** Recently used workspaces (IDE / CLI / ACP / default) for quick resume */
505
530
  recentWorkspaceActivity?: WorkspaceActivityEntry[];
531
+ /** Unified recent activity across IDE / CLI / ACP launch flows */
532
+ recentActivity?: RecentActivityEntry[];
506
533
  machineNickname: string | null;
507
534
  /**
508
535
  * Stable local machine ID (prefix: `mach_`) — generated locally on first run.
@@ -651,6 +678,8 @@ interface SessionEntry {
651
678
  currentAutoApprove?: string;
652
679
  acpConfigOptions?: AcpConfigOption[];
653
680
  acpModes?: AcpMode[];
681
+ controlValues?: Record<string, string | number | boolean>;
682
+ providerControls?: ProviderControlSchema[];
654
683
  errorMessage?: string;
655
684
  errorReason?: ProviderErrorReason;
656
685
  }
@@ -680,6 +709,26 @@ interface AcpMode {
680
709
  name: string;
681
710
  description?: string;
682
711
  }
712
+ /** Provider control schema (daemon → frontend) */
713
+ interface ProviderControlSchema {
714
+ id: string;
715
+ type: 'select' | 'toggle' | 'cycle' | 'slider' | 'action';
716
+ label: string;
717
+ icon?: string;
718
+ placement: 'bar' | 'header' | 'menu';
719
+ options?: { value: string; label: string; description?: string; group?: string }[];
720
+ dynamic?: boolean;
721
+ listScript?: string;
722
+ setScript?: string;
723
+ readFrom?: string;
724
+ defaultValue?: string | number | boolean;
725
+ invokeScript?: string;
726
+ resultDisplay?: 'toast' | 'inline' | 'none';
727
+ min?: number;
728
+ max?: number;
729
+ step?: number;
730
+ order?: number;
731
+ }
683
732
  /** Machine hardware/OS info (reported by daemon, displayed by web) */
684
733
  interface MachineInfo {
685
734
  hostname: string;
@@ -709,6 +758,18 @@ interface WorkspaceActivity {
709
758
  kind?: string;
710
759
  agentType?: string;
711
760
  }
761
+ interface RecentSessionEntry {
762
+ id: string;
763
+ sessionId?: string | null;
764
+ providerType: string;
765
+ providerName: string;
766
+ kind: 'ide' | 'cli' | 'acp';
767
+ title: string;
768
+ workspace?: string | null;
769
+ currentModel?: string;
770
+ status?: SessionEntry['status'];
771
+ lastUsedAt: number;
772
+ }
712
773
  interface StatusReportPayload {
713
774
  /** Daemon instance ID */
714
775
  instanceId: string;
@@ -738,6 +799,7 @@ interface StatusReportPayload {
738
799
  defaultWorkspaceId?: string | null;
739
800
  defaultWorkspacePath?: string | null;
740
801
  workspaceActivity?: WorkspaceActivity[];
802
+ recentSessions?: RecentSessionEntry[];
741
803
  }
742
804
 
743
805
  /**
@@ -882,4 +944,4 @@ declare function isManagedStatusWaiting(status?: string | null, opts?: {
882
944
  }): boolean;
883
945
  declare function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(activeChat: T): T;
884
946
 
885
- export { saveConfig as $, type AvailableProviderInfo as A, type ProviderInfo as B, type CommandResult as C, type DaemonEvent as D, type ExtensionInfo as E, type ProviderStatus as F, type SessionCapability as G, type SessionKind as H, type InstanceContext as I, type SystemInfo as J, type WorkspaceEntry as K, addCliHistory as L, type MachineInfo as M, getWorkspaceActivity as N, getWorkspaceState as O, type ProviderCategory as P, isManagedStatusWaiting as Q, type ResolvedProvider as R, type StatusResponse as S, isManagedStatusWorking as T, isSetupComplete as U, loadConfig as V, type WorkspaceActivity as W, markSetupComplete as X, normalizeActiveChatData as Y, normalizeManagedStatus as Z, resetConfig as _, type SessionEntry as a, updateConfig as a0, type ProviderModule as b, type ProviderSettingSchema as c, type CdpTargetFilter as d, type ProviderInstance as e, type ProviderState as f, type ProviderEvent as g, type SessionTransport as h, type StatusReportPayload as i, type ProviderResumeCapability as j, type AcpProviderState as k, type ContentBlock as l, type AcpConfigOption as m, type AcpMode as n, type ActiveChatData as o, type AgentEntry as p, type AgentSessionStream as q, type ChatMessage as r, type CliProviderState as s, type DetectedIde as t, type DetectedIdeInfo as u, type ExtensionProviderState as v, type IdeProviderState as w, type ManagedStatus as x, type ProviderConfig as y, type ProviderErrorReason as z };
947
+ export { markSetupComplete as $, type AvailableProviderInfo as A, type ProviderErrorReason as B, type CommandResult as C, type DaemonEvent as D, type ExtensionInfo as E, type ProviderInfo as F, type ProviderStatus as G, type RecentActivityEntry as H, type InstanceContext as I, type SessionCapability as J, type SessionKind as K, type SystemInfo as L, type MachineInfo as M, type WorkspaceEntry as N, addCliHistory as O, type ProviderCategory as P, appendRecentActivity as Q, type ResolvedProvider as R, type StatusResponse as S, getRecentActivity as T, getWorkspaceActivity as U, getWorkspaceState as V, type WorkspaceActivity as W, isManagedStatusWaiting as X, isManagedStatusWorking as Y, isSetupComplete as Z, loadConfig as _, type SessionEntry as a, normalizeActiveChatData as a0, normalizeManagedStatus as a1, resetConfig as a2, saveConfig as a3, updateConfig as a4, type ProviderModule as b, type ProviderSettingSchema as c, type CdpTargetFilter as d, type ProviderInstance as e, type ProviderState as f, type ProviderEvent as g, type SessionTransport as h, type StatusReportPayload as i, type ProviderResumeCapability as j, type AcpProviderState as k, type ContentBlock as l, type AcpConfigOption as m, type AcpMode as n, type ActiveChatData as o, type AgentEntry as p, type AgentSessionStream as q, type ChatMessage as r, type CliProviderState as s, type DetectedIde as t, type DetectedIdeInfo as u, type ExtensionProviderState as v, type IdeProviderState as w, type ManagedStatus as x, type ProviderConfig as y, type ProviderControlSchema as z };
@@ -1 +1 @@
1
- export { x as ManagedStatus, Q as isManagedStatusWaiting, T as isManagedStatusWorking, Y as normalizeActiveChatData, Z as normalizeManagedStatus } from '../normalize-auJAPmKy.mjs';
1
+ export { x as ManagedStatus, X as isManagedStatusWaiting, Y as isManagedStatusWorking, a0 as normalizeActiveChatData, a1 as normalizeManagedStatus } from '../normalize-DVI4Lo5I.mjs';
@@ -1 +1 @@
1
- export { x as ManagedStatus, Q as isManagedStatusWaiting, T as isManagedStatusWorking, Y as normalizeActiveChatData, Z as normalizeManagedStatus } from '../normalize-auJAPmKy.js';
1
+ export { x as ManagedStatus, X as isManagedStatusWaiting, Y as isManagedStatusWorking, a0 as normalizeActiveChatData, a1 as normalizeManagedStatus } from '../normalize-DVI4Lo5I.js';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.7.33",
3
+ "version": "0.7.36",
4
4
  "description": "ADHDev local session host core — session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.7.33",
3
+ "version": "0.7.36",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -67,6 +67,15 @@ export class DaemonAgentStreamManager {
67
67
  return this.activeSessionIdByParent.get(parentSessionId) || null;
68
68
  }
69
69
 
70
+ private isRecoverableSessionError(message: string): boolean {
71
+ return message.includes('timeout')
72
+ || message.includes('not connected')
73
+ || message.includes('Session')
74
+ || message.includes('Target closed')
75
+ || message.includes('execution context')
76
+ || message.includes('context with specified id');
77
+ }
78
+
70
79
  private getSessionTarget(sessionId: string) {
71
80
  return this.sessionRegistry?.get(sessionId);
72
81
  }
@@ -186,6 +195,10 @@ export class DaemonAgentStreamManager {
186
195
  cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
187
196
  const state = await agent.adapter.readChat(evaluate);
188
197
  LOG.debug('AgentStream', `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${state.model || ''}${state.status === 'error' ? ' error=' + JSON.stringify((state as any).error || (state as any)._error || 'unknown') : ''}`);
198
+ const stateError = String((state as any).error || (state as any)._error || '');
199
+ if (state.status === 'error' && this.isRecoverableSessionError(stateError)) {
200
+ throw new Error(stateError);
201
+ }
189
202
  agent.lastState = state;
190
203
  agent.lastError = null;
191
204
  if (state.status === 'panel_hidden') {
@@ -196,7 +209,7 @@ export class DaemonAgentStreamManager {
196
209
  const errorMsg = (e as Error)?.message || String(e);
197
210
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
198
211
  agent.lastError = errorMsg;
199
- if (errorMsg.includes('timeout') || errorMsg.includes('not connected') || errorMsg.includes('Session')) {
212
+ if (this.isRecoverableSessionError(errorMsg)) {
200
213
  try { await cdp.detachAgent(agent.cdpSessionId); } catch { }
201
214
  this.managedBySessionId.delete(activeSessionId);
202
215
  this.lastDiscoveryTimeByParent.set(parentSessionId, 0);
@@ -40,12 +40,33 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
40
40
  return typeof (this.provider.scripts as any)?.[name] === 'function';
41
41
  }
42
42
 
43
+ private summarizeRaw(raw: unknown): string {
44
+ try {
45
+ if (typeof raw === 'string') return raw.replace(/\s+/g, ' ').trim().slice(0, 240);
46
+ if (raw == null) return String(raw);
47
+ return JSON.stringify(raw).replace(/\s+/g, ' ').trim().slice(0, 240);
48
+ } catch {
49
+ return Object.prototype.toString.call(raw);
50
+ }
51
+ }
52
+
53
+ private isTransportError(reason: string): boolean {
54
+ return /Session with given id not found/i.test(reason)
55
+ || /CDP not connected/i.test(reason)
56
+ || /Target closed/i.test(reason)
57
+ || /WebSocket not open/i.test(reason)
58
+ || /not connected/i.test(reason)
59
+ || /execution context/i.test(reason)
60
+ || /Cannot find context with specified id/i.test(reason);
61
+ }
62
+
43
63
  async readChat(evaluate: AgentEvaluateFn): Promise<AgentStreamState> {
44
64
  const script = this.callScript('readChat');
45
65
  if (!script) return this.errorState('readChat script not available');
46
66
 
67
+ let raw: unknown = null;
47
68
  try {
48
- const raw = await evaluate(script) as string;
69
+ raw = await evaluate(script) as string;
49
70
  const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
50
71
  if (data?.error) {
51
72
  const state = this.errorState(data.error);
@@ -65,12 +86,33 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
65
86
  mode: data.mode,
66
87
  activeModal: data.activeModal,
67
88
  };
89
+ // Build controlValues from provider controls schema
90
+ if (this.provider.controls?.length) {
91
+ const cv: Record<string, string | number | boolean> = {};
92
+ for (const ctrl of this.provider.controls) {
93
+ if (!ctrl.readFrom) continue; // action type — no value
94
+ const val = data[ctrl.readFrom];
95
+ if (val !== undefined && val !== null) {
96
+ cv[ctrl.id] = typeof val === 'object' ? (val.name || val.id || String(val)) : val;
97
+ }
98
+ }
99
+ // Also include model/mode as fallback controls
100
+ if (data.model && !cv['model']) cv['model'] = data.model;
101
+ if (data.mode && !cv['mode']) cv['mode'] = data.mode;
102
+ if (Object.keys(cv).length > 0) state.controlValues = cv;
103
+ }
68
104
  if (state.messages.length > 0) {
69
105
  this.lastSuccessState = state;
70
106
  }
71
107
  return state;
72
- } catch {
73
- const state = this.errorState(`Failed to parse ${this.agentName} state`);
108
+ } catch (error) {
109
+ const reason = error instanceof Error ? error.message : String(error);
110
+ if (this.isTransportError(reason)) {
111
+ throw (error instanceof Error ? error : new Error(reason));
112
+ }
113
+ const preview = this.summarizeRaw(raw);
114
+ const detail = preview ? ` (reason=${reason}; raw=${preview})` : ` (reason=${reason})`;
115
+ const state = this.errorState(`Failed to parse ${this.agentName} state${detail}`);
74
116
  if (this.lastSuccessState?.messages?.length) {
75
117
  state.messages = this.lastSuccessState.messages;
76
118
  }
@@ -29,9 +29,13 @@ export interface AgentStreamState {
29
29
  status: 'idle' | 'streaming' | 'waiting_approval' | 'error' | 'disconnected' | 'panel_hidden' | 'not_monitored';
30
30
  messages: AgentChatMessage[];
31
31
  inputContent: string;
32
+ /** @deprecated Use controlValues['model'] — kept for backward compatibility */
32
33
  model?: string;
34
+ /** @deprecated Use controlValues['mode'] — kept for backward compatibility */
33
35
  mode?: string;
34
36
  activeModal?: { message: string; buttons: string[] };
37
+ /** Dynamic control current values (populated from readChat + provider controls schema) */
38
+ controlValues?: Record<string, string | number | boolean>;
35
39
  }
36
40
 
37
41
  /** Agent webview target info */
@@ -19,6 +19,21 @@ import { XtermTerminalBackend } from './terminal-backends/xterm-backend.js';
19
19
  const DEFAULT_SCROLLBACK = 2000;
20
20
  const loggedTerminalBackends = new Set<string>();
21
21
 
22
+ export function getTerminalBackendRuntimeStatus(): {
23
+ backend: TerminalViewportBackendKind;
24
+ preference: TerminalViewportBackendPreference;
25
+ ghosttyAvailable: boolean;
26
+ } {
27
+ const preference = resolveTerminalBackendPreference();
28
+ const ghosttyAvailable = isGhosttyVtBackendAvailable();
29
+ const backend: TerminalViewportBackendKind = (
30
+ preference === 'ghostty-vt' || (preference === 'auto' && ghosttyAvailable)
31
+ ? 'ghostty-vt'
32
+ : 'xterm'
33
+ );
34
+ return { backend, preference, ghosttyAvailable };
35
+ }
36
+
22
37
  function createTerminalBackend(
23
38
  options: TerminalViewportBackendOptions,
24
39
  preference: TerminalViewportBackendPreference,
@@ -14,6 +14,7 @@ import { detectCLI } from '../detection/cli-detector.js';
14
14
  import { loadConfig, saveConfig, addCliHistory } from '../config/config.js';
15
15
  import { getWorkspaceState, resolveLaunchDirectory } from '../config/workspaces.js';
16
16
  import { appendWorkspaceActivity } from '../config/workspace-activity.js';
17
+ import { appendRecentActivity } from '../config/recent-activity.js';
17
18
  import { CliProviderInstance } from '../providers/cli-provider-instance.js';
18
19
  import { AcpProviderInstance } from '../providers/acp-provider-instance.js';
19
20
  import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
@@ -109,6 +110,22 @@ export class DaemonCliManager {
109
110
  }
110
111
  }
111
112
 
113
+ private persistRecentActivity(entry: {
114
+ kind: 'ide' | 'cli' | 'acp';
115
+ providerType: string;
116
+ providerName: string;
117
+ workspace?: string;
118
+ currentModel?: string;
119
+ sessionId?: string;
120
+ title?: string;
121
+ }): void {
122
+ try {
123
+ saveConfig(appendRecentActivity(loadConfig(), entry));
124
+ } catch (e) {
125
+ console.error(colorize('red', ` ✗ Failed to save recent activity: ${e}`));
126
+ }
127
+ }
128
+
112
129
  private getTransportFactory(
113
130
  runtimeId: string,
114
131
  providerType: string,
@@ -300,6 +317,15 @@ export class DaemonCliManager {
300
317
  }
301
318
 
302
319
  try { addCliHistory({ category: 'acp', cliType: normalizedType, dir: resolvedDir, workspace: resolvedDir, cliArgs, model: initialModel }); } catch (e) { LOG.warn('CLI', `ACP history save failed: ${(e as Error)?.message}`); }
320
+ this.persistRecentActivity({
321
+ kind: 'acp',
322
+ providerType: normalizedType,
323
+ providerName: provider.displayName || provider.name || normalizedType,
324
+ workspace: resolvedDir,
325
+ currentModel: initialModel,
326
+ sessionId,
327
+ title: provider.displayName || provider.name || normalizedType,
328
+ });
303
329
  this.deps.onStatusChange();
304
330
  return;
305
331
  }
@@ -368,6 +394,15 @@ export class DaemonCliManager {
368
394
  }
369
395
 
370
396
  try { addCliHistory({ category: 'cli', cliType, dir: resolvedDir, workspace: resolvedDir, cliArgs, model: initialModel }); } catch (e) { LOG.warn('CLI', `CLI history save failed: ${(e as Error)?.message}`); }
397
+ this.persistRecentActivity({
398
+ kind: 'cli',
399
+ providerType: normalizedType,
400
+ providerName: provider?.displayName || provider?.name || normalizedType,
401
+ workspace: resolvedDir,
402
+ currentModel: initialModel,
403
+ sessionId: key,
404
+ title: provider?.displayName || provider?.name || normalizedType,
405
+ });
371
406
 
372
407
  this.deps.onStatusChange();
373
408
  }
@@ -20,6 +20,7 @@ import { loadConfig, saveConfig, updateConfig } from '../config/config.js';
20
20
  import { resolveIdeLaunchWorkspace } from '../config/workspaces.js';
21
21
  import { appendWorkspaceActivity } from '../config/workspace-activity.js';
22
22
  import { addCliHistory } from '../config/config.js';
23
+ import { appendRecentActivity, buildRecentActivityKey, markRecentSessionSeen } from '../config/recent-activity.js';
23
24
  import { detectIDEs } from '../detection/ide-detector.js';
24
25
  import { SessionRegistry } from '../sessions/registry.js';
25
26
  import { LOG } from '../logging/logger.js';
@@ -240,9 +241,26 @@ export class DaemonCommandRouter {
240
241
  this.deps.onIdeConnected?.();
241
242
  if (result.success && resolvedWorkspace) {
242
243
  try {
243
- saveConfig(appendWorkspaceActivity(loadConfig(), resolvedWorkspace, {
244
+ let next = appendWorkspaceActivity(loadConfig(), resolvedWorkspace, {
244
245
  kind: 'ide',
245
246
  agentType: result.ideId,
247
+ });
248
+ next = appendRecentActivity(next, {
249
+ kind: 'ide',
250
+ providerType: result.ideId || ideKey,
251
+ providerName: result.ideId || ideKey,
252
+ workspace: resolvedWorkspace,
253
+ title: result.ideId || ideKey,
254
+ });
255
+ saveConfig(next);
256
+ } catch { /* ignore activity persist errors */ }
257
+ } else if (result.success && (result.ideId || ideKey)) {
258
+ try {
259
+ saveConfig(appendRecentActivity(loadConfig(), {
260
+ kind: 'ide',
261
+ providerType: result.ideId || ideKey,
262
+ providerName: result.ideId || ideKey,
263
+ title: result.ideId || ideKey,
246
264
  }));
247
265
  } catch { /* ignore activity persist errors */ }
248
266
  }
@@ -264,6 +282,31 @@ export class DaemonCommandRouter {
264
282
  return { success: true, userName: name };
265
283
  }
266
284
 
285
+ case 'mark_recent_seen': {
286
+ const kind = args?.kind;
287
+ const providerType = args?.providerType;
288
+ if (!kind || !providerType) {
289
+ return { success: false, error: 'kind and providerType are required' };
290
+ }
291
+ const recentKey = args?.recentKey || buildRecentActivityKey({
292
+ kind,
293
+ providerType,
294
+ workspace: args?.workspace || null,
295
+ });
296
+ const next = markRecentSessionSeen(
297
+ loadConfig(),
298
+ recentKey,
299
+ typeof args?.seenAt === 'number' ? args.seenAt : Date.now(),
300
+ );
301
+ saveConfig(next);
302
+ this.deps.onStatusChange?.();
303
+ return {
304
+ success: true,
305
+ recentKey,
306
+ seenAt: next.recentSessionReads?.[recentKey] || Date.now(),
307
+ };
308
+ }
309
+
267
310
  // ─── Daemon Self-Upgrade ───
268
311
  case 'daemon_upgrade': {
269
312
  LOG.info('Upgrade', 'Remote upgrade requested from dashboard');
@@ -24,10 +24,11 @@ export function handleWorkspaceList(): WorkspaceCommandResult {
24
24
  export function handleWorkspaceAdd(args: any): WorkspaceCommandResult {
25
25
  const rawPath = (args?.path || args?.dir || '').trim();
26
26
  const label = (args?.label || '').trim() || undefined;
27
+ const createIfMissing = args?.createIfMissing === true;
27
28
  if (!rawPath) return { success: false, error: 'path required' };
28
29
 
29
30
  const config = loadConfig();
30
- const result = W.addWorkspaceEntry(config, rawPath, label);
31
+ const result = W.addWorkspaceEntry(config, rawPath, label, { createIfMissing });
31
32
  if ('error' in result) return { success: false, error: result.error };
32
33
 
33
34
  let cfg = appendWorkspaceActivity(result.config, result.entry.path, {});
@@ -5,8 +5,10 @@
5
5
  */
6
6
  import type { WorkspaceEntry } from './workspaces.js';
7
7
  import type { WorkspaceActivityEntry } from './workspace-activity.js';
8
+ import type { RecentActivityEntry } from './recent-activity.js';
8
9
  export type { WorkspaceEntry } from './workspaces.js';
9
10
  export type { WorkspaceActivityEntry } from './workspace-activity.js';
11
+ export type { RecentActivityEntry } from './recent-activity.js';
10
12
  export interface ADHDevConfig {
11
13
  serverUrl: string;
12
14
  apiToken: string | null;
@@ -35,6 +37,8 @@ export interface ADHDevConfig {
35
37
  defaultWorkspaceId?: string | null;
36
38
  /** Recently used workspaces (IDE / CLI / ACP / default) for quick resume */
37
39
  recentWorkspaceActivity?: WorkspaceActivityEntry[];
40
+ /** Unified recent activity across IDE / CLI / ACP launch flows */
41
+ recentActivity?: RecentActivityEntry[];
38
42
  machineNickname: string | null;
39
43
  /**
40
44
  * Stable local machine ID (prefix: `mach_`) — generated locally on first run.
@@ -11,8 +11,10 @@ import { randomUUID } from 'crypto';
11
11
  import { migrateWorkspacesFromRecent } from './workspaces.js';
12
12
  import type { WorkspaceEntry } from './workspaces.js';
13
13
  import type { WorkspaceActivityEntry } from './workspace-activity.js';
14
+ import type { RecentActivityEntry } from './recent-activity.js';
14
15
  export type { WorkspaceEntry } from './workspaces.js';
15
16
  export type { WorkspaceActivityEntry } from './workspace-activity.js';
17
+ export type { RecentActivityEntry } from './recent-activity.js';
16
18
 
17
19
  export interface ADHDevConfig {
18
20
  // Server connection
@@ -59,8 +61,12 @@ export interface ADHDevConfig {
59
61
  /** Default workspace id (from workspaces[]) — never used implicitly for launch */
60
62
  defaultWorkspaceId?: string | null;
61
63
 
62
- /** Recently used workspaces (IDE / CLI / ACP / default) for quick resume */
64
+ /** Recently used workspaces (IDE / CLI / ACP / default) for quick resume */
63
65
  recentWorkspaceActivity?: WorkspaceActivityEntry[];
66
+ /** Unified recent activity across IDE / CLI / ACP launch flows */
67
+ recentActivity?: RecentActivityEntry[];
68
+ /** Last seen timestamps for machine-facing recent/session entries */
69
+ recentSessionReads?: Record<string, number>;
64
70
 
65
71
  // Machine nickname (user-customizable label for this machine)
66
72
  machineNickname: string | null;
@@ -138,6 +144,8 @@ const DEFAULT_CONFIG: ADHDevConfig = {
138
144
  workspaces: [],
139
145
  defaultWorkspaceId: null,
140
146
  recentWorkspaceActivity: [],
147
+ recentActivity: [],
148
+ recentSessionReads: {},
141
149
  machineNickname: null,
142
150
  machineId: undefined,
143
151
  machineSecret: null,