@adhdev/daemon-core 0.8.58 → 0.8.60

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 (59) hide show
  1. package/dist/agent-stream/types.d.ts +3 -4
  2. package/dist/cli-adapters/provider-cli-adapter.d.ts +3 -0
  3. package/dist/commands/router.d.ts +1 -0
  4. package/dist/commands/stream-commands.d.ts +1 -0
  5. package/dist/config/recent-activity.d.ts +2 -1
  6. package/dist/config/saved-sessions.d.ts +2 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +579 -190
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +579 -190
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/providers/acp-provider-instance.d.ts +8 -2
  13. package/dist/providers/cli-provider-instance.d.ts +1 -0
  14. package/dist/providers/contracts.d.ts +3 -2
  15. package/dist/providers/extension-provider-instance.d.ts +1 -2
  16. package/dist/providers/provider-instance.d.ts +3 -4
  17. package/dist/providers/provider-patch-state.d.ts +23 -0
  18. package/dist/providers/summary-metadata.d.ts +22 -0
  19. package/dist/shared-types.d.ts +15 -9
  20. package/dist/status/snapshot.d.ts +16 -1
  21. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  22. package/package.json +1 -1
  23. package/src/agent-stream/forward.ts +1 -2
  24. package/src/agent-stream/manager.ts +2 -1
  25. package/src/agent-stream/provider-adapter.ts +7 -3
  26. package/src/agent-stream/types.d.ts +3 -4
  27. package/src/agent-stream/types.ts +3 -4
  28. package/src/cli-adapters/provider-cli-adapter.ts +26 -9
  29. package/src/commands/cli-manager.ts +10 -5
  30. package/src/commands/router.ts +155 -22
  31. package/src/commands/stream-commands.ts +19 -2
  32. package/src/config/recent-activity.d.ts +2 -1
  33. package/src/config/recent-activity.ts +12 -1
  34. package/src/config/saved-sessions.d.ts +2 -1
  35. package/src/config/saved-sessions.ts +12 -2
  36. package/src/daemon/dev-auto-implement.ts +1 -1
  37. package/src/daemon/dev-cli-debug.ts +0 -1
  38. package/src/daemon/dev-server.ts +1 -1
  39. package/src/daemon/scaffold-template.ts +8 -1
  40. package/src/index.d.ts +1 -1
  41. package/src/index.ts +2 -0
  42. package/src/providers/acp-provider-instance.d.ts +8 -2
  43. package/src/providers/acp-provider-instance.ts +80 -23
  44. package/src/providers/cli-provider-instance.ts +17 -22
  45. package/src/providers/contracts.d.ts +3 -2
  46. package/src/providers/contracts.ts +6 -4
  47. package/src/providers/control-effects.ts +3 -4
  48. package/src/providers/extension-provider-instance.d.ts +1 -2
  49. package/src/providers/extension-provider-instance.ts +26 -14
  50. package/src/providers/ide-provider-instance.ts +28 -15
  51. package/src/providers/provider-instance.d.ts +3 -4
  52. package/src/providers/provider-instance.ts +6 -7
  53. package/src/providers/provider-patch-state.ts +91 -0
  54. package/src/providers/summary-metadata.ts +118 -0
  55. package/src/shared-types.d.ts +15 -9
  56. package/src/shared-types.ts +17 -9
  57. package/src/status/builders.ts +18 -13
  58. package/src/status/reporter.ts +2 -4
  59. package/src/status/snapshot.ts +60 -2
@@ -31,6 +31,7 @@ import { logCommand } from '../logging/command-log.js';
31
31
  import type { CommandLogEntry } from '../logging/command-log.js';
32
32
  import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
33
33
  import { createInteractionId, getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
34
+ import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../session-host/runtime-surface.js';
34
35
  import { buildSessionEntries } from '../status/builders.js';
35
36
  import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
36
37
  import { getSessionCompletionMarker } from '../status/snapshot.js';
@@ -137,6 +138,58 @@ function toHostedCliRuntimeDescriptor(record: any): HostedCliRuntimeDescriptor |
137
138
  };
138
139
  }
139
140
 
