@adhdev/daemon-standalone 0.9.82-rc.211 → 0.9.82-rc.213

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.js CHANGED
@@ -29749,6 +29749,7 @@ var require_dist3 = __commonJS({
29749
29749
  maxParallelTasks: 2,
29750
29750
  spawnedSessionVisibility: "visible",
29751
29751
  sessionCleanupOnNodeRemove: "preserve",
29752
+ autoFastForward: { enabled: true },
29752
29753
  maxTaskRetries: 1
29753
29754
  };
29754
29755
  }
@@ -31074,7 +31075,17 @@ ${error48.message || ""}`;
31074
31075
  return identity;
31075
31076
  }
31076
31077
  function mergeMeshPolicy(base, patch) {
31077
- const policy = { ...DEFAULT_MESH_POLICY, ...base || {}, ...patch || {} };
31078
+ const autoFastForward = normalizeAutoFastForwardPolicy({
31079
+ ...DEFAULT_MESH_POLICY.autoFastForward,
31080
+ ...base?.autoFastForward && typeof base.autoFastForward === "object" ? base.autoFastForward : {},
31081
+ ...patch?.autoFastForward && typeof patch.autoFastForward === "object" ? patch.autoFastForward : {}
31082
+ });
31083
+ const policy = {
31084
+ ...DEFAULT_MESH_POLICY,
31085
+ ...base || {},
31086
+ ...patch || {},
31087
+ autoFastForward
31088
+ };
31078
31089
  if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
31079
31090
  policy.dirtyWorkspaceBehavior = "warn";
31080
31091
  }
@@ -31089,6 +31100,15 @@ ${error48.message || ""}`;
31089
31100
  }
31090
31101
  return policy;
31091
31102
  }
31103
+ function normalizeAutoFastForwardPolicy(value) {
31104
+ const record2 = value && typeof value === "object" && !Array.isArray(value) ? value : {};
31105
+ const maxBehind = Number(record2.maxBehind);
31106
+ return {
31107
+ enabled: record2.enabled !== false,
31108
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
31109
+ requireCleanSubmodules: record2.requireCleanSubmodules !== false
31110
+ };
31111
+ }
31092
31112
  function listMeshes() {
31093
31113
  return loadMeshConfig().meshes;
31094
31114
  }
@@ -35316,12 +35336,14 @@ ${rendered}`, "utf-8");
35316
35336
  const completionDiagnostic = event.completionDiagnostic && typeof event.completionDiagnostic === "object" ? event.completionDiagnostic : null;
35317
35337
  const diagnosticReason = completionDiagnostic ? readNonEmptyString2(completionDiagnostic.blockReason) || "present" : "";
35318
35338
  const finalAssistantPresent = typeof completionDiagnostic?.finalAssistantPresent === "boolean" ? String(completionDiagnostic.finalAssistantPresent) : "";
35339
+ const evidenceLevel = readNonEmptyString2(event.evidenceLevel);
35319
35340
  const parts = [
35320
35341
  readNonEmptyString2(event.targetSessionId) ? `session_id=${readNonEmptyString2(event.targetSessionId)}` : "",
35321
35342
  readNonEmptyString2(event.providerType) ? `provider=${readNonEmptyString2(event.providerType)}` : "",
35322
35343
  readNonEmptyString2(event.providerSessionId) ? `provider_session_id=${readNonEmptyString2(event.providerSessionId)}` : "",
35323
35344
  diagnosticReason ? `completion_diagnostic=${diagnosticReason}` : "",
35324
- finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : ""
35345
+ finalAssistantPresent ? `final_assistant=${finalAssistantPresent}` : "",
35346
+ evidenceLevel && evidenceLevel !== "sufficient" ? `evidence_level=${evidenceLevel}` : ""
35325
35347
  ].filter(Boolean);
35326
35348
  return parts.length > 0 ? ` (${parts.join("; ")})` : "";
35327
35349
  }
@@ -35331,7 +35353,8 @@ ${rendered}`, "utf-8");
35331
35353
  if (args.metadataEvent.source === "long_generating_reconciliation") {
35332
35354
  return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The long-generating monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
35333
35355
  }
