Commit Graph
102 Commits
Author SHA1 Message Date
can1357 735a345086 Merge PR #5902: fix(advisor): keep advise when Cursor emits ungranted native tools (@roboomp) 2026-07-18 19:57:44 +02:00
Christian Stewart c8f1972c8c fix(coding-agent): drain advisor reviews in print mode 2026-07-18 01:51:00 -07:00
roboomp 3d72284de5 fix(advisor): kept advise when cursor emits ungranted native tools
Cursor selects server-native tools (bash, grep, ...) outside the advisor's grant. Those exec-channel blocks are stamped kCursorExecResolved: they already ran server-side through the advisor-scoped CursorExecHandlers bridge, which rejects ungranted tools in-band. quarantineAdvisorUnsafeOutput was flagging them as pre-dispatch hazards and discarding the entire turn, dropping the legitimate advise emitted alongside them.

Skip exec-resolved native blocks in the unavailable-tool check so the scoped bridge stays the grant gate and the advisor can still deliver advice.

Fixes #5900
2026-07-17 19:18:42 +00:00
can1357 eac51b6a04 Merge: darkphilosophy/feat/advisor-per-agent-toggle
Brings the per-advisor toggle, status-line glyphs, quota display, and the
failing-advisor stall/abort fix (f4c8143) onto main's rewritten advisor
runtime. Conflict reconciliation kept main's architecture (fingerprint
prefix reconciliation, host-level onTurnError recovery + fallback chains,
terminal-failure classification) and ported the branch semantics onto it:

- #failing latch: waitForCatchup resolves immediately while an advisor is
  mid-failure; parked waiters wake the moment a turn fails, before any
  async hook or retry sleep.
- Turn-end render containment: a formatter bug restores the cursor/prefix/
  dedup snapshot and never propagates into the primary's turn-end callback
  (per-advisor try/catch boundary in AgentSession).
- Quota pause: when host recovery declines a usage-limit failure, the
  runtime latches quotaExhausted, requeues the batch, and notifies —
  cleared only by an explicit reset.
- Hard halt after a permanent rejection or three backlog-drop cycles.
- #recoverAdvisorTurn also marks usage limits for structural errors thrown
  before any assistant turn is recorded.
2026-07-17 07:37:29 +02:00
DarkPhilosophy f4c81434d0 fix(advisor): never let a failing advisor stall or abort the primary agent
A broken advisor could hold the primary agent on the per-turn catch-up
gate for its full 30s budget while retrying, and an exception thrown from
onTurnEnd propagated into the primary's turn-end callback.

- waitForCatchup resolves immediately while the advisor is mid-failure
  (new #failing latch, set at the failure catch BEFORE any async hook,
  cleared on the next successful turn or reset/seed).
- Every parked waiter is woken the moment an advisor turn fails.
- The turn-end boundary isolates advisor exceptions per advisor: a
  throwing advisor loses its delta, the primary and sibling advisors
  continue untouched.
- A failed render (poisoned message, formatter bug) restores the delta
  cursor and dedup state, so the delta is re-rendered next turn instead
  of silently lost; the size probe itself is guarded and falls back to
  the deferred renderer.
2026-07-17 07:24:30 +03:00
can1357 2594ae352b style: formatted conflict-resolved files with biome 2026-07-17 05:01:19 +02:00
can1357 dd84ec57ce apply PR #5468: fix(advisor): stop retrying terminal failures
Grafted the evaluator's port (ec2c1e632) onto the merged advisor
runtime: terminal provider failures classified non-retriable (and not
context overflow) drop the bounded batch after one attempt with a
single notification; fallback-chain recovery and overflow recovery
retain precedence. Includes the one-prompt regression test and tags the
rollback-retry fixture's synthetic failure as transient.
2026-07-17 05:00:06 +02:00
can1357 11a879e993 merge PR #5463 via eval/pr-5463: fix(advisor): anchor context maintenance on provider usage
Semantic merge with #5734 (delivered-prefix reconciliation) and #5748
(fallback chains): kept the coalescing round cap and wip threading,
adopted bounded cursor-preserving maintenance resets and overflow
recovery, and gated late-arrival consumption on coalescing rounds so
both suites' backlog and preserved-updates contracts hold.
2026-07-17 04:55:49 +02:00
DarkPhilosophy 1b4c292f8f Merge remote-tracking branch 'can1357/main' into feat/advisor-per-agent-toggle 2026-07-17 05:47:09 +03:00
DarkPhilosophy 3be0663bf2 fix(advisor): halt permanently rejected advisors and chunk large delta renders
Two shared failure modes with a single misbehaving advisor:

