@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.
package/dist/index.mjs CHANGED
@@ -1688,13 +1688,14 @@ function sliceFromOffset(text, start) {
1688
1688
  function hydrateCliParsedMessages(parsedMessages, options) {
1689
1689
  const { committedMessages, scope, lastOutputAt } = options;
1690
1690
  const referenceMessages = [...committedMessages];
1691
+ const referenceComparables = referenceMessages.map((message) => normalizeComparableMessageContent(message?.content || ""));
1691
1692
  const usedReferenceIndexes = /* @__PURE__ */ new Set();
1692
1693
  const now = options.now ?? Date.now();
1693
1694
  const findReferenceTimestamp = (role, content, parsedIndex) => {
1694
1695
  const normalizedContent = normalizeComparableMessageContent(content);
1695
1696
  if (!normalizedContent) return void 0;
1696
1697
  const sameIndex = referenceMessages[parsedIndex];
1697
- if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && normalizeComparableMessageContent(sameIndex.content) === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
1698
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && referenceComparables[parsedIndex] === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
1698
1699
  usedReferenceIndexes.add(parsedIndex);
1699
1700
  return sameIndex.timestamp;
1700
1701
  }
@@ -1702,7 +1703,7 @@ function hydrateCliParsedMessages(parsedMessages, options) {
1702
1703
  if (usedReferenceIndexes.has(i)) continue;
1703
1704
  const candidate = referenceMessages[i];
1704
1705
  if (!candidate || candidate.role !== role) continue;
1705
- const candidateContent = normalizeComparableMessageContent(candidate.content);
1706
+ const candidateContent = referenceComparables[i];
1706
1707
  if (!candidateContent) continue;
1707
1708
  const exactMatch = candidateContent === normalizedContent;
1708
1709
  const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
@@ -2083,6 +2084,8 @@ var init_provider_cli_adapter = __esm({
2083
2084
  messages = [];
2084
2085
  committedMessages = [];
2085
2086
  structuredMessages = [];
2087
+ committedMessagesActivitySignature = "";
2088
+ committedMessagesChangedAt = 0;
2086
2089
  currentStatus = "starting";
2087
2090
  onStatusChange = null;
2088
2091
  responseBuffer = "";
@@ -2164,10 +2167,35 @@ var init_provider_cli_adapter = __esm({
2164
2167
  providerResolutionMeta;
2165
2168
  static FINISH_RETRY_DELAY_MS = 300;
2166
2169
  static MAX_FINISH_RETRIES = 2;
2170
+ buildCommittedMessagesActivitySignature() {
2171
+ const last = this.committedMessages[this.committedMessages.length - 1];
2172
+ return [
2173
+ String(this.committedMessages.length),
2174
+ String(last?.role || ""),
2175
+ String(last?.kind || ""),
2176
+ String(last?.senderName || ""),
2177
+ String(last?.timestamp || ""),
2178
+ String(last?.receivedAt || ""),
2179
+ normalizeComparableMessageContent(last?.content || "").slice(-240)
2180
+ ].join("|");
2181
+ }
2167
2182
  syncMessageViews() {
2183
+ const signature = this.buildCommittedMessagesActivitySignature();
2184
+ if (signature !== this.committedMessagesActivitySignature) {
2185
+ this.committedMessagesActivitySignature = signature;
2186
+ this.committedMessagesChangedAt = Date.now();
2187
+ }
2168
2188
  this.messages = [...this.committedMessages];
2169
2189
  this.structuredMessages = [...this.committedMessages];
2170
2190
  }
2191
+ getLastCommittedMessageActivityAt() {
2192
+ const last = this.committedMessages[this.committedMessages.length - 1];
2193
+ const messageTime = Math.max(
2194
+ typeof last?.receivedAt === "number" && Number.isFinite(last.receivedAt) ? last.receivedAt : 0,
2195
+ typeof last?.timestamp === "number" && Number.isFinite(last.timestamp) ? last.timestamp : 0
2196
+ );
2197
+ return Math.max(messageTime, this.committedMessagesChangedAt || 0);
2198
+ }
2171
2199
  readTerminalScreenText(now = Date.now()) {
2172
2200
  const screenText = this.terminalScreen.getText() || "";
2173
2201
  this.lastScreenText = screenText;
@@ -2307,9 +2335,16 @@ var init_provider_cli_adapter = __esm({
2307
2335
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
2308
2336
  setCliScripts(scripts) {
2309
2337
  this.cliScripts = scripts;
2338
+ this.parsedStatusCache = null;
2339
+ this.parseErrorMessage = null;
2310
2340
  const scriptNames = listCliScriptNames(scripts);
2311
2341
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
2312
2342
  }
2343
+ /** Refresh provider scripts/config used by this adapter without restarting the PTY runtime. */
2344
+ refreshProviderDefinition(provider) {
2345
+ this.provider = provider;
2346
+ this.setCliScripts(provider.scripts || {});
2347
+ }
2313
2348
  updateRuntimeSettings(settings) {
2314
2349
  this.runtimeSettings = { ...settings };
2315
2350
  }
@@ -2793,7 +2828,7 @@ var init_provider_cli_adapter = __esm({
2793
2828
  return;
2794
2829
  }
2795
2830
  if (this.currentTurnScope && !lastParsedAssistant) {
2796
- LOG.info(
2831
+ LOG.debug(
2797
2832
  "CLI",
2798
2833
  `[${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 || "-"}`
2799
2834
  );
@@ -3609,9 +3644,43 @@ var init_provider_cli_adapter = __esm({
3609
3644
  }
3610
3645
  armResponseTimeout() {
3611
3646
  if (this.responseTimeout) clearTimeout(this.responseTimeout);
3647
+ const timeoutMs = this.timeouts.maxResponse;
3648
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3649
+ this.responseTimeout = null;
3650
+ return;
3651
+ }
3612
3652
  this.responseTimeout = setTimeout(() => {
3613
- if (this.isWaitingForResponse) this.finishResponse();
3614
- }, this.timeouts.maxResponse);
3653
+ this.responseTimeout = null;
3654
+ if (!this.isWaitingForResponse) return;
3655
+ const detectedStatusBeforeEval = this.runDetectStatus(this.recentOutputBuffer);
3656
+ this.recordTrace("response_timeout_check", {
3657
+ timeoutMs,
3658
+ detectedStatus: detectedStatusBeforeEval,
3659
+ currentStatus: this.currentStatus,
3660
+ isWaitingForResponse: this.isWaitingForResponse,
3661
+ hasActionableApproval: this.hasActionableApproval(),
3662
+ ...buildCliTraceParseSnapshot({
3663
+ accumulatedBuffer: this.accumulatedBuffer,
3664
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3665
+ responseBuffer: this.responseBuffer,
3666
+ partialResponse: this.responseBuffer,
3667
+ scope: this.currentTurnScope
3668
+ })
3669
+ });
3670
+ this.settledBuffer = this.recentOutputBuffer;
3671
+ this.evaluateSettled();
3672
+ if (this.isWaitingForResponse && !this.hasActionableApproval()) {
3673
+ const detectedStatusAfterEval = this.runDetectStatus(this.recentOutputBuffer);
3674
+ this.recordTrace("response_timeout_kept_open", {
3675
+ timeoutMs,
3676
+ detectedStatusBeforeEval,
3677
+ detectedStatusAfterEval,
3678
+ currentStatus: this.currentStatus,
3679
+ isWaitingForResponse: this.isWaitingForResponse
3680
+ });
3681
+ this.armResponseTimeout();
3682
+ }
3683
+ }, timeoutMs);
3615
3684
  }
3616
3685
  writeSubmitKeyForRetry(mode) {
3617
3686
  void this.writeToPty(this.sendKey).catch((error) => {
@@ -12790,7 +12859,14 @@ var DaemonCommandHandler = class {
12790
12859
  await this._ctx.providerLoader.fetchLatest().catch(() => {
12791
12860
  });
12792
12861
  this._ctx.providerLoader.reload();
12793
- return { success: true };
12862
+ this._ctx.providerLoader.registerToDetector();
12863
+ const refreshedInstances = this._ctx.instanceManager ? this._ctx.instanceManager.refreshProviderDefinitions((providerType) => this._ctx.providerLoader.resolve(providerType)) : 0;
12864
+ const providers = this._ctx.providerLoader.getAll().map((provider) => ({
12865
+ type: provider.type,
12866
+ name: provider.name,
12867
+ category: provider.category
12868
+ }));
12869
+ return { success: true, refreshedInstances, providers };
12794
12870
  }
12795
12871
  return { success: false, error: "ProviderLoader not initialized" };
12796
12872
  }
@@ -13045,6 +13121,11 @@ var CliProviderInstance = class {
13045
13121
  launchMode;
13046
13122
  startedAt = Date.now();
13047
13123
  onProviderSessionResolved;
13124
+ refreshProviderDefinition(provider) {
13125
+ if (provider.type !== this.type || provider.category !== "cli") return;
13126
+ this.provider = provider;
13127
+ this.adapter.refreshProviderDefinition(provider);
13128
+ }
13048
13129
  // ─── Lifecycle ─────────────────────────────────
13049
13130
  async init(context) {
13050
13131
  this.context = context;
@@ -13250,6 +13331,22 @@ var CliProviderInstance = class {
13250
13331
  getPresentationMode() {
13251
13332
  return this.presentationMode;
13252
13333
  }
13334
+ getHotChatSessionState() {
13335
+ const adapterStatus = this.adapter.getStatus();
13336
+ const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
13337
+ const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
13338
+ const runtime = this.adapter.getRuntimeMetadata();
13339
+ const lastCommittedMessageActivityAt = typeof this.adapter.getLastCommittedMessageActivityAt === "function" ? this.adapter.getLastCommittedMessageActivityAt() : 0;
13340
+ return {
13341
+ id: this.instanceId,
13342
+ status: visibleStatus,
13343
+ lastMessageAt: lastCommittedMessageActivityAt || void 0,
13344
+ runtimeLifecycle: runtime?.lifecycle ?? null,
13345
+ runtimeSurfaceKind: runtime?.surfaceKind,
13346
+ runtimeRestoredFromStorage: runtime?.restoredFromStorage === true,
13347
+ runtimeRecoveryState: runtime?.recoveryState ?? null
13348
+ };
13349
+ }
13253
13350
  updateSettings(newSettings) {
13254
13351
  this.settings = { ...newSettings };
13255
13352
  this.adapter.updateRuntimeSettings?.(this.settings);
@@ -13405,6 +13502,15 @@ var CliProviderInstance = class {
13405
13502
  this.completedDebouncePending = { chatTitle, duration, timestamp: now };
13406
13503
  this.completedDebounceTimer = setTimeout(() => {
13407
13504
  if (this.completedDebouncePending) {
13505
+ const latestStatus = this.adapter.getStatus();
13506
+ const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
13507
+ const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
13508
+ if (latestVisibleStatus !== "idle") {
13509
+ LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
13510
+ this.completedDebouncePending = null;
13511
+ this.completedDebounceTimer = null;
13512
+ return;
13513
+ }
13408
13514
  LOG.info("CLI", `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
13409
13515
  this.pushEvent({ event: "agent:generating_completed", ...this.completedDebouncePending });
13410
13516
  this.completedDebouncePending = null;
@@ -18416,6 +18522,51 @@ function killPid(pid) {
18416
18522
  return false;
18417
18523
  }
18418
18524
  }
18525
+ function getWindowsProcessCommandLine(pid) {
18526
+ const pidFilter = `ProcessId=${pid}`;
18527
+ try {
18528
+ const psOut = execFileSync2("powershell.exe", [
18529
+ "-NoProfile",
18530
+ "-NonInteractive",
18531
+ "-ExecutionPolicy",
18532
+ "Bypass",
18533
+ "-Command",
18534
+ `(Get-CimInstance Win32_Process -Filter "${pidFilter}").CommandLine`
18535
+ ], { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).trim();
18536
+ if (psOut) return psOut;
18537
+ } catch {
18538
+ }
18539
+ try {
18540
+ const wmicOut = execFileSync2("wmic", [
18541
+ "process",
18542
+ "where",
18543
+ pidFilter,
18544
+ "get",
18545
+ "CommandLine"
18546
+ ], { encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "ignore"] }).trim();
18547
+ if (wmicOut) return wmicOut;
18548
+ } catch {
18549
+ }
18550
+ return null;
18551
+ }
18552
+ function getProcessCommandLine(pid) {
18553
+ if (!Number.isFinite(pid) || pid <= 0) return null;
18554
+ if (process.platform === "win32") return getWindowsProcessCommandLine(pid);
18555
+ try {
18556
+ const text = execFileSync2("ps", ["-o", "command=", "-p", String(pid)], {
18557
+ encoding: "utf8",
18558
+ timeout: 3e3,
18559
+ stdio: ["ignore", "pipe", "ignore"]
18560
+ }).trim();
18561
+ return text || null;
18562
+ } catch {
18563
+ return null;
18564
+ }
18565
+ }
18566
+ function isManagedSessionHostPid(pid) {
18567
+ const commandLine = getProcessCommandLine(pid);
18568
+ return !!commandLine && /session-host-daemon/i.test(commandLine);
18569
+ }
18419
18570
  async function waitForPidExit(pid, timeoutMs) {
18420
18571
  const start = Date.now();
18421
18572
  while (Date.now() - start < timeoutMs) {
@@ -18432,7 +18583,7 @@ function stopSessionHostProcesses(appName) {
18432
18583
  try {
18433
18584
  if (fs8.existsSync(pidFile)) {
18434
18585
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
18435
- if (Number.isFinite(pid)) {
18586
+ if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
18436
18587
  killPid(pid);
18437
18588
  }
18438
18589
  }
@@ -18443,18 +18594,6 @@ function stopSessionHostProcesses(appName) {
18443
18594
  } catch {
18444
18595
  }
18445
18596
  }
18446
- if (process.platform !== "win32") {
18447
- try {
18448
- const raw = execFileSync2("pgrep", ["-f", "session-host-daemon"], { encoding: "utf8" }).trim();
18449
- for (const line of raw.split("\n")) {
18450
- const pid = Number.parseInt(line.trim(), 10);
18451
- if (Number.isFinite(pid)) {
18452
- killPid(pid);
18453
- }
18454
- }
18455
- } catch {
18456
- }
18457
- }
18458
18597
  }
18459
18598
  function removeDaemonPidFile() {
18460
18599
  const pidFile = path16.join(os17.homedir(), ".adhdev", "daemon.pid");
@@ -20689,6 +20828,23 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
20689
20828
 
20690
20829
  // src/providers/provider-instance-manager.ts
20691
20830
  init_logger();
20831
+ function projectHotChatSessionStatesFromProviderState(state) {
20832
+ const project = (item) => ({
20833
+ id: item.instanceId,
20834
+ status: item.activeChat?.status || item.status,
20835
+ unread: item.unread,
20836
+ inboxBucket: item.inboxBucket,
20837
+ lastMessageAt: item.lastMessageAt ?? item.activeChat?.lastMessageAt,
20838
+ runtimeLifecycle: item.runtime?.lifecycle ?? null,
20839
+ runtimeSurfaceKind: item.runtime?.surfaceKind,
20840
+ runtimeRestoredFromStorage: item.runtime?.restoredFromStorage === true,
20841
+ runtimeRecoveryState: item.runtime?.recoveryState ?? null
20842
+ });
20843
+ if (state.category === "ide") {
20844
+ return [project(state), ...state.extensions.map(project)];
20845
+ }
20846
+ return [project(state)];
20847
+ }
20692
20848
  var ProviderInstanceManager = class {
20693
20849
  instances = /* @__PURE__ */ new Map();
20694
20850
  tickTimer = null;
@@ -20784,6 +20940,27 @@ var ProviderInstanceManager = class {
20784
20940
  }
20785
20941
  return states;
20786
20942
  }
20943
+ collectHotChatSessionStates() {
20944
+ const sessions = [];
20945
+ for (const [id, instance] of this.instances) {
20946
+ try {
20947
+ const projected = instance.getHotChatSessionState?.();
20948
+ if (Array.isArray(projected)) {
20949
+ sessions.push(...projected.filter((session) => !!session?.id));
20950
+ continue;
20951
+ }
20952
+ if (projected?.id) {
20953
+ sessions.push(projected);
20954
+ continue;
20955
+ }
20956
+ const state = instance.getState();
20957
+ sessions.push(...projectHotChatSessionStatesFromProviderState(state));
20958
+ } catch (e) {
20959
+ LOG.warn("InstanceMgr", `[InstanceManager] Failed to collect hot chat metadata from ${id}: ${e.message}`);
20960
+ }
20961
+ }
20962
+ return sessions;
20963
+ }
20787
20964
  /**
20788
20965
  * Per-category status collect
20789
20966
  */
@@ -20865,6 +21042,17 @@ var ProviderInstanceManager = class {
20865
21042
  }
20866
21043
  return updated;
20867
21044
  }
21045
+ refreshProviderDefinitions(resolveProvider) {
21046
+ let refreshed = 0;
21047
+ for (const instance of this.instances.values()) {
21048
+ if (typeof instance.refreshProviderDefinition !== "function") continue;
21049
+ const provider = resolveProvider(instance.type);
21050
+ if (!provider || typeof provider !== "object") continue;
21051
+ instance.refreshProviderDefinition(provider);
21052
+ refreshed += 1;
21053
+ }
21054
+ return refreshed;
21055
+ }
20868
21056
  // ─── cleanup ──────────────────────────────────────
20869
21057
  /**
20870
21058
  * All terminate
@@ -25109,20 +25297,7 @@ var DevServer = class _DevServer {
25109
25297
  async handleReload(_req, res) {
25110
25298
  try {
25111
25299
  this.providerLoader.reload();
25112
- let refreshedInstances = 0;
25113
- if (this.instanceManager) {
25114
- for (const id of this.instanceManager.listInstanceIds()) {
25115
- const instance = this.instanceManager.getInstance(id);
25116
- const providerType = typeof instance?.type === "string" ? instance.type : "";
25117
- if (!providerType) continue;
25118
- const resolved = this.providerLoader.resolve(providerType);
25119
- if (!resolved) continue;
25120
- if (instance && typeof instance === "object" && "provider" in instance) {
25121
- instance.provider = resolved;
25122
- refreshedInstances += 1;
25123
- }
25124
- }
25125
- }
25300
+ const refreshedInstances = this.instanceManager ? this.instanceManager.refreshProviderDefinitions((providerType) => this.providerLoader.resolve(providerType)) : 0;
25126
25301
  const providers = this.providerLoader.getAll().map((p) => ({
25127
25302
  type: p.type,
25128
25303
  name: p.name,