@adhdev/daemon-core 0.9.33 → 0.9.35

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.
@@ -47,6 +47,7 @@ export interface CliAdapter {
47
47
  isReady(): boolean;
48
48
  setOnStatusChange(callback: () => void): void;
49
49
  updateRuntimeSettings?(settings: Record<string, unknown>): void;
50
+ setCliScripts?(scripts: Record<string, unknown>): void;
50
51
  setServerConn?(serverConn: unknown): void;
51
52
  clearHistory?(): void;
52
53
  resolveAction?(data: unknown): Promise<void>;
@@ -34,6 +34,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
34
34
  private messages;
35
35
  private committedMessages;
36
36
  private structuredMessages;
37
+ private committedMessagesActivitySignature;
38
+ private committedMessagesChangedAt;
37
39
  private currentStatus;
38
40
  private onStatusChange;
39
41
  private responseBuffer;
@@ -107,7 +109,9 @@ export declare class ProviderCliAdapter implements CliAdapter {
107
109
  private readonly providerResolutionMeta;
108
110
  private static readonly FINISH_RETRY_DELAY_MS;
109
111
  private static readonly MAX_FINISH_RETRIES;
112
+ private buildCommittedMessagesActivitySignature;
110
113
  private syncMessageViews;
114
+ getLastCommittedMessageActivityAt(): number;
111
115
  private readTerminalScreenText;
112
116
  private shouldReadTerminalScreenSnapshot;
113
117
  private resetTerminalScreen;
@@ -131,6 +135,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
131
135
  constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[], transportFactory?: PtyTransportFactory);
132
136
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
133
137
  setCliScripts(scripts: CliScripts): void;
138
+ /** Refresh provider scripts/config used by this adapter without restarting the PTY runtime. */
139
+ refreshProviderDefinition(provider: CliProviderModule): void;
134
140
  updateRuntimeSettings(settings: Record<string, any>): void;
135
141
  setServerConn(serverConn: any): void;
136
142
  setOnStatusChange(callback: () => void): void;
@@ -27,5 +27,6 @@ export declare function buildPinnedGlobalInstallCommand(options: {
27
27
  currentCliPath?: string;
28
28
  nodeExecutable?: string;
29
29
  }): PinnedGlobalInstallCommand;
30
+ export declare function stopSessionHostProcesses(appName: string): void;
30
31
  export declare function spawnDetachedDaemonUpgradeHelper(payload: DaemonUpgradeHelperPayload): void;
31
32
  export declare function maybeRunDaemonUpgradeHelperFromEnv(): Promise<boolean>;
package/dist/index.js CHANGED
@@ -1690,13 +1690,14 @@ function sliceFromOffset(text, start) {
1690
1690
  function hydrateCliParsedMessages(parsedMessages, options) {
1691
1691
  const { committedMessages, scope, lastOutputAt } = options;
1692
1692
  const referenceMessages = [...committedMessages];
1693
+ const referenceComparables = referenceMessages.map((message) => normalizeComparableMessageContent(message?.content || ""));
1693
1694
  const usedReferenceIndexes = /* @__PURE__ */ new Set();
1694
1695
  const now = options.now ?? Date.now();
1695
1696
  const findReferenceTimestamp = (role, content, parsedIndex) => {
1696
1697
  const normalizedContent = normalizeComparableMessageContent(content);
1697
1698
  if (!normalizedContent) return void 0;
1698
1699
  const sameIndex = referenceMessages[parsedIndex];
1699
- if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && normalizeComparableMessageContent(sameIndex.content) === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
1700
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && referenceComparables[parsedIndex] === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
1700
1701
  usedReferenceIndexes.add(parsedIndex);
1701
1702
  return sameIndex.timestamp;
1702
1703
  }
@@ -1704,7 +1705,7 @@ function hydrateCliParsedMessages(parsedMessages, options) {
1704
1705
  if (usedReferenceIndexes.has(i)) continue;
1705
1706
  const candidate = referenceMessages[i];
1706
1707
  if (!candidate || candidate.role !== role) continue;
1707
- const candidateContent = normalizeComparableMessageContent(candidate.content);
1708
+ const candidateContent = referenceComparables[i];
1708
1709
  if (!candidateContent) continue;
1709
1710
  const exactMatch = candidateContent === normalizedContent;
1710
1711
  const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
@@ -2086,6 +2087,8 @@ var init_provider_cli_adapter = __esm({
2086
2087
  messages = [];
2087
2088
  committedMessages = [];
2088
2089
  structuredMessages = [];
2090
+ committedMessagesActivitySignature = "";
2091
+ committedMessagesChangedAt = 0;
2089
2092
  currentStatus = "starting";
2090
2093
  onStatusChange = null;
2091
2094
  responseBuffer = "";
@@ -2167,10 +2170,35 @@ var init_provider_cli_adapter = __esm({
2167
2170
  providerResolutionMeta;
2168
2171
  static FINISH_RETRY_DELAY_MS = 300;
2169
2172
  static MAX_FINISH_RETRIES = 2;
2173
+ buildCommittedMessagesActivitySignature() {
2174
+ const last = this.committedMessages[this.committedMessages.length - 1];
2175
+ return [
2176
+ String(this.committedMessages.length),
2177
+ String(last?.role || ""),
2178
+ String(last?.kind || ""),
2179
+ String(last?.senderName || ""),
2180
+ String(last?.timestamp || ""),
2181
+ String(last?.receivedAt || ""),
2182
+ normalizeComparableMessageContent(last?.content || "").slice(-240)
2183
+ ].join("|");
2184
+ }
2170
2185
  syncMessageViews() {
2186
+ const signature = this.buildCommittedMessagesActivitySignature();
2187
+ if (signature !== this.committedMessagesActivitySignature) {
2188
+ this.committedMessagesActivitySignature = signature;
2189
+ this.committedMessagesChangedAt = Date.now();
2190
+ }
2171
2191
  this.messages = [...this.committedMessages];
2172
2192
  this.structuredMessages = [...this.committedMessages];
2173
2193
  }
2194
+ getLastCommittedMessageActivityAt() {
2195
+ const last = this.committedMessages[this.committedMessages.length - 1];
2196
+ const messageTime = Math.max(
2197
+ typeof last?.receivedAt === "number" && Number.isFinite(last.receivedAt) ? last.receivedAt : 0,
2198
+ typeof last?.timestamp === "number" && Number.isFinite(last.timestamp) ? last.timestamp : 0
2199
+ );
2200
+ return Math.max(messageTime, this.committedMessagesChangedAt || 0);
2201
+ }
2174
2202
  readTerminalScreenText(now = Date.now()) {
2175
2203
  const screenText = this.terminalScreen.getText() || "";
2176
2204
  this.lastScreenText = screenText;
@@ -2310,9 +2338,16 @@ var init_provider_cli_adapter = __esm({
2310
2338
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
2311
2339
  setCliScripts(scripts) {
2312
2340
  this.cliScripts = scripts;
2341
+ this.parsedStatusCache = null;
2342
+ this.parseErrorMessage = null;
2313
2343
  const scriptNames = listCliScriptNames(scripts);
2314
2344
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
2315
2345
  }
2346
+ /** Refresh provider scripts/config used by this adapter without restarting the PTY runtime. */
2347
+ refreshProviderDefinition(provider) {
2348
+ this.provider = provider;
2349
+ this.setCliScripts(provider.scripts || {});
2350
+ }
2316
2351
  updateRuntimeSettings(settings) {
2317
2352
  this.runtimeSettings = { ...settings };
2318
2353
  }
@@ -2796,7 +2831,7 @@ var init_provider_cli_adapter = __esm({
2796
2831
  return;
2797
2832
  }
2798
2833
  if (this.currentTurnScope && !lastParsedAssistant) {
2799
- LOG.info(
2834
+ LOG.debug(
2800
2835
  "CLI",
2801
2836
  `[${this.cliType}] Settled without assistant: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 220)).slice(0, 260)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"}`
2802
2837
  );
@@ -3612,9 +3647,43 @@ var init_provider_cli_adapter = __esm({
3612
3647
  }
3613
3648
  armResponseTimeout() {
3614
3649
  if (this.responseTimeout) clearTimeout(this.responseTimeout);
3650
+ const timeoutMs = this.timeouts.maxResponse;
3651
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3652
+ this.responseTimeout = null;
3653
+ return;
3654
+ }
3615
3655
  this.responseTimeout = setTimeout(() => {
3616
- if (this.isWaitingForResponse) this.finishResponse();
3617
- }, this.timeouts.maxResponse);
3656
+ this.responseTimeout = null;
3657
+ if (!this.isWaitingForResponse) return;
3658
+ const detectedStatusBeforeEval = this.runDetectStatus(this.recentOutputBuffer);
3659
+ this.recordTrace("response_timeout_check", {
3660
+ timeoutMs,
3661
+ detectedStatus: detectedStatusBeforeEval,
3662
+ currentStatus: this.currentStatus,
3663
+ isWaitingForResponse: this.isWaitingForResponse,
3664
+ hasActionableApproval: this.hasActionableApproval(),
3665
+ ...buildCliTraceParseSnapshot({
3666
+ accumulatedBuffer: this.accumulatedBuffer,
3667
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3668
+ responseBuffer: this.responseBuffer,
3669
+ partialResponse: this.responseBuffer,
3670
+ scope: this.currentTurnScope
3671
+ })
3672
+ });
3673
+ this.settledBuffer = this.recentOutputBuffer;
3674
+ this.evaluateSettled();
3675
+ if (this.isWaitingForResponse && !this.hasActionableApproval()) {
3676
+ const detectedStatusAfterEval = this.runDetectStatus(this.recentOutputBuffer);
3677
+ this.recordTrace("response_timeout_kept_open", {
3678
+ timeoutMs,
3679
+ detectedStatusBeforeEval,
3680
+ detectedStatusAfterEval,
3681
+ currentStatus: this.currentStatus,
3682
+ isWaitingForResponse: this.isWaitingForResponse
3683
+ });
3684
+ this.armResponseTimeout();
3685
+ }
3686
+ }, timeoutMs);
3618
3687
  }
3619
3688
  writeSubmitKeyForRetry(mode) {
3620
3689
  void this.writeToPty(this.sendKey).catch((error) => {
@@ -12943,7 +13012,14 @@ var DaemonCommandHandler = class {
12943
13012
  await this._ctx.providerLoader.fetchLatest().catch(() => {
12944
13013
  });
12945
13014
  this._ctx.providerLoader.reload();
12946
- return { success: true };
13015
+ this._ctx.providerLoader.registerToDetector();
13016
+ const refreshedInstances = this._ctx.instanceManager ? this._ctx.instanceManager.refreshProviderDefinitions((providerType) => this._ctx.providerLoader.resolve(providerType)) : 0;
13017
+ const providers = this._ctx.providerLoader.getAll().map((provider) => ({
13018
+ type: provider.type,
13019
+ name: provider.name,
13020
+ category: provider.category
13021
+ }));
13022
+ return { success: true, refreshedInstances, providers };
12947
13023
  }
12948
13024
  return { success: false, error: "ProviderLoader not initialized" };
12949
13025
  }
@@ -13198,6 +13274,11 @@ var CliProviderInstance = class {
13198
13274
  launchMode;
13199
13275
  startedAt = Date.now();
13200
13276
  onProviderSessionResolved;
13277
+ refreshProviderDefinition(provider) {
13278
+ if (provider.type !== this.type || provider.category !== "cli") return;
13279
+ this.provider = provider;
13280
+ this.adapter.refreshProviderDefinition(provider);
13281
+ }
13201
13282
  // ─── Lifecycle ─────────────────────────────────
13202
13283
  async init(context) {
13203
13284
  this.context = context;
@@ -13403,6 +13484,22 @@ var CliProviderInstance = class {
13403
13484
  getPresentationMode() {
13404
13485
  return this.presentationMode;
13405
13486
  }
13487
+ getHotChatSessionState() {
13488
+ const adapterStatus = this.adapter.getStatus();
13489
+ const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
13490
+ const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
13491
+ const runtime = this.adapter.getRuntimeMetadata();
13492
+ const lastCommittedMessageActivityAt = typeof this.adapter.getLastCommittedMessageActivityAt === "function" ? this.adapter.getLastCommittedMessageActivityAt() : 0;
13493
+ return {
13494
+ id: this.instanceId,
13495
+ status: visibleStatus,
13496
+ lastMessageAt: lastCommittedMessageActivityAt || void 0,
13497
+ runtimeLifecycle: runtime?.lifecycle ?? null,
13498
+ runtimeSurfaceKind: runtime?.surfaceKind,
13499
+ runtimeRestoredFromStorage: runtime?.restoredFromStorage === true,
13500
+ runtimeRecoveryState: runtime?.recoveryState ?? null
13501
+ };
13502
+ }
13406
13503
  updateSettings(newSettings) {
13407
13504
  this.settings = { ...newSettings };
13408
13505
  this.adapter.updateRuntimeSettings?.(this.settings);
@@ -13558,6 +13655,15 @@ var CliProviderInstance = class {
13558
13655
  this.completedDebouncePending = { chatTitle, duration, timestamp: now };
13559
13656
  this.completedDebounceTimer = setTimeout(() => {
13560
13657
  if (this.completedDebouncePending) {
13658
+ const latestStatus = this.adapter.getStatus();
13659
+ const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
13660
+ const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
13661
+ if (latestVisibleStatus !== "idle") {
13662
+ LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
13663
+ this.completedDebouncePending = null;
13664
+ this.completedDebounceTimer = null;
13665
+ return;
13666
+ }
13561
13667
  LOG.info("CLI", `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
13562
13668
  this.pushEvent({ event: "agent:generating_completed", ...this.completedDebouncePending });
13563
13669
  this.completedDebouncePending = null;
@@ -18564,6 +18670,51 @@ function killPid(pid) {
18564
18670
  return false;
18565
18671
  }
18566
18672
  }
18673
+ function getWindowsProcessCommandLine(pid) {
18674
+ const pidFilter = `ProcessId=${pid}`;
18675
+ try {
18676
+ const psOut = (0, import_child_process8.execFileSync)("powershell.exe", [
18677
+ "-NoProfile",
18678
+ "-NonInteractive",
18679
+ "-ExecutionPolicy",
18680
+ "Bypass",
18681
+ "-Command",
18682
+ `(Get-CimInstance Win32_Process -Filter "${pidFilter}").CommandLine`
18683
+ ], { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).trim();
18684
+ if (psOut) return psOut;
18685
+ } catch {
18686
+ }
18687
+ try {
18688
+ const wmicOut = (0, import_child_process8.execFileSync)("wmic", [
18689
+ "process",
18690
+ "where",
18691
+ pidFilter,
18692
+ "get",
18693
+ "CommandLine"
18694
+ ], { encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "ignore"] }).trim();
18695
+ if (wmicOut) return wmicOut;
18696
+ } catch {
18697
+ }
18698
+ return null;
18699
+ }
18700
+ function getProcessCommandLine(pid) {
18701
+ if (!Number.isFinite(pid) || pid <= 0) return null;
18702
+ if (process.platform === "win32") return getWindowsProcessCommandLine(pid);
18703
+ try {
18704
+ const text = (0, import_child_process8.execFileSync)("ps", ["-o", "command=", "-p", String(pid)], {
18705
+ encoding: "utf8",
18706
+ timeout: 3e3,
18707
+ stdio: ["ignore", "pipe", "ignore"]
18708
+ }).trim();
18709
+ return text || null;
18710
+ } catch {
18711
+ return null;
18712
+ }
18713
+ }
18714
+ function isManagedSessionHostPid(pid) {
18715
+ const commandLine = getProcessCommandLine(pid);
18716
+ return !!commandLine && /session-host-daemon/i.test(commandLine);
18717
+ }
18567
18718
  async function waitForPidExit(pid, timeoutMs) {
18568
18719
  const start = Date.now();
18569
18720
  while (Date.now() - start < timeoutMs) {
@@ -18580,7 +18731,7 @@ function stopSessionHostProcesses(appName) {
18580
18731
  try {
18581
18732
  if (fs8.existsSync(pidFile)) {
18582
18733
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
18583
- if (Number.isFinite(pid)) {
18734
+ if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
18584
18735
  killPid(pid);
18585
18736
  }
18586
18737
  }
@@ -18591,18 +18742,6 @@ function stopSessionHostProcesses(appName) {
18591
18742
  } catch {
18592
18743
  }
18593
18744
  }
18594
- if (process.platform !== "win32") {
18595
- try {
18596
- const raw = (0, import_child_process8.execFileSync)("pgrep", ["-f", "session-host-daemon"], { encoding: "utf8" }).trim();
18597
- for (const line of raw.split("\n")) {
18598
- const pid = Number.parseInt(line.trim(), 10);
18599
- if (Number.isFinite(pid)) {
18600
- killPid(pid);
18601
- }
18602
- }
18603
- } catch {
18604
- }
18605
- }
18606
18745
  }
18607
18746
  function removeDaemonPidFile() {
18608
18747
  const pidFile = path16.join(os17.homedir(), ".adhdev", "daemon.pid");
@@ -20837,6 +20976,23 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
20837
20976
 
20838
20977
  // src/providers/provider-instance-manager.ts
20839
20978
  init_logger();
20979
+ function projectHotChatSessionStatesFromProviderState(state) {
20980
+ const project = (item) => ({
20981
+ id: item.instanceId,
20982
+ status: item.activeChat?.status || item.status,
20983
+ unread: item.unread,
20984
+ inboxBucket: item.inboxBucket,
20985
+ lastMessageAt: item.lastMessageAt ?? item.activeChat?.lastMessageAt,
20986
+ runtimeLifecycle: item.runtime?.lifecycle ?? null,
20987
+ runtimeSurfaceKind: item.runtime?.surfaceKind,
20988
+ runtimeRestoredFromStorage: item.runtime?.restoredFromStorage === true,
20989
+ runtimeRecoveryState: item.runtime?.recoveryState ?? null
20990
+ });
20991
+ if (state.category === "ide") {
20992
+ return [project(state), ...state.extensions.map(project)];
20993
+ }
20994
+ return [project(state)];
20995
+ }
20840
20996
  var ProviderInstanceManager = class {
20841
20997
  instances = /* @__PURE__ */ new Map();
20842
20998
  tickTimer = null;
@@ -20932,6 +21088,27 @@ var ProviderInstanceManager = class {
20932
21088
  }
20933
21089
  return states;
20934
21090
  }
21091
+ collectHotChatSessionStates() {
21092
+ const sessions = [];
21093
+ for (const [id, instance] of this.instances) {
21094
+ try {
21095
+ const projected = instance.getHotChatSessionState?.();
21096
+ if (Array.isArray(projected)) {
21097
+ sessions.push(...projected.filter((session) => !!session?.id));
21098
+ continue;
21099
+ }
21100
+ if (projected?.id) {
21101
+ sessions.push(projected);
21102
+ continue;
21103
+ }
21104
+ const state = instance.getState();
21105
+ sessions.push(...projectHotChatSessionStatesFromProviderState(state));
21106
+ } catch (e) {
21107
+ LOG.warn("InstanceMgr", `[InstanceManager] Failed to collect hot chat metadata from ${id}: ${e.message}`);
21108
+ }
21109
+ }
21110
+ return sessions;
21111
+ }
20935
21112
  /**
20936
21113
  * Per-category status collect
20937
21114
  */
@@ -21013,6 +21190,17 @@ var ProviderInstanceManager = class {
21013
21190
  }
21014
21191
  return updated;
21015
21192
  }
21193
+ refreshProviderDefinitions(resolveProvider) {
21194
+ let refreshed = 0;
21195
+ for (const instance of this.instances.values()) {
21196
+ if (typeof instance.refreshProviderDefinition !== "function") continue;
21197
+ const provider = resolveProvider(instance.type);
21198
+ if (!provider || typeof provider !== "object") continue;
21199
+ instance.refreshProviderDefinition(provider);
21200
+ refreshed += 1;
21201
+ }
21202
+ return refreshed;
21203
+ }
21016
21204
  // ─── cleanup ──────────────────────────────────────
21017
21205
  /**
21018
21206
  * All terminate
@@ -25257,20 +25445,7 @@ var DevServer = class _DevServer {
25257
25445
  async handleReload(_req, res) {
25258
25446
  try {
25259
25447
  this.providerLoader.reload();
25260
- let refreshedInstances = 0;
25261
- if (this.instanceManager) {
25262
- for (const id of this.instanceManager.listInstanceIds()) {
25263
- const instance = this.instanceManager.getInstance(id);
25264
- const providerType = typeof instance?.type === "string" ? instance.type : "";
25265
- if (!providerType) continue;
25266
- const resolved = this.providerLoader.resolve(providerType);
25267
- if (!resolved) continue;
25268
- if (instance && typeof instance === "object" && "provider" in instance) {
25269
- instance.provider = resolved;
25270
- refreshedInstances += 1;
25271
- }
25272
- }
25273
- }
25448
+ const refreshedInstances = this.instanceManager ? this.instanceManager.refreshProviderDefinitions((providerType) => this.providerLoader.resolve(providerType)) : 0;
25274
25449
  const providers = this.providerLoader.getAll().map((p) => ({
25275
25450
  type: p.type,
25276
25451
  name: p.name,