@adhdev/daemon-core 0.7.35 → 0.7.37
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.
- package/dist/index.d.mts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +637 -253
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +637 -255
- package/dist/index.mjs.map +1 -1
- package/dist/{normalize-auJAPmKy.d.mts → normalize-DVI4Lo5I.d.mts} +63 -1
- package/dist/{normalize-auJAPmKy.d.ts → normalize-DVI4Lo5I.d.ts} +63 -1
- package/dist/status/normalize.d.mts +1 -1
- package/dist/status/normalize.d.ts +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/agent-stream/manager.ts +14 -1
- package/src/agent-stream/provider-adapter.ts +45 -3
- package/src/agent-stream/types.ts +4 -0
- package/src/cli-adapters/terminal-screen.ts +15 -0
- package/src/commands/cli-manager.ts +35 -0
- package/src/commands/router.ts +44 -1
- package/src/commands/workspace-commands.ts +2 -1
- package/src/config/config.d.ts +4 -0
- package/src/config/config.ts +9 -1
- package/src/config/recent-activity.ts +83 -0
- package/src/config/workspaces.d.ts +3 -1
- package/src/config/workspaces.ts +15 -1
- package/src/index.ts +16 -0
- package/src/providers/acp-provider-instance.ts +5 -0
- package/src/providers/cli-provider-instance.ts +2 -0
- package/src/providers/contracts.ts +94 -0
- package/src/providers/extension-provider-instance.ts +4 -0
- package/src/providers/ide-provider-instance.ts +2 -0
- package/src/providers/provider-instance.ts +5 -1
- package/src/shared-types.d.ts +35 -0
- package/src/shared-types.ts +70 -0
- package/src/status/builders.ts +90 -1
- package/src/status/reporter.ts +8 -0
- package/src/status/snapshot.ts +162 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified recent activity — machine-facing "pick up where you left off".
|
|
3
|
+
*
|
|
4
|
+
* Unlike cliHistory or workspaceActivity, this is task/session oriented:
|
|
5
|
+
* - one normalized row shape for IDE / CLI / ACP
|
|
6
|
+
* - deduped by kind + providerType + workspace
|
|
7
|
+
* - optionally linked to a live sessionId when known
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import type { ADHDevConfig } from './config.js';
|
|
12
|
+
import { expandPath } from './workspaces.js';
|
|
13
|
+
|
|
14
|
+
export interface RecentActivityEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
kind: 'ide' | 'cli' | 'acp';
|
|
17
|
+
providerType: string;
|
|
18
|
+
providerName: string;
|
|
19
|
+
workspace?: string | null;
|
|
20
|
+
currentModel?: string;
|
|
21
|
+
sessionId?: string | null;
|
|
22
|
+
title?: string;
|
|
23
|
+
lastUsedAt: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const MAX_ACTIVITY = 30;
|
|
27
|
+
|
|
28
|
+
function normalizeWorkspace(workspace?: string | null) {
|
|
29
|
+
if (!workspace) return '';
|
|
30
|
+
try {
|
|
31
|
+
return path.resolve(expandPath(workspace));
|
|
32
|
+
} catch {
|
|
33
|
+
return path.resolve(workspace);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function buildRecentActivityKey(entry: Pick<RecentActivityEntry, 'kind' | 'providerType' | 'workspace'>) {
|
|
38
|
+
return `${entry.kind}:${entry.providerType}:${normalizeWorkspace(entry.workspace)}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function appendRecentActivity(
|
|
42
|
+
config: ADHDevConfig,
|
|
43
|
+
entry: Omit<RecentActivityEntry, 'id' | 'lastUsedAt'> & { lastUsedAt?: number },
|
|
44
|
+
): ADHDevConfig {
|
|
45
|
+
const nextEntry: RecentActivityEntry = {
|
|
46
|
+
...entry,
|
|
47
|
+
workspace: entry.workspace ? normalizeWorkspace(entry.workspace) : undefined,
|
|
48
|
+
id: buildRecentActivityKey(entry),
|
|
49
|
+
lastUsedAt: entry.lastUsedAt || Date.now(),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const filtered = (config.recentActivity || []).filter((item) => item.id !== nextEntry.id);
|
|
53
|
+
return {
|
|
54
|
+
...config,
|
|
55
|
+
recentActivity: [nextEntry, ...filtered].slice(0, MAX_ACTIVITY),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getRecentActivity(config: ADHDevConfig, limit = 20): RecentActivityEntry[] {
|
|
60
|
+
return [...(config.recentActivity || [])]
|
|
61
|
+
.sort((a, b) => b.lastUsedAt - a.lastUsedAt)
|
|
62
|
+
.slice(0, limit);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getRecentSessionSeenAt(config: ADHDevConfig, recentKey: string): number {
|
|
66
|
+
return config.recentSessionReads?.[recentKey] || 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function markRecentSessionSeen(
|
|
70
|
+
config: ADHDevConfig,
|
|
71
|
+
recentKey: string,
|
|
72
|
+
seenAt = Date.now(),
|
|
73
|
+
): ADHDevConfig {
|
|
74
|
+
const prev = config.recentSessionReads || {};
|
|
75
|
+
const nextSeenAt = Math.max(prev[recentKey] || 0, seenAt);
|
|
76
|
+
return {
|
|
77
|
+
...config,
|
|
78
|
+
recentSessionReads: {
|
|
79
|
+
...prev,
|
|
80
|
+
[recentKey]: nextSeenAt,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -66,7 +66,9 @@ export declare function resolveIdeLaunchWorkspace(args: {
|
|
|
66
66
|
useDefaultWorkspace?: boolean;
|
|
67
67
|
} | undefined, config: ADHDevConfig): string | undefined;
|
|
68
68
|
export declare function findWorkspaceByPath(config: ADHDevConfig, rawPath: string): WorkspaceEntry | undefined;
|
|
69
|
-
export declare function addWorkspaceEntry(config: ADHDevConfig, rawPath: string, label?: string
|
|
69
|
+
export declare function addWorkspaceEntry(config: ADHDevConfig, rawPath: string, label?: string, options?: {
|
|
70
|
+
createIfMissing?: boolean;
|
|
71
|
+
}): {
|
|
70
72
|
config: ADHDevConfig;
|
|
71
73
|
entry: WorkspaceEntry;
|
|
72
74
|
} | {
|
package/src/config/workspaces.ts
CHANGED
|
@@ -208,8 +208,22 @@ export function findWorkspaceByPath(config: ADHDevConfig, rawPath: string): Work
|
|
|
208
208
|
return (config.workspaces || []).find(w => path.resolve(expandPath(w.path)) === abs);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
export function addWorkspaceEntry(
|
|
211
|
+
export function addWorkspaceEntry(
|
|
212
|
+
config: ADHDevConfig,
|
|
213
|
+
rawPath: string,
|
|
214
|
+
label?: string,
|
|
215
|
+
options?: { createIfMissing?: boolean },
|
|
216
|
+
): { config: ADHDevConfig; entry: WorkspaceEntry } | { error: string } {
|
|
212
217
|
const abs = expandPath(rawPath);
|
|
218
|
+
const createIfMissing = options?.createIfMissing === true;
|
|
219
|
+
if (!abs) return { error: 'Path required' };
|
|
220
|
+
if (!fs.existsSync(abs) && createIfMissing) {
|
|
221
|
+
try {
|
|
222
|
+
fs.mkdirSync(abs, { recursive: true });
|
|
223
|
+
} catch (e: any) {
|
|
224
|
+
return { error: e?.message || 'Could not create directory' };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
213
227
|
const v = validateWorkspacePath(abs);
|
|
214
228
|
if (!v.ok) return { error: v.error };
|
|
215
229
|
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export type {
|
|
|
28
28
|
AvailableProviderInfo,
|
|
29
29
|
AcpConfigOption,
|
|
30
30
|
AcpMode,
|
|
31
|
+
ProviderControlSchema,
|
|
31
32
|
StatusReportPayload,
|
|
32
33
|
MachineInfo,
|
|
33
34
|
DetectedIdeInfo,
|
|
@@ -42,6 +43,19 @@ export type {
|
|
|
42
43
|
ExtensionProviderState,
|
|
43
44
|
} from './shared-types.js';
|
|
44
45
|
|
|
46
|
+
export interface RecentSessionEntry {
|
|
47
|
+
id: string;
|
|
48
|
+
sessionId?: string | null;
|
|
49
|
+
providerType: string;
|
|
50
|
+
providerName: string;
|
|
51
|
+
kind: 'ide' | 'cli' | 'acp';
|
|
52
|
+
title: string;
|
|
53
|
+
workspace?: string | null;
|
|
54
|
+
currentModel?: string;
|
|
55
|
+
status?: 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting' | 'panel_hidden' | 'not_monitored' | 'disconnected';
|
|
56
|
+
lastUsedAt: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
// ── Core Interface ──
|
|
46
60
|
export type { IDaemonCore, DaemonCoreOptions } from './daemon-core.js';
|
|
47
61
|
|
|
@@ -49,6 +63,8 @@ export type { IDaemonCore, DaemonCoreOptions } from './daemon-core.js';
|
|
|
49
63
|
export { loadConfig, saveConfig, resetConfig, isSetupComplete, addCliHistory, markSetupComplete, updateConfig } from './config/config.js';
|
|
50
64
|
export { getWorkspaceState } from './config/workspaces.js';
|
|
51
65
|
export { getWorkspaceActivity } from './config/workspace-activity.js';
|
|
66
|
+
export { appendRecentActivity, getRecentActivity } from './config/recent-activity.js';
|
|
67
|
+
export type { RecentActivityEntry } from './config/recent-activity.js';
|
|
52
68
|
|
|
53
69
|
// ── Detection ──
|
|
54
70
|
export { detectIDEs } from './detection/ide-detector.js';
|
|
@@ -228,6 +228,11 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
228
228
|
// Error details for dashboard display
|
|
229
229
|
errorMessage: this.errorMessage || undefined,
|
|
230
230
|
errorReason: this.errorReason || undefined,
|
|
231
|
+
controlValues: {
|
|
232
|
+
...(this.currentModel ? { model: this.currentModel } : {}),
|
|
233
|
+
...(this.currentMode ? { mode: this.currentMode } : {}),
|
|
234
|
+
},
|
|
235
|
+
providerControls: this.provider.controls as any,
|
|
231
236
|
};
|
|
232
237
|
}
|
|
233
238
|
|
|
@@ -126,6 +126,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
126
126
|
attachedClients: runtime.attachedClients || [],
|
|
127
127
|
} : undefined,
|
|
128
128
|
resume: this.provider.resume,
|
|
129
|
+
controlValues: undefined, // CLI controls not yet wired from stream
|
|
130
|
+
providerControls: this.provider.controls as any,
|
|
129
131
|
};
|
|
130
132
|
}
|
|
131
133
|
|
|
@@ -373,6 +373,10 @@ export interface ProviderModule {
|
|
|
373
373
|
// ─── Provider Settings (variables controllable from dashboard) ───
|
|
374
374
|
settings?: Record<string, ProviderSettingDef>;
|
|
375
375
|
|
|
376
|
+
// ─── Provider Controls (interactive controls exposed in chat UI) ───
|
|
377
|
+
/** Dynamic controls declared by provider — rendered in chat panel bar/header */
|
|
378
|
+
controls?: ProviderControlDef[];
|
|
379
|
+
|
|
376
380
|
// ─── ACP Static Config (for agents without config/* support) ───
|
|
377
381
|
/** Static options used when agent does not provide configOptions */
|
|
378
382
|
staticConfigOptions?: Array<{
|
|
@@ -515,3 +519,93 @@ export interface ProviderSettingDef {
|
|
|
515
519
|
export interface ProviderSettingSchema extends ProviderSettingDef {
|
|
516
520
|
key: string;
|
|
517
521
|
}
|
|
522
|
+
|
|
523
|
+
// ─── Provider Controls (interactive chat-level controls) ────────
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Control types:
|
|
527
|
+
* - 'select' — dropdown list (model picker, mode picker)
|
|
528
|
+
* - 'toggle' — on/off switch (compact mode, auto-approve)
|
|
529
|
+
* - 'cycle' — click-to-cycle through options (thinking level: low→med→high)
|
|
530
|
+
* - 'slider' — numeric range (temperature: 0–2)
|
|
531
|
+
* - 'action' — one-shot button (show usage, restart, clear context)
|
|
532
|
+
*/
|
|
533
|
+
export type ProviderControlType = 'select' | 'toggle' | 'cycle' | 'slider' | 'action';
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Where the control appears in the chat UI:
|
|
537
|
+
* - 'bar' — thin strip below/above the chat input (always visible)
|
|
538
|
+
* - 'header' — in the agent header area
|
|
539
|
+
* - 'menu' — inside a ⋯ overflow menu
|
|
540
|
+
*/
|
|
541
|
+
export type ProviderControlPlacement = 'bar' | 'header' | 'menu';
|
|
542
|
+
|
|
543
|
+
/** Static option for select/cycle controls */
|
|
544
|
+
export interface ProviderControlOption {
|
|
545
|
+
value: string;
|
|
546
|
+
label: string;
|
|
547
|
+
description?: string;
|
|
548
|
+
group?: string;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* ProviderControlDef — A single interactive control declared by a provider.
|
|
553
|
+
*
|
|
554
|
+
* Controls are different from Settings:
|
|
555
|
+
* - Settings: background config, infrequently changed, managed in settings page
|
|
556
|
+
* - Controls: interactive, changed during chat, rendered inside chat panel
|
|
557
|
+
*
|
|
558
|
+
* Each control maps to provider scripts for get/set operations.
|
|
559
|
+
* The frontend renders controls automatically based on this schema —
|
|
560
|
+
* no hardcoded model/mode assumptions needed.
|
|
561
|
+
*
|
|
562
|
+
* For 'action' type:
|
|
563
|
+
* - Renders as a button. On click → calls invokeScript.
|
|
564
|
+
* - No value state. Optionally shows result via toast/inline.
|
|
565
|
+
*/
|
|
566
|
+
export interface ProviderControlDef {
|
|
567
|
+
/** Unique identifier (e.g. 'model', 'mode', 'thinking', 'usage') */
|
|
568
|
+
id: string;
|
|
569
|
+
/** Control type */
|
|
570
|
+
type: ProviderControlType;
|
|
571
|
+
/** Display label */
|
|
572
|
+
label: string;
|
|
573
|
+
/** Icon (emoji or icon name) */
|
|
574
|
+
icon?: string;
|
|
575
|
+
/** Where to show this control in the UI */
|
|
576
|
+
placement: ProviderControlPlacement;
|
|
577
|
+
|
|
578
|
+
// ─── Options (for select/cycle) ───
|
|
579
|
+
/** Static options — used when the list is known at definition time */
|
|
580
|
+
options?: ProviderControlOption[];
|
|
581
|
+
/** Dynamic options — load via script at runtime */
|
|
582
|
+
dynamic?: boolean;
|
|
583
|
+
/** Script name to list options (e.g. 'listModels') — required when dynamic=true */
|
|
584
|
+
listScript?: string;
|
|
585
|
+
|
|
586
|
+
// ─── Value (for select/toggle/cycle/slider) ───
|
|
587
|
+
/** Script name to change value (e.g. 'setModel') — required for value-based controls */
|
|
588
|
+
setScript?: string;
|
|
589
|
+
/** Field name in readChat() result to read current value (e.g. 'model', 'mode') */
|
|
590
|
+
readFrom?: string;
|
|
591
|
+
/** Default value */
|
|
592
|
+
defaultValue?: string | number | boolean;
|
|
593
|
+
|
|
594
|
+
// ─── Action (for 'action' type) ───
|
|
595
|
+
/** Script name to invoke (one-shot call, no value) */
|
|
596
|
+
invokeScript?: string;
|
|
597
|
+
/** How to display action result: 'toast' = notification, 'inline' = show in bar, 'none' = silent */
|
|
598
|
+
resultDisplay?: 'toast' | 'inline' | 'none';
|
|
599
|
+
|
|
600
|
+
// ─── Slider-specific ───
|
|
601
|
+
min?: number;
|
|
602
|
+
max?: number;
|
|
603
|
+
step?: number;
|
|
604
|
+
|
|
605
|
+
// ─── Display ───
|
|
606
|
+
/** Sort order within placement group (lower = first) */
|
|
607
|
+
order?: number;
|
|
608
|
+
/** Hide this control when condition not met */
|
|
609
|
+
hidden?: boolean;
|
|
610
|
+
}
|
|
611
|
+
|
|
@@ -25,6 +25,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
25
25
|
private activeModal: any = null;
|
|
26
26
|
private currentModel: string = '';
|
|
27
27
|
private currentMode: string = '';
|
|
28
|
+
private controlValues: Record<string, string | number | boolean> = {};
|
|
28
29
|
private lastAgentStatus: string = 'idle';
|
|
29
30
|
private generatingStartedAt: number = 0;
|
|
30
31
|
private monitor: StatusMonitor;
|
|
@@ -82,6 +83,8 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
82
83
|
} : null,
|
|
83
84
|
currentModel: this.currentModel || undefined,
|
|
84
85
|
currentPlan: this.currentMode || undefined,
|
|
86
|
+
controlValues: this.controlValues,
|
|
87
|
+
providerControls: this.provider.controls as any,
|
|
85
88
|
agentStreams: this.agentStreams,
|
|
86
89
|
instanceId: this.instanceId,
|
|
87
90
|
lastUpdated: Date.now(),
|
|
@@ -98,6 +101,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
98
101
|
if (data?.activeModal !== undefined) this.activeModal = data.activeModal;
|
|
99
102
|
if (data?.model) this.currentModel = data.model;
|
|
100
103
|
if (data?.mode) this.currentMode = data.mode;
|
|
104
|
+
if (data?.controlValues) this.controlValues = data.controlValues;
|
|
101
105
|
if (typeof data?.sessionId === 'string' && data.sessionId.trim()) this.chatId = data.sessionId;
|
|
102
106
|
if (typeof data?.title === 'string' && data.title.trim()) this.chatTitle = data.title;
|
|
103
107
|
if (typeof data?.agentName === 'string' && data.agentName.trim()) this.agentName = data.agentName;
|
|
@@ -125,6 +125,8 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
125
125
|
currentModel: this.cachedChat?.model || undefined,
|
|
126
126
|
currentPlan: this.cachedChat?.mode || undefined,
|
|
127
127
|
currentAutoApprove: this.cachedChat?.autoApprove || undefined,
|
|
128
|
+
controlValues: this.cachedChat?.controlValues || undefined,
|
|
129
|
+
providerControls: this.provider.controls as any,
|
|
128
130
|
instanceId: this.instanceId,
|
|
129
131
|
lastUpdated: Date.now(),
|
|
130
132
|
settings: this.settings,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ProviderModule, ProviderSettingDef, ProviderResumeCapability } from './contracts.js';
|
|
12
|
-
import type { AcpConfigOption, AcpMode } from '../shared-types.js';
|
|
12
|
+
import type { AcpConfigOption, AcpMode, ProviderControlSchema } from '../shared-types.js';
|
|
13
13
|
import type { ChatMessage } from '../types.js';
|
|
14
14
|
|
|
15
15
|
// ─── ProviderState — Discriminated union by category ─────────────
|
|
@@ -83,6 +83,10 @@ interface ProviderStateBase {
|
|
|
83
83
|
pendingEvents: ProviderEvent[];
|
|
84
84
|
runtime?: ProviderRuntimeInfo;
|
|
85
85
|
resume?: ProviderResumeCapability;
|
|
86
|
+
/** Dynamic control current values */
|
|
87
|
+
controlValues?: Record<string, string | number | boolean>;
|
|
88
|
+
/** Provider-declared controls schema (from provider.controls) */
|
|
89
|
+
providerControls?: ProviderControlSchema[];
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/** IDE provider state */
|
package/src/shared-types.d.ts
CHANGED
|
@@ -67,6 +67,8 @@ export interface SessionEntry {
|
|
|
67
67
|
currentAutoApprove?: string;
|
|
68
68
|
acpConfigOptions?: AcpConfigOption[];
|
|
69
69
|
acpModes?: AcpMode[];
|
|
70
|
+
controlValues?: Record<string, string | number | boolean>;
|
|
71
|
+
providerControls?: ProviderControlSchema[];
|
|
70
72
|
errorMessage?: string;
|
|
71
73
|
errorReason?: _ProviderErrorReason;
|
|
72
74
|
}
|
|
@@ -96,6 +98,26 @@ export interface AcpMode {
|
|
|
96
98
|
name: string;
|
|
97
99
|
description?: string;
|
|
98
100
|
}
|
|
101
|
+
/** Provider control schema (daemon → frontend) */
|
|
102
|
+
export interface ProviderControlSchema {
|
|
103
|
+
id: string;
|
|
104
|
+
type: 'select' | 'toggle' | 'cycle' | 'slider' | 'action';
|
|
105
|
+
label: string;
|
|
106
|
+
icon?: string;
|
|
107
|
+
placement: 'bar' | 'header' | 'menu';
|
|
108
|
+
options?: { value: string; label: string; description?: string; group?: string }[];
|
|
109
|
+
dynamic?: boolean;
|
|
110
|
+
listScript?: string;
|
|
111
|
+
setScript?: string;
|
|
112
|
+
readFrom?: string;
|
|
113
|
+
defaultValue?: string | number | boolean;
|
|
114
|
+
invokeScript?: string;
|
|
115
|
+
resultDisplay?: 'toast' | 'inline' | 'none';
|
|
116
|
+
min?: number;
|
|
117
|
+
max?: number;
|
|
118
|
+
step?: number;
|
|
119
|
+
order?: number;
|
|
120
|
+
}
|
|
99
121
|
/** Machine hardware/OS info (reported by daemon, displayed by web) */
|
|
100
122
|
export interface MachineInfo {
|
|
101
123
|
hostname: string;
|
|
@@ -125,6 +147,18 @@ export interface WorkspaceActivity {
|
|
|
125
147
|
kind?: string;
|
|
126
148
|
agentType?: string;
|
|
127
149
|
}
|
|
150
|
+
export interface RecentSessionEntry {
|
|
151
|
+
id: string;
|
|
152
|
+
sessionId?: string | null;
|
|
153
|
+
providerType: string;
|
|
154
|
+
providerName: string;
|
|
155
|
+
kind: 'ide' | 'cli' | 'acp';
|
|
156
|
+
title: string;
|
|
157
|
+
workspace?: string | null;
|
|
158
|
+
currentModel?: string;
|
|
159
|
+
status?: SessionEntry['status'];
|
|
160
|
+
lastUsedAt: number;
|
|
161
|
+
}
|
|
128
162
|
export interface StatusReportPayload {
|
|
129
163
|
/** Daemon instance ID */
|
|
130
164
|
instanceId: string;
|
|
@@ -154,4 +188,5 @@ export interface StatusReportPayload {
|
|
|
154
188
|
defaultWorkspaceId?: string | null;
|
|
155
189
|
defaultWorkspacePath?: string | null;
|
|
156
190
|
workspaceActivity?: WorkspaceActivity[];
|
|
191
|
+
recentSessions?: RecentSessionEntry[];
|
|
157
192
|
}
|
package/src/shared-types.ts
CHANGED
|
@@ -117,8 +117,17 @@ export interface SessionEntry {
|
|
|
117
117
|
currentAutoApprove?: string;
|
|
118
118
|
acpConfigOptions?: AcpConfigOption[];
|
|
119
119
|
acpModes?: AcpMode[];
|
|
120
|
+
/** Dynamic control current values (generic key-value) */
|
|
121
|
+
controlValues?: Record<string, string | number | boolean>;
|
|
122
|
+
/** Provider-declared controls schema (transmitted once, cached by frontend) */
|
|
123
|
+
providerControls?: ProviderControlSchema[];
|
|
120
124
|
errorMessage?: string;
|
|
121
125
|
errorReason?: _ProviderErrorReason;
|
|
126
|
+
lastUpdated?: number;
|
|
127
|
+
recentKey?: string;
|
|
128
|
+
unread?: boolean;
|
|
129
|
+
lastSeenAt?: number;
|
|
130
|
+
inboxBucket?: RecentSessionBucket;
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
/** Available provider information */
|
|
@@ -145,6 +154,40 @@ export interface AcpMode {
|
|
|
145
154
|
description?: string;
|
|
146
155
|
}
|
|
147
156
|
|
|
157
|
+
// ─── Provider Controls Schema (daemon → frontend) ──────────────────
|
|
158
|
+
// Serializable subset of ProviderControlDef — used for dynamic UI rendering
|
|
159
|
+
|
|
160
|
+
/** Provider control schema transmitted to frontend */
|
|
161
|
+
export interface ProviderControlSchema {
|
|
162
|
+
id: string;
|
|
163
|
+
type: 'select' | 'toggle' | 'cycle' | 'slider' | 'action';
|
|
164
|
+
label: string;
|
|
165
|
+
icon?: string;
|
|
166
|
+
placement: 'bar' | 'header' | 'menu';
|
|
167
|
+
/** Static options (for select/cycle) */
|
|
168
|
+
options?: { value: string; label: string; description?: string; group?: string }[];
|
|
169
|
+
/** Dynamic options — frontend should call listScript to load */
|
|
170
|
+
dynamic?: boolean;
|
|
171
|
+
/** Script name to list options */
|
|
172
|
+
listScript?: string;
|
|
173
|
+
/** Script name to change value (value-based controls) */
|
|
174
|
+
setScript?: string;
|
|
175
|
+
/** Field name in readChat result for current value */
|
|
176
|
+
readFrom?: string;
|
|
177
|
+
/** Default value */
|
|
178
|
+
defaultValue?: string | number | boolean;
|
|
179
|
+
/** Script name to invoke (action type) */
|
|
180
|
+
invokeScript?: string;
|
|
181
|
+
/** How to display action result */
|
|
182
|
+
resultDisplay?: 'toast' | 'inline' | 'none';
|
|
183
|
+
/** Slider range */
|
|
184
|
+
min?: number;
|
|
185
|
+
max?: number;
|
|
186
|
+
step?: number;
|
|
187
|
+
/** Sort order */
|
|
188
|
+
order?: number;
|
|
189
|
+
}
|
|
190
|
+
|
|
148
191
|
// ─── Common Sub-Types (used across StatusReportPayload, BaseDaemonData, etc.) ──
|
|
149
192
|
|
|
150
193
|
/** Machine hardware/OS info (reported by daemon, displayed by web) */
|
|
@@ -179,6 +222,31 @@ export interface WorkspaceActivity {
|
|
|
179
222
|
agentType?: string;
|
|
180
223
|
}
|
|
181
224
|
|
|
225
|
+
export interface RecentSessionEntry {
|
|
226
|
+
id: string;
|
|
227
|
+
recentKey: string;
|
|
228
|
+
sessionId?: string | null;
|
|
229
|
+
providerType: string;
|
|
230
|
+
providerName: string;
|
|
231
|
+
kind: 'ide' | 'cli' | 'acp';
|
|
232
|
+
title: string;
|
|
233
|
+
workspace?: string | null;
|
|
234
|
+
currentModel?: string;
|
|
235
|
+
status?: SessionEntry['status'];
|
|
236
|
+
lastUsedAt: number;
|
|
237
|
+
unread?: boolean;
|
|
238
|
+
lastSeenAt?: number;
|
|
239
|
+
inboxBucket?: RecentSessionBucket;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type RecentSessionBucket = 'needs_attention' | 'working' | 'task_complete' | 'idle';
|
|
243
|
+
|
|
244
|
+
export interface TerminalBackendStatus {
|
|
245
|
+
backend: 'xterm' | 'ghostty-vt';
|
|
246
|
+
preference: 'auto' | 'xterm' | 'ghostty-vt';
|
|
247
|
+
ghosttyAvailable: boolean;
|
|
248
|
+
}
|
|
249
|
+
|
|
182
250
|
// ─── Status Report Payload (daemon → server) ────────────────────────
|
|
183
251
|
// Full payload shape sent via WebSocket status_report
|
|
184
252
|
|
|
@@ -206,4 +274,6 @@ export interface StatusReportPayload {
|
|
|
206
274
|
defaultWorkspaceId?: string | null;
|
|
207
275
|
defaultWorkspacePath?: string | null;
|
|
208
276
|
workspaceActivity?: WorkspaceActivity[];
|
|
277
|
+
recentSessions?: RecentSessionEntry[];
|
|
278
|
+
terminalBackend?: TerminalBackendStatus;
|
|
209
279
|
}
|
package/src/status/builders.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { DaemonCdpManager } from '../cdp/manager.js';
|
|
12
|
-
import type { SessionEntry, SessionCapability } from '../shared-types.js';
|
|
12
|
+
import type { SessionEntry, SessionCapability, ProviderControlSchema, AcpConfigOption, AcpMode } from '../shared-types.js';
|
|
13
13
|
import type {
|
|
14
14
|
IdeProviderState,
|
|
15
15
|
CliProviderState,
|
|
@@ -75,6 +75,67 @@ export function isCdpConnected(
|
|
|
75
75
|
return m?.isConnected ?? false;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Build legacy controls for providers that haven't been updated to the new schema.
|
|
80
|
+
* Replaces the frontend fallback logic.
|
|
81
|
+
*/
|
|
82
|
+
function buildFallbackControls(
|
|
83
|
+
providerControls?: ProviderControlSchema[],
|
|
84
|
+
serverModel?: string,
|
|
85
|
+
serverMode?: string,
|
|
86
|
+
acpConfigOptions?: AcpConfigOption[],
|
|
87
|
+
acpModes?: AcpMode[],
|
|
88
|
+
): ProviderControlSchema[] {
|
|
89
|
+
if (providerControls && providerControls.length > 0) return providerControls;
|
|
90
|
+
const controls: ProviderControlSchema[] = [];
|
|
91
|
+
|
|
92
|
+
const isAcp = !!(acpConfigOptions || acpModes);
|
|
93
|
+
|
|
94
|
+
// Legacy model control
|
|
95
|
+
const modelFromAcp = acpConfigOptions?.find(c => c.category === 'model');
|
|
96
|
+
if (!isAcp || modelFromAcp) {
|
|
97
|
+
controls.push({
|
|
98
|
+
id: 'model',
|
|
99
|
+
type: 'select',
|
|
100
|
+
label: 'Model',
|
|
101
|
+
icon: '🤖',
|
|
102
|
+
placement: 'bar',
|
|
103
|
+
dynamic: !modelFromAcp,
|
|
104
|
+
listScript: 'listModels',
|
|
105
|
+
setScript: 'setModel',
|
|
106
|
+
readFrom: 'model',
|
|
107
|
+
...(modelFromAcp && {
|
|
108
|
+
options: modelFromAcp.options.map((o: any) => ({ value: o.value, label: o.name || o.value })),
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Legacy mode control
|
|
114
|
+
const modeFromAcp = acpModes && acpModes.length > 0;
|
|
115
|
+
const thoughtFromAcp = !modeFromAcp && acpConfigOptions?.find((c: any) => c.category !== 'model');
|
|
116
|
+
if (!isAcp || modeFromAcp || thoughtFromAcp) {
|
|
117
|
+
controls.push({
|
|
118
|
+
id: 'mode',
|
|
119
|
+
type: thoughtFromAcp ? 'cycle' : 'select',
|
|
120
|
+
label: thoughtFromAcp ? 'Thinking' : 'Mode',
|
|
121
|
+
icon: thoughtFromAcp ? '🧠' : '⚡',
|
|
122
|
+
placement: 'bar',
|
|
123
|
+
dynamic: !modeFromAcp && !thoughtFromAcp,
|
|
124
|
+
listScript: 'listModes',
|
|
125
|
+
setScript: thoughtFromAcp ? 'setThinkingLevel' : 'setMode',
|
|
126
|
+
readFrom: 'mode',
|
|
127
|
+
...(modeFromAcp && {
|
|
128
|
+
options: acpModes!.map((m: any) => ({ value: m.id, label: m.name || m.id })),
|
|
129
|
+
}),
|
|
130
|
+
...(thoughtFromAcp && {
|
|
131
|
+
options: thoughtFromAcp.options.map((o: any) => ({ value: o.value, label: o.name || o.value })),
|
|
132
|
+
}),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return controls;
|
|
137
|
+
}
|
|
138
|
+
|
|
78
139
|
const IDE_SESSION_CAPABILITIES: SessionCapability[] = [
|
|
79
140
|
'read_chat',
|
|
80
141
|
'send_message',
|
|
@@ -140,8 +201,15 @@ function buildIdeWorkspaceSession(
|
|
|
140
201
|
currentModel: state.currentModel,
|
|
141
202
|
currentPlan: state.currentPlan,
|
|
142
203
|
currentAutoApprove: state.currentAutoApprove,
|
|
204
|
+
controlValues: state.controlValues,
|
|
205
|
+
providerControls: buildFallbackControls(
|
|
206
|
+
state.providerControls,
|
|
207
|
+
state.currentModel,
|
|
208
|
+
state.currentPlan
|
|
209
|
+
),
|
|
143
210
|
errorMessage: state.errorMessage,
|
|
144
211
|
errorReason: state.errorReason,
|
|
212
|
+
lastUpdated: state.lastUpdated,
|
|
145
213
|
};
|
|
146
214
|
}
|
|
147
215
|
|
|
@@ -166,8 +234,15 @@ function buildExtensionAgentSession(
|
|
|
166
234
|
capabilities: EXTENSION_SESSION_CAPABILITIES,
|
|
167
235
|
currentModel: ext.currentModel,
|
|
168
236
|
currentPlan: ext.currentPlan,
|
|
237
|
+
controlValues: ext.controlValues,
|
|
238
|
+
providerControls: buildFallbackControls(
|
|
239
|
+
ext.providerControls,
|
|
240
|
+
ext.currentModel,
|
|
241
|
+
ext.currentPlan
|
|
242
|
+
),
|
|
169
243
|
errorMessage: ext.errorMessage,
|
|
170
244
|
errorReason: ext.errorReason,
|
|
245
|
+
lastUpdated: ext.lastUpdated,
|
|
171
246
|
};
|
|
172
247
|
}
|
|
173
248
|
|
|
@@ -193,8 +268,13 @@ function buildCliSession(state: CliProviderState): SessionEntry {
|
|
|
193
268
|
resume: state.resume,
|
|
194
269
|
activeChat,
|
|
195
270
|
capabilities: PTY_SESSION_CAPABILITIES,
|
|
271
|
+
controlValues: state.controlValues,
|
|
272
|
+
providerControls: buildFallbackControls(
|
|
273
|
+
state.providerControls
|
|
274
|
+
),
|
|
196
275
|
errorMessage: state.errorMessage,
|
|
197
276
|
errorReason: state.errorReason,
|
|
277
|
+
lastUpdated: state.lastUpdated,
|
|
198
278
|
};
|
|
199
279
|
}
|
|
200
280
|
|
|
@@ -218,8 +298,17 @@ function buildAcpSession(state: AcpProviderState): SessionEntry {
|
|
|
218
298
|
currentPlan: state.currentPlan,
|
|
219
299
|
acpConfigOptions: state.acpConfigOptions,
|
|
220
300
|
acpModes: state.acpModes,
|
|
301
|
+
controlValues: state.controlValues,
|
|
302
|
+
providerControls: buildFallbackControls(
|
|
303
|
+
state.providerControls,
|
|
304
|
+
state.currentModel,
|
|
305
|
+
state.currentPlan,
|
|
306
|
+
state.acpConfigOptions,
|
|
307
|
+
state.acpModes
|
|
308
|
+
),
|
|
221
309
|
errorMessage: state.errorMessage,
|
|
222
310
|
errorReason: state.errorReason,
|
|
311
|
+
lastUpdated: state.lastUpdated,
|
|
223
312
|
};
|
|
224
313
|
}
|
|
225
314
|
|
package/src/status/reporter.ts
CHANGED
|
@@ -203,6 +203,14 @@ export class DaemonStatusReporter {
|
|
|
203
203
|
currentModel: session.currentModel,
|
|
204
204
|
currentPlan: session.currentPlan,
|
|
205
205
|
currentAutoApprove: session.currentAutoApprove,
|
|
206
|
+
recentKey: (session as any).recentKey,
|
|
207
|
+
unread: (session as any).unread,
|
|
208
|
+
lastSeenAt: (session as any).lastSeenAt,
|
|
209
|
+
inboxBucket: (session as any).inboxBucket,
|
|
210
|
+
controlValues: session.controlValues,
|
|
211
|
+
providerControls: session.providerControls,
|
|
212
|
+
acpConfigOptions: session.acpConfigOptions,
|
|
213
|
+
acpModes: session.acpModes,
|
|
206
214
|
})),
|
|
207
215
|
p2p: payload.p2p,
|
|
208
216
|
timestamp: now,
|