@adhdev/daemon-core 0.7.39 → 0.7.41
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/commands/cli-manager.d.ts +2 -1
- package/dist/config/config.d.ts +4 -2
- package/dist/config/recent-activity.d.ts +6 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +247 -224
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +247 -224
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +10 -1
- package/dist/providers/contracts.d.ts +79 -0
- package/dist/providers/provider-instance.d.ts +2 -0
- package/dist/shared-types-extra.d.ts +1 -1
- package/dist/shared-types.d.ts +8 -12
- package/dist/status/snapshot.d.ts +13 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/cli-manager.ts +43 -5
- package/src/commands/router.ts +26 -16
- package/src/config/config.ts +6 -3
- package/src/config/recent-activity.ts +24 -12
- package/src/index.ts +2 -2
- package/src/providers/cli-provider-instance.ts +29 -2
- package/src/providers/contracts.ts +80 -0
- package/src/providers/provider-instance.ts +2 -0
- package/src/shared-types-extra.ts +1 -1
- package/src/shared-types.d.ts +4 -6
- package/src/shared-types.ts +8 -12
- package/src/status/builders.ts +2 -0
- package/src/status/reporter.ts +1 -1
- package/src/status/snapshot.ts +61 -91
|
@@ -26,7 +26,16 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
26
26
|
private lastApprovalEventAt;
|
|
27
27
|
private historyWriter;
|
|
28
28
|
readonly instanceId: string;
|
|
29
|
-
|
|
29
|
+
private launchMode;
|
|
30
|
+
private resolvedOutputFormat;
|
|
31
|
+
constructor(provider: ProviderModule, workingDir: string, cliArgs?: string[], instanceId?: string, transportFactory?: PtyTransportFactory, launchModeId?: string);
|
|
32
|
+
/**
|
|
33
|
+
* Determine output rendering format from:
|
|
34
|
+
* 1. launchMode.outputFormat (explicit override)
|
|
35
|
+
* 2. launchOptions[].outputFormatMap — check actual args for matching values
|
|
36
|
+
* 3. Default: 'terminal'
|
|
37
|
+
*/
|
|
38
|
+
private resolveOutputFormat;
|
|
30
39
|
init(context: InstanceContext): Promise<void>;
|
|
31
40
|
onTick(): Promise<void>;
|
|
32
41
|
getState(): ProviderState;
|
|
@@ -245,6 +245,20 @@ export interface ProviderModule {
|
|
|
245
245
|
shell?: boolean;
|
|
246
246
|
env?: Record<string, string>;
|
|
247
247
|
};
|
|
248
|
+
/**
|
|
249
|
+
* Configurable options shown at session launch time (schema declaration).
|
|
250
|
+
* The frontend renders these as a launch config UI.
|
|
251
|
+
* Values are passed to launchArgBuilder to produce the final args.
|
|
252
|
+
*/
|
|
253
|
+
launchOptions?: ProviderLaunchOption[];
|
|
254
|
+
/**
|
|
255
|
+
* Builds extra spawn args from user-selected launch option values.
|
|
256
|
+
* Called with the merged defaults + mode preset + user overrides.
|
|
257
|
+
* When defined, takes precedence over launchMode.extraArgs.
|
|
258
|
+
*/
|
|
259
|
+
launchArgBuilder?: (options: Record<string, string | boolean | number>) => string[];
|
|
260
|
+
/** Named presets — shortcuts that set launchOption values in bulk */
|
|
261
|
+
launchModes?: ProviderLaunchMode[];
|
|
248
262
|
patterns?: {
|
|
249
263
|
prompt?: RegExp[];
|
|
250
264
|
generating?: RegExp[];
|
|
@@ -306,6 +320,71 @@ export interface ProviderModule {
|
|
|
306
320
|
/** ACP agent auth methods (multiple supported — in priority order) */
|
|
307
321
|
auth?: AcpAuthMethod[];
|
|
308
322
|
}
|
|
323
|
+
export type ProviderLaunchOptionType = 'select' | 'boolean' | 'string' | 'number';
|
|
324
|
+
/**
|
|
325
|
+
* A single configurable option exposed at session launch time.
|
|
326
|
+
* Providers declare these so the frontend can render a launch config UI.
|
|
327
|
+
* The final args are built by launchArgBuilder(selectedValues).
|
|
328
|
+
*
|
|
329
|
+
* Example — Claude Code:
|
|
330
|
+
* { id: 'outputFormat', type: 'select', options: [
|
|
331
|
+
* { value: 'terminal', label: 'Terminal' },
|
|
332
|
+
* { value: 'stream-json', label: 'Chat' },
|
|
333
|
+
* ], default: 'terminal' }
|
|
334
|
+
*/
|
|
335
|
+
export interface ProviderLaunchOption {
|
|
336
|
+
/** Unique identifier — key used in launchArgBuilder options map */
|
|
337
|
+
id: string;
|
|
338
|
+
/** Display label */
|
|
339
|
+
name: string;
|
|
340
|
+
description?: string;
|
|
341
|
+
type: ProviderLaunchOptionType;
|
|
342
|
+
/** Options for 'select' type */
|
|
343
|
+
options?: {
|
|
344
|
+
value: string;
|
|
345
|
+
label: string;
|
|
346
|
+
description?: string;
|
|
347
|
+
}[];
|
|
348
|
+
/** Default value — applied when not explicitly set */
|
|
349
|
+
default?: string | boolean | number;
|
|
350
|
+
/**
|
|
351
|
+
* Maps specific values of this option to a frontend rendering hint.
|
|
352
|
+
* e.g. { 'stream-json': 'stream-json' } tells the UI to switch to Chat mode.
|
|
353
|
+
*/
|
|
354
|
+
outputFormatMap?: Record<string, 'terminal' | 'stream-json'>;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* A named launch preset — shorthand for a specific set of launchOption values.
|
|
358
|
+
* When selected, its `options` are merged with user-configured values before
|
|
359
|
+
* calling launchArgBuilder. Falls back to `extraArgs` if no launchArgBuilder defined.
|
|
360
|
+
*/
|
|
361
|
+
export interface ProviderLaunchMode {
|
|
362
|
+
/** Unique mode identifier (e.g. 'terminal', 'chat') */
|
|
363
|
+
id: string;
|
|
364
|
+
/** Display name */
|
|
365
|
+
name: string;
|
|
366
|
+
description?: string;
|
|
367
|
+
/**
|
|
368
|
+
* Preset option values — merged over defaults before calling launchArgBuilder.
|
|
369
|
+
* Keys correspond to ProviderLaunchOption.id.
|
|
370
|
+
*/
|
|
371
|
+
options?: Record<string, string | boolean | number>;
|
|
372
|
+
/**
|
|
373
|
+
* Fallback: raw args appended when no launchArgBuilder is defined.
|
|
374
|
+
* Use launchArgBuilder + options for anything more than trivial cases.
|
|
375
|
+
*/
|
|
376
|
+
extraArgs?: string[];
|
|
377
|
+
/** Env var overrides applied on top of spawn.env */
|
|
378
|
+
env?: Record<string, string>;
|
|
379
|
+
/**
|
|
380
|
+
* Output rendering hint (shorthand when not using launchOptions/outputFormatMap).
|
|
381
|
+
* - 'terminal' — raw PTY stream (default)
|
|
382
|
+
* - 'stream-json' — structured JSON events → chat messages
|
|
383
|
+
*/
|
|
384
|
+
outputFormat?: 'terminal' | 'stream-json';
|
|
385
|
+
/** Whether this is the default mode when none is specified */
|
|
386
|
+
default?: boolean;
|
|
387
|
+
}
|
|
309
388
|
export interface ProviderResumeCapability {
|
|
310
389
|
supported: boolean;
|
|
311
390
|
stopStrategy?: 'command' | 'ctrl_c';
|
|
@@ -86,6 +86,8 @@ export interface CliProviderState extends ProviderStateBase {
|
|
|
86
86
|
category: 'cli';
|
|
87
87
|
/** terminal = PTY stream, chat = parsed conversation */
|
|
88
88
|
mode: 'terminal' | 'chat';
|
|
89
|
+
/** Active launch mode id (e.g. 'chat') — undefined means default terminal */
|
|
90
|
+
launchMode?: string;
|
|
89
91
|
}
|
|
90
92
|
/** ACP provider state */
|
|
91
93
|
export interface AcpProviderState extends ProviderStateBase {
|
|
@@ -17,7 +17,7 @@ export interface RuntimeAttachedClient {
|
|
|
17
17
|
type: 'daemon' | 'web' | 'local-terminal';
|
|
18
18
|
readOnly: boolean;
|
|
19
19
|
}
|
|
20
|
-
/** Session status union (used by SessionEntry.status,
|
|
20
|
+
/** Session status union (used by SessionEntry.status, legacy recent-launch metadata, etc.) */
|
|
21
21
|
export type SessionStatus = 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting' | 'panel_hidden' | 'not_monitored' | 'disconnected';
|
|
22
22
|
/** Inbox bucket categories for recent sessions */
|
|
23
23
|
export type RecentSessionBucket = 'needs_attention' | 'working' | 'task_complete' | 'idle';
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -51,6 +51,10 @@ export interface SessionEntry {
|
|
|
51
51
|
runtimeKey?: string;
|
|
52
52
|
runtimeDisplayName?: string;
|
|
53
53
|
runtimeWorkspaceLabel?: string;
|
|
54
|
+
/** CLI only: active launch mode id (e.g. 'terminal', 'chat') */
|
|
55
|
+
launchMode?: string;
|
|
56
|
+
/** CLI only: output rendering mode derived from launchMode.outputFormat */
|
|
57
|
+
mode?: 'terminal' | 'chat';
|
|
54
58
|
runtimeWriteOwner?: RuntimeWriteOwner | null;
|
|
55
59
|
runtimeAttachedClients?: RuntimeAttachedClient[];
|
|
56
60
|
resume?: ProviderResumeCapability;
|
|
@@ -69,7 +73,6 @@ export interface SessionEntry {
|
|
|
69
73
|
errorMessage?: string;
|
|
70
74
|
errorReason?: _ProviderErrorReason;
|
|
71
75
|
lastUpdated?: number;
|
|
72
|
-
recentKey?: string;
|
|
73
76
|
unread?: boolean;
|
|
74
77
|
lastSeenAt?: number;
|
|
75
78
|
inboxBucket?: RecentSessionBucket;
|
|
@@ -161,22 +164,15 @@ export interface DetectedIdeInfo {
|
|
|
161
164
|
export type { RecentSessionBucket, TerminalBackendStatus } from './shared-types-extra.js';
|
|
162
165
|
import type { RecentSessionBucket } from './shared-types-extra.js';
|
|
163
166
|
import type { TerminalBackendStatus } from './shared-types-extra.js';
|
|
164
|
-
export interface
|
|
167
|
+
export interface RecentLaunchEntry {
|
|
165
168
|
id: string;
|
|
166
|
-
recentKey: string;
|
|
167
|
-
sessionId?: string | null;
|
|
168
169
|
providerType: string;
|
|
169
170
|
providerName: string;
|
|
170
171
|
kind: 'ide' | 'cli' | 'acp';
|
|
171
|
-
title
|
|
172
|
+
title?: string;
|
|
172
173
|
workspace?: string | null;
|
|
173
174
|
currentModel?: string;
|
|
174
|
-
|
|
175
|
-
lastUsedAt: number;
|
|
176
|
-
unread?: boolean;
|
|
177
|
-
lastSeenAt?: number;
|
|
178
|
-
inboxBucket?: RecentSessionBucket;
|
|
179
|
-
surfaceHidden?: boolean;
|
|
175
|
+
lastLaunchedAt: number;
|
|
180
176
|
}
|
|
181
177
|
export interface StatusReportPayload {
|
|
182
178
|
/** Unique daemon instance identifier */
|
|
@@ -206,7 +202,7 @@ export interface StatusReportPayload {
|
|
|
206
202
|
workspaces?: WorkspaceEntry[];
|
|
207
203
|
defaultWorkspaceId?: string | null;
|
|
208
204
|
defaultWorkspacePath?: string | null;
|
|
209
|
-
|
|
205
|
+
recentLaunches?: RecentLaunchEntry[];
|
|
210
206
|
terminalBackend?: TerminalBackendStatus;
|
|
211
207
|
/** Available providers (present in StatusSnapshot, optional in raw payload) */
|
|
212
208
|
availableProviders?: AvailableProviderInfo[];
|
|
@@ -35,4 +35,17 @@ export interface StatusSnapshotOptions {
|
|
|
35
35
|
export interface StatusSnapshot extends StatusReportPayload {
|
|
36
36
|
availableProviders: AvailableProviderInfo[];
|
|
37
37
|
}
|
|
38
|
+
export declare function getSessionCompletionMarker(session: {
|
|
39
|
+
activeChat?: {
|
|
40
|
+
messages?: Array<{
|
|
41
|
+
role?: string;
|
|
42
|
+
id?: string;
|
|
43
|
+
index?: number;
|
|
44
|
+
timestamp?: number | string;
|
|
45
|
+
receivedAt?: number | string;
|
|
46
|
+
createdAt?: number | string;
|
|
47
|
+
_turnKey?: string;
|
|
48
|
+
}> | null;
|
|
49
|
+
} | null;
|
|
50
|
+
}): string;
|
|
38
51
|
export declare function buildStatusSnapshot(options: StatusSnapshotOptions): StatusSnapshot;
|
package/package.json
CHANGED
|
@@ -60,6 +60,7 @@ export interface HostedCliRuntimeDescriptor {
|
|
|
60
60
|
cliType: string;
|
|
61
61
|
workspace: string;
|
|
62
62
|
cliArgs?: string[];
|
|
63
|
+
launchMode?: string;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
const chalkApi: any = (chalk as any)?.yellow
|
|
@@ -178,12 +179,13 @@ export class DaemonCliManager {
|
|
|
178
179
|
provider: any,
|
|
179
180
|
settings: Record<string, any>,
|
|
180
181
|
attachExisting = false,
|
|
182
|
+
launchModeId?: string,
|
|
181
183
|
): Promise<void> {
|
|
182
184
|
const instanceManager = this.deps.getInstanceManager();
|
|
183
185
|
const sessionRegistry = this.deps.getSessionRegistry?.() || null;
|
|
184
186
|
if (!instanceManager) throw new Error('InstanceManager not available');
|
|
185
187
|
const transportFactory = this.getTransportFactory(key, normalizedType, resolvedDir, cliArgs, attachExisting);
|
|
186
|
-
const cliInstance = new CliProviderInstance(provider, resolvedDir, cliArgs, key, transportFactory);
|
|
188
|
+
const cliInstance = new CliProviderInstance(provider, resolvedDir, cliArgs, key, transportFactory, launchModeId);
|
|
187
189
|
try {
|
|
188
190
|
await instanceManager.addInstance(key, cliInstance, {
|
|
189
191
|
serverConn: this.deps.getServerConn(),
|
|
@@ -212,7 +214,7 @@ export class DaemonCliManager {
|
|
|
212
214
|
|
|
213
215
|
// ─── Session start/management ──────────────────────────────
|
|
214
216
|
|
|
215
|
-
async startSession(cliType: string, workingDir: string, cliArgs?: string[], initialModel?: string): Promise<void> {
|
|
217
|
+
async startSession(cliType: string, workingDir: string, cliArgs?: string[], initialModel?: string, launchMode?: string, launchOptionValues?: Record<string, string | boolean | number>): Promise<void> {
|
|
216
218
|
const trimmed = (workingDir || '').trim();
|
|
217
219
|
if (!trimmed) throw new Error('working directory required');
|
|
218
220
|
const resolvedDir = trimmed.startsWith('~')
|
|
@@ -318,6 +320,40 @@ export class DaemonCliManager {
|
|
|
318
320
|
console.log(colorize('cyan', ` 📦 Using provider: ${provider.name} (${provider.type})`));
|
|
319
321
|
}
|
|
320
322
|
|
|
323
|
+
// ─── Resolve launch options → extra args ───
|
|
324
|
+
let resolvedCliArgs = cliArgs;
|
|
325
|
+
let resolvedLaunchMode = launchMode;
|
|
326
|
+
|
|
327
|
+
const activeMode = provider?.launchModes?.length
|
|
328
|
+
? (launchMode
|
|
329
|
+
? provider.launchModes.find((m: any) => m.id === launchMode)
|
|
330
|
+
: provider.launchModes.find((m: any) => m.default))
|
|
331
|
+
: undefined;
|
|
332
|
+
|
|
333
|
+
if (activeMode) {
|
|
334
|
+
resolvedLaunchMode = activeMode.id;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (provider?.launchArgBuilder) {
|
|
338
|
+
// Build option values: schema defaults → mode preset → user args?.launchOptionValues
|
|
339
|
+
const defaults: Record<string, string | boolean | number> = {};
|
|
340
|
+
for (const opt of (provider.launchOptions || [])) {
|
|
341
|
+
if (opt.default !== undefined) defaults[opt.id] = opt.default;
|
|
342
|
+
}
|
|
343
|
+
const modeOptions: Record<string, string | boolean | number> = activeMode?.options || {};
|
|
344
|
+
const userOptions: Record<string, string | boolean | number> = launchOptionValues || {};
|
|
345
|
+
const merged = { ...defaults, ...modeOptions, ...userOptions };
|
|
346
|
+
const extraArgs = provider.launchArgBuilder(merged);
|
|
347
|
+
if (extraArgs.length) {
|
|
348
|
+
resolvedCliArgs = [...(cliArgs || []), ...extraArgs];
|
|
349
|
+
console.log(colorize('cyan', ` 🚀 Launch options applied: ${extraArgs.join(' ')}`));
|
|
350
|
+
}
|
|
351
|
+
} else if (activeMode?.extraArgs?.length) {
|
|
352
|
+
// Fallback: simple extraArgs from mode (no launchArgBuilder)
|
|
353
|
+
resolvedCliArgs = [...(cliArgs || []), ...activeMode.extraArgs];
|
|
354
|
+
console.log(colorize('cyan', ` 🚀 Launch mode '${activeMode.name}': appending args ${activeMode.extraArgs.join(' ')}`));
|
|
355
|
+
}
|
|
356
|
+
|
|
321
357
|
// If InstanceManager exists, manage as CliProviderInstance unified
|
|
322
358
|
const instanceManager = this.deps.getInstanceManager();
|
|
323
359
|
if (provider && instanceManager) {
|
|
@@ -327,15 +363,16 @@ export class DaemonCliManager {
|
|
|
327
363
|
normalizedType,
|
|
328
364
|
cliType,
|
|
329
365
|
resolvedDir,
|
|
330
|
-
|
|
366
|
+
resolvedCliArgs,
|
|
331
367
|
resolvedProvider,
|
|
332
368
|
{},
|
|
333
369
|
false,
|
|
370
|
+
resolvedLaunchMode,
|
|
334
371
|
);
|
|
335
372
|
console.log(colorize('green', ` ✓ CLI started: ${cliInfo.displayName} v${cliInfo.version || 'unknown'} in ${resolvedDir}`));
|
|
336
373
|
} else {
|
|
337
374
|
// Fallback: InstanceManager without directly adapter manage
|
|
338
|
-
const adapter = this.createAdapter(cliType, resolvedDir,
|
|
375
|
+
const adapter = this.createAdapter(cliType, resolvedDir, resolvedCliArgs, key, false);
|
|
339
376
|
try {
|
|
340
377
|
await adapter.spawn();
|
|
341
378
|
} catch (spawnErr: any) {
|
|
@@ -458,6 +495,7 @@ export class DaemonCliManager {
|
|
|
458
495
|
resolvedProvider,
|
|
459
496
|
{},
|
|
460
497
|
true,
|
|
498
|
+
record.launchMode,
|
|
461
499
|
);
|
|
462
500
|
restored += 1;
|
|
463
501
|
LOG.info('CLI', `♻ Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
@@ -537,7 +575,7 @@ export class DaemonCliManager {
|
|
|
537
575
|
const launchSource = resolved.source;
|
|
538
576
|
if (!cliType) throw new Error('cliType required');
|
|
539
577
|
|
|
540
|
-
await this.startSession(cliType, dir, args?.cliArgs, args?.initialModel);
|
|
578
|
+
await this.startSession(cliType, dir, args?.cliArgs, args?.initialModel, args?.launchMode, args?.launchOptionValues);
|
|
541
579
|
|
|
542
580
|
// On startSession success, new UUID key exists in adapters (last added item)
|
|
543
581
|
let newKey: string | null = null;
|
package/src/commands/router.ts
CHANGED
|
@@ -18,12 +18,14 @@ import type { ProviderInstanceManager } from '../providers/provider-instance-man
|
|
|
18
18
|
import { launchWithCdp, killIdeProcess, isIdeRunning } from '../launch.js';
|
|
19
19
|
import { loadConfig, saveConfig, updateConfig } from '../config/config.js';
|
|
20
20
|
import { resolveIdeLaunchWorkspace } from '../config/workspaces.js';
|
|
21
|
-
import { appendRecentActivity,
|
|
21
|
+
import { appendRecentActivity, markSessionSeen } from '../config/recent-activity.js';
|
|
22
22
|
import { detectIDEs } from '../detection/ide-detector.js';
|
|
23
23
|
import { SessionRegistry } from '../sessions/registry.js';
|
|
24
24
|
import { LOG } from '../logging/logger.js';
|
|
25
25
|
import { logCommand } from '../logging/command-log.js';
|
|
26
26
|
import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
|
|
27
|
+
import { buildSessionEntries } from '../status/builders.js';
|
|
28
|
+
import { getSessionCompletionMarker } from '../status/snapshot.js';
|
|
27
29
|
import * as fs from 'fs';
|
|
28
30
|
|
|
29
31
|
// ─── Types ───
|
|
@@ -61,6 +63,7 @@ const CHAT_COMMANDS = [
|
|
|
61
63
|
'send_chat', 'new_chat', 'switch_chat', 'set_mode',
|
|
62
64
|
'change_model',
|
|
63
65
|
];
|
|
66
|
+
const READ_DEBUG_ENABLED = process.argv.includes('--dev') || process.env.ADHDEV_READ_DEBUG === '1';
|
|
64
67
|
|
|
65
68
|
export class DaemonCommandRouter {
|
|
66
69
|
private deps: CommandRouterDeps;
|
|
@@ -265,28 +268,35 @@ export class DaemonCommandRouter {
|
|
|
265
268
|
return { success: true, userName: name };
|
|
266
269
|
}
|
|
267
270
|
|
|
268
|
-
case '
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return { success: false, error: 'kind and providerType are required' };
|
|
271
|
+
case 'mark_session_seen': {
|
|
272
|
+
const sessionId = args?.sessionId;
|
|
273
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
274
|
+
return { success: false, error: 'sessionId is required' };
|
|
273
275
|
}
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
276
|
+
const currentConfig = loadConfig();
|
|
277
|
+
const prevSeenAt = currentConfig.sessionReads?.[sessionId] || 0;
|
|
278
|
+
const sessionEntries = buildSessionEntries(
|
|
279
|
+
this.deps.instanceManager.collectAllStates(),
|
|
280
|
+
this.deps.cdpManagers as Map<string, any>,
|
|
281
|
+
);
|
|
282
|
+
const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
|
|
283
|
+
const completionMarker = targetSession ? getSessionCompletionMarker(targetSession) : '';
|
|
284
|
+
const next = markSessionSeen(
|
|
285
|
+
currentConfig,
|
|
286
|
+
sessionId,
|
|
282
287
|
typeof args?.seenAt === 'number' ? args.seenAt : Date.now(),
|
|
288
|
+
completionMarker,
|
|
283
289
|
);
|
|
290
|
+
if (READ_DEBUG_ENABLED) {
|
|
291
|
+
LOG.info('RecentRead', `mark_session_seen sessionId=${sessionId} seenAt=${String(args?.seenAt || '')} prevSeenAt=${String(prevSeenAt)} nextSeenAt=${String(next.sessionReads?.[sessionId] || 0)} marker=${completionMarker || '-'}`);
|
|
292
|
+
}
|
|
284
293
|
saveConfig(next);
|
|
285
294
|
this.deps.onStatusChange?.();
|
|
286
295
|
return {
|
|
287
296
|
success: true,
|
|
288
|
-
|
|
289
|
-
seenAt: next.
|
|
297
|
+
sessionId,
|
|
298
|
+
seenAt: next.sessionReads?.[sessionId] || Date.now(),
|
|
299
|
+
completionMarker,
|
|
290
300
|
};
|
|
291
301
|
}
|
|
292
302
|
|
package/src/config/config.ts
CHANGED
|
@@ -59,8 +59,10 @@ export interface ADHDevConfig {
|
|
|
59
59
|
|
|
60
60
|
/** Unified recent activity across IDE / CLI / ACP launch flows */
|
|
61
61
|
recentActivity?: RecentActivityEntry[];
|
|
62
|
-
/** Last seen timestamps for
|
|
63
|
-
|
|
62
|
+
/** Last seen timestamps for live sessions, keyed by sessionId */
|
|
63
|
+
sessionReads?: Record<string, number>;
|
|
64
|
+
/** Last seen completion marker for live sessions, keyed by sessionId */
|
|
65
|
+
sessionReadMarkers?: Record<string, string>;
|
|
64
66
|
|
|
65
67
|
// Machine nickname (user-customizable label for this machine)
|
|
66
68
|
machineNickname: string | null;
|
|
@@ -122,7 +124,8 @@ const DEFAULT_CONFIG: ADHDevConfig = {
|
|
|
122
124
|
workspaces: [],
|
|
123
125
|
defaultWorkspaceId: null,
|
|
124
126
|
recentActivity: [],
|
|
125
|
-
|
|
127
|
+
sessionReads: {},
|
|
128
|
+
sessionReadMarkers: {},
|
|
126
129
|
machineNickname: null,
|
|
127
130
|
machineId: undefined,
|
|
128
131
|
machineSecret: null,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unified recent activity —
|
|
2
|
+
* Unified recent activity — launcher-facing "pick up where you launched".
|
|
3
3
|
*
|
|
4
|
-
* Unlike
|
|
4
|
+
* Unlike live session state, this is launch oriented:
|
|
5
5
|
* - one normalized row shape for IDE / CLI / ACP
|
|
6
6
|
* - deduped by kind + providerType + workspace
|
|
7
|
-
* -
|
|
7
|
+
* - used only for quick-launch shortcuts
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as path from 'path';
|
|
@@ -18,7 +18,6 @@ export interface RecentActivityEntry {
|
|
|
18
18
|
providerName: string;
|
|
19
19
|
workspace?: string | null;
|
|
20
20
|
currentModel?: string;
|
|
21
|
-
sessionId?: string | null;
|
|
22
21
|
title?: string;
|
|
23
22
|
lastUsedAt: number;
|
|
24
23
|
}
|
|
@@ -62,22 +61,35 @@ export function getRecentActivity(config: ADHDevConfig, limit = 20): RecentActiv
|
|
|
62
61
|
.slice(0, limit);
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
export function
|
|
66
|
-
return config.
|
|
64
|
+
export function getSessionSeenAt(config: ADHDevConfig, sessionId: string): number {
|
|
65
|
+
return config.sessionReads?.[sessionId] || 0;
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
export function
|
|
68
|
+
export function getSessionSeenMarker(config: ADHDevConfig, sessionId: string): string {
|
|
69
|
+
return config.sessionReadMarkers?.[sessionId] || '';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function markSessionSeen(
|
|
70
73
|
config: ADHDevConfig,
|
|
71
|
-
|
|
74
|
+
sessionId: string,
|
|
72
75
|
seenAt = Date.now(),
|
|
76
|
+
completionMarker?: string | null,
|
|
73
77
|
): ADHDevConfig {
|
|
74
|
-
const prev = config.
|
|
75
|
-
const nextSeenAt = Math.max(prev[
|
|
78
|
+
const prev = config.sessionReads || {};
|
|
79
|
+
const nextSeenAt = Math.max(prev[sessionId] || 0, seenAt);
|
|
80
|
+
const prevMarkers = config.sessionReadMarkers || {};
|
|
81
|
+
const nextMarker = typeof completionMarker === 'string' ? completionMarker : '';
|
|
76
82
|
return {
|
|
77
83
|
...config,
|
|
78
|
-
|
|
84
|
+
sessionReads: {
|
|
79
85
|
...prev,
|
|
80
|
-
[
|
|
86
|
+
[sessionId]: nextSeenAt,
|
|
81
87
|
},
|
|
88
|
+
sessionReadMarkers: nextMarker
|
|
89
|
+
? {
|
|
90
|
+
...prevMarkers,
|
|
91
|
+
[sessionId]: nextMarker,
|
|
92
|
+
}
|
|
93
|
+
: prevMarkers,
|
|
82
94
|
};
|
|
83
95
|
}
|
package/src/index.ts
CHANGED
|
@@ -46,11 +46,11 @@ export type {
|
|
|
46
46
|
// rollup-dts cannot resolve re-exports from shared-types.ts for them.
|
|
47
47
|
import type { RuntimeWriteOwner as _RuntimeWriteOwner } from './shared-types-extra.js';
|
|
48
48
|
import type { RuntimeAttachedClient as _RuntimeAttachedClient } from './shared-types-extra.js';
|
|
49
|
-
import type {
|
|
49
|
+
import type { RecentLaunchEntry as _RecentLaunchEntry } from './shared-types.js';
|
|
50
50
|
import type { TerminalBackendStatus as _TerminalBackendStatus } from './shared-types-extra.js';
|
|
51
51
|
export type RuntimeWriteOwner = _RuntimeWriteOwner;
|
|
52
52
|
export type RuntimeAttachedClient = _RuntimeAttachedClient;
|
|
53
|
-
export type
|
|
53
|
+
export type RecentLaunchEntry = _RecentLaunchEntry;
|
|
54
54
|
export type TerminalBackendStatus = _TerminalBackendStatus;
|
|
55
55
|
|
|
56
56
|
// Type aliases — rollup-dts cannot bundle re-exported type aliases at all.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
|
-
import type { ProviderModule } from './contracts.js';
|
|
10
|
+
import type { ProviderModule, ProviderLaunchMode, ProviderLaunchOption } from './contracts.js';
|
|
11
11
|
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
12
12
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
13
13
|
import type { CliProviderModule } from '../cli-adapters/provider-cli-adapter.js';
|
|
@@ -33,20 +33,46 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
33
33
|
private historyWriter: ChatHistoryWriter;
|
|
34
34
|
readonly instanceId: string;
|
|
35
35
|
|
|
36
|
+
private launchMode: ProviderLaunchMode | null;
|
|
37
|
+
private resolvedOutputFormat: 'terminal' | 'stream-json';
|
|
38
|
+
|
|
36
39
|
constructor(
|
|
37
40
|
private provider: ProviderModule,
|
|
38
41
|
private workingDir: string,
|
|
39
42
|
private cliArgs: string[] = [],
|
|
40
43
|
instanceId?: string,
|
|
41
44
|
transportFactory?: PtyTransportFactory,
|
|
45
|
+
launchModeId?: string,
|
|
42
46
|
) {
|
|
43
47
|
this.type = provider.type;
|
|
44
48
|
this.instanceId = instanceId || crypto.randomUUID();
|
|
49
|
+
this.launchMode = (launchModeId && provider.launchModes?.find(m => m.id === launchModeId)) || null;
|
|
50
|
+
this.resolvedOutputFormat = this.resolveOutputFormat();
|
|
45
51
|
this.adapter = new ProviderCliAdapter(provider as any as CliProviderModule, workingDir, cliArgs, transportFactory);
|
|
46
52
|
this.monitor = new StatusMonitor();
|
|
47
53
|
this.historyWriter = new ChatHistoryWriter();
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Determine output rendering format from:
|
|
58
|
+
* 1. launchMode.outputFormat (explicit override)
|
|
59
|
+
* 2. launchOptions[].outputFormatMap — check actual args for matching values
|
|
60
|
+
* 3. Default: 'terminal'
|
|
61
|
+
*/
|
|
62
|
+
private resolveOutputFormat(): 'terminal' | 'stream-json' {
|
|
63
|
+
if (this.launchMode?.outputFormat) return this.launchMode.outputFormat;
|
|
64
|
+
if (this.provider.launchOptions?.length) {
|
|
65
|
+
for (const opt of this.provider.launchOptions) {
|
|
66
|
+
if (!opt.outputFormatMap) continue;
|
|
67
|
+
// Check if any cliArg matches a value with an outputFormatMap entry
|
|
68
|
+
for (const [val, fmt] of Object.entries(opt.outputFormatMap)) {
|
|
69
|
+
if (this.cliArgs.includes(val)) return fmt;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return 'terminal';
|
|
74
|
+
}
|
|
75
|
+
|
|
50
76
|
// ─── Lifecycle ─────────────────────────────────
|
|
51
77
|
|
|
52
78
|
async init(context: InstanceContext): Promise<void> {
|
|
@@ -102,7 +128,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
102
128
|
name: this.provider.name,
|
|
103
129
|
category: 'cli',
|
|
104
130
|
status: adapterStatus.status,
|
|
105
|
-
mode: 'terminal',
|
|
131
|
+
mode: this.resolvedOutputFormat === 'stream-json' ? 'chat' : 'terminal',
|
|
132
|
+
launchMode: this.launchMode?.id,
|
|
106
133
|
activeChat: {
|
|
107
134
|
id: `${this.type}_${this.workingDir}`,
|
|
108
135
|
title: `${this.provider.name} · ${dirName}`,
|