@adhdev/daemon-core 0.9.53 → 0.9.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +5 -0
  2. package/dist/cli-adapters/provider-cli-adapter.d.ts +1 -0
  3. package/dist/cli-adapters/provider-cli-config.d.ts +1 -0
  4. package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
  5. package/dist/commands/handler.d.ts +7 -0
  6. package/dist/git/git-commands.d.ts +139 -0
  7. package/dist/git/git-diff.d.ts +17 -0
  8. package/dist/git/git-executor.d.ts +34 -0
  9. package/dist/git/git-monitor.d.ts +57 -0
  10. package/dist/git/git-snapshot-store.d.ts +50 -0
  11. package/dist/git/git-status.d.ts +19 -0
  12. package/dist/git/git-summary.d.ts +10 -0
  13. package/dist/git/git-types.d.ts +113 -0
  14. package/dist/git/index.d.ts +16 -0
  15. package/dist/git/turn-snapshot-tracker.d.ts +16 -0
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +1772 -386
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +1744 -383
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/providers/contracts.d.ts +2 -0
  22. package/dist/shared-types.d.ts +7 -1
  23. package/dist/status/builders.d.ts +2 -0
  24. package/dist/status/snapshot.d.ts +2 -0
  25. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  26. package/package.json +1 -1
  27. package/src/boot/daemon-lifecycle.ts +6 -0
  28. package/src/cli-adapters/provider-cli-adapter.ts +19 -0
  29. package/src/cli-adapters/provider-cli-config.d.ts +1 -0
  30. package/src/cli-adapters/provider-cli-config.ts +2 -0
  31. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  32. package/src/cli-adapters/provider-cli-shared.ts +2 -0
  33. package/src/commands/handler.ts +25 -1
  34. package/src/git/git-commands.ts +582 -0
  35. package/src/git/git-diff.ts +303 -0
  36. package/src/git/git-executor.ts +268 -0
  37. package/src/git/git-monitor.ts +194 -0
  38. package/src/git/git-snapshot-store.ts +238 -0
  39. package/src/git/git-status.ts +193 -0
  40. package/src/git/git-summary.ts +43 -0
  41. package/src/git/git-types.ts +154 -0
  42. package/src/git/index.ts +75 -0
  43. package/src/git/turn-snapshot-tracker.ts +31 -0
  44. package/src/index.ts +4 -0
  45. package/src/providers/contracts.d.ts +8 -0
  46. package/src/providers/contracts.ts +2 -0
  47. package/src/providers/provider-schema.ts +1 -0
  48. package/src/shared-types.ts +33 -1
  49. package/src/status/builders.ts +26 -4
  50. package/src/status/snapshot.ts +10 -2