35334
- return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path; use mesh_read_chat once to review its final progress, but do not poll repeatedly.`;
35356
+ const reviewNote = args.metadataEvent.reviewRecommended === true ? " Completion evidence is insufficient \u2014 verify via git status or provider_session_id before assuming the task is done. Use mesh_read_chat once if needed, but do not poll repeatedly." : " Use mesh_read_chat once to review its final progress, but do not poll repeatedly.";
35357
+ return `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}. This completion came from the agent status event path;${reviewNote}`;
35335
35358
  }
35336
35359
  if (args.event === "agent:waiting_approval") {
35337
35360
  return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
@@ -36479,6 +36502,34 @@ Next step: ${nextStep}`;
36479
36502
  function isDirtyNode(node) {
36480
36503
  return node?.health === "dirty" || node?.git?.dirty === true;
36481
36504
  }
36505
+ function resolveAutoFastForwardPolicy(mesh) {
36506
+ const record2 = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
36507
+ const maxBehind = Number(record2.maxBehind);
36508
+ return {
36509
+ enabled: record2.enabled !== false,
36510
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
36511
+ requireCleanSubmodules: record2.requireCleanSubmodules !== false
36512
+ };
36513
+ }
36514
+ function sessionStateLooksActive(state) {
36515
+ const status = readNonEmptyString2(state?.status).toLowerCase();
36516
+ const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
36517
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
36518
+ return active.has(status) || active.has(chatStatus);
36519
+ }
36520
+ function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
36521
+ if (nodeHasActiveAssignment(meshId, nodeId)) return true;
36522
+ return components.instanceManager.getByCategory("cli").some((inst) => {
36523
+ const state = inst.getState();
36524
+ const settings = state.settings || {};
36525
+ if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
36526
+ const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
36527
+ if (instNodeId !== nodeId) return false;
36528
+ const sessionId = readNonEmptyString2(state.instanceId);
36529
+ if (currentSessionId && sessionId === currentSessionId && isIdleSessionState(state)) return false;
36530
+ return sessionStateLooksActive(state);
36531
+ });
36532
+ }
36482
36533
  function isLaunchableNode(node) {
36483
36534
  if (!node || node.status === "disabled" || node.status === "removed") return false;
36484
36535
  const health = readNonEmptyString2(node.health).toLowerCase();
@@ -36807,6 +36858,9 @@ Next step: ${nextStep}`;
36807
36858
  const workspace = readNonEmptyString2(node?.workspace);
36808
36859
  if (!workspace) return;
36809
36860
  if (!(0, import_fs10.existsSync)(workspace)) return;
36861
+ const policy = resolveAutoFastForwardPolicy(mesh);
36862
+ if (!policy.enabled) return;
36863
+ if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
36810
36864
  const throttleKey = `${args.meshId}:${args.nodeId}`;
36811
36865
  const now = Date.now();
36812
36866
  const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
@@ -36825,6 +36879,12 @@ Next step: ${nextStep}`;
36825
36879
  trigger: "idle_auto"
36826
36880
  });
36827
36881
  if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return;
