@adhdev/daemon-core 0.7.40 → 0.7.42

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.
@@ -26,10 +26,13 @@ export declare class CliProviderInstance implements ProviderInstance {
26
26
  private lastApprovalEventAt;
27
27
  private historyWriter;
28
28
  readonly instanceId: string;
29
+ private presentationMode;
29
30
  constructor(provider: ProviderModule, workingDir: string, cliArgs?: string[], instanceId?: string, transportFactory?: PtyTransportFactory);
30
31
  init(context: InstanceContext): Promise<void>;
31
32
  onTick(): Promise<void>;
32
33
  getState(): ProviderState;
34
+ setPresentationMode(mode: 'terminal' | 'chat'): void;
35
+ getPresentationMode(): 'terminal' | 'chat';
33
36
  onEvent(event: string, data?: any): void;
34
37
  dispose(): void;
35
38
  private completedDebounceTimer;
@@ -41,4 +41,5 @@ export declare class ExtensionProviderInstance implements ProviderInstance {
41
41
  private pushEvent;
42
42
  private flushEvents;
43
43
  private resolveChatTitle;
44
+ private resetStreamState;
44
45
  }
@@ -51,6 +51,8 @@ export interface SessionEntry {
51
51
  runtimeKey?: string;
52
52
  runtimeDisplayName?: string;
53
53
  runtimeWorkspaceLabel?: string;
54
+ /** CLI only: active presentation mode */
55
+ mode?: 'terminal' | 'chat';
54
56
  runtimeWriteOwner?: RuntimeWriteOwner | null;
55
57
  runtimeAttachedClients?: RuntimeAttachedClient[];
56
58
  resume?: ProviderResumeCapability;
@@ -34,6 +34,63 @@ var WORKING_STATUSES = /* @__PURE__ */ new Set([
34
34
  "thinking",
35
35
  "active"
36
36
  ]);
37
+ var STATUS_ACTIVE_CHAT_MESSAGE_LIMIT = 60;
38
+ var STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT = 96 * 1024;
39
+ var STATUS_ACTIVE_CHAT_STRING_LIMIT = 4 * 1024;
40
+ var STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT = 1024;
41
+ var STATUS_TERMINAL_HISTORY_LIMIT = 8 * 1024;
42
+ var STATUS_INPUT_CONTENT_LIMIT = 2 * 1024;
43
+ var STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;
44
+ var STATUS_MODAL_BUTTON_LIMIT = 120;
45
+ function truncateString(value, maxChars) {
46
+ if (value.length <= maxChars) return value;
47
+ if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));
48
+ return `${value.slice(0, maxChars - 12)}...[truncated]`;
49
+ }
50
+ function truncateStringTail(value, maxChars) {
51
+ if (value.length <= maxChars) return value;
52
+ if (maxChars <= 12) return value.slice(value.length - Math.max(0, maxChars));
53
+ return `...[truncated]${value.slice(value.length - (maxChars - 12))}`;
54
+ }
55
+ function trimStructuredStrings(value, maxChars) {
56
+ if (typeof value === "string") return truncateString(value, maxChars);
57
+ if (Array.isArray(value)) return value.map((item) => trimStructuredStrings(item, maxChars));
58
+ if (!value || typeof value !== "object") return value;
59
+ return Object.fromEntries(
60
+ Object.entries(value).map(([key, nested]) => [key, trimStructuredStrings(nested, maxChars)])
61
+ );
62
+ }
63
+ function estimateBytes(value) {
64
+ try {
65
+ return JSON.stringify(value).length;
66
+ } catch {
67
+ return String(value ?? "").length;
68
+ }
69
+ }
70
+ function trimMessageForStatus(message, stringLimit) {
71
+ if (!message || typeof message !== "object") return message;
72
+ return trimStructuredStrings(message, stringLimit);
73
+ }
74
+ function trimMessagesForStatus(messages) {
75
+ if (!Array.isArray(messages) || messages.length === 0) return [];
76
+ const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
77
+ const kept = [];
78
+ let totalBytes = 0;
79
+ for (let i = recent.length - 1; i >= 0; i -= 1) {
80
+ let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
81
+ let size = estimateBytes(normalized);
82
+ if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
83
+ normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
84
+ size = estimateBytes(normalized);
85
+ }
86
+ if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
87
+ continue;
88
+ }
89
+ kept.push(normalized);
90
+ totalBytes += size;
91
+ }
92
+ return kept.reverse();
93
+ }
37
94
  function hasApprovalButtons(activeModal) {
38
95
  return (activeModal?.buttons?.length ?? 0) > 0;
39
96
  }