- A permanently rejected request (invalid_request_error, e.g. a model the
  account no longer supports) retried forever: one notice, then silent
  re-attempts on every turn, rebuilding heavy context each cycle. Quota
  exhaustion already paused with a notice; this class now hard-stops the
  runtime after a permanent rejection or three consecutive backlog-drop
  cycles, with a visible notice. An explicit reset (/new, config rebuild,
  restart) re-enables it, and waitForCatchup resolves while halted so the
  primary agent never parks on a runtime that cannot drain.

- The delta render ran synchronously on the event loop; replaying a
  multi-MB transcript after a reset blocked it for 600ms+ per render
  (measured 675ms at ~54MB). Large deltas now render in size- and
  count-bounded chunks that yield between slices (675ms -> single-digit
  ms stalls). Tool call/result pairing survives chunk boundaries via a
  shared whole-delta result index in formatSessionHistoryMarkdown; small
  per-turn deltas keep the synchronous fast path.
2026-07-17 05:47:01 +03:00
can1357 6f42a4375f merge PR #5748 via eval/pr-5748: fix(advisor): apply configured fallback chains 2026-07-17 04:45:46 +02:00
can1357 b28dd5e50a merge PR #5734 via eval/pr-5734: fix(advisor): reconcile rewritten transcript prefixes 2026-07-17 04:45:38 +02:00
roboomp 777e5e0982 fix(advisor): applied configured fallback chains
- Switched advisor turns to the next configured model after provider quota or rate-limit failures.
- Emitted fallback applied and succeeded lifecycle events without reporting advisor unavailability after recovery.
- Added an end-to-end advisor quota fallback regression test.

Fixes #5740
2026-07-16 19:56:24 +00:00
roboomp 4d0f9c1b66 fix(advisor): reconciled rewritten transcript prefixes
- Tracked delivered message identities alongside the numeric cursor.
- Re-primed advisor context when a live transcript prefix diverged.
- Covered accepted empty-stop pruning before the next real user turn.

Fixes #5731
2026-07-16 17:37:40 +00:00
roboomp 1824faf21e fix(advisor): recognized authorized cursor deletes
Cursor synthesizes an executed native delete as a tool call named delete. When write or edit enabled native deletion, the advisor quarantine allowlist still omitted that synthetic name and rolled back the completed turn.

Add delete to the allowlist from the same mutation-capability boolean that enables native deletion, with regression coverage for the quarantine path.

Fixes #5680
2026-07-16 10:46:42 +00:00
DarkPhilosophy 4695db1ed6 fix(advisor): preserve YAML whitespace on CI 2026-07-16 03:31:10 +03:00
roboomp 54df76be81 fix(advisor): steer late blocker after terminal answer
A late interrupting advisor note delivered after the primary ended with a
terminal text answer (no queued work) was routed to `preserve` for every
severity, so a `blocker` flagging a mistake in the final output became a
passive card that the model ignored until the next user turn.

