@adhdev/daemon-core 0.8.57 → 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.
- package/dist/agent-stream/types.d.ts +3 -4
- package/dist/boot/daemon-lifecycle.d.ts +1 -0
- package/dist/commands/handler.d.ts +1 -0
- package/dist/commands/router.d.ts +1 -0
- package/dist/commands/stream-commands.d.ts +3 -0
- package/dist/config/chat-history.d.ts +3 -0
- package/dist/config/config.d.ts +3 -0
- package/dist/config/provider-source-config.d.ts +23 -0
- package/dist/config/recent-activity.d.ts +2 -1
- package/dist/config/saved-sessions.d.ts +2 -1
- package/dist/daemon/dev-server-types.d.ts +1 -0
- package/dist/daemon/dev-server.d.ts +4 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +876 -337
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +875 -337
- package/dist/index.mjs.map +1 -1
- package/dist/providers/acp-provider-instance.d.ts +8 -2
- package/dist/providers/cli-provider-instance.d.ts +10 -0
- package/dist/providers/contracts.d.ts +4 -2
- package/dist/providers/extension-provider-instance.d.ts +1 -2
- package/dist/providers/provider-instance.d.ts +3 -4
- package/dist/providers/provider-loader.d.ts +14 -1
- package/dist/providers/provider-patch-state.d.ts +23 -0
- package/dist/providers/summary-metadata.d.ts +22 -0
- package/dist/shared-types.d.ts +15 -9
- package/dist/status/snapshot.d.ts +16 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/agent-stream/forward.ts +1 -2
- package/src/agent-stream/manager.ts +2 -1
- package/src/agent-stream/provider-adapter.ts +7 -3
- package/src/agent-stream/types.d.ts +3 -4
- package/src/agent-stream/types.ts +3 -4
- package/src/boot/daemon-lifecycle.ts +13 -3
- package/src/commands/cli-manager.ts +10 -5
- package/src/commands/handler.ts +3 -0
- package/src/commands/router.ts +160 -26
- package/src/commands/stream-commands.ts +60 -2
- package/src/config/chat-history.ts +39 -0
- package/src/config/config.d.ts +3 -0
- package/src/config/config.ts +19 -3
- package/src/config/provider-source-config.ts +42 -0
- package/src/config/recent-activity.d.ts +2 -1
- package/src/config/recent-activity.ts +12 -1
- package/src/config/saved-sessions.d.ts +2 -1
- package/src/config/saved-sessions.ts +12 -2
- package/src/daemon/dev-auto-implement.ts +1 -14
- package/src/daemon/dev-cli-debug.ts +0 -1
- package/src/daemon/dev-server-types.ts +1 -0
- package/src/daemon/dev-server.ts +46 -21
- package/src/daemon/scaffold-template.ts +8 -1
- package/src/index.d.ts +1 -1
- package/src/index.ts +4 -0
- package/src/providers/acp-provider-instance.d.ts +8 -2
- package/src/providers/acp-provider-instance.ts +80 -23
- package/src/providers/cli-provider-instance.ts +42 -22
- package/src/providers/contracts.d.ts +3 -2
- package/src/providers/contracts.ts +7 -4
- package/src/providers/control-effects.ts +3 -4
- package/src/providers/extension-provider-instance.d.ts +1 -2
- package/src/providers/extension-provider-instance.ts +26 -14
- package/src/providers/ide-provider-instance.ts +28 -15
- package/src/providers/provider-instance.d.ts +3 -4
- package/src/providers/provider-instance.ts +6 -7
- package/src/providers/provider-loader.d.ts +4 -1
- package/src/providers/provider-loader.ts +61 -23
- package/src/providers/provider-patch-state.ts +91 -0
- package/src/providers/provider-schema.ts +3 -0
- package/src/providers/summary-metadata.ts +118 -0
- package/src/shared-types.d.ts +15 -9
- package/src/shared-types.ts +17 -9
- package/src/status/builders.ts +18 -13
- package/src/status/reporter.ts +2 -4
- package/src/status/snapshot.ts +60 -2
package/src/commands/router.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { logCommand } from '../logging/command-log.js';
|
|
|
31
31
|
import type { CommandLogEntry } from '../logging/command-log.js';
|
|
32
32
|
import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
|
|
33
33
|
import { createInteractionId, getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
|
|
34
|
+
import { getSessionHostSurfaceKind, partitionSessionHostRecords } from '../session-host/runtime-surface.js';
|
|
34
35
|
import { buildSessionEntries } from '../status/builders.js';
|
|
35
36
|
import { buildMachineInfo, buildStatusSnapshot } from '../status/snapshot.js';
|
|
36
37
|
import { getSessionCompletionMarker } from '../status/snapshot.js';
|
|
@@ -137,6 +138,58 @@ function toHostedCliRuntimeDescriptor(record: any): HostedCliRuntimeDescriptor |
|
|
|
137
138
|
};
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
function getWriteConflictOwnerClientId(error: unknown): string | undefined {
|
|
142
|
+
const message = typeof error === 'string'
|
|
143
|
+
? error
|
|
144
|
+
: error instanceof Error
|
|
145
|
+
? error.message
|
|
146
|
+
: '';
|
|
147
|
+
const match = /^Write owned by\s+(.+)$/.exec(message.trim());
|
|
148
|
+
return match?.[1]?.trim() || undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function summarizeSessionHostRecord(result: unknown): Record<string, unknown> {
|
|
152
|
+
if (!result || typeof result !== 'object') return {};
|
|
153
|
+
const record = result as Record<string, any>;
|
|
154
|
+
return {
|
|
155
|
+
runtimeKey: typeof record.runtimeKey === 'string' ? record.runtimeKey : undefined,
|
|
156
|
+
lifecycle: typeof record.lifecycle === 'string' ? record.lifecycle : undefined,
|
|
157
|
+
surfaceKind: getSessionHostSurfaceKind(record as any),
|
|
158
|
+
attachedClientCount: Array.isArray(record.attachedClients) ? record.attachedClients.length : undefined,
|
|
159
|
+
hasWriteOwner: !!record.writeOwner,
|
|
160
|
+
writeOwnerClientId: typeof record.writeOwner?.clientId === 'string' ? record.writeOwner.clientId : undefined,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function summarizeSessionHostRecords(result: unknown): Record<string, unknown> {
|
|
165
|
+
const records = Array.isArray(result) ? result : [];
|
|
166
|
+
const groups = partitionSessionHostRecords(records as any[]);
|
|
167
|
+
return {
|
|
168
|
+
sessionCount: records.length,
|
|
169
|
+
liveRuntimeCount: groups.liveRuntimes.length,
|
|
170
|
+
recoverySnapshotCount: groups.recoverySnapshots.length,
|
|
171
|
+
inactiveRecordCount: groups.inactiveRecords.length,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function summarizeSessionHostDiagnostics(result: unknown): Record<string, unknown> {
|
|
176
|
+
const diagnostics = result && typeof result === 'object' ? result as Record<string, any> : {};
|
|
177
|
+
const sessions = Array.isArray(diagnostics.sessions) ? diagnostics.sessions : [];
|
|
178
|
+
return {
|
|
179
|
+
runtimeCount: typeof diagnostics.runtimeCount === 'number' ? diagnostics.runtimeCount : undefined,
|
|
180
|
+
...summarizeSessionHostRecords(sessions),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function summarizeSessionHostPruneResult(result: unknown): Record<string, unknown> {
|
|
185
|
+
const value = result && typeof result === 'object' ? result as Record<string, any> : {};
|
|
186
|
+
return {
|
|
187
|
+
duplicateGroupCount: typeof value.duplicateGroupCount === 'number' ? value.duplicateGroupCount : undefined,
|
|
188
|
+
prunedCount: Array.isArray(value.prunedSessionIds) ? value.prunedSessionIds.length : undefined,
|
|
189
|
+
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : undefined,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
140
193
|
export class DaemonCommandRouter {
|
|
141
194
|
private deps: CommandRouterDeps;
|
|
142
195
|
|
|
@@ -144,6 +197,64 @@ export class DaemonCommandRouter {
|
|
|
144
197
|
this.deps = deps;
|
|
145
198
|
}
|
|
146
199
|
|
|
200
|
+
private async traceSessionHostAction<T>(
|
|
201
|
+
action: string,
|
|
202
|
+
args: any,
|
|
203
|
+
run: () => Promise<T>,
|
|
204
|
+
summarizeResult?: (result: T) => Record<string, unknown>,
|
|
205
|
+
): Promise<T> {
|
|
206
|
+
const interactionId = typeof args?._interactionId === 'string' ? args._interactionId : undefined;
|
|
207
|
+
const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : undefined;
|
|
208
|
+
const requestedPayload: Record<string, unknown> = { action };
|
|
209
|
+
if (sessionId) requestedPayload.sessionId = sessionId;
|
|
210
|
+
if (typeof args?.clientId === 'string') requestedPayload.clientId = args.clientId;
|
|
211
|
+
if (typeof args?.signal === 'string') requestedPayload.signal = args.signal;
|
|
212
|
+
if (typeof args?.providerType === 'string') requestedPayload.providerType = args.providerType;
|
|
213
|
+
if (typeof args?.workspace === 'string') requestedPayload.workspace = args.workspace;
|
|
214
|
+
if (typeof args?.dryRun === 'boolean') requestedPayload.dryRun = args.dryRun;
|
|
215
|
+
|
|
216
|
+
recordDebugTrace({
|
|
217
|
+
interactionId,
|
|
218
|
+
category: 'session_host',
|
|
219
|
+
stage: 'action_requested',
|
|
220
|
+
level: 'info',
|
|
221
|
+
sessionId,
|
|
222
|
+
payload: requestedPayload,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const result = await run();
|
|
227
|
+
recordDebugTrace({
|
|
228
|
+
interactionId,
|
|
229
|
+
category: 'session_host',
|
|
230
|
+
stage: 'action_result',
|
|
231
|
+
level: 'info',
|
|
232
|
+
sessionId,
|
|
233
|
+
payload: {
|
|
234
|
+
...requestedPayload,
|
|
235
|
+
success: true,
|
|
236
|
+
...(summarizeResult ? summarizeResult(result) : {}),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
return result;
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
recordDebugTrace({
|
|
242
|
+
interactionId,
|
|
243
|
+
category: 'session_host',
|
|
244
|
+
stage: 'action_failed',
|
|
245
|
+
level: 'error',
|
|
246
|
+
sessionId,
|
|
247
|
+
payload: {
|
|
248
|
+
...requestedPayload,
|
|
249
|
+
error: error?.message || String(error),
|
|
250
|
+
failureKind: getWriteConflictOwnerClientId(error) ? 'write_conflict' : 'request_failed',
|
|
251
|
+
conflictOwnerClientId: getWriteConflictOwnerClientId(error),
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
147
258
|
/**
|
|
148
259
|
* Unified command routing.
|
|
149
260
|
* Returns result for all commands:
|
|
@@ -270,16 +381,20 @@ export class DaemonCommandRouter {
|
|
|
270
381
|
|
|
271
382
|
case 'session_host_get_diagnostics': {
|
|
272
383
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
273
|
-
const diagnostics = await this.deps.sessionHostControl
|
|
384
|
+
const diagnostics = await this.traceSessionHostAction('session_host_get_diagnostics', args, () => this.deps.sessionHostControl!.getDiagnostics({
|
|
274
385
|
includeSessions: args?.includeSessions !== false,
|
|
275
386
|
limit: Number(args?.limit) || undefined,
|
|
276
|
-
})
|
|
387
|
+
}), (result) => ({
|
|
388
|
+
includeSessions: args?.includeSessions !== false,
|
|
389
|
+
limit: Number(args?.limit) || undefined,
|
|
390
|
+
...summarizeSessionHostDiagnostics(result),
|
|
391
|
+
}));
|
|
277
392
|
return { success: true, diagnostics };
|
|
278
393
|
}
|
|
279
394
|
|
|
280
395
|
case 'session_host_list_sessions': {
|
|
281
396
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
282
|
-
const sessions = await this.deps.sessionHostControl
|
|
397
|
+
const sessions = await this.traceSessionHostAction('session_host_list_sessions', args, () => this.deps.sessionHostControl!.listSessions(), (records) => summarizeSessionHostRecords(records));
|
|
283
398
|
return { success: true, sessions };
|
|
284
399
|
}
|
|
285
400
|
|
|
@@ -287,7 +402,7 @@ export class DaemonCommandRouter {
|
|
|
287
402
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
288
403
|
const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : '';
|
|
289
404
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
290
|
-
const record = await this.deps.sessionHostControl
|
|
405
|
+
const record = await this.traceSessionHostAction('session_host_stop_session', args, () => this.deps.sessionHostControl!.stopSession(sessionId), (result) => summarizeSessionHostRecord(result));
|
|
291
406
|
return { success: true, record };
|
|
292
407
|
}
|
|
293
408
|
|
|
@@ -295,11 +410,17 @@ export class DaemonCommandRouter {
|
|
|
295
410
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
296
411
|
const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : '';
|
|
297
412
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
298
|
-
const record = await this.
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
413
|
+
const record = await this.traceSessionHostAction('session_host_resume_session', args, async () => {
|
|
414
|
+
const nextRecord = await this.deps.sessionHostControl!.resumeSession(sessionId);
|
|
415
|
+
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
416
|
+
if (hosted) {
|
|
417
|
+
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
418
|
+
}
|
|
419
|
+
return nextRecord;
|
|
420
|
+
}, (result) => ({
|
|
421
|
+
...summarizeSessionHostRecord(result),
|
|
422
|
+
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result),
|
|
423
|
+
}));
|
|
303
424
|
return { success: true, record };
|
|
304
425
|
}
|
|
305
426
|
|
|
@@ -307,11 +428,17 @@ export class DaemonCommandRouter {
|
|
|
307
428
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
308
429
|
const sessionId = typeof args?.sessionId === 'string' ? args.sessionId : '';
|
|
309
430
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
310
|
-
const record = await this.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
431
|
+
const record = await this.traceSessionHostAction('session_host_restart_session', args, async () => {
|
|
432
|
+
const nextRecord = await this.deps.sessionHostControl!.restartSession(sessionId);
|
|
433
|
+
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
434
|
+
if (hosted) {
|
|
435
|
+
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
436
|
+
}
|
|
437
|
+
return nextRecord;
|
|
438
|
+
}, (result) => ({
|
|
439
|
+
...summarizeSessionHostRecord(result),
|
|
440
|
+
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result),
|
|
441
|
+
}));
|
|
315
442
|
return { success: true, record };
|
|
316
443
|
}
|
|
317
444
|
|
|
@@ -321,7 +448,7 @@ export class DaemonCommandRouter {
|
|
|
321
448
|
const signal = typeof args?.signal === 'string' ? args.signal : '';
|
|
322
449
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
323
450
|
if (!signal) return { success: false, error: 'signal required' };
|
|
324
|
-
const record = await this.deps.sessionHostControl
|
|
451
|
+
const record = await this.traceSessionHostAction('session_host_send_signal', args, () => this.deps.sessionHostControl!.sendSignal(sessionId, signal), (result) => summarizeSessionHostRecord(result));
|
|
325
452
|
return { success: true, record };
|
|
326
453
|
}
|
|
327
454
|
|
|
@@ -331,17 +458,17 @@ export class DaemonCommandRouter {
|
|
|
331
458
|
const clientId = typeof args?.clientId === 'string' ? args.clientId : '';
|
|
332
459
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
333
460
|
if (!clientId) return { success: false, error: 'clientId required' };
|
|
334
|
-
const record = await this.deps.sessionHostControl
|
|
461
|
+
const record = await this.traceSessionHostAction('session_host_force_detach_client', args, () => this.deps.sessionHostControl!.forceDetachClient(sessionId, clientId), (result) => summarizeSessionHostRecord(result));
|
|
335
462
|
return { success: true, record };
|
|
336
463
|
}
|
|
337
464
|
|
|
338
465
|
case 'session_host_prune_duplicate_sessions': {
|
|
339
466
|
if (!this.deps.sessionHostControl) return { success: false, error: 'Session host control unavailable' };
|
|
340
|
-
const result = await this.deps.sessionHostControl
|
|
467
|
+
const result = await this.traceSessionHostAction('session_host_prune_duplicate_sessions', args, () => this.deps.sessionHostControl!.pruneDuplicateSessions({
|
|
341
468
|
providerType: typeof args?.providerType === 'string' ? args.providerType : undefined,
|
|
342
469
|
workspace: typeof args?.workspace === 'string' ? args.workspace : undefined,
|
|
343
470
|
dryRun: args?.dryRun === true,
|
|
344
|
-
});
|
|
471
|
+
}), (value) => summarizeSessionHostPruneResult(value));
|
|
345
472
|
return { success: true, result };
|
|
346
473
|
}
|
|
347
474
|
|
|
@@ -352,12 +479,15 @@ export class DaemonCommandRouter {
|
|
|
352
479
|
const ownerType = args?.ownerType === 'agent' ? 'agent' : 'user';
|
|
353
480
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
354
481
|
if (!clientId) return { success: false, error: 'clientId required' };
|
|
355
|
-
const record = await this.deps.sessionHostControl
|
|
482
|
+
const record = await this.traceSessionHostAction('session_host_acquire_write', args, () => this.deps.sessionHostControl!.acquireWrite({
|
|
356
483
|
sessionId,
|
|
357
484
|
clientId,
|
|
358
485
|
ownerType,
|
|
359
486
|
force: args?.force !== false,
|
|
360
|
-
})
|
|
487
|
+
}), (result) => ({
|
|
488
|
+
...summarizeSessionHostRecord(result),
|
|
489
|
+
ownerType,
|
|
490
|
+
}));
|
|
361
491
|
return { success: true, record };
|
|
362
492
|
}
|
|
363
493
|
|
|
@@ -367,7 +497,10 @@ export class DaemonCommandRouter {
|
|
|
367
497
|
const clientId = typeof args?.clientId === 'string' ? args.clientId : '';
|
|
368
498
|
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
369
499
|
if (!clientId) return { success: false, error: 'clientId required' };
|
|
370
|
-
const record = await this.deps.sessionHostControl
|
|
500
|
+
const record = await this.traceSessionHostAction('session_host_release_write', args, () => this.deps.sessionHostControl!.releaseWrite({
|
|
501
|
+
sessionId,
|
|
502
|
+
clientId,
|
|
503
|
+
}), (result) => summarizeSessionHostRecord(result));
|
|
371
504
|
return { success: true, record };
|
|
372
505
|
}
|
|
373
506
|
|
|
@@ -382,8 +515,9 @@ export class DaemonCommandRouter {
|
|
|
382
515
|
return { success: false, error: 'providerType required' };
|
|
383
516
|
}
|
|
384
517
|
|
|
385
|
-
const
|
|
386
|
-
const
|
|
518
|
+
const wantsAll = args?.all === true;
|
|
519
|
+
const offset = wantsAll ? 0 : Math.max(0, Number(args?.offset) || 0);
|
|
520
|
+
const limit = wantsAll ? Number.MAX_SAFE_INTEGER : Math.max(1, Math.min(100, Number(args?.limit) || 30));
|
|
387
521
|
const { sessions: historySessions, hasMore } = listSavedHistorySessions(providerType, { offset, limit });
|
|
388
522
|
const state = loadState();
|
|
389
523
|
const savedSessions = getSavedProviderSessions(state, { providerType, kind });
|
|
@@ -406,13 +540,13 @@ export class DaemonCommandRouter {
|
|
|
406
540
|
providerName: saved?.providerName || recent?.providerName || providerType,
|
|
407
541
|
kind: saved?.kind || recent?.kind || kind,
|
|
408
542
|
title: saved?.title || recent?.title || session.sessionTitle || session.preview || providerType,
|
|
409
|
-
workspace: saved?.workspace || recent?.workspace,
|
|
410
|
-
|
|
543
|
+
workspace: saved?.workspace || recent?.workspace || session.workspace,
|
|
544
|
+
summaryMetadata: saved?.summaryMetadata || recent?.summaryMetadata,
|
|
411
545
|
preview: session.preview,
|
|
412
546
|
messageCount: session.messageCount,
|
|
413
547
|
firstMessageAt: session.firstMessageAt,
|
|
414
548
|
lastMessageAt: session.lastMessageAt,
|
|
415
|
-
canResume: !!(saved?.workspace || recent?.workspace) && canResumeById,
|
|
549
|
+
canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById,
|
|
416
550
|
};
|
|
417
551
|
}),
|
|
418
552
|
hasMore,
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import type { CommandResult, CommandHelpers } from './handler.js';
|
|
7
7
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
8
8
|
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
9
|
+
import { loadConfig, saveConfig } from '../config/config.js';
|
|
10
|
+
import { parseProviderSourceConfigUpdate } from '../config/provider-source-config.js';
|
|
9
11
|
import { getCliScriptCommand, parseCliScriptResult } from '../providers/cli-script-results.js';
|
|
10
12
|
import {
|
|
11
13
|
normalizeControlInvokeResult,
|
|
@@ -107,10 +109,66 @@ export async function handleSetProviderSetting(h: CommandHelpers, args: any): Pr
|
|
|
107
109
|
return { success: false, error: `Failed to set ${providerType}.${key} — invalid key, value, or not a public setting` };
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
export function handleGetProviderSourceConfig(h: CommandHelpers, _args: any): CommandResult {
|
|
113
|
+
const loader = h.ctx.providerLoader as ProviderLoader | undefined;
|
|
114
|
+
if (!loader) return { success: false, error: 'providerLoader not available' };
|
|
115
|
+
return { success: true, ...loader.getSourceConfig() };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function handleSetProviderSourceConfig(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
119
|
+
const loader = h.ctx.providerLoader as ProviderLoader | undefined;
|
|
120
|
+
if (!loader) return { success: false, error: 'providerLoader not available' };
|
|
121
|
+
|
|
122
|
+
const parsed = parseProviderSourceConfigUpdate(args || {});
|
|
123
|
+
if ('error' in parsed) {
|
|
124
|
+
return { success: false, error: parsed.error };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const currentConfig = loadConfig();
|
|
128
|
+
const nextConfig = {
|
|
129
|
+
...currentConfig,
|
|
130
|
+
...(parsed.updates.providerSourceMode ? { providerSourceMode: parsed.updates.providerSourceMode } : {}),
|
|
131
|
+
...(Object.prototype.hasOwnProperty.call(parsed.updates, 'providerDir') ? { providerDir: parsed.updates.providerDir } : {}),
|
|
132
|
+
};
|
|
133
|
+
saveConfig(nextConfig);
|
|
134
|
+
|
|
135
|
+
const sourceConfig = loader.applySourceConfig({
|
|
136
|
+
sourceMode: nextConfig.providerSourceMode,
|
|
137
|
+
userDir: Object.prototype.hasOwnProperty.call(parsed.updates, 'providerDir') ? parsed.updates.providerDir : loader.getSourceConfig().explicitProviderDir || undefined,
|
|
138
|
+
});
|
|
139
|
+
loader.reload();
|
|
140
|
+
loader.registerToDetector();
|
|
141
|
+
await h.ctx.onProviderSourceConfigChanged?.();
|
|
142
|
+
|
|
143
|
+
LOG.info(
|
|
144
|
+
'Command',
|
|
145
|
+
`[set_provider_source_config] mode=${sourceConfig.sourceMode} explicitProviderDir=${sourceConfig.explicitProviderDir || '-'} userDir=${sourceConfig.userDir}`,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return { success: true, reloaded: true, ...sourceConfig };
|
|
149
|
+
}
|
|
150
|
+
|
|
110
151
|
// ─── Extension Script Execution (Model/Mode) ─────
|
|
111
152
|
|
|
112
|
-
function normalizeProviderScriptArgs(args: any): Record<string, any> {
|
|
153
|
+
export function normalizeProviderScriptArgs(args: any, scriptName?: string): Record<string, any> {
|
|
113
154
|
const normalizedArgs = { ...(args || {}) };
|
|
155
|
+
const normalizedScriptName = String(scriptName || '').toLowerCase();
|
|
156
|
+
|
|
157
|
+
if (Object.prototype.hasOwnProperty.call(normalizedArgs, 'value')) {
|
|
158
|
+
if (
|
|
159
|
+
normalizedArgs.model === undefined
|
|
160
|
+
&& (normalizedScriptName === 'setmodel' || normalizedScriptName === 'setmodelgui' || normalizedScriptName === 'webviewsetmodel')
|
|
161
|
+
) {
|
|
162
|
+
normalizedArgs.model = normalizedArgs.value;
|
|
163
|
+
}
|
|
164
|
+
if (
|
|
165
|
+
normalizedArgs.mode === undefined
|
|
166
|
+
&& (normalizedScriptName === 'setmode' || normalizedScriptName === 'webviewsetmode')
|
|
167
|
+
) {
|
|
168
|
+
normalizedArgs.mode = normalizedArgs.value;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
114
172
|
for (const key of ['mode', 'model', 'message', 'action', 'button', 'text', 'sessionId', 'value']) {
|
|
115
173
|
if (key in normalizedArgs && !(key.toUpperCase() in normalizedArgs)) {
|
|
116
174
|
normalizedArgs[key.toUpperCase()] = normalizedArgs[key];
|
|
@@ -172,7 +230,7 @@ async function executeProviderScript(h: CommandHelpers, args: any, scriptName: s
|
|
|
172
230
|
return { success: false, error: `Script '${actualScriptName}' not available for ${resolvedProviderType}` };
|
|
173
231
|
}
|
|
174
232
|
|
|
175
|
-
const normalizedArgs = normalizeProviderScriptArgs(args);
|
|
233
|
+
const normalizedArgs = normalizeProviderScriptArgs(args, actualScriptName);
|
|
176
234
|
|
|
177
235
|
if (provider.category === 'cli') {
|
|
178
236
|
const adapter = h.getCliAdapter(args?.targetSessionId || resolvedProviderType);
|
|
@@ -27,6 +27,7 @@ interface HistoryMessage {
|
|
|
27
27
|
instanceId?: string; // IDE instance UUID (distinguishes windows of the same agent type)
|
|
28
28
|
historySessionId?: string; // Persistent provider-side conversation/session key
|
|
29
29
|
sessionTitle?: string;
|
|
30
|
+
workspace?: string; // Working directory at session start (kind: 'session_start' only)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
|
|
@@ -123,6 +124,7 @@ export interface SavedHistorySessionSummary {
|
|
|
123
124
|
firstMessageAt: number;
|
|
124
125
|
lastMessageAt: number;
|
|
125
126
|
preview?: string;
|
|
127
|
+
workspace?: string;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
export class ChatHistoryWriter {
|
|
@@ -325,6 +327,37 @@ export class ChatHistoryWriter {
|
|
|
325
327
|
);
|
|
326
328
|
}
|
|
327
329
|
|
|
330
|
+
writeSessionStart(
|
|
331
|
+
agentType: string,
|
|
332
|
+
historySessionId: string,
|
|
333
|
+
workspace: string,
|
|
334
|
+
instanceId?: string,
|
|
335
|
+
): void {
|
|
336
|
+
const id = String(historySessionId || '').trim();
|
|
337
|
+
const ws = String(workspace || '').trim();
|
|
338
|
+
if (!id || !ws) return;
|
|
339
|
+
try {
|
|
340
|
+
const dir = path.join(HISTORY_DIR, this.sanitize(agentType));
|
|
341
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
342
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
343
|
+
const filePath = path.join(dir, `${this.sanitize(id)}_${date}.jsonl`);
|
|
344
|
+
const record: HistoryMessage = {
|
|
345
|
+
ts: new Date().toISOString(),
|
|
346
|
+
receivedAt: Date.now(),
|
|
347
|
+
role: 'system',
|
|
348
|
+
kind: 'session_start',
|
|
349
|
+
content: ws,
|
|
350
|
+
agent: agentType,
|
|
351
|
+
instanceId,
|
|
352
|
+
historySessionId: id,
|
|
353
|
+
workspace: ws,
|
|
354
|
+
};
|
|
355
|
+
fs.appendFileSync(filePath, JSON.stringify(record) + '\n', 'utf-8');
|
|
356
|
+
} catch {
|
|
357
|
+
// Ignore — must not affect main functionality
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
328
361
|
promoteHistorySession(
|
|
329
362
|
agentType: string,
|
|
330
363
|
previousHistorySessionId: string,
|
|
@@ -606,6 +639,7 @@ export function listSavedHistorySessions(
|
|
|
606
639
|
let lastMessageAt = 0;
|
|
607
640
|
let sessionTitle = '';
|
|
608
641
|
let preview = '';
|
|
642
|
+
let workspace = '';
|
|
609
643
|
|
|
610
644
|
for (const file of files.sort()) {
|
|
611
645
|
const filePath = path.join(dir, file);
|
|
@@ -619,6 +653,10 @@ export function listSavedHistorySessions(
|
|
|
619
653
|
parsed = null;
|
|
620
654
|
}
|
|
621
655
|
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
656
|
+
if (parsed.kind === 'session_start') {
|
|
657
|
+
if (!workspace && parsed.workspace) workspace = parsed.workspace;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
622
660
|
messageCount += 1;
|
|
623
661
|
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
624
662
|
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
@@ -635,6 +673,7 @@ export function listSavedHistorySessions(
|
|
|
635
673
|
firstMessageAt,
|
|
636
674
|
lastMessageAt,
|
|
637
675
|
preview: preview || undefined,
|
|
676
|
+
workspace: workspace || undefined,
|
|
638
677
|
});
|
|
639
678
|
}
|
|
640
679
|
|
package/src/config/config.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export type { WorkspaceEntry } from './workspaces.js';
|
|
|
8
8
|
export type { RecentActivityEntry } from './recent-activity.js';
|
|
9
9
|
export type { SavedProviderSessionEntry } from './saved-sessions.js';
|
|
10
10
|
export type { DaemonState } from './state-store.js';
|
|
11
|
+
export type ProviderSourceMode = 'normal' | 'no-upstream';
|
|
12
|
+
export declare function resolveProviderSourceMode(providerSourceMode: unknown, legacyDisableUpstream: unknown): ProviderSourceMode;
|
|
11
13
|
export interface ADHDevConfig {
|
|
12
14
|
serverUrl: string;
|
|
13
15
|
selectedIde: string | null;
|
|
@@ -45,6 +47,7 @@ export interface ADHDevConfig {
|
|
|
45
47
|
}>;
|
|
46
48
|
}>;
|
|
47
49
|
disableUpstream?: boolean;
|
|
50
|
+
providerSourceMode?: ProviderSourceMode;
|
|
48
51
|
providerDir?: string;
|
|
49
52
|
/**
|
|
50
53
|
* Browser terminal sizing behavior for dashboard CLI panes.
|
package/src/config/config.ts
CHANGED
|
@@ -14,6 +14,18 @@ export type { RecentActivityEntry } from './recent-activity.js';
|
|
|
14
14
|
export type { SavedProviderSessionEntry } from './saved-sessions.js';
|
|
15
15
|
export type { DaemonState } from './state-store.js';
|
|
16
16
|
|
|
17
|
+
export type ProviderSourceMode = 'normal' | 'no-upstream';
|
|
18
|
+
|
|
19
|
+
export function resolveProviderSourceMode(
|
|
20
|
+
providerSourceMode: unknown,
|
|
21
|
+
legacyDisableUpstream: unknown,
|
|
22
|
+
): ProviderSourceMode {
|
|
23
|
+
if (providerSourceMode === 'normal' || providerSourceMode === 'no-upstream') {
|
|
24
|
+
return providerSourceMode;
|
|
25
|
+
}
|
|
26
|
+
return legacyDisableUpstream === true ? 'no-upstream' : 'normal';
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
export interface ADHDevConfig {
|
|
18
30
|
// Server connection
|
|
19
31
|
serverUrl: string;
|
|
@@ -81,9 +93,13 @@ export interface ADHDevConfig {
|
|
|
81
93
|
|
|
82
94
|
// Disable upstream provider auto-download (use builtin only)
|
|
83
95
|
// Controllable from CLI (--no-upstream) and dashboard (machine page)
|
|
96
|
+
// Deprecated legacy boolean; prefer providerSourceMode.
|
|
84
97
|
disableUpstream?: boolean;
|
|
85
98
|
|
|
86
|
-
//
|
|
99
|
+
// Explicit machine-level provider source policy.
|
|
100
|
+
providerSourceMode?: ProviderSourceMode;
|
|
101
|
+
|
|
102
|
+
// Optional explicit provider override root (for example a local adhdev-providers checkout)
|
|
87
103
|
providerDir?: string;
|
|
88
104
|
|
|
89
105
|
/**
|
|
@@ -113,7 +129,7 @@ const DEFAULT_CONFIG: ADHDevConfig = {
|
|
|
113
129
|
registeredMachineId: undefined,
|
|
114
130
|
providerSettings: {},
|
|
115
131
|
ideSettings: {},
|
|
116
|
-
|
|
132
|
+
providerSourceMode: 'normal',
|
|
117
133
|
terminalSizingMode: 'measured',
|
|
118
134
|
};
|
|
119
135
|
|
|
@@ -164,7 +180,7 @@ function normalizeConfig(raw: unknown): ADHDevConfig & { activeWorkspaceId?: str
|
|
|
164
180
|
registeredMachineId: asOptionalString(parsed.registeredMachineId),
|
|
165
181
|
providerSettings: isPlainObject(parsed.providerSettings) ? parsed.providerSettings : {},
|
|
166
182
|
ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
|
|
167
|
-
|
|
183
|
+
providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
|
|
168
184
|
providerDir: asOptionalString(parsed.providerDir),
|
|
169
185
|
terminalSizingMode: parsed.terminalSizingMode === 'fit' ? 'fit' : 'measured',
|
|
170
186
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ProviderSourceMode } from './config.js'
|
|
2
|
+
|
|
3
|
+
export interface ProviderSourceConfigSnapshot {
|
|
4
|
+
sourceMode: ProviderSourceMode
|
|
5
|
+
disableUpstream: boolean
|
|
6
|
+
explicitProviderDir: string | null
|
|
7
|
+
userDir: string
|
|
8
|
+
upstreamDir: string
|
|
9
|
+
providerRoots: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ProviderSourceConfigUpdate {
|
|
13
|
+
providerSourceMode?: ProviderSourceMode
|
|
14
|
+
providerDir?: string | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeProviderDir(value: unknown): string | undefined {
|
|
18
|
+
if (typeof value !== 'string') return undefined
|
|
19
|
+
const trimmed = value.trim()
|
|
20
|
+
return trimmed ? trimmed : undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parseProviderSourceConfigUpdate(input: {
|
|
24
|
+
providerSourceMode?: unknown
|
|
25
|
+
providerDir?: unknown
|
|
26
|
+
}): { ok: true; updates: ProviderSourceConfigUpdate } | { ok: false; error: string } {
|
|
27
|
+
const updates: ProviderSourceConfigUpdate = {}
|
|
28
|
+
|
|
29
|
+
if (Object.prototype.hasOwnProperty.call(input, 'providerSourceMode')) {
|
|
30
|
+
const { providerSourceMode } = input
|
|
31
|
+
if (providerSourceMode !== 'normal' && providerSourceMode !== 'no-upstream') {
|
|
32
|
+
return { ok: false, error: "providerSourceMode must be 'normal' or 'no-upstream'" }
|
|
33
|
+
}
|
|
34
|
+
updates.providerSourceMode = providerSourceMode
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (Object.prototype.hasOwnProperty.call(input, 'providerDir')) {
|
|
38
|
+
updates.providerDir = normalizeProviderDir(input.providerDir)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { ok: true, updates }
|
|
42
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - deduped by provider session when available, else by kind + providerType + workspace
|
|
7
7
|
* - used only for quick-launch shortcuts
|
|
8
8
|
*/
|
|
9
|
+
import type { ProviderSummaryMetadata } from '../shared-types.js';
|
|
9
10
|
import type { DaemonState } from './state-store.js';
|
|
10
11
|
export interface RecentActivityEntry {
|
|
11
12
|
id: string;
|
|
@@ -14,7 +15,7 @@ export interface RecentActivityEntry {
|
|
|
14
15
|
providerName: string;
|
|
15
16
|
providerSessionId?: string;
|
|
16
17
|
workspace?: string | null;
|
|
17
|
-
|
|
18
|
+
summaryMetadata?: ProviderSummaryMetadata;
|
|
18
19
|
title?: string;
|
|
19
20
|
lastUsedAt: number;
|
|
20
21
|
}
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as path from 'path';
|
|
11
|
+
import type { ProviderSummaryMetadata } from '../shared-types.js';
|
|
11
12
|
import type { DaemonState } from './state-store.js';
|
|
12
13
|
import { expandPath } from './workspaces.js';
|
|
14
|
+
import { normalizePersistedSummaryMetadata } from '../providers/summary-metadata.js';
|
|
13
15
|
|
|
14
16
|
export interface RecentActivityEntry {
|
|
15
17
|
id: string;
|
|
@@ -18,7 +20,7 @@ export interface RecentActivityEntry {
|
|
|
18
20
|
providerName: string;
|
|
19
21
|
providerSessionId?: string;
|
|
20
22
|
workspace?: string | null;
|
|
21
|
-
|
|
23
|
+
summaryMetadata?: ProviderSummaryMetadata;
|
|
22
24
|
title?: string;
|
|
23
25
|
lastUsedAt: number;
|
|
24
26
|
}
|
|
@@ -55,6 +57,9 @@ export function appendRecentActivity(
|
|
|
55
57
|
const nextEntry: RecentActivityEntry = {
|
|
56
58
|
...entry,
|
|
57
59
|
workspace: entry.workspace ? normalizeWorkspace(entry.workspace) : undefined,
|
|
60
|
+
summaryMetadata: normalizePersistedSummaryMetadata({
|
|
61
|
+
summaryMetadata: entry.summaryMetadata,
|
|
62
|
+
}),
|
|
58
63
|
id: buildRecentActivityKeyForEntry(entry),
|
|
59
64
|
lastUsedAt: entry.lastUsedAt || Date.now(),
|
|
60
65
|
};
|
|
@@ -68,6 +73,12 @@ export function appendRecentActivity(
|
|
|
68
73
|
|
|
69
74
|
export function getRecentActivity(state: DaemonState, limit = 20): RecentActivityEntry[] {
|
|
70
75
|
return [...(state.recentActivity || [])]
|
|
76
|
+
.map(entry => ({
|
|
77
|
+
...entry,
|
|
78
|
+
summaryMetadata: normalizePersistedSummaryMetadata({
|
|
79
|
+
summaryMetadata: entry.summaryMetadata,
|
|
80
|
+
}),
|
|
81
|
+
}))
|
|
71
82
|
.sort((a, b) => b.lastUsedAt - a.lastUsedAt)
|
|
72
83
|
.slice(0, limit);
|
|
73
84
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProviderSummaryMetadata } from '../shared-types.js';
|
|
1
2
|
import type { DaemonState } from './state-store.js';
|
|
2
3
|
export interface SavedProviderSessionEntry {
|
|
3
4
|
id: string;
|
|
@@ -6,7 +7,7 @@ export interface SavedProviderSessionEntry {
|
|
|
6
7
|
providerName: string;
|
|
7
8
|
providerSessionId: string;
|
|
8
9
|
workspace?: string | null;
|
|
9
|
-
|
|
10
|
+
summaryMetadata?: ProviderSummaryMetadata;
|
|
10
11
|
title?: string;
|
|
11
12
|
createdAt: number;
|
|
12
13
|
lastUsedAt: number;
|