@@ -60,7 +117,16 @@ function normalizeActiveChatData(activeChat) {
60
117
  if (!activeChat) return activeChat;
61
118
  return {
62
119
  ...activeChat,
63
- status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal })
120
+ status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),
121
+ messages: trimMessagesForStatus(activeChat.messages),
122
+ activeModal: activeChat.activeModal ? {
123
+ message: truncateString(activeChat.activeModal.message || "", STATUS_MODAL_MESSAGE_LIMIT),
124
+ buttons: (activeChat.activeModal.buttons || []).map(
125
+ (button) => truncateString(String(button || ""), STATUS_MODAL_BUTTON_LIMIT)
126
+ )
127
+ } : activeChat.activeModal,
128
+ terminalHistory: activeChat.terminalHistory ? truncateStringTail(activeChat.terminalHistory, STATUS_TERMINAL_HISTORY_LIMIT) : activeChat.terminalHistory,
129
+ inputContent: activeChat.inputContent ? truncateString(activeChat.inputContent, STATUS_INPUT_CONTENT_LIMIT) : activeChat.inputContent
64
130
  };
65
131
  }
66
132
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n): T {\n if (!activeChat) return activeChat;\n return {\n ...activeChat,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n } as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAED,SAAS,mBAAmB,aAA8D;AACtF,UAAQ,aAAa,SAAS,UAAU,KAAK;AACjD;AAEO,SAAS,uBACZ,QACA,MACa;AACb,MAAI,mBAAmB,MAAM,WAAW,EAAG,QAAO;AAElD,QAAM,aAAa,OAAO,UAAU,MAAM,EAAE,KAAK,EAAE,YAAY;AAC/D,MAAI,eAAe,mBAAoB,QAAO;AAC9C,MAAI,iBAAiB,IAAI,UAAU,EAAG,QAAO;AAC7C,MAAI,eAAe,QAAS,QAAO;AACnC,MAAI,eAAe,UAAW,QAAO;AACrC,MAAI,eAAe,WAAY,QAAO;AACtC,MAAI,eAAe,eAAgB,QAAO;AAC1C,MAAI,eAAe,gBAAiB,QAAO;AAC3C,MAAI,eAAe,eAAgB,QAAO;AAC1C,SAAO;AACX;AAEO,SAAS,uBAAuB,QAAiC;AACpE,SAAO,uBAAuB,MAAM,MAAM;AAC9C;AAEO,SAAS,uBACZ,QACA,MACO;AACP,SAAO,uBAAuB,QAAQ,IAAI,MAAM;AACpD;AAEO,SAAS,wBACZ,YACC;AACD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO;AAAA,IACH,GAAG;AAAA,IACH,QAAQ,uBAAuB,WAAW,QAAQ,EAAE,aAAa,WAAW,YAAY,CAAC;AAAA,EAC7F;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\n// Status snapshots are sent over P2P every 5s, so keep only a recent live window here.\n// Older history is fetched on demand via `chat_history`, and CLI terminals stream via runtime events.\nconst STATUS_ACTIVE_CHAT_MESSAGE_LIMIT = 60;\nconst STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT = 96 * 1024;\nconst STATUS_ACTIVE_CHAT_STRING_LIMIT = 4 * 1024;\nconst STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT = 1024;\nconst STATUS_TERMINAL_HISTORY_LIMIT = 8 * 1024;\nconst STATUS_INPUT_CONTENT_LIMIT = 2 * 1024;\nconst STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;\nconst STATUS_MODAL_BUTTON_LIMIT = 120;\n\nfunction truncateString(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));\n return `${value.slice(0, maxChars - 12)}...[truncated]`;\n}\n\nfunction truncateStringTail(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(value.length - Math.max(0, maxChars));\n return `...[truncated]${value.slice(value.length - (maxChars - 12))}`;\n}\n\nfunction trimStructuredStrings(value: unknown, maxChars: number): unknown {\n if (typeof value === 'string') return truncateString(value, maxChars);\n if (Array.isArray(value)) return value.map((item) => trimStructuredStrings(item, maxChars));\n if (!value || typeof value !== 'object') return value;\n return Object.fromEntries(\n Object.entries(value).map(([key, nested]) => [key, trimStructuredStrings(nested, maxChars)]),\n );\n}\n\nfunction estimateBytes(value: unknown): number {\n try {\n return JSON.stringify(value).length;\n } catch {\n return String(value ?? '').length;\n }\n}\n\nfunction trimMessageForStatus(message: unknown, stringLimit: number): unknown {\n if (!message || typeof message !== 'object') return message;\n return trimStructuredStrings(message, stringLimit);\n}\n\nfunction trimMessagesForStatus(messages: unknown[] | null | undefined): unknown[] {\n if (!Array.isArray(messages) || messages.length === 0) return [];\n\n const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);\n const kept: unknown[] = [];\n let totalBytes = 0;\n\n for (let i = recent.length - 1; i >= 0; i -= 1) {\n let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);\n let size = estimateBytes(normalized);\n\n if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {\n normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);\n size = estimateBytes(normalized);\n }\n\n if (kept.length > 0 && (totalBytes + size) > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {\n continue;\n }\n\n kept.push(normalized);\n totalBytes += size;\n }\n\n return kept.reverse();\n}\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n): T {\n if (!activeChat) return activeChat;\n return {\n ...activeChat,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n messages: trimMessagesForStatus(activeChat.messages) as T extends { messages: infer M } ? M : never,\n activeModal: activeChat.activeModal ? {\n message: truncateString(activeChat.activeModal.message || '', STATUS_MODAL_MESSAGE_LIMIT),\n buttons: (activeChat.activeModal.buttons || []).map((button) =>\n truncateString(String(button || ''), STATUS_MODAL_BUTTON_LIMIT)\n ),\n } : activeChat.activeModal,\n terminalHistory: activeChat.terminalHistory\n ? truncateStringTail(activeChat.terminalHistory, STATUS_TERMINAL_HISTORY_LIMIT)\n : activeChat.terminalHistory,\n inputContent: activeChat.inputContent\n ? truncateString(activeChat.inputContent, STATUS_INPUT_CONTENT_LIMIT)\n : activeChat.inputContent,\n } as T;\n}\n"],"mappings":"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","names":[]}
@@ -7,6 +7,63 @@ var WORKING_STATUSES = /* @__PURE__ */ new Set([
7
7
  "thinking",
8
8
  "active"
9
9
  ]);
10
+ var STATUS_ACTIVE_CHAT_MESSAGE_LIMIT = 60;
11
+ var STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT = 96 * 1024;
12
+ var STATUS_ACTIVE_CHAT_STRING_LIMIT = 4 * 1024;
13
+ var STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT = 1024;
14
+ var STATUS_TERMINAL_HISTORY_LIMIT = 8 * 1024;
15
+ var STATUS_INPUT_CONTENT_LIMIT = 2 * 1024;
16
+ var STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;
17
+ var STATUS_MODAL_BUTTON_LIMIT = 120;
18
+ function truncateString(value, maxChars) {
19
+ if (value.length <= maxChars) return value;
20
+ if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));
21
+ return `${value.slice(0, maxChars - 12)}...[truncated]`;
22
+ }
23
+ function truncateStringTail(value, maxChars) {
24
+ if (value.length <= maxChars) return value;
25
+ if (maxChars <= 12) return value.slice(value.length - Math.max(0, maxChars));
26
+ return `...[truncated]${value.slice(value.length - (maxChars - 12))}`;
27
+ }
28
+ function trimStructuredStrings(value, maxChars) {
29
+ if (typeof value === "string") return truncateString(value, maxChars);
30
+ if (Array.isArray(value)) return value.map((item) => trimStructuredStrings(item, maxChars));
31
+ if (!value || typeof value !== "object") return value;
32
+ return Object.fromEntries(
33
+ Object.entries(value).map(([key, nested]) => [key, trimStructuredStrings(nested, maxChars)])
34
+ );
35
+ }
36
+ function estimateBytes(value) {
37
+ try {
38
+ return JSON.stringify(value).length;
39
+ } catch {
40
+ return String(value ?? "").length;
41
+ }
42
+ }
43
+ function trimMessageForStatus(message, stringLimit) {
44
+ if (!message || typeof message !== "object") return message;
45
+ return trimStructuredStrings(message, stringLimit);
46
+ }
47
+ function trimMessagesForStatus(messages) {
48
+ if (!Array.isArray(messages) || messages.length === 0) return [];
49
+ const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
50
+ const kept = [];
51
+ let totalBytes = 0;
52
+ for (let i = recent.length - 1; i >= 0; i -= 1) {
53
+ let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
54
+ let size = estimateBytes(normalized);
55
+ if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
56
+ normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
57
+ size = estimateBytes(normalized);
58
+ }
59
+ if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
60
+ continue;
61
+ }
62
+ kept.push(normalized);
63
+ totalBytes += size;
64
+ }
65
+ return kept.reverse();
66
+ }
10
67
  function hasApprovalButtons(activeModal) {
11
68
  return (activeModal?.buttons?.length ?? 0) > 0;
12
69
  }
