@adhdev/daemon-core 0.9.82-rc.353 → 0.9.82-rc.354

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.
@@ -102,6 +102,21 @@ export declare class DaemonCommandHandler implements CommandHelpers {
102
102
  private logCommandStart;
103
103
  private logCommandEnd;
104
104
  setAgentStreamManager(manager: DaemonAgentStreamManager): void;
105
+ /**
106
+ * When a command in the manual-attendance set arrives for a session this
107
+ * daemon hosts, stamp the live instance so auto-approve holds while the user
108
+ * drives the session by hand. Provider-common: the signal is the command
109
+ * (foreground select_session / open_panel, controlbar invoke_provider_script
110
+ * / set_mode / change_model / set_thought_level, manual resolve_action,
111
+ * pty_input), never any CLI-specific modal text — so it works identically for
112
+ * every CLI/ACP provider. send_chat is deliberately excluded because a
113
+ * coordinator delegating a task to a worker also uses send_chat; counting it
114
+ * would wrongly suppress the worker's delegated auto-approve. For a remote
115
+ * mesh worker session the controlbar commands are forwarded to the owning
116
+ * worker daemon, which runs this same hook there, so attendance is recorded
117
+ * on the daemon that actually hosts the instance.
118
+ */
119
+ private noteManualAttendanceIfApplicable;
105
120
  handle(cmd: string, args: any): Promise<CommandResult>;
106
121
  private dispatch;
107
122
  /**
package/dist/index.js CHANGED
@@ -316,10 +316,10 @@ function readInjected(value) {
316
316
  }
317
317
  function getDaemonBuildInfo() {
318
318
  if (cached) return cached;
319
- const commit = readInjected(true ? "a680b74d9b940b82f691ebb85a725a1a72445bfc" : void 0) ?? "unknown";
320
- const commitShort = readInjected(true ? "a680b74d" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
- const version = readInjected(true ? "0.9.82-rc.353" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
- const builtAt = readInjected(true ? "2026-06-22T09:11:13.499Z" : void 0);
319
+ const commit = readInjected(true ? "5d100a177f412ae29a58048f0a211c3928b06910" : void 0) ?? "unknown";
320
+ const commitShort = readInjected(true ? "5d100a17" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
321
+ const version = readInjected(true ? "0.9.82-rc.354" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
322
+ const builtAt = readInjected(true ? "2026-06-22T11:32:05.389Z" : void 0);
323
323
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
324
324
  return cached;
325
325
  }
@@ -1070,6 +1070,9 @@ __export(git_diff_exports, {
1070
1070
  getGitDiffSummary: () => getGitDiffSummary,
1071
1071
  getGitFileDiff: () => getGitFileDiff
1072
1072
  });
1073
+ function withCollectionTimeout(options) {
1074
+ return options.timeoutMs === void 0 ? { ...options, timeoutMs: GIT_STATUS_TIMEOUT_MS } : options;
1075
+ }
1073
1076
  function validateBaseRef(ref) {
1074
1077
  const trimmed = ref.trim();
1075
1078
  if (!trimmed || trimmed.startsWith("-") || trimmed.includes("..") || !/^[A-Za-z0-9][A-Za-z0-9._/@-]*$/.test(trimmed)) {
@@ -1079,14 +1082,15 @@ function validateBaseRef(ref) {
1079
1082
  }
1080
1083
  async function getGitDiffSummary(workspace, options = {}) {
1081
1084
  const lastCheckedAt = Date.now();
1085
+ const effectiveOptions = withCollectionTimeout(options);
1082
1086
  try {
1083
- const repo = await resolveGitRepository(workspace, options);
1087
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
1084
1088
  const repoRoot = repo.repoRoot;
1085
1089
  if (options.baseRef) {
1086
1090
  const range = `${validateBaseRef(options.baseRef)}...HEAD`;
1087
1091
  const [nameStatus, numstat] = await Promise.all([
1088
- runGit(repo, ["diff", "--no-ext-diff", "--name-status", range, "--"], { ...options, cwd: repoRoot }),
1089
- runGit(repo, ["diff", "--no-ext-diff", "--numstat", range, "--"], { ...options, cwd: repoRoot })
1092
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status", range, "--"], { ...effectiveOptions, cwd: repoRoot }),
1093
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat", range, "--"], { ...effectiveOptions, cwd: repoRoot })
1090
1094
  ]);
1091
1095
  const outputBytes2 = byteLength(nameStatus.stdout + numstat.stdout);
1092
1096
  const changes2 = combineDiffEntries(nameStatus.stdout, numstat.stdout, false);
@@ -1105,11 +1109,11 @@ async function getGitDiffSummary(workspace, options = {}) {
1105
1109
  };
1106
1110
  }
1107
1111
  const [unstagedNameStatus, unstagedNumstat, stagedNameStatus, stagedNumstat, untracked] = await Promise.all([
1108
- runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
1109
- runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
1110
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
1111
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
1112
- runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...options, cwd: repoRoot })
1112
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...effectiveOptions, cwd: repoRoot }),
1113
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...effectiveOptions, cwd: repoRoot }),
1114
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...effectiveOptions, cwd: repoRoot }),
1115
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...effectiveOptions, cwd: repoRoot }),
1116
+ runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...effectiveOptions, cwd: repoRoot })
1113
1117
  ]);
1114
1118
  const outputBytes = byteLength(
1115
1119
  unstagedNameStatus.stdout + unstagedNumstat.stdout + stagedNameStatus.stdout + stagedNumstat.stdout + untracked.stdout
@@ -1151,13 +1155,14 @@ async function getGitDiffSummary(workspace, options = {}) {
1151
1155
  }
1152
1156
  async function getGitFileDiff(workspace, filePath, options = {}) {
1153
1157
  const lastCheckedAt = Date.now();
1154
- const repo = await resolveGitRepository(workspace, options);
1158
+ const effectiveOptions = withCollectionTimeout(options);
1159
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
1155
1160
  const repoRoot = repo.repoRoot;
1156
1161
  const selected = await resolveRepoFilePath(repoRoot, filePath);
1157
1162
  const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
1158
1163
  if (options.baseRef) {
1159
1164
  const range = `${validateBaseRef(options.baseRef)}...HEAD`;
1160
- const result = await runGit(repo, ["diff", "--no-ext-diff", range, "--", selected.relativePath], { ...options, cwd: repoRoot });
1165
+ const result = await runGit(repo, ["diff", "--no-ext-diff", range, "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot });
1161
1166
  const bounded2 = truncateText(result.stdout, maxBytes);
1162
1167
  return {
1163
1168
  workspace: repo.workspace,
@@ -1170,13 +1175,13 @@ async function getGitFileDiff(workspace, filePath, options = {}) {
1170
1175
  };
1171
1176
  }
1172
1177
  const [unstaged, staged] = await Promise.all([
1173
- runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot }),
1174
- runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot })
1178
+ runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot }),
1179
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...effectiveOptions, cwd: repoRoot })
1175
1180
  ]);
1176
1181
  let diff = [unstaged.stdout, staged.stdout].filter((part) => part.length > 0).join("\n");
1177
1182
  if (!diff) {
1178
1183
  const untracked = await runGit(repo, ["ls-files", "--others", "--exclude-standard", "--", selected.relativePath], {
1179
- ...options,
1184
+ ...effectiveOptions,
1180
1185
  cwd: repoRoot
1181
1186
  });
1182
1187
  const untrackedFiles = untracked.stdout.split("\n").filter(Boolean);
@@ -27440,6 +27445,42 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
27440
27445
  return fn() || null;
27441
27446
  }
27442
27447
 
27448
+ // src/providers/manual-attendance.ts
27449
+ var AUTO_APPROVE_MANUAL_ATTENDANCE_SUPPRESS_MS = 6e4;
27450
+ var ManualAttendanceTracker = class {
27451
+ constructor(suppressMs = AUTO_APPROVE_MANUAL_ATTENDANCE_SUPPRESS_MS) {
27452
+ this.suppressMs = suppressMs;
27453
+ }
27454
+ lastInteractionAt = 0;
27455
+ /** Record that a human just drove this session by hand. */
27456
+ note(now = Date.now()) {
27457
+ this.lastInteractionAt = now;
27458
+ }
27459
+ /** True while a manual interaction is recent enough to suppress auto-approve. */
27460
+ isAttended(now = Date.now()) {
27461
+ return this.lastInteractionAt > 0 && now - this.lastInteractionAt < this.suppressMs;
27462
+ }
27463
+ /**
27464
+ * Milliseconds remaining in the current suppression window, or 0 when not
27465
+ * attended. Used to re-arm a re-check timer so auto-approve fires the moment
27466
+ * the window lapses even if the PTY/agent has since gone silent.
27467
+ */
27468
+ remainingMs(now = Date.now()) {
27469
+ if (this.lastInteractionAt <= 0) return 0;
27470
+ return Math.max(0, this.suppressMs - (now - this.lastInteractionAt));
27471
+ }
27472
+ };
27473
+ var MANUAL_ATTENDANCE_COMMANDS = /* @__PURE__ */ new Set([
27474
+ "select_session",
27475
+ "open_panel",
27476
+ "invoke_provider_script",
27477
+ "set_mode",
27478
+ "change_model",
27479
+ "set_thought_level",
27480
+ "resolve_action",
27481
+ "pty_input"
27482
+ ]);
27483
+
27443
27484
  // src/commands/chat-commands.ts