141
+ function getWriteConflictOwnerClientId(error: unknown): string | undefined {
142
+ const message = typeof error === 'string'
143
+ ? error
144
+ : error instanceof Error
145
+ ? error.message
146
+ : '';
147
+ const match = /^Write owned by\s+(.+)$/.exec(message.trim());
148
+ return match?.[1]?.trim() || undefined;
149
+ }
150
+
151
+ function summarizeSessionHostRecord(result: unknown): Record<string, unknown> {
152
+ if (!result || typeof result !== 'object') return {};
153
+ const record = result as Record<string, any>;
154
+ return {
155
+ runtimeKey: typeof record.runtimeKey === 'string' ? record.runtimeKey : undefined,
156
+ lifecycle: typeof record.lifecycle === 'string' ? record.lifecycle : undefined,
157
+ surfaceKind: getSessionHostSurfaceKind(record as any),
158
+ attachedClientCount: Array.isArray(record.attachedClients) ? record.attachedClients.length : undefined,
159
+ hasWriteOwner: !!record.writeOwner,
160
+ writeOwnerClientId: typeof record.writeOwner?.clientId === 'string' ? record.writeOwner.clientId : undefined,
161
+ };
162
+ }
163
+
164
+ function summarizeSessionHostRecords(result: unknown): Record<string, unknown> {
165
+ const records = Array.isArray(result) ? result : [];
166
+ const groups = partitionSessionHostRecords(records as any[]);
167
+ return {
168
+ sessionCount: records.length,
169
+ liveRuntimeCount: groups.liveRuntimes.length,
170
+ recoverySnapshotCount: groups.recoverySnapshots.length,
171
+ inactiveRecordCount: groups.inactiveRecords.length,
172
+ };
173
+ }
174
+
175
+ function summarizeSessionHostDiagnostics(result: unknown): Record<string, unknown> {
176
+ const diagnostics = result && typeof result === 'object' ? result as Record<string, any> : {};
177
+ const sessions = Array.isArray(diagnostics.sessions) ? diagnostics.sessions : [];
178
+ return {
179
+ runtimeCount: typeof diagnostics.runtimeCount === 'number' ? diagnostics.runtimeCount : undefined,
180
+ ...summarizeSessionHostRecords(sessions),
181
+ };
182
+ }
183
+
184
+ function summarizeSessionHostPruneResult(result: unknown): Record<string, unknown> {
185
+ const value = result && typeof result === 'object' ? result as Record<string, any> : {};
186
+ return {
187
+ duplicateGroupCount: typeof value.duplicateGroupCount === 'number' ? value.duplicateGroupCount : undefined,
188
+ prunedCount: Array.isArray(value.prunedSessionIds) ? value.prunedSessionIds.length : undefined,
189
+ keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : undefined,
190
+ };
191
+ }
192
+
140
193
  export class DaemonCommandRouter {
141
194
  private deps: CommandRouterDeps;
142
195
 
@@ -144,6 +197,64 @@ export class DaemonCommandRouter {
144
197
  this.deps = deps;
145
198
  }
146
199
 
200
+ private async traceSessionHostAction<T>(
201
+ action: string,
202
+ args: any,
203
+ run: () => Promise<T>,
204
+ summarizeResult?: (result: T) => Record<string, unknown>,
205
+ ): Promise<T> {
206
+ const interactionId = typeof args?._interactionId === 'string' ? args._interactionId : undefined;
207
+ const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : undefined;
208
+ const requestedPayload: Record<string, unknown> = { action };
209
+ if (sessionId) requestedPayload.sessionId = sessionId;
210
+ if (typeof args?.clientId === 'string') requestedPayload.clientId = args.clientId;
211
+ if (typeof args?.signal === 'string') requestedPayload.signal = args.signal;
212
+ if (typeof args?.providerType === 'string') requestedPayload.providerType = args.providerType;
213
+ if (typeof args?.workspace === 'string') requestedPayload.workspace = args.workspace;
214
+ if (typeof args?.dryRun === 'boolean') requestedPayload.dryRun = args.dryRun;
215
+
216
+ recordDebugTrace({
217
+ interactionId,
218
+ category: 'session_host',
219
+ stage: 'action_requested',
220
+ level: 'info',
221
+ sessionId,
222
+ payload: requestedPayload,
223
+ });
224
+
225
+ try {
226
+ const result = await run();
227
+ recordDebugTrace({
228
+ interactionId,
229
+ category: 'session_host',
230
+ stage: 'action_result',
231
+ level: 'info',
232
+ sessionId,
233
+ payload: {
234
+ ...requestedPayload,
235
+ success: true,
236
+ ...(summarizeResult ? summarizeResult(result) : {}),
237
+ },
238
+ });
239
+ return result;
240
+ } catch (error: any) {
241
+ recordDebugTrace({
242
+ interactionId,
243
+ category: 'session_host',
244
+ stage: 'action_failed',
245
+ level: 'error',
246
+ sessionId,
247
+ payload: {
248
+ ...requestedPayload,
249
+ error: error?.message || String(error),
250
+ failureKind: getWriteConflictOwnerClientId(error) ? 'write_conflict' : 'request_failed',
251
+ conflictOwnerClientId: getWriteConflictOwnerClientId(error),
252
+ },
253
+ });
254
+ throw error;
255
+ }
256
+ }
257
+
147
258
  /**
148
259
  * Unified command routing.
149
260
  * Returns result for all commands:
@@ -270,16 +381,20 @@ export class DaemonCommandRouter {
270
381
 
271
382
  case 'session_host_get_diagnostics': {
272
383
  if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
273
- const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
384
+ const diagnostics = await this.traceSessionHostAction('session_host_get_diagnostics', args, () => this.deps.sessionHostControl!.getDiagnostics({
274
385
  includeSessions: args?.includeSessions !== false,
275
386
  limit: Number(args?.limit) || undefined,
276
- });
387
+ }), (result) => ({
388
+ includeSessions: args?.includeSessions !== false,
389
+ limit: Number(args?.limit) || undefined,
390
+ ...summarizeSessionHostDiagnostics(result),
391
+ }));
277
392
  return { success: true, diagnostics };
278
393
  }
279
394
 
280
395
  case 'session_host_list_sessions': {
281
396
  if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
282
- const sessions = await this.deps.sessionHostControl.listSessions();
397
+ const sessions = await this.traceSessionHostAction('session_host_list_sessions', args, () => this.deps.sessionHostControl!.listSessions(), (records) => summarizeSessionHostRecords(records));
283
398
  return { success: true, sessions };
284
399
  }
285
400
 
@@ -287,7 +402,7 @@ export class DaemonCommandRouter {
287
402
  if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
288
403
  const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : '';
289
404
  if (!sessionId) return { success: false, error: 'sessionId required' };
290
- const record = await this.deps.sessionHostControl.stopSession(sessionId);
405
+ const record = await this.traceSessionHostAction('session_host_stop_session', args, () => this.deps.sessionHostControl!.stopSession(sessionId), (result) => summarizeSessionHostRecord(result));
291
406
  return { success: true, record };
292
407
  }
293
408
 
@@ -295,11 +410,17 @@ export class DaemonCommandRouter {
295
410
  if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
296
411
  const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : '';
297
412
  if (!sessionId) return { success: false, error: 'sessionId required' };
298
- const record = await this.deps.sessionHostControl.resumeSession(sessionId);
299
- const hosted = toHostedCliRuntimeDescriptor(record);
300
- if (hosted) {
301
- await this.deps.cliManager.restoreHostedSessions([hosted]);
302
- }
413
+ const record = await this.traceSessionHostAction('session_host_resume_session', args, async () => {
414
+ const nextRecord = await this.deps.sessionHostControl!.resumeSession(sessionId);
415
+ const hosted = toHostedCliRuntimeDescriptor(nextRecord);
416
+ if (hosted) {
417
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
418
+ }
419
+ return nextRecord;
420
+ }, (result) => ({
421
+ ...summarizeSessionHostRecord(result),
422
+ restoredHostedSession: !!toHostedCliRuntimeDescriptor(result),
423
+ }));
303
424
  return { success: true, record };
304
425
  }
305
426
 
@@ -307,11 +428,17 @@ export class DaemonCommandRouter {
307
428
  if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
308
429
  const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : '';
309
430
  if (!sessionId) return { success: false, error: 'sessionId required' };
310
- const record = await this.deps.sessionHostControl.restartSession(sessionId);
311
- const hosted = toHostedCliRuntimeDescriptor(record);
312
- if (hosted) {
313
- await this.deps.cliManager.restoreHostedSessions([hosted]);
314
- }
431
+ const record = await this.traceSessionHostAction('session_host_restart_session', args, async () => {
432
+ const nextRecord = await this.deps.sessionHostControl!.restartSession(sessionId);
433
+ const hosted = toHostedCliRuntimeDescriptor(nextRecord);
434
+ if (hosted) {
435
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
436
+ }
437
+ return nextRecord;
438
+ }, (result) => ({
439
+ ...summarizeSessionHostRecord(result),
440
+ restoredHostedSession: !!toHostedCliRuntimeDescriptor(result),
441
+ }));
315
442
  return { success: true, record };
316
443
  }
317
444
 
@@ -321,7 +448,7 @@ export class DaemonCommandRouter {
321
448
  const signal = typeof args?.signal === 'string' ? args.signal : '';
322
449
  if (!sessionId) return { success: false, error: 'sessionId required' };
323
450
  if (!signal) return { success: false, error: 'signal required' };
324
- const record = await this.deps.sessionHostControl.sendSignal(sessionId, signal);
451
+ const record = await this.traceSessionHostAction('session_host_send_signal', args, () => this.deps.sessionHostControl!.sendSignal(sessionId, signal), (result) => summarizeSessionHostRecord(result));
325
452
  return { success: true, record };
326
453
  }
327
454
 
@@ -331,17 +458,17 @@ export class DaemonCommandRouter {
331
458
  const clientId = typeof args?.clientId === 'string' ? args.clientId : '';
332
459
  if (!sessionId) return { success: false, error: 'sessionId required' };
333
460
  if (!clientId) return { success: false, error: 'clientId required' };
334
- const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
461
+ const record = await this.traceSessionHostAction('session_host_force_detach_client', args, () => this.deps.sessionHostControl!.forceDetachClient(sessionId, clientId), (result) => summarizeSessionHostRecord(result));
335
462
  return { success: true, record };
336
463
  }
337
464
 
338
465
  case 'session_host_prune_duplicate_sessions': {
339
466
  if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
340
- const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
467
+ const result = await this.traceSessionHostAction('session_host_prune_duplicate_sessions', args, () => this.deps.sessionHostControl!.pruneDuplicateSessions({
341
468
  providerType: typeof args?.providerType === 'string' ? args.providerType : undefined,
342
469
  workspace: typeof args?.workspace === 'string' ? args.workspace : undefined,
343
470
  dryRun: args?.dryRun === true,
344
- });
471
+ }), (value) => summarizeSessionHostPruneResult(value));
345
472
  return { success: true, result };
346
473
  }
347
474
 
@@ -352,12 +479,15 @@ export class DaemonCommandRouter {
352
479
  const ownerType = args?.ownerType === 'agent' ? 'agent' : 'user';
353
480
  if (!sessionId) return { success: false, error: 'sessionId required' };
354
481
  if (!clientId) return { success: false, error: 'clientId required' };
355
- const record = await this.deps.sessionHostControl.acquireWrite({
482
+ const record = await this.traceSessionHostAction('session_host_acquire_write', args, () => this.deps.sessionHostControl!.acquireWrite({
356
483
  sessionId,
357
484
  clientId,
358
485
  ownerType,
359
486
  force: args?.force !== false,
360
- });
487
+ }), (result) => ({
488
+ ...summarizeSessionHostRecord(result),
489
+ ownerType,
490
+ }));
361
491
  return { success: true, record };
362
492
  }
363
493
 
@@ -367,7 +497,10 @@ export class DaemonCommandRouter {
367
497
  const clientId = typeof args?.clientId === 'string' ? args.clientId : '';
368
498
  if (!sessionId) return { success: false, error: 'sessionId required' };
369
499
  if (!clientId) return { success: false, error: 'clientId required' };
370
- const record = await this.deps.sessionHostControl.releaseWrite({ sessionId, clientId });
500
+ const record = await this.traceSessionHostAction('session_host_release_write', args, () => this.deps.sessionHostControl!.releaseWrite({
501
+ sessionId,
502
+ clientId,
503
+ }), (result) => summarizeSessionHostRecord(result));
371
504
  return { success: true, record };
372
505
  }
373
506
 
@@ -408,7 +541,7 @@ export class DaemonCommandRouter {
408
541
  kind: saved?.kind || recent?.kind || kind,
409
542
  title: saved?.title || recent?.title || session.sessionTitle || session.preview || providerType,
410
543
  workspace: saved?.workspace || recent?.workspace || session.workspace,
411
- currentModel: saved?.currentModel || recent?.currentModel,
544
+ summaryMetadata: saved?.summaryMetadata || recent?.summaryMetadata,
412
545
  preview: session.preview,
413
546
  messageCount: session.messageCount,
414
547
  firstMessageAt: session.firstMessageAt,
@@ -150,8 +150,25 @@ export async function handleSetProviderSourceConfig(h: CommandHelpers, args: any
150
150
 
151
151
  // ─── Extension Script Execution (Model/Mode) ─────
152
152
 
153
- function normalizeProviderScriptArgs(args: any): Record<string, any> {
153
+ export function normalizeProviderScriptArgs(args: any, scriptName?: string): Record<string, any> {
154
154
  const normalizedArgs = { ...(args || {}) };
155
+ const normalizedScriptName = String(scriptName || '').toLowerCase();
156
+
157
+ if (Object.prototype.hasOwnProperty.call(normalizedArgs, 'value')) {
158
+ if (
159
+ normalizedArgs.model === undefined
160
+ && (normalizedScriptName === 'setmodel' || normalizedScriptName === 'setmodelgui' || normalizedScriptName === 'webviewsetmodel')
161
+ ) {
162
+ normalizedArgs.model = normalizedArgs.value;
163
+ }
164
+ if (
165
+ normalizedArgs.mode === undefined
166
+ && (normalizedScriptName === 'setmode' || normalizedScriptName === 'webviewsetmode')
167
+ ) {
168
+ normalizedArgs.mode = normalizedArgs.value;
169
+ }
170
+ }
171
+
155
172
  for (const key of ['mode', 'model', 'message', 'action', 'button', 'text', 'sessionId', 'value']) {
156
173
  if (key in normalizedArgs && !(key.toUpperCase() in normalizedArgs)) {
157
174
  normalizedArgs[key.toUpperCase()] = normalizedArgs[key];
@@ -213,7 +230,7 @@ async function executeProviderScript(h: CommandHelpers, args: any, scriptName: s
213
230
  return { success: false, error: `Script '${actualScriptName}' not available for ${resolvedProviderType}` };
214
231
  }
215
232
 
216
- const normalizedArgs = normalizeProviderScriptArgs(args);
233
+ const normalizedArgs = normalizeProviderScriptArgs(args, actualScriptName);
217
234
 
218
235
  if (provider.category === 'cli') {
219
236
  const adapter = h.getCliAdapter(args?.targetSessionId || resolvedProviderType);
@@ -6,6 +6,7 @@
6
6
  * - deduped by provider session when available, else by kind + providerType + workspace
7
7
  * - used only for quick-launch shortcuts
8
8
  */
9
+ import type { ProviderSummaryMetadata } from '../shared-types.js';
9
10
  import type { DaemonState } from './state-store.js';
10
11
  export interface RecentActivityEntry {
11
12
  id: string;
@@ -14,7 +15,7 @@ export interface RecentActivityEntry {
14
15
  providerName: string;
15
16
  providerSessionId?: string;
16
17
  workspace?: string | null;
17
- currentModel?: string;
18
+ summaryMetadata?: ProviderSummaryMetadata;
18
19
  title?: string;
19
20
  lastUsedAt: number;
20
21
  }
@@ -8,8 +8,10 @@
8
8
  */
9
9
 
10
10
  import * as path from 'path';
11
+ import type { ProviderSummaryMetadata } from '../shared-types.js';
11
12
  import type { DaemonState } from './state-store.js';
12
13
  import { expandPath } from './workspaces.js';
14
+ import { normalizePersistedSummaryMetadata } from '../providers/summary-metadata.js';
13
15
 
14
16
  export interface RecentActivityEntry {
15
17
  id: string;
@@ -18,7 +20,7 @@ export interface RecentActivityEntry {
18
20
  providerName: string;
19
21
  providerSessionId?: string;
20
22
  workspace?: string | null;
21
- currentModel?: string;
23
+ summaryMetadata?: ProviderSummaryMetadata;
22
24
  title?: string;
23
25
  lastUsedAt: number;
24
26
  }
@@ -55,6 +57,9 @@ export function appendRecentActivity(
55
57
  const nextEntry: RecentActivityEntry = {
56
58
  ...entry,
57
59
  workspace: entry.workspace ? normalizeWorkspace(entry.workspace) : undefined,
60
+ summaryMetadata: normalizePersistedSummaryMetadata({
61
+ summaryMetadata: entry.summaryMetadata,
62
+ }),
58
63
  id: buildRecentActivityKeyForEntry(entry),
59
64
  lastUsedAt: entry.lastUsedAt || Date.now(),
60
65
  };
@@ -68,6 +73,12 @@ export function appendRecentActivity(
68
73
 
69
74
  export function getRecentActivity(state: DaemonState, limit = 20): RecentActivityEntry[] {
70
75
  return [...(state.recentActivity || [])]
76
+ .map(entry => ({
77
+ ...entry,
78
+ summaryMetadata: normalizePersistedSummaryMetadata({
79
+ summaryMetadata: entry.summaryMetadata,
80
+ }),
81
+ }))
71
82
  .sort((a, b) => b.lastUsedAt - a.lastUsedAt)
72
83
  .slice(0, limit);
73
84
  }
@@ -1,3 +1,4 @@
1
+ import type { ProviderSummaryMetadata } from '../shared-types.js';
1
2
  import type { DaemonState } from './state-store.js';
2
3
  export interface SavedProviderSessionEntry {
3
4
  id: string;
@@ -6,7 +7,7 @@ export interface SavedProviderSessionEntry {
6
7
  providerName: string;
7
8
  providerSessionId: string;
8
9
  workspace?: string | null;
9
- currentModel?: string;
10
+ summaryMetadata?: ProviderSummaryMetadata;
10
11
  title?: string;
11
12
  createdAt: number;
12
13
  lastUsedAt: number;
@@ -1,6 +1,8 @@
1
1
  import * as path from 'path';
2
+ import type { ProviderSummaryMetadata } from '../shared-types.js';
2
3
  import type { DaemonState } from './state-store.js';
3
4
  import { expandPath } from './workspaces.js';
5
+ import { normalizePersistedSummaryMetadata } from '../providers/summary-metadata.js';
4
6
 
5
7
  export interface SavedProviderSessionEntry {
6
8
  id: string;
@@ -9,7 +11,7 @@ export interface SavedProviderSessionEntry {
9
11
  providerName: string;
10
12
  providerSessionId: string;
11
13
  workspace?: string | null;
12
- currentModel?: string;
14
+ summaryMetadata?: ProviderSummaryMetadata;
13
15
  title?: string;
14
16
  createdAt: number;
15
17
  lastUsedAt: number;
@@ -46,7 +48,9 @@ export function upsertSavedProviderSession(
46
48
  providerName: entry.providerName,
47
49
  providerSessionId,
48
50
  workspace: entry.workspace ? normalizeWorkspace(entry.workspace) : undefined,
49
- currentModel: entry.currentModel,
51
+ summaryMetadata: normalizePersistedSummaryMetadata({
52
+ summaryMetadata: entry.summaryMetadata,
53
+ }),
50
54
  title: entry.title,
51
55
  createdAt: existing?.createdAt || entry.createdAt || Date.now(),
52
56
  lastUsedAt: entry.lastUsedAt || Date.now(),
@@ -69,5 +73,11 @@ export function getSavedProviderSessions(
69
73
  if (filters?.kind && entry.kind !== filters.kind) return false;
70
74
  return true;
71
75
  })
76
+ .map(entry => ({
77
+ ...entry,
78
+ summaryMetadata: normalizePersistedSummaryMetadata({
79
+ summaryMetadata: entry.summaryMetadata,
80
+ }),
81
+ }))
72
82
  .sort((a, b) => b.lastUsedAt - a.lastUsedAt);
73
83
  }
@@ -888,7 +888,7 @@ export function buildAutoImplPrompt(ctx: DevServerContext,
888
888
  lines.push('## Required Return Format');
889
889
  lines.push('| Function | Return JSON |');
890
890
  lines.push('|---|---|');
891
- lines.push('| readChat | `{ id, status, title, messages: [{role, content, index, kind?, meta?}], inputContent, activeModal }` — optional `kind`: standard, thought, tool, terminal; optional `meta`: e.g. `{ label, isRunning }` for dashboard |');
891
+ lines.push('| readChat | `{ id, status, title, messages: [{role, content, index, kind?, meta?}], inputContent, activeModal, controlValues?, summaryMetadata? }` — optional `kind`: standard, thought, tool, terminal; prefer explicit `controlValues` for current selections and `summaryMetadata` for compact always-visible UI metadata |');
892
892
  lines.push('| sendMessage | `{ sent: false, needsTypeAndSend: true, selector }` |');
893
893
  lines.push('| resolveAction | `{ resolved: true/false, clicked? }` |');
894
894
  lines.push('| listSessions | `{ sessions: [{ id, title, active, index }] }` |');
@@ -757,7 +757,6 @@ export async function handleCliStatus(ctx: DevServerContext, _req: http.Incoming
757
757
  lastMessage: s.activeChat?.messages?.slice(-1)[0] || null,
758
758
  activeModal: s.activeChat?.activeModal || null,
759
759
  pendingEvents: s.pendingEvents || [],
760
- currentModel: s.currentModel,
761
760
  settings: s.settings,
762
761
  }));
763
762
  ctx.json(res, 200, { instances: result, count: result.length });
@@ -1211,7 +1211,7 @@ export class DevServer implements DevServerContext {
1211
1211
  lines.push('## Required Return Format');
1212
1212
  lines.push('| Function | Return JSON |');
1213
1213
  lines.push('|---|---|');
1214
- lines.push('| readChat | `{ id, status, title, messages: [{role, content, index, kind?, meta?}], inputContent, activeModal }` — optional `kind`: standard, thought, tool, terminal; optional `meta`: e.g. `{ label, isRunning }` for dashboard |');
1214
+ lines.push('| readChat | `{ id, status, title, messages: [{role, content, index, kind?, meta?}], inputContent, activeModal, controlValues?, summaryMetadata? }` — optional `kind`: standard, thought, tool, terminal; prefer explicit `controlValues` for current selections and `summaryMetadata` for compact always-visible UI metadata |');
1215
1215
  lines.push('| sendMessage | `{ sent: false, needsTypeAndSend: true, selector }` |');
1216
1216
  lines.push('| resolveAction | `{ resolved: true/false, clicked? }` |');
1217
1217
  lines.push('| listSessions | `{ sessions: [{ id, title, active, index }] }` |');
@@ -178,7 +178,11 @@ module.exports.setMode = (params) => {
178
178
  * 5. Approval dialog detection (buttons, modal)
179
179
  * 6. Input field selector
180
180
  *
181
- * { id, status, title, messages[], inputContent, activeModal }
181
+ * Preferred live-state surface:
182
+ * - controlValues: explicit current control selections (model/mode/etc.)
183
+ * - summaryMetadata: compact always-visible metadata for dashboard/recent views
184
+ * Legacy top-level model/mode output is no longer the preferred shape.
185
+ * → { id, status, title, messages[], inputContent, activeModal, controlValues?, summaryMetadata? }
182
186
  */
183
187
  (() => {
184
188
  try {
@@ -206,6 +210,9 @@ module.exports.setMode = (params) => {
206
210
  messages,
207
211
  inputContent,
208
212
  activeModal,
213
+ // TODO: Return explicit selections when available, e.g.
214
+ // controlValues: { model: selectedModel, mode: selectedMode },
215
+ // summaryMetadata: { items: [{ id: 'model', value: selectedModelLabel || selectedModel, shortValue: selectedModel, order: 10 }] },
209
216
  });
210
217
  } catch(e) {
211
218
  return JSON.stringify({ id: '', status: 'error', messages: [], error: e.message });
package/src/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * Core logic for daemon: CDP, Provider, IDE detection, CLI/ACP adapters and more.
5
5
  */
6
6
  export type { ChatMessage, ExtensionInfo, CommandResult as CoreCommandResult, ProviderConfig, DaemonEvent, StatusResponse, SystemInfo, DetectedIde, ProviderInfo, AgentEntry, } from './types.js';
7
- export type { SessionEntry, CompactSessionEntry, CompactDaemonEntry, SessionTransport, SessionKind, SessionCapability, AgentSessionStream, ReadChatCursor, ReadChatSyncMode, ReadChatSyncResult, TransportTopic, SessionChatTailSubscriptionParams, MachineRuntimeSubscriptionParams, SessionHostDiagnosticsSubscriptionParams, SessionModalSubscriptionParams, DaemonMetadataSubscriptionParams, SessionChatTailUpdate, MachineRuntimeUpdate, SessionHostDiagnosticsUpdate, SessionModalUpdate, DaemonMetadataUpdate, TopicUpdateEnvelope, SubscribeRequest, UnsubscribeRequest, StandaloneWsStatusPayload, AvailableProviderInfo, AcpConfigOption, AcpMode, ProviderControlSchema, StatusReportPayload, MachineInfo, SessionHostDiagnosticsSnapshot, SessionHostRecord, SessionHostWriteOwner, SessionHostAttachedClient, SessionHostLogEntry, SessionHostRequestTrace, SessionHostRuntimeTransition, DetectedIdeInfo, WorkspaceEntry, ProviderState, ProviderStatus, ProviderErrorReason, ActiveChatData, IdeProviderState, CliProviderState, AcpProviderState, ExtensionProviderState, } from './shared-types.js';
7
+ export type { SessionEntry, CompactSessionEntry, CompactDaemonEntry, SessionTransport, SessionKind, SessionCapability, AgentSessionStream, ReadChatCursor, ReadChatSyncMode, ReadChatSyncResult, TransportTopic, SessionChatTailSubscriptionParams, MachineRuntimeSubscriptionParams, SessionHostDiagnosticsSubscriptionParams, SessionModalSubscriptionParams, DaemonMetadataSubscriptionParams, SessionChatTailUpdate, MachineRuntimeUpdate, SessionHostDiagnosticsUpdate, SessionModalUpdate, DaemonMetadataUpdate, TopicUpdateEnvelope, SubscribeRequest, UnsubscribeRequest, StandaloneWsStatusPayload, AvailableProviderInfo, AcpConfigOption, AcpMode, ProviderControlSchema, StatusReportPayload, MachineInfo, SessionHostDiagnosticsSnapshot, SessionHostRecord, SessionHostWriteOwner, SessionHostAttachedClient, SessionHostLogEntry, SessionHostRequestTrace, SessionHostRuntimeTransition, DetectedIdeInfo, WorkspaceEntry, ProviderSummaryItem, ProviderSummaryMetadata, ProviderState, ProviderStatus, ProviderErrorReason, ActiveChatData, IdeProviderState, CliProviderState, AcpProviderState, ExtensionProviderState, } from './shared-types.js';
8
8
  import type { RuntimeWriteOwner as _RuntimeWriteOwner } from './shared-types-extra.js';
9
9
  import type { RuntimeAttachedClient as _RuntimeAttachedClient } from './shared-types-extra.js';
10
10
  import type { RecentLaunchEntry as _RecentLaunchEntry } from './shared-types.js';
package/src/index.ts CHANGED
@@ -66,6 +66,8 @@ export type {
66
66
  SessionHostRuntimeTransition,
67
67
  DetectedIdeInfo,
68
68
  WorkspaceEntry,
69
+ ProviderSummaryItem,
70
+ ProviderSummaryMetadata,
69
71
  ProviderState,
70
72
  ProviderStatus,
71
73
  ProviderErrorReason,
@@ -34,8 +34,7 @@ export declare class AcpProviderInstance implements ProviderInstance {
34
34
  private lastStatus;
35
35
  private generatingStartedAt;
36
36
  private agentCapabilities;
37
- private currentModel;
38
- private currentMode;
37
+ private currentSelections;
39
38
  private activeToolCalls;
40
39
  private stopReason;
41
40
  private partialContent;
@@ -61,6 +60,13 @@ export declare class AcpProviderInstance implements ProviderInstance {
61
60
  getState(): AcpProviderState;
62
61
  onEvent(event: string, data?: any): void;
63
62
  getInstanceId(): string;
63
+ private resolveConfigOptionLabel;
64
+ private resolveModeLabel;
65
+ private getCurrentSelection;
66
+ private setCurrentSelection;
67
+ private getSelectionControlValues;
68
+ private resolveSelectionLabel;
69
+ private buildSelectionSummaryMetadata;
64
70
  private parseConfigOptions;
65
71
  private parseModes;
66
72
  setConfigOption(category: string, value: string): Promise<void>;