Scope the terminal-answer preserve rule to non-blocker severities: a
`blocker` now steers a triggered turn so the primary acknowledges and
continues before the turn is considered done, while a late `concern` still
preserves as a visible card (keeps the #4840 no-duplicate-completion path).

Fixes #5628
2026-07-15 23:12:42 +00:00
DarkPhilosophy 1d69621776 fix(advisor): preserve watchdog configuration 2026-07-16 02:07:51 +03:00
DarkPhilosophy 4a0ba05fe8 fix(advisor): ignore stale quota hooks 2026-07-15 04:42:37 +03:00
DarkPhilosophy a5b4832cc0 fix(advisor): align quota retry handling 2026-07-15 04:05:59 +03:00
DarkPhilosophy 0a9a5aa186 Merge remote-tracking branch 'can1357/main' into feat/advisor-per-agent-toggle
# Conflicts:
#	packages/coding-agent/src/advisor/runtime.ts
2026-07-15 03:32:11 +03:00
can1357 4114d280bd fix(coding-agent/advisor): treated zero-content advisor stop turns as valid review completions
- Replaced the advisor turn validation to raise errors only when a turn has no assistant response at all, instead of treating a content-less `stop` as a failure.
- Kept zero-content silent reviews from entering the retry/rollback/warn path by no longer raising "Advisor unavailable" for completed empty-stop turns.
- Updated advisor runtime tests and changelog language to assert and document that consecutive zero-usage silent turns now succeed without notifications.
2026-07-14 23:36:08 +02:00
roboomp 1a4c195017 fix(coding-agent): accepted silent advisor stops with output tokens
The advisor runtime rejected every content-less stop completion as a
failed turn, so a deliberate silent review (the documented verifier
behavior) triggered retries and a spurious "unavailable" warning. Only
treat a content-less stop as a failure when it produced no output signal
(zero output and reasoning tokens), so a token-bearing silent stop counts
as a successful silent review.

Fixes #5493
2026-07-14 20:31:21 +00:00
roboomp cc9977cce4 fix(advisor): anchored context maintenance on provider usage
- Anchored advisor compaction on provider-reported context usage (cached
  input + generated output) floored by a full local estimate including the
  advisor system prompt and tool schemas, so a near-full cached context is no
  longer undercounted by the per-message estimate.
- Rejected stale provider usage retained across advisor compaction via a
  runtime-only usage-anchor boundary recorded on the summary message.
- Recovered provider overflow by clearing only the advisor's own context at
  the current primary cursor, retrying the bounded failing batch once against
  a fresh context without replaying old primary history, and keeping later
  updates eligible.
- Threaded the selected dashboard range through the stats Recent Errors UI,
  API, and database timestamp filter before ordering and the 50-row limit.

Fixes #5282
2026-07-14 17:58:19 +00:00
can1357 5e74444c27 fix(test): repair auth-storage-rotation merge resolution and normalize changelogs 2026-07-14 18:50:47 +02:00
can1357 1dfec0d161 Merge PR #5090: fix(advisor): reduce stale advisories via delta coalescing, WIP markers, and delivery annotation (@apoc)
# Conflicts:
#	packages/coding-agent/src/advisor/__tests__/advisor.test.ts
#	packages/coding-agent/src/advisor/runtime.ts
#	packages/coding-agent/src/session/agent-session.ts
2026-07-14 18:44:57 +02:00
can1357 e96f5451db Merge PR #5216: fix(coding-agent): reject empty advisor stop completions (@roboomp) 2026-07-14 18:41:15 +02:00
can1357 b52a4cdede Merge PR #5186: fix(coding-agent): preserve late advisor notes after terminal answers (@roboomp)
# Conflicts:
#	packages/coding-agent/test/agent-session-advisor-suppression.test.ts
2026-07-14 18:40:35 +02:00
can1357 c0a0216867 fix(advisor): close quarantine containment gaps 2026-07-14 18:39:54 +02:00
DarkPhilosophy 600682d447 fix(advisor): classify switched-retry errors before pausing quota
When onTurnError signals a switched sibling credential (switched=true),
the retry error is now classified via isQuotaError:
- Second quota: marks the sibling via onTurnError, then enters quota pause
- Non-quota transient: routes through onTurnError and the generic
  failure/requeue/notify path instead of unconditionally pausing
2026-07-14 00:52:00 +03:00
DarkPhilosophy cc2831e0f0 fix(advisor): signal-based quota retry + bare-model provider resolution
- onTurnError returns Promise<boolean|undefined> signaling credential switch
- agent-session returns markUsageLimitReached().switched from the hook
- runtime retries once when switched===true, otherwise preserves quota pause
- advisor-config uses liveStat model provider for bare (slash-less) selectors
- 2 regression tests: switched=true (retry succeeds) and switched=false (pause)
2026-07-14 00:13:55 +03:00
DarkPhilosophy ca8bf8dda1 Merge remote-tracking branch 'can1357/main' into feat/advisor-per-agent-toggle
# Conflicts:
#	packages/ai/test/pi-native-client.test.ts
#	packages/coding-agent/src/modes/components/advisor-config.ts
#	packages/coding-agent/src/session/agent-session.ts
2026-07-11 22:13:08 +03:00
roboomp 8608395eef fix(coding-agent): rejected empty advisor stops
Treat content-less advisor stop completions as failed turns so the advisor retry/drop path handles silent provider responses instead of accepting them as successful reviews.

Fixes #5212
2026-07-11 17:02:18 +00:00
Miroslav Drbal 74be4d5f67 style(advisor): apply biome formatting 2026-07-11 19:00:34 +02:00
Miroslav Drbal 59017f2616 fix(advisor): address final review: wip return type, import order, cap edge case, cap test
Blockers from final review:
- runtime.ts:457 TS2741: #collectAndMaintainBatch now returns wip in its
  result type; #drain destructures it and passes it to the retry-requeue
  unshift — a WIP batch that fails retry no longer silently loses its
  [in progress] heading.
- agent-session.ts import order: annotateForStaleness moved before
  formatAdvisorBatchContent (biome enforces case-insensitive alpha order).

Cap edge case (advisor nit + review CONCERN): final round of the
  coalescing for-loop now breaks BEFORE the late-item splice so any
  deltas that arrived during round MAX_COALESCE_ROUNDS-1's
  maintainContext call stay in #pending for the next drain iteration
  instead of being merged into an unbudgeted batch. Doc comment updated
  to match ('left for the next drain iteration' is now accurate).

Cap test: bounded version that only pushes new turns for the first 3
  maintainContext calls so the drain while-loop terminates cleanly after
  a second iteration. The previous unbounded version created an infinite
  drain loop (each maintainContext call unconditionally pushed another
  turn) and timed out.
2026-07-11 18:59:11 +02:00
Miroslav Drbal 441197b462 fix(advisor): address review: wip on PendingDelta, MAX_COALESCE_ROUNDS cap, annotateForStaleness extraction
Blocker: wip field added to PendingDelta and threaded into the reprime
  path. #collectAndMaintainBatch now captures the most-recent WIP state
  from each delta batch and forwards it to #renderDelta in both the
  normal and reprime branches, so a willContinue:true turn never loses
  its [in progress] heading through a reprime.

Safety cap: MAX_COALESCE_ROUNDS=3 constant defined and used in the
  coalescing for-loop, preventing indefinite dispatch stall under
  pathological fast-primary + slow-maintainContext conditions.

Testability: annotateForStaleness extracted as an exported pure function
  in advise-tool.ts and used in AgentSession#routeAdvice. Three unit
  tests added in advisor.test.ts covering the no-staleness, staleness,
  and note-preservation contracts. This addresses the ReviewSession
  regression-test concern without requiring a full AgentSession harness.

Reprime turn-tally coverage: new test 'backlog stays accurate when a
  delta arrives during the reprime-triggering maintainContext' asserts
  runtime.backlog === 0 after all three turns, catching a deleted
  turns += reduce(...) line.

Fragile double-await tests converted: sends-batch-when-maintenance-fails
  and expands-plan-mode-context now use Promise.withResolvers signals
  instead of counted await Promise.resolve() hops.

Docs: onTurnEnd JSDoc added; hasFreshBacklog comment broadened to cover
  all drain-busy phases (not just agent.prompt).
2026-07-11 18:59:11 +02:00
Miroslav Drbal 74715f8cca fix(advisor): reduce stale advisories via delta coalescing, WIP markers, and delivery annotation
Three related changes that address the pattern of the advisor flagging things
the primary already fixed:

Fix 1 — coalesce late-arriving deltas before agent.prompt (runtime.ts)
  Refactored #drain into a reusable #collectAndMaintainBatch helper that loops
  until the pending queue is stable (no new deltas arrive during a maintenance
  check) before calling agent.prompt. Previously, any turn queued during the
  maintainContext await was deferred a full extra model-call cycle; now it is
  merged into the current batch after re-checking the token budget for the
  expanded payload. Every await in the loop has an epoch guard so a
  reset/dispose mid-await cannot leak a stale batch. finalTurns always counts
  all merged turns so #backlog decrements correctly.

Fix 2 — hasFreshBacklog + delivery-time staleness annotation (runtime.ts, agent-session.ts)
  Added AdvisorRuntime.hasFreshBacklog getter (true when #pending.length > 0
  while agent.prompt is running — i.e., newer primary turns arrived after the
  reviewed window). #routeAdvice checks it at delivery time and appends a
  lightweight caveat to the note so the primary agent knows to verify before
  acting. Uses #pending.length not #backlog, which is always > 0 mid-call.

Fix 3 — willContinue WIP marker in rendered delta + system prompt (runtime.ts, agent-session.ts, system.md)
  onTurnEnd now accepts { willContinue } and passes it through to #renderDelta,
  which tags the heading '[in progress — more steps follow]' for intermediate
  turns. The agent-session.ts call site passes context.willContinue. The advisor
  system prompt instructs the model to withhold critique on WIP updates.

Also fixed pre-existing inline casts in #renderDelta and #dedupContextMessage
that suppressed the type checker instead of using the narrowing already provided
by the role discriminant.

All 75 advisor tests pass; pre-existing type errors in cursor.ts are unrelated.
2026-07-11 18:56:04 +02:00
roboomp ea5324fb10 fix(advisor): trusted advisor tool result provenance
Included advisor tool-result text in the quarantine source check so legitimate findings from granted read/grep tools are not treated as model-generated contamination.

Kept assistant text out of the source set to avoid laundering prior advisor hallucinations.

Fixes #5181
2026-07-11 13:17:46 +00:00
roboomp 77115fe16a fix(advisor): quarantined unsafe advise notes
Scanned allowed advise tool notes for output-only destructive directives before the tool can route them to the primary agent.

Kept provenance checks against the watched session update so legitimate warnings about user-provided dangerous text still pass.

Fixes #5181
2026-07-11 13:06:36 +00:00
roboomp 708eafaf8d fix(coding-agent): preserved late advisor terminal notes
Prevented late interrupting advisor findings from waking the primary after a terminal text answer when no queued work remains.

Added regression coverage for the advisor-confirmation path so duplicate primary turns are caught.

Fixes #4840
2026-07-11 12:59:22 +00:00
roboomp 9c961acd69 fix(advisor): cleared quarantined native payloads
Cleared provider-native replay payloads and stop details when Advisor output is quarantined so persisted transcripts only contain the sanitized error.

Added regression coverage for OpenAI Responses-style providerPayload leakage.

Fixes #5181
2026-07-11 12:56:39 +00:00
roboomp a58e8faa09 fix(advisor): quarantined unknown tool responses
Quarantined Advisor assistant turns that request tools outside the granted tool pool before they can enter the Advisor context.

Reset the Advisor runtime after quarantine so the next update re-primes from the primary transcript instead of replaying contaminated private context.

Fixes #5181
2026-07-11 12:47:26 +00:00
roboomp dabb2291a7 fix(advisor): kept defaults for invalid tools
Returned to default advisor tools when a non-empty configured tools list filters down to zero valid names.

Fixes #5155
2026-07-11 06:14:11 +00:00
roboomp 7d72ee9e0e fix(advisor): preserved empty tool lists
Kept explicit advisor tools: [] distinct from an omitted tools field so /advisor config can persist no tool access.

Fixes #5155
2026-07-11 06:01:16 +00:00
roboomp 325375f801 fix(advisor): used uuidv7 codex session ids
Separated advisor provider session identity from local advisor labels so Codex requests carry stable UUIDv7 values while transcripts keep their advisor-specific names.

Fixes #5040
2026-07-10 07:24:36 +00:00
DarkPhilosophy 8b4d644b4e fix(advisor): address review — no arbitrary auto-resume, active-account quota filter, dedup imports 2026-07-08 18:33:22 +03:00
DarkPhilosophy 36b17cd12d fix(advisor): call onTurnError before quota pause, preserve disabled default advisor on save 2026-07-08 18:01:36 +03:00
DarkPhilosophy 760682b11f fix(advisor): address review — quota requeue, legacy slug, discoverAdvisorConfigs enabled
- Quota-paused advisor retains the failed batch in pending queue (not dropped),
  releases catchup waiters so the primary agent isn't blocked, and replays the
  turn after the quota cooldown resets
- Legacy no-model advisor status now tracked via slug 'default' instead of ''
  so #advisorStatuses covers all configurations
- discoverAdvisorConfigs() now preserves the enabled field from WATCHDOG.yml
  (was silently dropped during YAML discovery)
- Test error message fixed to match isQuotaError regex (rate limit with space)
2026-07-08 17:42:51 +03:00
DarkPhilosophy 2c6b1b1428 Merge remote-tracking branch 'can1357/main' into feat/advisor-per-agent-toggle 2026-07-08 17:10:07 +03:00
DarkPhilosophy a33928ee04 feat(advisor): wire per-advisor toggle into config overlay
- Add 'Enabled' toggle field to detail editor (● on / ○ off)
- Show ●/○ markers in roster list for enabled/disabled advisors
- Show enabled status in advisor preview panel
- Add overlay test verifying disabled advisors render with ○ marker
2026-07-08 15:17:53 +03:00