@@ -33,7 +90,16 @@ function normalizeActiveChatData(activeChat) {
33
90
  if (!activeChat) return activeChat;
34
91
  return {
35
92
  ...activeChat,
36
- status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal })
93
+ status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),
94
+ messages: trimMessagesForStatus(activeChat.messages),
95
+ activeModal: activeChat.activeModal ? {
96
+ message: truncateString(activeChat.activeModal.message || "", STATUS_MODAL_MESSAGE_LIMIT),
97
+ buttons: (activeChat.activeModal.buttons || []).map(
98
+ (button) => truncateString(String(button || ""), STATUS_MODAL_BUTTON_LIMIT)
99
+ )
100
+ } : activeChat.activeModal,
101
+ terminalHistory: activeChat.terminalHistory ? truncateStringTail(activeChat.terminalHistory, STATUS_TERMINAL_HISTORY_LIMIT) : activeChat.terminalHistory,
102
+ inputContent: activeChat.inputContent ? truncateString(activeChat.inputContent, STATUS_INPUT_CONTENT_LIMIT) : activeChat.inputContent
37
103
  };
38
104
  }
39
105
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n): T {\n if (!activeChat) return activeChat;\n return {\n ...activeChat,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n } as T;\n}\n"],"mappings":";AAaA,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAED,SAAS,mBAAmB,aAA8D;AACtF,UAAQ,aAAa,SAAS,UAAU,KAAK;AACjD;AAEO,SAAS,uBACZ,QACA,MACa;AACb,MAAI,mBAAmB,MAAM,WAAW,EAAG,QAAO;AAElD,QAAM,aAAa,OAAO,UAAU,MAAM,EAAE,KAAK,EAAE,YAAY;AAC/D,MAAI,eAAe,mBAAoB,QAAO;AAC9C,MAAI,iBAAiB,IAAI,UAAU,EAAG,QAAO;AAC7C,MAAI,eAAe,QAAS,QAAO;AACnC,MAAI,eAAe,UAAW,QAAO;AACrC,MAAI,eAAe,WAAY,QAAO;AACtC,MAAI,eAAe,eAAgB,QAAO;AAC1C,MAAI,eAAe,gBAAiB,QAAO;AAC3C,MAAI,eAAe,eAAgB,QAAO;AAC1C,SAAO;AACX;AAEO,SAAS,uBAAuB,QAAiC;AACpE,SAAO,uBAAuB,MAAM,MAAM;AAC9C;AAEO,SAAS,uBACZ,QACA,MACO;AACP,SAAO,uBAAuB,QAAQ,IAAI,MAAM;AACpD;AAEO,SAAS,wBACZ,YACC;AACD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO;AAAA,IACH,GAAG;AAAA,IACH,QAAQ,uBAAuB,WAAW,QAAQ,EAAE,aAAa,WAAW,YAAY,CAAC;AAAA,EAC7F;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\n// Status snapshots are sent over P2P every 5s, so keep only a recent live window here.\n// Older history is fetched on demand via `chat_history`, and CLI terminals stream via runtime events.\nconst STATUS_ACTIVE_CHAT_MESSAGE_LIMIT = 60;\nconst STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT = 96 * 1024;\nconst STATUS_ACTIVE_CHAT_STRING_LIMIT = 4 * 1024;\nconst STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT = 1024;\nconst STATUS_TERMINAL_HISTORY_LIMIT = 8 * 1024;\nconst STATUS_INPUT_CONTENT_LIMIT = 2 * 1024;\nconst STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;\nconst STATUS_MODAL_BUTTON_LIMIT = 120;\n\nfunction truncateString(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));\n return `${value.slice(0, maxChars - 12)}...[truncated]`;\n}\n\nfunction truncateStringTail(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(value.length - Math.max(0, maxChars));\n return `...[truncated]${value.slice(value.length - (maxChars - 12))}`;\n}\n\nfunction trimStructuredStrings(value: unknown, maxChars: number): unknown {\n if (typeof value === 'string') return truncateString(value, maxChars);\n if (Array.isArray(value)) return value.map((item) => trimStructuredStrings(item, maxChars));\n if (!value || typeof value !== 'object') return value;\n return Object.fromEntries(\n Object.entries(value).map(([key, nested]) => [key, trimStructuredStrings(nested, maxChars)]),\n );\n}\n\nfunction estimateBytes(value: unknown): number {\n try {\n return JSON.stringify(value).length;\n } catch {\n return String(value ?? '').length;\n }\n}\n\nfunction trimMessageForStatus(message: unknown, stringLimit: number): unknown {\n if (!message || typeof message !== 'object') return message;\n return trimStructuredStrings(message, stringLimit);\n}\n\nfunction trimMessagesForStatus(messages: unknown[] | null | undefined): unknown[] {\n if (!Array.isArray(messages) || messages.length === 0) return [];\n\n const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);\n const kept: unknown[] = [];\n let totalBytes = 0;\n\n for (let i = recent.length - 1; i >= 0; i -= 1) {\n let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);\n let size = estimateBytes(normalized);\n\n if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {\n normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);\n size = estimateBytes(normalized);\n }\n\n if (kept.length > 0 && (totalBytes + size) > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {\n continue;\n }\n\n kept.push(normalized);\n totalBytes += size;\n }\n\n return kept.reverse();\n}\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n): T {\n if (!activeChat) return activeChat;\n return {\n ...activeChat,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n messages: trimMessagesForStatus(activeChat.messages) as T extends { messages: infer M } ? M : never,\n activeModal: activeChat.activeModal ? {\n message: truncateString(activeChat.activeModal.message || '', STATUS_MODAL_MESSAGE_LIMIT),\n buttons: (activeChat.activeModal.buttons || []).map((button) =>\n truncateString(String(button || ''), STATUS_MODAL_BUTTON_LIMIT)\n ),\n } : activeChat.activeModal,\n terminalHistory: activeChat.terminalHistory\n ? truncateStringTail(activeChat.terminalHistory, STATUS_TERMINAL_HISTORY_LIMIT)\n : activeChat.terminalHistory,\n inputContent: activeChat.inputContent\n ? truncateString(activeChat.inputContent, STATUS_INPUT_CONTENT_LIMIT)\n : activeChat.inputContent,\n } as T;\n}\n"],"mappings":"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","names":[]}
@@ -61,6 +61,7 @@ export declare class DaemonStatusReporter {
61
61
  /** Reset P2P dedup hash — forces next send to transmit even if content unchanged */
62
62
  resetP2PHash(): void;
63
63
  private ts;
64
+ private summarizeLargePayloadSessions;
64
65
  sendUnifiedStatusReport(opts?: {
65
66
  p2pOnly?: boolean;
66
67
  }): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.7.40",
3
+ "version": "0.7.42",
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.40",
3
+ "version": "0.7.42",
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",
@@ -10,12 +10,20 @@ export function forwardAgentStreamsToIdeInstance(
10
10
  streams: any[],
11
11
  ): void {
12
12
  const ideInstance = instanceManager.getInstance(`ide:${ideType}`) as
13
- | { onEvent?: (event: string, payload: Record<string, unknown>) => void }
13
+ | {
14
+ onEvent?: (event: string, payload?: Record<string, unknown>) => void;
15
+ getExtensionTypes?: () => string[];
16
+ }
14
17
  | undefined;
15
18
 
16
19
  if (!ideInstance?.onEvent) return;
17
20
 
21
+ const seenExtensionTypes = new Set<string>();
22
+
18
23
  for (const stream of streams) {
24
+ if (typeof stream.agentType === 'string' && stream.agentType) {
25
+ seenExtensionTypes.add(stream.agentType);
26
+ }
19
27
  ideInstance.onEvent('stream_update', {
20
28
  extensionType: stream.agentType,
21
29
  streams: [stream],
@@ -32,4 +40,16 @@ export function forwardAgentStreamsToIdeInstance(
32
40
  inputContent: stream.inputContent || '',
33
41
  });
34
42
  }
43
+
44
+ const extensionTypes = ideInstance.getExtensionTypes?.() || [];
45
+ if (streams.length === 0) {
46
+ ideInstance.onEvent('stream_reset_all');
47
+ return;
48
+ }
49
+
50
+ for (const extensionType of extensionTypes) {
51
+ if (!seenExtensionTypes.has(extensionType)) {
52
+ ideInstance.onEvent('stream_reset', { extensionType });
53
+ }
54
+ }
35
55
  }
@@ -167,7 +167,12 @@ export class AgentStreamPoller {
167
167
  } catch { }
168
168
  }
169
169
 
170
- if (!resolvedActiveSessionId || !parentSessionId) continue;
170
+ if (!resolvedActiveSessionId || !parentSessionId) {
171
+ if (parentSessionId) {
172
+ this.deps.onStreamsUpdated?.(ideType, []);
173
+ }
174
+ continue;
175
+ }
171
176
 
172
177
  try {
173
178
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
@@ -440,6 +440,18 @@ export class ProviderCliAdapter implements CliAdapter {
440
440
  this.structuredMessages = [...this.committedMessages];
441
441
  }
442
442
 
443
+ private normalizeParsedMessages(parsedMessages: any[]): CliChatMessage[] {
444
+ return parsedMessages
445
+ .filter((message) => message && (message.role === 'user' || message.role === 'assistant'))
446
+ .map((message) => ({
447
+ role: message.role,
448
+ content: typeof message.content === 'string' ? message.content : String(message.content || ''),
449
+ timestamp: typeof message.timestamp === 'number' && Number.isFinite(message.timestamp)
450
+ ? message.timestamp
451
+ : Date.now(),
452
+ }));
453
+ }
454
+
443
455
  private sliceFromOffset(text: string, start: number): string {
444
456
  if (!text) return '';
445
457
  if (!Number.isFinite(start) || start <= 0) return text;
@@ -951,8 +963,15 @@ export class ProviderCliAdapter implements CliAdapter {
951
963
  }
952
964
 
953
965
  private commitCurrentTranscript(): void {
954
- // No-op: terminal output is the canonical display for CLI providers.
955
- // Assistant text extraction from PTY output has been removed.
966
+ const parsed = this.parseCurrentTranscript(
967
+ this.committedMessages,
968
+ this.responseBuffer,
969
+ this.currentTurnScope,
970
+ );
971
+ if (parsed && Array.isArray(parsed.messages)) {
972
+ this.committedMessages = this.normalizeParsedMessages(parsed.messages);
973
+ this.syncMessageViews();
974
+ }
956
975
  }
957
976
 
958
977
  // ─── Script Execution ──────────────────────────
@@ -1003,6 +1022,22 @@ export class ProviderCliAdapter implements CliAdapter {
1003
1022
  * Called by command handler / dashboard for rich content rendering.
1004
1023
  */
1005
1024
  getScriptParsedStatus(): any {
1025
+ const parsed = this.parseCurrentTranscript(
1026
+ this.committedMessages,
1027
+ this.responseBuffer,
1028
+ this.currentTurnScope,
1029
+ );
1030
+ if (parsed && Array.isArray(parsed.messages)) {
1031
+ return {
1032
+ id: parsed.id || 'cli_session',
1033
+ status: parsed.status || this.currentStatus,
1034
+ title: parsed.title || this.cliName,
1035
+ terminalHistory: this.terminalHistory,
1036
+ messages: parsed.messages,
1037
+ activeModal: parsed.activeModal ?? this.activeModal,
1038
+ };
1039
+ }
1040
+
1006
1041
  const messages = [...this.committedMessages];
1007
1042
  return {
1008
1043
  id: 'cli_session',
@@ -90,6 +90,19 @@ export class DaemonCliManager {
90
90
  return `${cliType}_${hash}`;
91
91
  }
92
92
 
93
+ getSessionPresentationMode(sessionId: string): 'terminal' | 'chat' | null {
94
+ if (!sessionId) return null;
95
+ const instance = this.deps.getInstanceManager()?.getInstance(sessionId) as any;
96
+ const mode = instance?.category === 'cli'
97
+ ? instance.getPresentationMode?.()
98
+ : null;
99
+ return mode === 'chat' || mode === 'terminal' ? mode : null;
100
+ }
101
+
102
+ isTerminalSession(sessionId: string): boolean {
103
+ return this.getSessionPresentationMode(sessionId) === 'terminal';
104
+ }
105
+
93
106
  private persistRecentActivity(entry: {
94
107
  kind: 'ide' | 'cli' | 'acp';
95
108
  providerType: string;
@@ -318,6 +331,9 @@ export class DaemonCliManager {
318
331
  console.log(colorize('cyan', ` 📦 Using provider: ${provider.name} (${provider.type})`));
319
332
  }
320
333
 
334
+ // ─── Resolve launch options → extra args ───
335
+ const resolvedCliArgs = cliArgs;
336
+
321
337
  // If InstanceManager exists, manage as CliProviderInstance unified
322
338
  const instanceManager = this.deps.getInstanceManager();
323
339
  if (provider && instanceManager) {
@@ -327,7 +343,7 @@ export class DaemonCliManager {
327
343
  normalizedType,
328
344
  cliType,
329
345
  resolvedDir,
330
- cliArgs,
346
+ resolvedCliArgs,
331
347
  resolvedProvider,
332
348
  {},
333
349
  false,
@@ -335,7 +351,7 @@ export class DaemonCliManager {
335
351
  console.log(colorize('green', ` ✓ CLI started: ${cliInfo.displayName} v${cliInfo.version || 'unknown'} in ${resolvedDir}`));
336
352
  } else {
337
353
  // Fallback: InstanceManager without directly adapter manage
338
- const adapter = this.createAdapter(cliType, resolvedDir, cliArgs, key, false);
354
+ const adapter = this.createAdapter(cliType, resolvedDir, resolvedCliArgs, key, false);
339
355
  try {
340
356
  await adapter.spawn();
341
357
  } catch (spawnErr: any) {
@@ -507,6 +523,15 @@ export class DaemonCliManager {
507
523
  return null;
508
524
  }
509
525
 
526
+ private findAdapterBySessionId(instanceKey?: string): { adapter: CliAdapter; key: string } | null {
527
+ if (!instanceKey) return null;
528
+ let ik = instanceKey;
529
+ const colonIdx = ik.lastIndexOf(':');
530
+ if (colonIdx >= 0) ik = ik.substring(colonIdx + 1);
531
+ const adapter = this.adapters.get(ik);
532
+ return adapter ? { adapter, key: ik } : null;
533
+ }
534
+
510
535
  // ─── CLI command handling ────────────────────────────
511
536
 
512
537
  async handleCliCommand(cmd: string, args: any): Promise<CommandResult | null> {
@@ -563,6 +588,24 @@ export class DaemonCliManager {
563
588
  }
564
589
  return { success: true, cliType, dir, stopped: true, mode };
565
590
  }
591
+ case 'set_cli_view_mode': {
592
+ const mode = args?.mode === 'chat' ? 'chat' : 'terminal';
593
+ const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId : '';
594
+ const cliType = args?.cliType || args?.agentType || '';
595
+ const dir = args?.dir || '';
596
+ const found = this.findAdapterBySessionId(targetSessionId)
597
+ || (cliType ? this.findAdapter(cliType, { instanceKey: targetSessionId, dir }) : null);
598
+ if (!found) {
599
+ return { success: false, error: 'CLI session not found', code: 'CLI_SESSION_NOT_FOUND' };
600
+ }
601
+ const instance = this.deps.getInstanceManager()?.getInstance(found.key);
602
+ if (!(instance instanceof CliProviderInstance)) {
603
+ return { success: false, error: 'CLI instance not found', code: 'CLI_INSTANCE_NOT_FOUND' };
604
+ }
605
+ instance.setPresentationMode(mode);
606
+ this.deps.onStatusChange();
607
+ return { success: true, id: found.key, mode };
608
+ }
566
609
  case 'restart_session': {
567
610
  const cliType = args?.cliType || args?.agentType || args?.ideType;
568
611
  const cfg = loadConfig();
@@ -121,6 +121,7 @@ export class DaemonCommandRouter {
121
121
  // ─── CLI / ACP commands ───
122
122
  case 'launch_cli':
123
123
  case 'stop_cli':
124
+ case 'set_cli_view_mode':
124
125
  case 'agent_command': {
125
126
  return this.deps.cliManager.handleCliCommand(cmd, args);
126
127
  }
@@ -7,6 +7,14 @@ import type { CommandResult, CommandHelpers } from './handler.js';
7
7
  import type { ProviderLoader } from '../providers/provider-loader.js';
8
8
  import { LOG } from '../logging/logger.js';
9
9
 
10
+ function getCliPresentationMode(h: CommandHelpers, targetSessionId?: string): 'terminal' | 'chat' | null {
11
+ if (!targetSessionId) return null;
12
+ const instance = h.ctx.instanceManager?.getInstance(targetSessionId) as any;
13
+ if (instance?.category !== 'cli') return null;
14
+ const mode = instance.getPresentationMode?.();
15
+ return mode === 'chat' || mode === 'terminal' ? mode : null;
16
+ }
17
+
10
18
  export async function handleFocusSession(h: CommandHelpers, args: any): Promise<CommandResult> {
11
19
  if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
12
20
  const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
@@ -20,6 +28,9 @@ export async function handleFocusSession(h: CommandHelpers, args: any): Promise<
20
28
  export function handlePtyInput(h: CommandHelpers, args: any): CommandResult {
21
29
  const { cliType, data, targetSessionId } = args || {};
22
30
  if (!data) return { success: false, error: 'data required' };
31
+ if (getCliPresentationMode(h, targetSessionId) === 'chat') {
32
+ return { success: false, error: 'CLI session is in chat mode', code: 'CLI_VIEW_MODE_NOT_TERMINAL' };
33
+ }
23
34
  const adapter = h.getCliAdapter(targetSessionId || cliType);
24
35
  if (!adapter || typeof adapter.writeRaw !== 'function') {
25
36
  return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
@@ -31,6 +42,9 @@ export function handlePtyInput(h: CommandHelpers, args: any): CommandResult {
31
42
  export function handlePtyResize(h: CommandHelpers, args: any): CommandResult {
32
43
  const { cliType, cols, rows, force, targetSessionId } = args || {};
33
44
  if (!cols || !rows) return { success: false, error: 'cols and rows required' };
45
+ if (getCliPresentationMode(h, targetSessionId) === 'chat') {
46
+ return { success: false, error: 'CLI session is in chat mode', code: 'CLI_VIEW_MODE_NOT_TERMINAL' };
47
+ }
34
48
  const adapter = h.getCliAdapter(targetSessionId || cliType);
35
49
  if (!adapter || typeof adapter.resize !== 'function') {
36
50
  return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
@@ -1,65 +1,39 @@
1
1
  /**
2
2
  * ADHDev Launcher — Configuration
3
3
  *
4
- * Manages launcher config, server connection tokens, and user preferences.
4
+ * Manages launcher config, machine auth, and user preferences.
5
5
  */
6
6
  import type { WorkspaceEntry } from './workspaces.js';
7
- import type { WorkspaceActivityEntry } from './workspace-activity.js';
8
7
  import type { RecentActivityEntry } from './recent-activity.js';
9
8
  export type { WorkspaceEntry } from './workspaces.js';
10
- export type { WorkspaceActivityEntry } from './workspace-activity.js';
11
9
  export type { RecentActivityEntry } from './recent-activity.js';
12
10
  export interface ADHDevConfig {
13
11
  serverUrl: string;
14
- apiToken: string | null;
15
- connectionToken: string | null;
16
12
  selectedIde: string | null;
17
13
  configuredIdes: string[];
18
14
  installedExtensions: string[];
19
- autoConnect: boolean;
20
- /**
21
- * @deprecated Not read at runtime. Notification preferences are now managed by:
22
- * - Web UI layer: useNotificationPrefs (localStorage)
23
- * - Daemon layer: per-provider settings (approvalAlert, longGeneratingAlert)
24
- * Kept for backward config compat — will be removed in v0.7+.
25
- */
26
- notifications: boolean;
27
15
  userEmail: string | null;
28
16
  userName: string | null;
29
17
  setupCompleted: boolean;
30
18
  setupDate: string | null;
31
- configuredCLIs: string[];
32
19
  enabledIdes: string[];
33
- recentCliWorkspaces: string[];
34
20
  /** Saved workspaces for IDE/CLI/ACP launch (daemon-local) */
35
21
  workspaces?: WorkspaceEntry[];
36
22
  /** Default workspace id (from workspaces[]) — never used implicitly for launch */
37
23
  defaultWorkspaceId?: string | null;
38
- /** Recently used workspaces (IDE / CLI / ACP / default) for quick resume */
39
- recentWorkspaceActivity?: WorkspaceActivityEntry[];
40
24
  /** Unified recent activity across IDE / CLI / ACP launch flows */
41
25
  recentActivity?: RecentActivityEntry[];
26
+ /** Last seen timestamps for live sessions, keyed by sessionId */
27
+ sessionReads?: Record<string, number>;
28
+ /** Last seen completion marker for live sessions, keyed by sessionId */
29
+ sessionReadMarkers?: Record<string, string>;
42
30
  machineNickname: string | null;
43
31
  /**
44
32
  * Stable local machine ID (prefix: `mach_`) — generated locally on first run.
45
33
  * Used as daemon instance key (`daemon_<machineId>`) and in status reports.
46
- * NOT the same as the server-side D1 `machines.id` — see `registeredMachineId`.
47
34
  */
48
35
  machineId?: string;
49
36
  machineSecret?: string | null;
50
- /**
51
- * Server-side D1 `machines.id` — the row ID assigned when daemon registers via
52
- * `POST /cli/complete`. Corresponds to `machineId` in server DO context
53
- * (`DaemonConnection.machineId`, `StatusContext.machineId`).
54
- *
55
- * Naming differs from server-side `machineId` to avoid confusion with the local
56
- * `config.machineId` (mach_ prefix) which is a different value.
57
- *
58
- * @deprecated Legacy bridge field — will be removed after 2026-04-06.
59
- * Modern auth flow uses `machineSecret` (adm_) to identify machines.
60
- */
61
- registeredMachineId?: string;
62
- cliHistory: CliHistoryEntry[];
63
37
  providerSettings: Record<string, Record<string, any>>;
64
38
  ideSettings: Record<string, {
65
39
  extensions?: Record<string, {
@@ -69,17 +43,6 @@ export interface ADHDevConfig {
69
43
  disableUpstream?: boolean;
70
44
  providerDir?: string;
71
45
  }
72
- export interface CliHistoryEntry {
73
- category?: 'ide' | 'cli' | 'acp';
74
- cliType: string;
75
- dir: string;
76
- cliArgs?: string[];
77
- workspace?: string;
78
- newWindow?: boolean;
79
- model?: string;
80
- timestamp: number;
81
- label?: string;
82
- }
83
46
  export declare function generateMachineId(): string;
84
47
  export declare function isStableMachineId(machineId?: string | null): boolean;
85
48
  /**
@@ -110,11 +73,3 @@ export declare function isSetupComplete(): boolean;
110
73
  * Reset configuration
111
74
  */
112
75
  export declare function resetConfig(): void;
113
- /**
114
- * Generate a connection token for server authentication
115
- */
116
- export declare function generateConnectionToken(): string;
117
- /**
118
- * Add launch to history (max 20, dedup by category+type+dir+args+workspace+model)
119
- */
120
- export declare function addCliHistory(entry: Omit<CliHistoryEntry, 'timestamp'>): void;