36882
+ const behind = Number(dryRun.current?.behind);
36883
+ if (policy.maxBehind !== void 0 && Number.isFinite(behind) && behind > policy.maxBehind) return;
36884
+ if (policy.requireCleanSubmodules) {
36885
+ const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
36886
+ if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return;
36887
+ }
36828
36888
  await fastForwardMeshNode({
36829
36889
  meshId: args.meshId,
36830
36890
  nodeId: args.nodeId,
@@ -58126,6 +58186,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
58126
58186
  "busy_hold_ms": { "type": "integer", "minimum": 0 },
58127
58187
  "idle_hold_ms": { "type": "integer", "minimum": 0 },
58128
58188
  "startup_grace_ms": { "type": "integer", "minimum": 0 },
58189
+ "screen_active_hold_ms": { "type": "integer", "minimum": 0 },
58129
58190
  "completion_marker": {
58130
58191
  "type": "object",
58131
58192
  "additionalProperties": false,
@@ -58466,6 +58527,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
58466
58527
  ...t.busy_hold_ms !== void 0 ? { busy_hold_ms: t.busy_hold_ms } : {},
58467
58528
  ...t.idle_hold_ms !== void 0 ? { idle_hold_ms: t.idle_hold_ms } : {},
58468
58529
  ...t.startup_grace_ms !== void 0 ? { startup_grace_ms: t.startup_grace_ms } : {},
58530
+ ...t.screen_active_hold_ms !== void 0 ? { screen_active_hold_ms: t.screen_active_hold_ms } : {},
58469
58531
  ...cm ? {
58470
58532
  completion_idle_after: {
58471
58533
  ...cm.section ? { section: cm.section } : {},
@@ -58767,6 +58829,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
58767
58829
  completionIdleKey = "";
58768
58830
  /** Previous screen lines — passed to evaluate() for `changed` condition detection. */
58769
58831
  prevScreenLines = [];
58832
+ /** Timestamp of the last PTY frame that changed the screen content.
58833
+ * Used by screen_active_hold_ms to suppress idle downshifts while
58834
+ * the terminal is still actively updating. */
58835
+ lastScreenChangedAt = 0;
58770
58836
  /** Timer that re-runs evaluate() once the hold window expires. Needed
58771
58837
  * because the PTY stops emitting once the agent finishes; without an
58772
58838
  * explicit wake-up there's nothing to trigger the busy → idle
@@ -58968,8 +59034,12 @@ ${formatManifestValidationIssues2(validation.issues)}`,
58968
59034
  reevaluate(forceEmit = false) {
58969
59035
  const screen = this.adapter.snapshot();
58970
59036
  const cursor = this.adapter.getCursorPosition();
59037
+ const currentLines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
58971
59038
  const ev = evaluate(this.spec, screen, cursor, this.prevScreenLines.length > 0 ? this.prevScreenLines : void 0);
58972
- this.prevScreenLines = screen.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
59039
+ if (this.prevScreenLines.length > 0 && currentLines.join("\n") !== this.prevScreenLines.join("\n")) {
59040
+ this.lastScreenChangedAt = Date.now();
59041
+ }
59042
+ this.prevScreenLines = currentLines;
58973
59043
  let evState = ev.state;
58974
59044
  const busyHoldMs = this.spec.debounce?.busy_hold_ms ?? BUSY_HOLD_MS;
58975
59045
  if (this.currentStateId === "busy" && evState.id === "idle") {
@@ -58987,12 +59057,19 @@ ${formatManifestValidationIssues2(validation.issues)}`,
58987
59057
  }
58988
59058
  }
58989
59059
  const completionIdleRule = this.spec.debounce?.completion_idle_after;
59060
+ const screenActiveHoldMs = this.spec.debounce?.screen_active_hold_ms;
58990
59061
  let busyWakeMs = busyHoldMs;
58991
59062
  const postModalGraceMs = busyHoldMs;
58992
59063
  const now = Date.now();
58993
59064
  const recentlyInModal = isModalState(this.currentStateId) || this.lastModalAt > 0 && now - this.lastModalAt < postModalGraceMs;
58994
59065
  const recentlyLeftModal = !recentlyInModal && this.lastModalExitAt > 0 && now - this.lastModalExitAt < postModalGraceMs;
58995
- if (evState.id === "busy" && completionIdleRule && !recentlyInModal && !recentlyLeftModal) {
59066
+ const screenActiveMs = screenActiveHoldMs ?? 0;
59067
+ const screenStableMs = this.lastScreenChangedAt > 0 ? now - this.lastScreenChangedAt : Infinity;
59068
+ const screenIsActive = screenActiveMs > 0 && screenStableMs < screenActiveMs;
59069
+ if (screenIsActive) {
59070
+ busyWakeMs = Math.min(busyWakeMs, screenActiveMs - screenStableMs + 50);
59071
+ }
59072
+ if (evState.id === "busy" && completionIdleRule && !recentlyInModal && !recentlyLeftModal && !screenIsActive) {
58996
59073
  const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
58997
59074
  if (completionKey) {
58998
59075
  const now2 = Date.now();
@@ -59022,8 +59099,13 @@ ${formatManifestValidationIssues2(validation.issues)}`,
59022
59099
  this.completionIdleFirstSeenAt = 0;
59023
59100
  }
59024
59101
  } else if (evState.id !== "busy") {
59025
- this.completionIdleKey = "";
59026
- this.completionIdleFirstSeenAt = 0;
59102
+ if (!screenIsActive) {
59103
+ this.completionIdleKey = "";
59104
+ this.completionIdleFirstSeenAt = 0;
59105
+ }
59106
+ }
59107
+ if (screenIsActive && this.currentStateId === "busy" && evState.id === (this.spec.default_state ?? "idle")) {
59108
+ evState = this.lastBusyState ?? evState;
59027
59109
  }
59028
59110
  if (evState.id === "busy") {
59029
59111
  this.lastBusyAt = Date.now();
@@ -61718,7 +61800,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61718
61800
  message: typeof modal?.message === "string" ? modal.message.trim() : "",
61719
61801
  buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button) => String(button).trim()) : []
61720
61802
  });
61721
- if (this.lastStatus !== "waiting_approval" && approvalFingerprint !== this.lastApprovalEventFingerprint) {
61803
+ if (approvalFingerprint !== this.lastApprovalEventFingerprint) {
61722
61804
  this.lastApprovalEventFingerprint = approvalFingerprint;
61723
61805
  this.appendRuntimeSystemMessage(
61724
61806
  this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
@@ -69738,6 +69820,23 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
69738
69820
  }
69739
69821
  return { dirty, outOfSync };
69740
69822
  }
69823
+ function isInlineMeshAutoFastForwardEligible(git) {
69824
+ if (!git) return false;
69825
+ if (readBooleanValue(git.isGitRepo) !== true) return false;
69826
+ if (!readStringValue(git.branch)) return false;
69827
+ if (!readStringValue(git.upstream)) return false;
69828
+ const upstreamStatus = readStringValue(git.upstreamStatus, git.upstream_status);
69829
+ if (upstreamStatus !== "fresh") return false;
69830
+ if ((readNumberValue(git.ahead) ?? 0) !== 0) return false;
69831
+ if ((readNumberValue(git.behind) ?? 0) <= 0) return false;
69832
+ const hasConflicts = readBooleanValue(git.hasConflicts) ?? (Array.isArray(git.conflictFiles) && git.conflictFiles.length > 0);
69833
+ if (hasConflicts) return false;
69834
+ if ((readNumberValue(git.stashCount, git.stash_count) ?? 0) > 0) return false;
69835
+ const submoduleDrift = getGitSubmoduleDriftState(git);
69836
+ if (submoduleDrift.dirty || submoduleDrift.outOfSync) return false;
69837
+ const dirty = readBooleanValue(git.dirty) ?? countGitWorktreeChanges(git) > 0;
69838
+ return dirty !== true && countGitWorktreeChanges(git) === 0;
69839
+ }
69741
69840
  function deriveMeshNodeHealthFromGit(git) {
69742
69841
  if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
69743
69842
  const branch = readStringValue(git.branch);
@@ -69867,6 +69966,12 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
69867
69966
  status.isDirty = uncommittedChanges > 0;
69868
69967
  status.uncommittedChanges = uncommittedChanges;
69869
69968
  status.branchConvergence = buildInlineMeshBranchConvergence({ mesh, node, status });
69969
+ status.autoFastForwardEligible = isInlineMeshAutoFastForwardEligible(git);
69970
+ if (status.autoFastForwardEligible) {
69971
+ status.suggestedAction = "auto_fast_forward";
69972
+ } else {
69973
+ delete status.suggestedAction;
69974
+ }
69870
69975
  }
69871
69976
  function summarizeInlineMeshBranchConvergence(nodes) {
69872
69977
  const followUps = nodes.filter((node) => readObjectRecord(node.branchConvergence).needsConvergence === true).map((node) => {