27444
27485
  var fs7 = __toESM(require("fs"));
27445
27486
  var os10 = __toESM(require("os"));
@@ -31779,11 +31820,38 @@ var DaemonCommandHandler = class {
31779
31820
  setAgentStreamManager(manager) {
31780
31821
  this._agentStream = manager;
31781
31822
  }
31823
+ /**
31824
+ * When a command in the manual-attendance set arrives for a session this
31825
+ * daemon hosts, stamp the live instance so auto-approve holds while the user
31826
+ * drives the session by hand. Provider-common: the signal is the command
31827
+ * (foreground select_session / open_panel, controlbar invoke_provider_script
31828
+ * / set_mode / change_model / set_thought_level, manual resolve_action,
31829
+ * pty_input), never any CLI-specific modal text — so it works identically for
31830
+ * every CLI/ACP provider. send_chat is deliberately excluded because a
31831
+ * coordinator delegating a task to a worker also uses send_chat; counting it
31832
+ * would wrongly suppress the worker's delegated auto-approve. For a remote
31833
+ * mesh worker session the controlbar commands are forwarded to the owning
31834
+ * worker daemon, which runs this same hook there, so attendance is recorded
31835
+ * on the daemon that actually hosts the instance.
31836
+ */
31837
+ noteManualAttendanceIfApplicable(cmd, args) {
31838
+ if (!MANUAL_ATTENDANCE_COMMANDS.has(cmd)) return;
31839
+ const sessionId = this._currentRoute.session?.sessionId || (typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "");
31840
+ if (!sessionId) return;
31841
+ const session = this._ctx.sessionRegistry?.get(sessionId);
31842
+ const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
31843
+ const instance = this._ctx.instanceManager?.getInstance(instanceKey);
31844
+ try {
31845
+ instance?.noteManualInteraction?.();
31846
+ } catch {
31847
+ }
31848
+ }
31782
31849
  // ─── Command Dispatcher ──────────────────────────
31783
31850
  async handle(cmd, args) {
31784
31851
  this._currentRoute = this.resolveRoute(args);
31785
31852
  const startedAt = Date.now();
31786
31853
  this.logCommandStart(cmd, args);
31854
+ this.noteManualAttendanceIfApplicable(cmd, args);
31787
31855
  let result;
31788
31856
  if (isGitCommandName(cmd)) {
31789
31857
  result = await handleGitCommand(cmd, args, this._ctx.gitCommandServices);
@@ -35737,6 +35805,11 @@ var CliProviderInstance = class _CliProviderInstance {
35737
35805
  // a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
35738
35806
  // brief generating flip does not immediately wipe the settle clock.
35739
35807
  autoApproveInactiveSince = 0;
35808
+ // Provider-common manual-attendance signal: while a human is actively driving
35809
+ // this session from the dashboard, auto-approve holds so they can take manual
35810
+ // control. Background mesh workers are never attended → delegated auto-approve
35811
+ // is unaffected.
35812
+ manualAttendance = new ManualAttendanceTracker();
35740
35813
  controlValues = {};
35741
35814
  summaryMetadata = void 0;
35742
35815
  appliedEffectKeys = /* @__PURE__ */ new Set();
@@ -36012,7 +36085,7 @@ var CliProviderInstance = class _CliProviderInstance {
36012
36085
  }
36013
36086
  getHotChatSessionState() {
36014
36087
  const adapterStatus = this.adapter.getStatus({ allowParse: false });
36015
- const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
36088
+ const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status);
36016
36089
  const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === "idle";
36017
36090
  const visibleStatus = autoApproveActive || autoApproveHoldIdle ? "generating" : adapterStatus.status;
36018
36091
  const runtime = this.adapter.getRuntimeMetadata();
@@ -36027,7 +36100,7 @@ var CliProviderInstance = class _CliProviderInstance {
36027
36100
  }
36028
36101
  getSessionModalState(sessionId) {
36029
36102
  const adapterStatus = this.adapter.getStatus({ allowParse: true });
36030
- const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
36103
+ const autoApproveActive = this.autoApproveEffectivelyActive(adapterStatus.status);
36031
36104
  const autoApproveHoldIdle = this.autoApproveBusy && adapterStatus.status === "idle";
36032
36105
  const visibleStatus = autoApproveActive || autoApproveHoldIdle ? "generating" : adapterStatus.status;
36033
36106
  const dirName = workingDirBasename(this.workingDir);
@@ -36108,7 +36181,7 @@ var CliProviderInstance = class _CliProviderInstance {
36108
36181
  } catch {
36109
36182
  return null;
36110
36183
  }
36111
- if (adapterStatus.status === "waiting_approval" && !this.shouldAutoApprove()) {
36184
+ if (adapterStatus.status === "waiting_approval" && !this.autoApproveEffectivelyActive(adapterStatus.status)) {
36112
36185
  return "waiting_approval";
36113
36186
  }
36114
36187
  return null;
@@ -36554,6 +36627,18 @@ var CliProviderInstance = class _CliProviderInstance {
36554
36627
  this.lastApprovalEventFingerprint = "";
36555
36628
  }
36556
36629
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
36630
+ if (adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove() && this.manualAttendance.isAttended(now)) {
36631
+ this.lastAutoApprovalSignature = "";
36632
+ this.pendingAutoApprovalSignature = "";
36633
+ this.pendingAutoApprovalSince = 0;
36634
+ this.autoApproveInactiveSince = 0;
36635
+ if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
36636
+ this.autoApproveSettleTimer = setTimeout(() => {
36637
+ this.autoApproveSettleTimer = null;
36638
+ this.recheckAutoApproveSettled();
36639
+ }, this.manualAttendance.remainingMs(now) + 20);
36640
+ return false;
36641
+ }
36557
36642
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
36558
36643
  if (!autoApproveActive) {
36559
36644
  this.lastAutoApprovalSignature = "";
@@ -37019,6 +37104,21 @@ ${effect.notification.body || ""}`.trim();
37019
37104
  }
37020
37105
  return false;
37021
37106
  }
37107
+ /** @see ProviderInstance.noteManualInteraction */
37108
+ noteManualInteraction(now = Date.now()) {
37109
+ this.manualAttendance.note(now);
37110
+ }
37111
+ /**
37112
+ * Whether auto-approve should be treated as active *right now* for display
37113
+ * and firing decisions: the configured intent AND the user is not currently
37114
+ * attending this session by hand. When a human is attending, auto-approve is
37115
+ * held so the modal stays visible and they can drive it via the controlbar.
37116
+ * Provider-agnostic — the attendance signal is the command set, never any
37117
+ * CLI-specific modal text.
37118
+ */
37119
+ autoApproveEffectivelyActive(status, now = Date.now()) {
37120
+ return status === "waiting_approval" && this.shouldAutoApprove() && !this.manualAttendance.isAttended(now);
37121
+ }
37022
37122
  recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
37023
37123
  this.appendRuntimeSystemMessage(
37024
37124
  formatAutoApprovalMessage(modalMessage, buttonLabel),
@@ -37964,7 +38064,7 @@ var AcpProviderInstance = class {
37964
38064
  input: tc.rawInput ? typeof tc.rawInput === "string" ? tc.rawInput : JSON.stringify(tc.rawInput) : void 0
37965
38065
  });
37966
38066
  }
37967
- if (this.settings.autoApprove !== false) {
38067
+ if (this.settings.autoApprove !== false && !this.manualAttendance.isAttended()) {
37968
38068
  const toolTitle = tc.title || tc.toolCallId || "tool call";
37969
38069
  this.log.info(`[${this.type}] Auto-approving: ${toolTitle}`);
37970
38070
  this.appendSystemMessage(`Auto-approved: ${toolTitle}`);
@@ -38195,6 +38295,15 @@ var AcpProviderInstance = class {
38195
38295
  this.detectStatusTransition();
38196
38296
  }
38197
38297
  permissionResolvers = [];
38298
+ // Provider-common manual-attendance signal: while a human is actively driving
38299
+ // this session from the dashboard, auto-approve holds so they can decide on
38300
+ // the permission request themselves. Background workers are never attended →
38301
+ // delegated auto-approve is unaffected.
38302
+ manualAttendance = new ManualAttendanceTracker();
38303
+ /** @see ProviderInstance.noteManualInteraction */
38304
+ noteManualInteraction(now = Date.now()) {
38305
+ this.manualAttendance.note(now);
38306
+ }
38198
38307
  async resolvePermission(approved) {
38199
38308
  const resolver = this.permissionResolvers.shift();
38200
38309
  if (resolver) {
@@ -39352,6 +39461,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
39352
39461
  );
39353
39462
  continue;
39354
39463
  }
39464
+ const restoredSettings = { ...this.providerLoader.getSettings(normalizedType) };
39465
+ const coordinatorEntry = getCoordinatorForSession(record.runtimeId);
39466
+ if (coordinatorEntry?.meshId) {
39467
+ restoredSettings.meshCoordinatorFor = coordinatorEntry.meshId;
39468
+ }
39355
39469
  try {
39356
39470
  await this.registerCliInstance(
39357
39471
  record.runtimeId,
@@ -39360,7 +39474,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39360
39474
  record.workspace,
39361
39475
  record.cliArgs,
39362
39476
  resolvedProvider,
39363
- {},
39477
+ restoredSettings,
39364
39478
  true,
39365
39479
  {
39366
39480
  providerSessionId: sessionBinding.providerSessionId,