@adhdev/daemon-core 0.8.58 → 0.8.59

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 (57) hide show
  1. package/dist/agent-stream/types.d.ts +3 -4
  2. package/dist/commands/router.d.ts +1 -0
  3. package/dist/commands/stream-commands.d.ts +1 -0
  4. package/dist/config/recent-activity.d.ts +2 -1
  5. package/dist/config/saved-sessions.d.ts +2 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +560 -182
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +560 -182
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/providers/acp-provider-instance.d.ts +8 -2
  12. package/dist/providers/cli-provider-instance.d.ts +1 -0
  13. package/dist/providers/contracts.d.ts +3 -2
  14. package/dist/providers/extension-provider-instance.d.ts +1 -2
  15. package/dist/providers/provider-instance.d.ts +3 -4
  16. package/dist/providers/provider-patch-state.d.ts +23 -0
  17. package/dist/providers/summary-metadata.d.ts +22 -0
  18. package/dist/shared-types.d.ts +15 -9
  19. package/dist/status/snapshot.d.ts +16 -1
  20. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  21. package/package.json +1 -1
  22. package/src/agent-stream/forward.ts +1 -2
  23. package/src/agent-stream/manager.ts +2 -1
  24. package/src/agent-stream/provider-adapter.ts +7 -3
  25. package/src/agent-stream/types.d.ts +3 -4
  26. package/src/agent-stream/types.ts +3 -4
  27. package/src/commands/cli-manager.ts +10 -5
  28. package/src/commands/router.ts +155 -22
  29. package/src/commands/stream-commands.ts +19 -2
  30. package/src/config/recent-activity.d.ts +2 -1
  31. package/src/config/recent-activity.ts +12 -1
  32. package/src/config/saved-sessions.d.ts +2 -1
  33. package/src/config/saved-sessions.ts +12 -2
  34. package/src/daemon/dev-auto-implement.ts +1 -1
  35. package/src/daemon/dev-cli-debug.ts +0 -1
  36. package/src/daemon/dev-server.ts +1 -1
  37. package/src/daemon/scaffold-template.ts +8 -1
  38. package/src/index.d.ts +1 -1
  39. package/src/index.ts +2 -0
  40. package/src/providers/acp-provider-instance.d.ts +8 -2
  41. package/src/providers/acp-provider-instance.ts +80 -23
  42. package/src/providers/cli-provider-instance.ts +17 -22
  43. package/src/providers/contracts.d.ts +3 -2
  44. package/src/providers/contracts.ts +6 -4
  45. package/src/providers/control-effects.ts +3 -4
  46. package/src/providers/extension-provider-instance.d.ts +1 -2
  47. package/src/providers/extension-provider-instance.ts +26 -14
  48. package/src/providers/ide-provider-instance.ts +28 -15
  49. package/src/providers/provider-instance.d.ts +3 -4
  50. package/src/providers/provider-instance.ts +6 -7
  51. package/src/providers/provider-patch-state.ts +91 -0
  52. package/src/providers/summary-metadata.ts +118 -0
  53. package/src/shared-types.d.ts +15 -9
  54. package/src/shared-types.ts +17 -9
  55. package/src/status/builders.ts +18 -13
  56. package/src/status/reporter.ts +2 -4
  57. package/src/status/snapshot.ts +60 -2