@@ -358,6 +358,8 @@ export interface ProviderModule {
358
358
  sendKey?: string;
359
359
  /** How the CLI adapter decides when to submit typed input */
360
360
  submitStrategy?: 'wait_for_echo' | 'immediate';
361
+ /** If true, typed input must echo on the PTY screen before the adapter sends Enter. */
362
+ requirePromptEchoBeforeSubmit?: boolean;
361
363
  /** Keep this provider out of the upstream auto-updated bundle */
362
364
  /** @deprecated Machine-level provider source policy now lives in config.providerSourceMode. Local overrides shadow upstream by root precedence and should not rely on provider-level disableUpstream. */
363
365
  disableUpstream?: boolean;
@@ -13,6 +13,8 @@ export type { ProviderErrorReason } from './providers/provider-instance.js';
13
13
  import type { ActiveChatData as _ActiveChatData, ProviderErrorReason as _ProviderErrorReason } from './providers/provider-instance.js';
14
14
  import type { WorkspaceEntry } from './config/workspaces.js';
15
15
  import type { ProviderResumeCapability } from './providers/contracts.js';
16
+ import type { GitCompactSummary, GitWorkspaceUpdate, WorkspaceGitSubscriptionParams } from './git/git-types.js';
17
+ export type { GitCommandName, GitCompactSummary, GitDiffSummary, GitFailureReason, GitFileChange, GitFileChangeStatus, GitRepoIdentity, GitRepoStatus, GitSnapshot, GitSnapshotCompareSummary, GitSnapshotReason, GitWorkspaceUpdate, WorkspaceGitSubscriptionParams, } from './git/git-types.js';
16
18
  export interface SessionActiveChatData extends Omit<_ActiveChatData, 'messages'> {
17
19
  messages?: _ActiveChatData['messages'];
18
20
  }
@@ -132,7 +134,7 @@ export interface SessionHostDiagnosticsSnapshot {
132
134
  recentRequests: SessionHostRequestTrace[];
133
135
  recentTransitions: SessionHostRuntimeTransition[];
134
136
  }
135
- export type TransportTopic = 'session.chat_tail' | 'session.runtime_output' | 'machine.runtime' | 'session_host.diagnostics' | 'session.modal' | 'daemon.metadata';
137
+ export type TransportTopic = 'session.chat_tail' | 'session.runtime_output' | 'machine.runtime' | 'session_host.diagnostics' | 'session.modal' | 'daemon.metadata' | 'workspace.git';
136
138
  export interface SessionChatTailSubscriptionParams extends ReadChatCursor {
137
139
  targetSessionId: string;
138
140
  historySessionId?: string;
@@ -213,6 +215,7 @@ export interface TopicUpdateEnvelopeMap {
213
215
  'session_host.diagnostics': SessionHostDiagnosticsUpdate;
214
216
  'session.modal': SessionModalUpdate;
215
217
  'daemon.metadata': DaemonMetadataUpdate;
218
+ 'workspace.git': GitWorkspaceUpdate;
216
219
  }
217
220
  export type TopicUpdateEnvelope = TopicUpdateEnvelopeMap[TransportTopic];
218
221
  export interface SubscribeRequestMap {
@@ -222,6 +225,7 @@ export interface SubscribeRequestMap {
222
225
  'session_host.diagnostics': SessionHostDiagnosticsSubscriptionParams;
223
226
  'session.modal': SessionModalSubscriptionParams;
224
227
  'daemon.metadata': DaemonMetadataSubscriptionParams;
228
+ 'workspace.git': WorkspaceGitSubscriptionParams;
225
229
  }
226
230
  export type SubscribeRequest = {
227
231
  [K in TransportTopic]: {
@@ -255,6 +259,7 @@ export interface SessionEntry {
255
259
  status: SessionStatus;
256
260
  title: string;
257
261
  workspace?: string | null;
262
+ git?: GitCompactSummary;
258
263
  runtimeKey?: string;
259
264
  runtimeDisplayName?: string;
260
265
  runtimeWorkspaceLabel?: string;
@@ -305,6 +310,7 @@ export interface CompactSessionEntry {
305
310
  status: SessionStatus;
306
311
  title: string;
307
312
  workspace: string | null;
313
+ git?: GitCompactSummary;
308
314
  cdpConnected?: boolean;
309
315
  runtimeKey?: string;
310
316
  runtimeDisplayName?: string;
@@ -8,11 +8,13 @@
8
8
  * Consolidates ProviderState→ManagedEntry mapping logic.
9
9
  */
10
10
  import type { DaemonCdpManager } from '../cdp/manager.js';
11
+ import type { GitCompactSummary } from '../git/git-types.js';
11
12
  import type { SessionEntry } from '../shared-types.js';
12
13
  import type { ProviderState } from '../providers/provider-instance.js';
13
14
  export type SessionEntryProfile = 'full' | 'live' | 'metadata';
14
15
  export interface SessionEntryBuildOptions {
15
16
  profile?: SessionEntryProfile;
17
+ getGitSummaryForWorkspace?: (workspace: string) => GitCompactSummary | null | undefined;
16
18
  }
17
19
  /**
18
20
  * Find a CDP manager by key. Supports single-window (`cursor`) and full multi-window keys (`cursor_<targetId>`).
@@ -6,6 +6,7 @@
6
6
  * - daemon-standalone HTTP/WS status responses
7
7
  */
8
8
  import type { DaemonCdpManager } from '../cdp/manager.js';
9
+ import type { GitCompactSummary } from '../git/git-types.js';
9
10
  import { type SessionEntryProfile } from './builders.js';
10
11
  import type { ProviderState } from '../providers/provider-instance.js';
11
12
  import type { AvailableProviderInfo, MachineInfo, RecentSessionBucket, StatusReportPayload } from '../shared-types.js';
@@ -45,6 +46,7 @@ export interface StatusSnapshotOptions {
45
46
  p2p?: StatusReportPayload['p2p'];
46
47
  machineNickname?: string | null;
47
48
  profile?: SessionEntryProfile;
49
+ getGitSummaryForWorkspace?: (workspace: string) => GitCompactSummary | null | undefined;
48
50
  }
49
51
  export type StatusSnapshot = StatusReportPayload;
50
52
  export interface RecentReadDebugSnapshot {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.9.53",
3
+ "version": "0.9.55",
4
4
  "description": "ADHDev local session host core \u2014 session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.53",
3
+ "version": "0.9.55",
4
4
  "description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,6 +35,7 @@ import {
35
35
  import { loadConfig } from '../config/config.js';
36
36
  import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
37
37
  import type { IdeProviderInstance } from '../providers/ide-provider-instance.js';
38
+ import { createDefaultGitCommandServices } from '../git/git-commands.js';
38
39
 
39
40
  // ─── Init Config ───
40
41
 
@@ -78,6 +79,9 @@ export interface DaemonInitConfig {
78
79
  statusInstanceId?: string;
79
80
  statusVersion?: string;
80
81
  statusDaemonMode?: boolean;
82
+
83
+ /** Fired before send_chat is dispatched — used for turn snapshot hooks */
84
+ onBeforeSendChat?: (params: { workspace: string; sessionId: string }) => void;
81
85
  }
82
86
 
83
87
  // ─── Result ───
@@ -256,6 +260,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
256
260
  providerLoader,
257
261
  instanceManager,
258
262
  sessionRegistry,
263
+ gitCommandServices: createDefaultGitCommandServices(),
259
264
  onProviderSettingChanged: async (providerType) => {
260
265
  await refreshProviderAvailability(providerType);
261
266
  config.onStatusChange?.();
@@ -264,6 +269,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
264
269
  await refreshProviderAvailability();
265
270
  config.onStatusChange?.();
266
271
  },
272
+ onBeforeSendChat: config.onBeforeSendChat,
267
273
  });
268
274
 
269
275
  // 8. AgentStreamManager
@@ -598,6 +598,7 @@ export class ProviderCliAdapter implements CliAdapter {
598
598
  private readonly sendDelayMs: number;
599
599
  private readonly sendKey: string;
600
600
  private readonly submitStrategy: 'wait_for_echo' | 'immediate';
601
+ private readonly requirePromptEchoBeforeSubmit: boolean;
601
602
  private static readonly SCRIPT_STATUS_DEBOUNCE_MS = 3000;
602
603
 
603
604
  constructor(
@@ -620,6 +621,7 @@ export class ProviderCliAdapter implements CliAdapter {
620
621
  this.sendDelayMs = resolvedConfig.sendDelayMs;
621
622
  this.sendKey = resolvedConfig.sendKey;
622
623
  this.submitStrategy = resolvedConfig.submitStrategy;
624
+ this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
623
625
  this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
624
626
 
625
627
  // Scripts are required — loaded by ProviderLoader via compatibility array
@@ -2392,6 +2394,22 @@ export class ProviderCliAdapter implements CliAdapter {
2392
2394
  }
2393
2395
 
2394
2396
  if (elapsed >= state.maxEchoWaitMs) {
2397
+ const diagnostic = {
2398
+ elapsed,
2399
+ maxEchoWaitMs: state.maxEchoWaitMs,
2400
+ submitDelayMs: state.submitDelayMs,
2401
+ promptSnippet: state.normalizedPromptSnippet,
2402
+ requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
2403
+ screenText: summarizeCliTraceText(screenText, 1000),
2404
+ };
2405
+ this.recordTrace('submit_echo_missing', diagnostic);
2406
+ if (this.requirePromptEchoBeforeSubmit) {
2407
+ const message = `${this.cliName} prompt echo was not observed on the PTY screen before submit`;
2408
+ LOG.warn('CLI', `[${this.cliType}] ${message} elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
2409
+ completion.rejectOnce(new Error(message));
2410
+ return;
2411
+ }
2412
+ LOG.warn('CLI', `[${this.cliType}] prompt echo was not observed before submit; sending submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs}`);
2395
2413
  this.submitSendKey(state, completion);
2396
2414
  return;
2397
2415
  }
@@ -2880,6 +2898,7 @@ export class ProviderCliAdapter implements CliAdapter {
2880
2898
  sendDelayMs: this.sendDelayMs,
2881
2899
  sendKey: this.sendKey,
2882
2900
  submitStrategy: this.submitStrategy,
2901
+ requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
2883
2902
  submitPendingUntil: this.submitPendingUntil,
2884
2903
  responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
2885
2904
  resizeSuppressUntil: this.resizeSuppressUntil,
@@ -25,6 +25,7 @@ export interface ResolvedCliAdapterConfig {
25
25
  sendDelayMs: number;
26
26
  sendKey: string;
27
27
  submitStrategy: 'wait_for_echo' | 'immediate';
28
+ requirePromptEchoBeforeSubmit: boolean;
28
29
  providerResolutionMeta: ProviderResolutionMeta;
29
30
  }
30
31
  export declare function resolveCliAdapterConfig(provider: CliProviderModule): ResolvedCliAdapterConfig;
@@ -29,6 +29,7 @@ export interface ResolvedCliAdapterConfig {
29
29
  sendDelayMs: number;
30
30
  sendKey: string;
31
31
  submitStrategy: 'wait_for_echo' | 'immediate';
32
+ requirePromptEchoBeforeSubmit: boolean;
32
33
  providerResolutionMeta: ProviderResolutionMeta;
33
34
  }
34
35
 
@@ -55,6 +56,7 @@ export function resolveCliAdapterConfig(provider: CliProviderModule): ResolvedCl
55
56
  ? provider.sendKey
56
57
  : '\r',
57
58
  submitStrategy: provider.submitStrategy === 'immediate' ? 'immediate' : 'wait_for_echo',
59
+ requirePromptEchoBeforeSubmit: provider.requirePromptEchoBeforeSubmit === true,
58
60
  providerResolutionMeta: {
59
61
  type: provider.type,
60
62
  name: provider.name,
@@ -104,6 +104,8 @@ export interface CliProviderModule {
104
104
  sendDelayMs?: number;
105
105
  sendKey?: string;
106
106
  submitStrategy?: 'wait_for_echo' | 'immediate';
107
+ /** Require the typed prompt to be visible on the PTY screen before sending Enter. */
108
+ requirePromptEchoBeforeSubmit?: boolean;
107
109
  /** Allow sending another prompt while the CLI is still generating so users can intervene mid-turn. */
108
110
  allowInputDuringGeneration?: boolean;
109
111
  scripts?: CliScripts;
@@ -122,6 +122,8 @@ export interface CliProviderModule {
122
122
  sendDelayMs?: number;
123
123
  sendKey?: string;
124
124
  submitStrategy?: 'wait_for_echo' | 'immediate';
125
+ /** Require the typed prompt to be visible on the PTY screen before sending Enter. */
126
+ requirePromptEchoBeforeSubmit?: boolean;
125
127
  /** Allow sending another prompt while the CLI is still generating so users can intervene mid-turn. */
126
128
  allowInputDuringGeneration?: boolean;
127
129
  /** When provider-owned, daemon treats provider parser output as canonical transcript authority. */
@@ -32,6 +32,7 @@ import * as Cdp from './cdp-commands.js';
32
32
  import * as Stream from './stream-commands.js';
33
33
  import * as WorkspaceCmd from './workspace-commands.js';
34
34
  import { getWorkspaceState } from '../config/workspaces.js';
35
+ import { handleGitCommand, isGitCommandName, type GitCommandServices } from '../git/git-commands.js';
35
36
 
36
37
  export interface CommandResult {
37
38
  success: boolean;
@@ -48,6 +49,9 @@ export interface CommandContext {
48
49
  sessionRegistry?: SessionRegistry;
49
50
  onProviderSettingChanged?: (providerType: string, key: string, value: any) => Promise<void> | void;
50
51
  onProviderSourceConfigChanged?: () => Promise<void> | void;
52
+ gitCommandServices?: GitCommandServices;
53
+ /** Fired synchronously before send_chat is dispatched; fire-and-forget for callers */
54
+ onBeforeSendChat?: (params: { workspace: string; sessionId: string }) => void;
51
55
  }
52
56
 
53
57
  /**
@@ -377,6 +381,13 @@ export class DaemonCommandHandler implements CommandHelpers {
377
381
  this._currentRoute = this.resolveRoute(args);
378
382
  const startedAt = Date.now();
379
383
  this.logCommandStart(cmd, args);
384
+ let result: CommandResult;
385
+
386
+ if (isGitCommandName(cmd)) {
387
+ result = await handleGitCommand(cmd, args, this._ctx.gitCommandServices);
388
+ this.logCommandEnd(cmd, result, startedAt);
389
+ return result;
390
+ }
380
391
 
381
392
  const sessionScopedCommands = new Set([
382
393
  'read_chat',
@@ -406,7 +417,6 @@ export class DaemonCommandHandler implements CommandHelpers {
406
417
  }
407
418
 
408
419
  // Commands without ideType CDP silently fail (prevent P2P retry spam)
409
- let result: CommandResult;
410
420
  if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
411
421
  const cdpCommands = ['send_chat', 'read_chat', 'list_chats', 'new_chat', 'switch_chat', 'set_mode', 'change_model', 'set_thought_level', 'resolve_action'];
412
422
  if (cdpCommands.includes(cmd)) {
@@ -416,6 +426,20 @@ export class DaemonCommandHandler implements CommandHelpers {
416
426
  }
417
427
  }
418
428
 
429
+ if (cmd === 'send_chat' && this._ctx.onBeforeSendChat) {
430
+ const sessionId = this._currentRoute.session?.sessionId;
431
+ const workspace = sessionId
432
+ ? (this._ctx.instanceManager?.getInstance(sessionId) as any)?.getState?.()?.workspace
433
+ : undefined;
434
+ if (workspace && sessionId) {
435
+ try {
436
+ this._ctx.onBeforeSendChat({ workspace, sessionId });
437
+ } catch {
438
+ // hook must not block send_chat
439
+ }
440
+ }
441
+ }
442
+
419
443
  try {
420
444
  result = await this.dispatch(cmd, args);
421
445
  this.logCommandEnd(cmd, result, startedAt);