@@ -50,6 +50,7 @@ import type { ProviderModule, ContentBlock, InputEnvelope, InputPart, ToolCallIn
50
50
  import { normalizeContent, flattenContent, normalizeInputEnvelope } from './contracts.js';
51
51
  import type { ProviderInstance, ProviderState, AcpProviderState, ProviderErrorReason, ProviderEvent, InstanceContext } from './provider-instance.js';
52
52
  import { StatusMonitor } from './status-monitor.js';
53
+ import { buildLegacyModelModeSummaryMetadata } from './summary-metadata.js';
53
54
  import { LOG } from '../logging/logger.js';
54
55
 
55
56
  // ─── Internal Display Types (for dashboard) ────────────────────────────
@@ -84,6 +85,8 @@ interface AcpMode {
84
85
  description?: string;
85
86
  }
86
87
 
88
+ type SelectionCategory = 'model' | 'mode';
89
+
87
90
  interface PromptCapabilityFlags {
88
91
  image: boolean;
89
92
  audio: boolean;
@@ -223,8 +226,7 @@ export class AcpProviderInstance implements ProviderInstance {
223
226
  private lastStatus: string = 'starting';
224
227
  private generatingStartedAt = 0;
225
228
  private agentCapabilities: Record<string, any> = {};
226
- private currentModel: string | undefined;
227
- private currentMode: string | undefined;
229
+ private currentSelections: Partial<Record<SelectionCategory, string>> = {};
228
230
  private activeToolCalls: AcpToolCall[] = [];
229
231
  private stopReason: string | null = null;
230
232
  private partialContent = '';
@@ -332,8 +334,6 @@ export class AcpProviderInstance implements ProviderInstance {
332
334
  inputContent: '',
333
335
  },
334
336
  workspace: this.workingDir,
335
- currentModel: this.currentModel,
336
- currentPlan: this.currentMode,
337
337
  instanceId: this.instanceId,
338
338
  lastUpdated: Date.now(),
339
339
  settings: this.settings,
@@ -344,11 +344,9 @@ export class AcpProviderInstance implements ProviderInstance {
344
344
  // Error details for dashboard display
345
345
  errorMessage: this.errorMessage || undefined,
346
346
  errorReason: this.errorReason || undefined,
347
- controlValues: {
348
- ...(this.currentModel ? { model: this.currentModel } : {}),
349
- ...(this.currentMode ? { mode: this.currentMode } : {}),
350
- },
347
+ controlValues: this.getSelectionControlValues(),
351
348
  providerControls: this.provider.controls,
349
+ summaryMetadata: this.buildSelectionSummaryMetadata(),
352
350
  };
353
351
  }
354
352
 
@@ -386,6 +384,61 @@ export class AcpProviderInstance implements ProviderInstance {
386
384
  return this.instanceId;
387
385
  }
388
386
 
387
+ private resolveConfigOptionLabel(category: string, value: string | undefined): string | undefined {
388
+ if (!value) return undefined;
389
+ const option = this.configOptions.find((entry) => entry.category === category);
390
+ return option?.options.find((candidate) => candidate.value === value)?.name || value;
391
+ }
392
+
393
+ private resolveModeLabel(modeId: string | undefined): string | undefined {
394
+ if (!modeId) return undefined;
395
+ return this.availableModes.find((mode) => mode.id === modeId)?.name || modeId;
396
+ }
397
+
398
+ private getCurrentSelection(category: SelectionCategory): string | undefined {
399
+ return this.currentSelections[category];
400
+ }
401
+
402
+ private setCurrentSelection(category: SelectionCategory, value: string | null | undefined): void {
403
+ const normalized = typeof value === 'string' ? value.trim() : '';
404
+ if (normalized) {
405
+ this.currentSelections[category] = normalized;
406
+ return;
407
+ }
408
+ delete this.currentSelections[category];
409
+ }
410
+
411
+ private getSelectionControlValues(): Record<string, string> {
412
+ const model = this.getCurrentSelection('model');
413
+ const mode = this.getCurrentSelection('mode');
414
+ return {
415
+ ...(model ? { model } : {}),
416
+ ...(mode ? { mode } : {}),
417
+ };
418
+ }
419
+
420
+ private resolveSelectionLabel(category: SelectionCategory, value: string | undefined): string | undefined {
421
+ if (!value) return undefined;
422
+ const configLabel = this.resolveConfigOptionLabel(category, value);
423
+ if (configLabel && configLabel !== value) return configLabel;
424
+ if (category === 'mode') {
425
+ const modeLabel = this.resolveModeLabel(value);
426
+ if (modeLabel) return modeLabel;
427
+ }
428
+ return configLabel || value;
429
+ }
430
+
431
+ private buildSelectionSummaryMetadata() {
432
+ const model = this.getCurrentSelection('model');
433
+ const mode = this.getCurrentSelection('mode');
434
+ return buildLegacyModelModeSummaryMetadata({
435
+ model,
436
+ mode,
437
+ modelLabel: this.resolveSelectionLabel('model', model),
438
+ modeLabel: this.resolveSelectionLabel('mode', mode),
439
+ });
440
+ }
441
+
389
442
  // ─── ACP Config Options & Modes ─────────────────────
390
443
 
391
444
  private parseConfigOptions(raw: any): void {
@@ -425,15 +478,17 @@ export class AcpProviderInstance implements ProviderInstance {
425
478
 
426
479
  this.configOptions.push({ category: category as 'model' | 'mode' | 'thought_level' | 'other', configId, currentValue, options: flatOptions });
427
480
 
428
- // Auto-set currentModel/currentMode from config
429
- if (category === 'model' && currentValue) this.currentModel = currentValue;
481
+ // Auto-set current selections from config
482
+ if (category === 'model' || category === 'mode') {
483
+ this.setCurrentSelection(category, currentValue);
484
+ }
430
485
  }
431
486
  }
432
487
 
433
488
  private parseModes(raw: any): void {
434
489
  if (!raw) return;
435
490
  // modes: { currentModeId, availableModes: [{ id, name, description }] }
436
- if (raw.currentModeId) this.currentMode = raw.currentModeId;
491
+ this.setCurrentSelection('mode', raw.currentModeId);
437
492
  if (Array.isArray(raw.availableModes)) {
438
493
  this.availableModes = raw.availableModes.map((m: any) => ({
439
494
  id: m.id, name: m.name || m.id, description: m.description,
@@ -454,8 +509,7 @@ export class AcpProviderInstance implements ProviderInstance {
454
509
  if (this.useStaticConfig) {
455
510
  opt.currentValue = value;
456
511
  this.selectedConfig[opt.configId] = value;
457
- if (category === 'model') this.currentModel = value;
458
- if (category === 'mode') this.currentMode = value;
512
+ if (category === 'model' || category === 'mode') this.setCurrentSelection(category, value);
459
513
  this.log.info(`[${this.type}] Static config ${category} set to: ${value} — restarting agent`);
460
514
  await this.restartWithNewConfig();
461
515
  return;
@@ -476,7 +530,7 @@ export class AcpProviderInstance implements ProviderInstance {
476
530
  });
477
531
  // Update local state
478
532
  opt.currentValue = value;
479
- if (category === 'model') this.currentModel = value;
533
+ if (category === 'model' || category === 'mode') this.setCurrentSelection(category, value);
480
534
  // Response may include updated configOptions
481
535
  if (result?.configOptions) this.parseConfigOptions(result.configOptions);
482
536
  this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
@@ -495,7 +549,7 @@ export class AcpProviderInstance implements ProviderInstance {
495
549
  opt.currentValue = modeId;
496
550
  this.selectedConfig[opt.configId] = modeId;
497
551
  }
498
- this.currentMode = modeId;
552
+ this.setCurrentSelection('mode', modeId);
499
553
  this.log.info(`[${this.type}] Static mode set to: ${modeId} — restarting agent`);
500
554
  await this.restartWithNewConfig();
501
555
  return;
@@ -512,7 +566,7 @@ export class AcpProviderInstance implements ProviderInstance {
512
566
  sessionId: this.sessionId,
513
567
  modeId,
514
568
  });
515
- this.currentMode = modeId;
569
+ this.setCurrentSelection('mode', modeId);
516
570
  this.log.info(`[${this.type}] Mode set to: ${modeId}`);
517
571
  } catch (e: any) {
518
572
  const message = e?.message || 'Unknown ACP mode error';
@@ -845,8 +899,8 @@ export class AcpProviderInstance implements ProviderInstance {
845
899
  this.parseModes(result?.modes);
846
900
 
847
901
  // Legacy: models.currentModelId (some agent compat)
848
- if (!this.currentModel && result?.models?.currentModelId) {
849
- this.currentModel = result.models.currentModelId;
902
+ if (!this.getCurrentSelection('model') && result?.models?.currentModelId) {
903
+ this.setCurrentSelection('model', result.models.currentModelId);
850
904
  }
851
905
 
852
906
  // ─── Static config fallback (for agents without config/* support) ───
@@ -862,14 +916,17 @@ export class AcpProviderInstance implements ProviderInstance {
862
916
  });
863
917
  if (defaultVal) {
864
918
  this.selectedConfig[sc.configId] = defaultVal;
865
- if (sc.category === 'model') this.currentModel = defaultVal;
866
- if (sc.category === 'mode') this.currentMode = defaultVal;
919
+ if (sc.category === 'model' || sc.category === 'mode') {
920
+ this.setCurrentSelection(sc.category, defaultVal);
921
+ }
867
922
  }
868
923
  }
869
924
  this.log.info(`[${this.type}] Using static configOptions (${this.configOptions.length} options)`);
870
925
  }
871
926
 
872
- this.log.info(`[${this.type}] Session created: ${this.sessionId}${this.currentModel ? ` (model: ${this.currentModel})` : ''}${this.currentMode ? ` (mode: ${this.currentMode})` : ''}`);
927
+ const currentModel = this.getCurrentSelection('model');
928
+ const currentMode = this.getCurrentSelection('mode');
929
+ this.log.info(`[${this.type}] Session created: ${this.sessionId}${currentModel ? ` (model: ${currentModel})` : ''}${currentMode ? ` (mode: ${currentMode})` : ''}`);
873
930
  if (this.configOptions.length > 0) {
874
931
  this.log.info(`[${this.type}] Config options: ${this.configOptions.map(c => `${c.category}(${c.options.length})`).join(', ')}`);
875
932
  }
@@ -1073,7 +1130,7 @@ export class AcpProviderInstance implements ProviderInstance {
1073
1130
  break;
1074
1131
  }
1075
1132
  case 'current_mode_update': {
1076
- this.currentMode = update.currentModeId;
1133
+ this.setCurrentSelection('mode', update.currentModeId);
1077
1134
  break;
1078
1135
  }
1079
1136
  case 'config_option_update': {
@@ -1162,7 +1219,7 @@ export class AcpProviderInstance implements ProviderInstance {
1162
1219
 
1163
1220
  // Legacy: model info
1164
1221
  if (params.model) {
1165
- this.currentModel = params.model;
1222
+ this.setCurrentSelection('model', params.model);
1166
1223
  }
1167
1224
  }
1168
1225
 
@@ -19,9 +19,10 @@ import { StatusMonitor } from './status-monitor.js';
19
19
  import { ChatHistoryWriter, readChatHistory } from '../config/chat-history.js';
20
20
  import { LOG } from '../logging/logger.js';
21
21
  import type { ChatMessage } from '../types.js';
22
- import { extractProviderControlValues, normalizeProviderEffects } from './control-effects.js';
22
+ import { normalizeProviderEffects } from './control-effects.js';
23
23
  import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.js';
24
24
  import { getCliScriptCommand, parseCliScriptResult } from './cli-script-results.js';
25
+ import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
25
26
 
26
27
  let CachedDatabaseSync: (new (path: string, options?: { readOnly?: boolean }) => {
27
28
  prepare(sql: string): { get(...params: Array<string | number>): unknown };
@@ -104,6 +105,7 @@ export class CliProviderInstance implements ProviderInstance {
104
105
  private generatingDebouncePending: { chatTitle: string; timestamp: number } | null = null;
105
106
  private lastApprovalEventAt = 0;
106
107
  private controlValues: Record<string, string | number | boolean> = {};
108
+ private summaryMetadata: unknown = undefined;
107
109
  private appliedEffectKeys = new Set<string>();
108
110
  private historyWriter: ChatHistoryWriter;
109
111
  private runtimeMessages: Array<{ key: string; message: ChatMessage }> = [];
@@ -317,21 +319,7 @@ export class CliProviderInstance implements ProviderInstance {
317
319
  ? parsedMessages.slice(-historyMessageCount)
318
320
  : [];
319
321
  }
320
- const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
321
- if (controlValues) {
322
- this.controlValues = { ...this.controlValues, ...controlValues };
323
- }
324
322
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
325
- const currentModel = typeof parsedStatus?.model === 'string' && parsedStatus.model.trim()
326
- ? parsedStatus.model.trim()
327
- : (typeof this.controlValues.model === 'string' && this.controlValues.model.trim()
328
- ? this.controlValues.model.trim()
329
- : undefined);
330
- const currentPlan = typeof parsedStatus?.mode === 'string' && parsedStatus.mode.trim()
331
- ? parsedStatus.mode.trim()
332
- : (typeof this.controlValues.mode === 'string' && this.controlValues.mode.trim()
333
- ? this.controlValues.mode.trim()
334
- : undefined);
335
323
 
336
324
  const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
337
325
 
@@ -359,6 +347,10 @@ export class CliProviderInstance implements ProviderInstance {
359
347
  }
360
348
 
361
349
  this.applyProviderResponse(parsedStatus, { phase: 'immediate' });
350
+ const surface = resolveProviderStateSurface({
351
+ summaryMetadata: this.summaryMetadata as any,
352
+ controlValues: this.controlValues,
353
+ });
362
354
 
363
355
  return {
364
356
  type: this.type,
@@ -377,8 +369,6 @@ export class CliProviderInstance implements ProviderInstance {
377
369
  inputContent: '',
378
370
  },
379
371
  workspace: this.workingDir,
380
- currentModel,
381
- currentPlan,
382
372
  instanceId: this.instanceId,
383
373
  providerSessionId: this.providerSessionId,
384
374
  lastUpdated: Date.now(),
@@ -393,8 +383,9 @@ export class CliProviderInstance implements ProviderInstance {
393
383
  attachedClients: runtime.attachedClients || [],
394
384
  } : undefined,
395
385
  resume: this.provider.resume,
396
- controlValues: this.controlValues,
386
+ controlValues: surface.controlValues,
397
387
  providerControls: this.provider.controls,
388
+ summaryMetadata: surface.summaryMetadata as any,
398
389
  };
399
390
  }
400
391
 
@@ -617,10 +608,14 @@ export class CliProviderInstance implements ProviderInstance {
617
608
  this.adapter.clearHistory();
618
609
  }
619
610
 
620
- const controlValues = extractProviderControlValues(this.provider.controls, data);
621
- if (controlValues) {
622
- this.controlValues = { ...this.controlValues, ...controlValues };
623
- }
611
+ const patchedState = mergeProviderPatchState({
612
+ providerControls: this.provider.controls,
613
+ data,
614
+ currentControlValues: this.controlValues,
615
+ currentSummaryMetadata: this.summaryMetadata,
616
+ });
617
+ this.controlValues = patchedState.controlValues;
618
+ this.summaryMetadata = patchedState.summaryMetadata;
624
619
 
625
620
  const effects = normalizeProviderEffects(data);
626
621
  for (const effect of effects) {
@@ -6,6 +6,7 @@
6
6
  * - Common across all categories (cli, ide, extension)
7
7
  * - User custom providers use the same contracts
8
8
  */
9
+ import type { ProviderSummaryMetadata } from '../shared-types.js';
9
10
  export interface ReadChatResult {
10
11
  messages: ChatMessage[];
11
12
  status: AgentStatus;
@@ -21,10 +22,10 @@ export interface ReadChatResult {
21
22
  isVisible?: boolean;
22
23
  isWelcomeScreen?: boolean;
23
24
  inputContent?: string;
24
- model?: string;
25
- autoApprove?: string;
26
25
  /** Explicit dynamic control values returned by the provider */
27
26
  controlValues?: Record<string, string | number | boolean>;
27
+ /** Flexible always-visible metadata for compact/live surfaces. */
28
+ summaryMetadata?: ProviderSummaryMetadata;
28
29
  /** Provider-driven UI effects derived from chat state */
29
30
  effects?: ProviderEffect[];
30
31
  }
@@ -9,6 +9,8 @@
9
9
 
10
10
  // ─── readChat() return value ───────────────────────────
11
11
 
12
+ import type { ProviderSummaryMetadata } from '../shared-types.js';
13
+
12
14
  export interface ReadChatResult {
13
15
  messages: ChatMessage[];
14
16
  status: AgentStatus;
@@ -20,15 +22,15 @@ export interface ReadChatResult {
20
22
  agentType?: string;
21
23
  agentName?: string;
22
24
  extensionId?: string;
23
- /** Status metadata */
25
+ /** Status metadata */
24
26
  isVisible?: boolean;
25
27
  isWelcomeScreen?: boolean;
26
28
  inputContent?: string;
27
- model?: string;
28
- autoApprove?: string;
29
29
  /** Explicit dynamic control values returned by the provider */
30
30
  controlValues?: Record<string, string | number | boolean>;
31
- /** Provider-driven UI effects derived from chat state */
31
+ /** Flexible always-visible metadata for compact/live surfaces. */
32
+ summaryMetadata?: ProviderSummaryMetadata;
33
+ /** Provider-driven UI effects derived from chat state */
32
34
  effects?: ProviderEffect[];
33
35
  }
34
36
 
@@ -32,9 +32,6 @@ export function extractProviderControlValues(
32
32
  values[ctrl.id] = normalizeControlValue(rawValue);
33
33
  }
34
34
 
35
- if (data.model !== undefined && values.model === undefined) values.model = normalizeControlValue(data.model);
36
- if (data.mode !== undefined && values.mode === undefined) values.mode = normalizeControlValue(data.mode);
37
-
38
35
  return Object.keys(values).length > 0 ? values : undefined;
39
36
  }
40
37
 
@@ -171,7 +168,9 @@ function normalizeControlOption(option: unknown): ProviderControlOption | null {
171
168
  const record = option as Record<string, unknown>;
172
169
  const value = typeof record.value === 'string'
173
170
  ? record.value
174
- : (typeof record.id === 'string' ? record.id : null);
171
+ : (typeof record.id === 'string'
172
+ ? record.id
173
+ : (typeof record.name === 'string' ? record.name : null));
175
174
  if (!value) return null;
176
175
  const label = typeof record.label === 'string'
177
176
  ? record.label
@@ -18,9 +18,8 @@ export declare class ExtensionProviderInstance implements ProviderInstance {
18
18
  private messages;
19
19
  private prevMessageHashes;
20
20
  private activeModal;
21
- private currentModel;
22
- private currentMode;
23
21
  private controlValues;
22
+ private summaryMetadata;
24
23
  private appliedEffectKeys;
25
24
  private runtimeMessages;
26
25
  private lastAgentStatus;
@@ -8,9 +8,10 @@
8
8
  import type { ProviderModule } from './contracts.js';
9
9
  import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
10
10
  import { StatusMonitor } from './status-monitor.js';
11
- import { extractProviderControlValues, normalizeProviderEffects } from './control-effects.js';
11
+ import { normalizeProviderEffects } from './control-effects.js';
12
12
  import { ChatHistoryWriter } from '../config/chat-history.js';
13
13
  import type { ChatMessage } from '../types.js';
14
+ import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
14
15
 
15
16
  export class ExtensionProviderInstance implements ProviderInstance {
16
17
  readonly type: string;
@@ -27,9 +28,8 @@ export class ExtensionProviderInstance implements ProviderInstance {
27
28
  private messages: any[] = [];
28
29
  private prevMessageHashes = new Map<string, number>();
29
30
  private activeModal: any = null;
30
- private currentModel: string = '';
31
- private currentMode: string = '';
32
31
  private controlValues: Record<string, string | number | boolean> = {};
32
+ private summaryMetadata: unknown = undefined;
33
33
  private appliedEffectKeys = new Set<string>();
34
34
  private runtimeMessages: Array<{ key: string; message: ChatMessage }> = [];
35
35
  private lastAgentStatus: string = 'idle';
@@ -76,6 +76,11 @@ export class ExtensionProviderInstance implements ProviderInstance {
76
76
  }
77
77
 
78
78
  getState(): ProviderState {
79
+ const surface = resolveProviderStateSurface({
80
+ summaryMetadata: this.summaryMetadata as any,
81
+ controlValues: this.controlValues,
82
+ })
83
+
79
84
  return {
80
85
  type: this.type,
81
86
  name: this.provider.name,
@@ -89,10 +94,9 @@ export class ExtensionProviderInstance implements ProviderInstance {
89
94
  activeModal: this.activeModal,
90
95
  inputContent: '',
91
96
  } : null,
92
- currentModel: this.currentModel || undefined,
93
- currentPlan: this.currentMode || undefined,
94
- controlValues: this.controlValues,
97
+ controlValues: surface.controlValues,
95
98
  providerControls: this.provider.controls,
99
+ summaryMetadata: surface.summaryMetadata as any,
96
100
  agentStreams: this.agentStreams,
97
101
  instanceId: this.instanceId,
98
102
  lastUpdated: Date.now(),
@@ -107,10 +111,14 @@ export class ExtensionProviderInstance implements ProviderInstance {
107
111
  if (data?.streams) this.agentStreams = data.streams;
108
112
  if (data?.messages) this.messages = this.assignReceivedAt(data.messages);
109
113
  if (data?.activeModal !== undefined) this.activeModal = data.activeModal;
110
- if (data?.model) this.currentModel = data.model;
111
- if (data?.mode) this.currentMode = data.mode;
112
- const controlValues = extractProviderControlValues(this.provider.controls, data) || data?.controlValues;
113
- if (controlValues) this.controlValues = controlValues;
114
+ const patchedState = mergeProviderPatchState({
115
+ providerControls: this.provider.controls,
116
+ data,
117
+ currentControlValues: this.controlValues,
118
+ currentSummaryMetadata: this.summaryMetadata,
119
+ });
120
+ this.controlValues = patchedState.controlValues;
121
+ this.summaryMetadata = patchedState.summaryMetadata;
114
122
  if (typeof data?.sessionId === 'string' && data.sessionId.trim()) this.chatId = data.sessionId;
115
123
  if (typeof data?.title === 'string' && data.title.trim()) this.chatTitle = data.title;
116
124
  if (typeof data?.agentName === 'string' && data.agentName.trim()) this.agentName = data.agentName;
@@ -231,8 +239,14 @@ export class ExtensionProviderInstance implements ProviderInstance {
231
239
  private applyProviderResponse(data: any, options: { phase: 'immediate' | 'turn_completed' }): void {
232
240
  if (!data || typeof data !== 'object') return;
233
241
 
234
- const controlValues = extractProviderControlValues(this.provider.controls, data);
235
- if (controlValues) this.controlValues = { ...this.controlValues, ...controlValues };
242
+ const patchedState = mergeProviderPatchState({
243
+ providerControls: this.provider.controls,
244
+ data,
245
+ currentControlValues: this.controlValues,
246
+ currentSummaryMetadata: this.summaryMetadata,
247
+ });
248
+ this.controlValues = patchedState.controlValues;
249
+ this.summaryMetadata = patchedState.summaryMetadata;
236
250
 
237
251
  const effects = normalizeProviderEffects(data);
238
252
  for (const effect of effects) {
@@ -406,8 +420,6 @@ export class ExtensionProviderInstance implements ProviderInstance {
406
420
  this.messages = [];
407
421
  this.prevMessageHashes.clear();
408
422
  this.activeModal = null;
409
- this.currentModel = '';
410
- this.currentMode = '';
411
423
  this.controlValues = {};
412
424
  this.currentStatus = 'idle';
413
425
  this.chatId = null;
@@ -17,9 +17,10 @@ import { ExtensionProviderInstance } from './extension-provider-instance.js';
17
17
  import { StatusMonitor } from './status-monitor.js';
18
18
  import { ChatHistoryWriter } from '../config/chat-history.js';
19
19
  import { LOG } from '../logging/logger.js';
20
- import { extractProviderControlValues, normalizeProviderEffects } from './control-effects.js';
20
+ import { normalizeProviderEffects } from './control-effects.js';
21
21
  import type { ChatMessage } from '../types.js';
22
22
  import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.js';
23
+ import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
23
24
 
24
25
  type ReadChatModal = {
25
26
  message?: string;
@@ -134,6 +135,11 @@ export class IdeProviderInstance implements ProviderInstance {
134
135
  extensionStates.push(ext.getState());
135
136
  }
136
137
 
138
+ const surface = resolveProviderStateSurface({
139
+ summaryMetadata: this.cachedChat?.summaryMetadata,
140
+ controlValues: this.cachedChat?.controlValues,
141
+ });
142
+
137
143
  return {
138
144
  type: this.type,
139
145
  name: this.provider.name,
@@ -152,11 +158,9 @@ export class IdeProviderInstance implements ProviderInstance {
152
158
  workspace: this.workspace || null,
153
159
  extensions: extensionStates,
154
160
  cdpConnected: cdp?.isConnected || false,
155
- currentModel: this.cachedChat?.model || undefined,
156
- currentPlan: this.cachedChat?.mode || undefined,
157
- currentAutoApprove: this.cachedChat?.autoApprove || undefined,
158
- controlValues: this.cachedChat?.controlValues || undefined,
161
+ controlValues: surface.controlValues,
159
162
  providerControls: this.provider.controls,
163
+ summaryMetadata: surface.summaryMetadata as any,
160
164
  instanceId: this.instanceId,
161
165
  lastUpdated: Date.now(),
162
166
  settings: this.settings,
@@ -348,8 +352,13 @@ export class IdeProviderInstance implements ProviderInstance {
348
352
  }
349
353
  }
350
354
 
351
- const controlValues = extractProviderControlValues(this.provider.controls, chat);
352
- if (controlValues) chat.controlValues = controlValues;
355
+ const patchedState = mergeProviderPatchState({
356
+ providerControls: this.provider.controls,
357
+ data: chat,
358
+ mergeWithCurrent: false,
359
+ });
360
+ chat.controlValues = Object.keys(patchedState.controlValues).length > 0 ? patchedState.controlValues : undefined;
361
+ chat.summaryMetadata = patchedState.summaryMetadata;
353
362
 
354
363
  this.cachedChat = { ...chat, activeModal };
355
364
  this.detectAgentTransitions(chat, now);
@@ -464,14 +473,18 @@ export class IdeProviderInstance implements ProviderInstance {
464
473
  private applyProviderResponse(data: any, options: { phase: 'immediate' | 'turn_completed' }): void {
465
474
  if (!data || typeof data !== 'object') return;
466
475
 
467
- const controlValues = extractProviderControlValues(this.provider.controls, data);
468
- if (controlValues) {
469
- this.cachedChat = {
470
- ...(this.cachedChat || {}),
471
- ...data,
472
- controlValues: { ...(this.cachedChat?.controlValues || {}), ...controlValues },
473
- };
474
- }
476
+ const patchedState = mergeProviderPatchState({
477
+ providerControls: this.provider.controls,
478
+ data,
479
+ currentControlValues: this.cachedChat?.controlValues,
480
+ currentSummaryMetadata: this.cachedChat?.summaryMetadata,
481
+ });
482
+ this.cachedChat = {
483
+ ...(this.cachedChat || {}),
484
+ ...data,
485
+ controlValues: Object.keys(patchedState.controlValues).length > 0 ? patchedState.controlValues : undefined,
486
+ summaryMetadata: patchedState.summaryMetadata,
487
+ };
475
488
 
476
489
  const effects = normalizeProviderEffects(data);
477
490
  for (const effect of effects) {
@@ -8,7 +8,7 @@
8
8
  * Each Instance manages its own status.
9
9
  */
10
10
  import type { ProviderResumeCapability } from './contracts.js';
11
- import type { AcpConfigOption, AcpMode, ProviderControlSchema } from '../shared-types.js';
11
+ import type { AcpConfigOption, AcpMode, ProviderControlSchema, ProviderSummaryMetadata } from '../shared-types.js';
12
12
  import type { ChatMessage } from '../types.js';
13
13
  export type ProviderStatus = 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting';
14
14
  export interface ProviderRuntimeWriteOwner {
@@ -54,8 +54,6 @@ interface ProviderStateBase {
54
54
  /** Workspace — project path or name (all categories) */
55
55
  workspace?: string | null;
56
56
  /** Runtime info (real-time detection) */
57
- currentModel?: string;
58
- currentPlan?: string;
59
57
  /** Error details (when status === 'error') */
60
58
  errorMessage?: string;
61
59
  errorReason?: ProviderErrorReason;
@@ -72,6 +70,8 @@ interface ProviderStateBase {
72
70
  controlValues?: Record<string, string | number | boolean>;
73
71
  /** Provider-declared controls schema (from provider.controls) */
74
72
  providerControls?: ProviderControlSchema[];
73
+ /** Flexible always-visible metadata for compact/live surfaces. */
74
+ summaryMetadata?: ProviderSummaryMetadata;
75
75
  }
76
76
  /** IDE provider state */
77
77
  export interface IdeProviderState extends ProviderStateBase {
@@ -79,7 +79,6 @@ export interface IdeProviderState extends ProviderStateBase {
79
79
  cdpConnected: boolean;
80
80
  /** IDE child Extension Instance status */
81
81
  extensions: ProviderState[];
82
- currentAutoApprove?: string;
83
82
  }
84
83
  /** CLI provider state */
85
84
  export interface CliProviderState extends ProviderStateBase {
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import type { ProviderModule, ProviderSettingDef, ProviderResumeCapability } from './contracts.js';
12
- import type { AcpConfigOption, AcpMode, ProviderControlSchema } from '../shared-types.js';
12
+ import type { AcpConfigOption, AcpMode, ProviderControlSchema, ProviderSummaryMetadata } from '../shared-types.js';
13
13
  import type { ChatMessage } from '../types.js';
14
14
 
15
15
  // ─── ProviderState — Discriminated union by category ─────────────
@@ -68,10 +68,8 @@ interface ProviderStateBase {
68
68
  activeChat: ActiveChatData | null;
69
69
  /** Workspace — project path or name (all categories) */
70
70
  workspace?: string | null;
71
- /** Runtime info (real-time detection) */
72
- currentModel?: string;
73
- currentPlan?: string;
74
- /** Error details (when status === 'error') */
71
+ /** Runtime info (real-time detection) */
72
+ /** Error details (when status === 'error') */
75
73
  errorMessage?: string;
76
74
  errorReason?: ProviderErrorReason;
77
75
  /** meta */
@@ -85,8 +83,10 @@ interface ProviderStateBase {
85
83
  resume?: ProviderResumeCapability;
86
84
  /** Dynamic control current values */
87
85
  controlValues?: Record<string, string | number | boolean>;
88
- /** Provider-declared controls schema (from provider.controls) */
86
+ /** Provider-declared controls schema (from provider.controls) */
89
87
  providerControls?: ProviderControlSchema[];
88
+ /** Flexible always-visible metadata for compact/live surfaces. */
89
+ summaryMetadata?: ProviderSummaryMetadata;
90
90
  }
91
91
 
92
92
  /** IDE provider state */
@@ -95,7 +95,6 @@ export interface IdeProviderState extends ProviderStateBase {
95
95
  cdpConnected: boolean;
96
96
  /** IDE child Extension Instance status */
97
97
  extensions: ProviderState[];
98
- currentAutoApprove?: string;
99
98
  }
100
99
 
101
100
  /** CLI provider state */