Commit Graph

32331 Commits

Author SHA1 Message Date
Teknium 2f090fbdec fix(serve): run idle skill maintenance on the existing timer
Desktop-only backends now poll curator and personal/org skill sync without another long-lived loop. Respect active turns, the actual idle threshold, and messaging gateway ownership. Credit Jackal991 for the report and candidate #95453.
2026-09-07 04:56:38 -07:00
Teknium 04fedadbe9 chore: map history contributor email to PLASMA-FR 2026-09-07 04:56:22 -07:00
Teknium 3cb64cdcbc test(desktop): add isolated live history projection witness 2026-09-07 04:56:22 -07:00
Teknium 42f8389987 fix: restore persisted assistant replies alongside tools and reasoning 2026-09-07 04:56:22 -07:00
Ahmad Al-Faqih 9d1eec0ce0 fix: retain Responses assistant replies in RPC session history
Carve the history-only implementation and regression from #104754
(b4bfa76facb43111a863b1256c89875e3f01fde0) by PLASMA-FR; omit
the unrelated locale-picker changes. Complement merged #104523.

Real serve + WebSocket reconnect: SQLite retains two rows; before,
resume/activate/history return only the user; after, both survive.
Plain-content control retains both rows in both arms. Native macOS
sleep and the full desktop symptom are not established by this probe.

Refs #68321
2026-09-07 04:56:22 -07:00
Teknium e412727e72 fix: keep task-scroll node tests out of Vitest discovery 2026-09-07 04:56:05 -07:00
Teknium 80e585fad3 test: verify desktop task tails remain reachable in Chromium 2026-09-07 04:56:05 -07:00
Sylvester Kaczmarek d5ad0c2be2 fix(desktop): keep composer status card scrollable 2026-09-07 04:56:05 -07:00
Teknium d87d7657c0 test(desktop): require successful isolated hook turns and exact calls 2026-09-07 04:55:48 -07:00
Teknium 3094cde513 fix(desktop): honor configured shell hooks for each chat profile
Wire the existing consent-aware, profile-keyed hook registrar at agent
construction, where the correct session home is already bound. This covers
serve and TUI agent construction without a startup-only registration or a
new helper that swallows registration failures.

Live isolated serve/WebSocket probes reproduce the missing registration on
base for both write_file and terminal, then verify each consented profile
blocks its configured tool while the unapproved profile still runs normally.
Repeated alpha construction does not duplicate hook callbacks.

Slim implementation of the agent-build placement proposed in #57020;
thanks also to the profile-scoped analysis in #102691.

Co-authored-by: grimmjoww578 <willies578@gmail.com>
2026-09-07 04:55:48 -07:00
Teknium 18955c35b5 fix(sessions): repair closed lanes through canonical disconnect cleanup
The real closed-WebSocket resident variant still wedges after the missing-timer repair. Re-enter existing transport cleanup before rearming orphan timers, preserving viewer transfer and the reconnect/delegation fence rather than deleting registry rows from a stale snapshot. Extend the same invariant to both dead-transport shapes. Live class investigation informed by #104710; no direct-vouch reclaim machinery imported.
2026-09-07 04:55:32 -07:00
fangliquanflq 919584e971 fix(sessions): rearm lost detached chat teardown timers
Salvage #104704 (e9423d2d0bbe3e795c5eaccb86a913f1d95444ba, 5fa2b98fc833fc4e1ee7f1aaa7eb45cdd3cd7cde). Reuse guarded orphan teardown instead of deleting ownership fences. Real two-backend WebSocket probe reproduces the missing-timer wedge on base and proves reconnect/delegation protection and recovery. Add reusable probe and user documentation.
2026-09-07 04:55:32 -07:00
Teknium 7252d403d8 chore: map preserved Linux launcher contributor 2026-09-07 04:55:17 -07:00
Teknium 93fef377df test(desktop): retain native launcher and backend campaign probes 2026-09-07 04:55:17 -07:00
HoneyTyagii 381d6064d7 fix(desktop): preserve custom Linux launcher entries when opted out
Salvage #101453 (03a3f466d38134ba416764185884b3d655197a1d). Preserve its opt-out and first-run behavior; replace predicate-mocked tests with one native config/filesystem invariant and clarify XDG docs. Real venv/XDG probe: base clobbers custom entry, fix preserves it; targeted suite 94 passed.
2026-09-07 04:55:17 -07:00
Teknium 94ff4fe8f9 test: verify profile rebuild persistence through live serve 2026-09-07 04:54:49 -07:00
HexLab98 8da23bdae4 fix(tui): keep in-session agent rebuilds on their own profile store
_sync_bot_capabilities (Bot Chat capability refresh, run at every turn start) and
_reset_session_agent (/new, tools.set) swap a fresh AIAgent into a LIVE session but
called _make_agent with neither the session's state.db handle nor its HERMES_HOME.
_make_agent resolves prompt/skills/toolsets through get_hermes_home() and defaults
session_db to the process-wide LAUNCH _get_db() handle, so a named-profile session
silently migrated onto the launch profile: every later turn appended to
~/.hermes/state.db under the same session id while the desktop replayed
profiles/<name>/state.db and showed a stale transcript.

Route both rebuilds through _rebuild_session_agent, which binds the session's
profile scopes and inherits the outgoing agent's handle (same session, same file)
so no second refcount is taken and teardown still releases it exactly once.
2026-09-07 04:54:49 -07:00
HexLab98 dffcf84e25 test(tui): pin profile store binding across in-session agent rebuilds 2026-09-07 04:54:49 -07:00
Teknium 285829c3fe test(kanban): exercise acceptance races through real run lifecycle 2026-09-07 04:46:54 -07:00
Teknium dccdf31732 test(kanban): use distinct IDs for paginated acceptance fixtures 2026-09-07 04:46:54 -07:00
Teknium ac07da2674 fix(kanban): enforce declared PR acceptance at completion boundary 2026-09-07 04:46:54 -07:00
Teknium 768f42f72f chore: preserve home-link diagnostic contributor credit 2026-09-07 04:46:37 -07:00
Teknium e6df8675f5 fix(config): diagnose unavailable home links without claiming YAML corruption
Keep externally managed directory links and permissions intact during home
initialization. Refuse missing targets rather than creating directories on an
unmounted volume's underlying filesystem. Report link, target, mount and access
guidance through doctor while preserving config.yaml.

Extract the home initialization phase into config_home, and memoize successful
resolved aliases so plugin discovery cannot repeat chmod after losing the
symlink spelling. Live Linux doctor PTY A/B verified directory and root links,
plain paths, missing targets, mount-style missing paths and file conflicts.
Targeted invariant tests are queued under the campaign's shared serial lock;
this checkpoint is not a unit-suite or merge-readiness claim.

Inspired by #104774 and #103735; deliberately does not auto-create external
targets or silently ignore an unavailable sessions directory.

Co-authored-by: ca-shrimp <320556551+ca-shrimp@users.noreply.github.com>
Co-authored-by: Craig Richardson <craigrichardson@Craigs-Mac-mini.local>
2026-09-07 04:46:37 -07:00
Teknium 7e77401ad0 refactor: read retry deadlines only from the ledger 2026-09-07 04:46:20 -07:00
Teknium af34ba3c01 fix: keep flood retry wakeups off the event loop and retain two invariants 2026-09-07 04:46:20 -07:00
Alexander Russell 5495c29cf8 fix(gateway): recognise every definite flood refusal, not only the canonical one
The redelivery hook keyed on the canonical flood_control:<seconds> result, so
two real refusals slipped past it and armed no timer, leaving the reply for the
next restart. A short wait that outlived the send retries raised instead of
failing closed, and an edit refused again after its inline wait returned the
platform's raw text. Both now fail closed canonically, the second carrying the
new delay rather than the first refusal's.

The ledger also accepts a row still carrying the platform's own wording, so a
row persisted by an unnormalized path is dated from the delay it states instead
of the generic default. Without that a boot sweep claims it at once and spends
its one attempt inside the penalty. Matching requires the flood wording as well
as a delay, so an unrelated retry suggestion is never read as a flood.

Six new assertions fail without this change. 770 passed across the ledger,
Telegram, send-retry and queued suites.
2026-09-07 04:46:20 -07:00
Alexander Russell e41e5e236b fix(gateway): read the flood re-arm ledger synchronously so a mid-sweep refusal is not lost
Round-2 review. Making _arm_flood_timers_for_waiting_rows read the ledger in a
worker thread introduced a race: a flood refusal recorded during that thread's
yield had its own _schedule_flood_redelivery request declined (the timer still
held the slot), and the timer then cleared its slot on a snapshot taken before
the new row committed, leaving that reply with no timer until the next reconnect
or restart. The read is a single indexed SELECT; doing it synchronously keeps the
arm decision atomic with respect to concurrent schedule requests. A test drives a
refusal during the timer's own redelivery send and asserts the same timer arms it.
2026-09-07 04:46:20 -07:00
Alexander Russell 289ece2e59 fix(gateway): adopted flood rows clear resume flags and legacy profiles reach the timer
Second review pass on the flood-retry change.

- sweep_recoverable returns a dead owner's not-yet-due flood row flagged
  `adopted` (with its `not_before`) instead of dropping it from the result,
  so _claim_pending_obligations clears its session's resume_pending flag like
  every other claimed row; the answer is in the ledger and the turn must not
  be re-run at boot. _redeliver_claimed_obligations skips adopted rows and
  still arms the timer for them.
- A legacy row without adapter_profile is normalised to 'default' when the
  boot sweep claims or adopts it: the runtime sweep matches profiles exactly
  and the timer asks for 'default', so an adopted NULL row could only wake the
  timer without ever being sent.
- A sleeping timer is replaced by a shorter refusal's timer; the shorter one
  re-arms for the longer row after its sweep. A timer already sweeping is
  never cancelled from outside.
- pending_flood_retries is read in a worker thread like the other ledger calls.
- Adoption tests seed a distinct dead-owner stamp and assert ownership moves.
- Scratch tags and long comment lines removed.
2026-09-07 04:46:20 -07:00
Alexander Russell 678b0649ab fix(gateway): mark every flood redelivery and keep a released flood claim on the timer
Review on the first cut reproduced two gaps.

The raw UTF-16 length of the stored reply said nothing about how many
requests the adapter made: MarkdownV2 escaping turns 3000 dots into 6000
units, two Telegram messages, and the send result does not say which chunk
the platform refused. Drop the length-based certainty (and its helper and
constant) and mark every flood redelivery with the rate-limit marker, at
runtime and at boot. The cost is a marker on a reply nothing of which was
delivered; the alternative was a silent duplicate.

Releasing an unsent runtime claim always wrote send_path_degraded, so a flood
row whose resume flag could not be cleared (or whose adapter vanished before
dispatch) left the flood timer's list and stayed stranded until a reconnect or
restart. The claimed row now carries its pre-claim error and the release
writes it back, so the row waits the platform's figure once more and is sent
on the next timer.
2026-09-07 04:46:20 -07:00
Alexander Russell ae900cba9c fix(gateway): redeliver a flood-refused final reply once the penalty passes, without a false duplicate marker
The Telegram adapter fails a flood-controlled final send closed as
flood_control:<seconds> so the send coroutine never sleeps a long penalty
(#91969), on the understanding that the delivery ledger owns the wait. The
ledger did not: sweep_failed_for_runtime only replayed send_path_degraded
rows, so a flood-refused row sat in 'failed' until the next restart, whose
sweep_recoverable then redelivered it hours late prefixed with "Recovered
reply, the gateway restarted during delivery, so this may be a duplicate".
Observed: a reply refused at 08:58 UTC (both the MarkdownV2 send and the
plain fallback got flood_control:185) arrived at 12:15 UTC after a restart,
labelled as a possible duplicate although the platform had never accepted it.

Ledger (gateway/delivery_ledger.py):
- flood_control rows are runtime-retryable, but only once their own deadline
  has passed: the refusal's updated_at plus the platform's wait
  (flood_not_before). Neither an early timer nor a reconnect sweep spends a
  redelivery attempt inside the penalty window.
- A flood refusal of a reply that fits in one Telegram message (4096 UTF-16
  units) proves non-delivery, so that redelivery carries no duplicate marker.
  A chunked reply may have had its first chunk accepted before the refusal
  and keeps the marker.
- Claiming a flood row clears the stale refusal (last_error NULL, state
  'attempting'), so a resend interrupted before mark_delivered is seen as
  uncertain by the next boot and gets the marker.
- At boot, a dead owner's flood row that is not yet due is adopted (owner
  re-stamped, no attempt spent) instead of being resent early.
- pending_flood_retries() lists this process's waiting flood rows per adapter
  identity with the earliest deadline.

Runner (gateway/run_startup.py):
- _schedule_flood_redelivery arms one timer per adapter identity that runs
  the existing runtime sweep after the wait (capped at 15 minutes per sleep;
  the row's deadline, not the timer, decides eligibility, so a capped timer
  wakes early, sends nothing, and re-arms for the remainder). The slot stays
  occupied until the timer ends and only the running timer may arm its
  successor into it, so a refusal during the sweep can never strand the row.
- _arm_flood_timers_for_waiting_rows runs after every redelivery pass (boot
  and runtime), covering adopted rows, rows skipped as not yet due, and rows
  refused again.

Adapter (gateway/platforms/base.py): _finalize_delivery_obligation arms the
timer on a flood_control failure, best-effort inside the existing try.

Tests: 35 in tests/gateway/test_delivery_ledger_flood_retry.py, with a
controllable clock shared by ledger and runner. Each guard was mutation
tested. Existing ledger, reconnect and redelivery suites (131 tests) pass.
2026-09-07 04:46:20 -07:00
Teknium 0d8bba885c test: reproduce flood-refused delivery through local Telegram wire 2026-09-07 04:46:20 -07:00
Teknium 780aee0deb test(acp): verify existing empty metadata over native session load 2026-09-07 04:46:04 -07:00
Teknium 8f7640b622 fix(acp): retain only ephemeral-new-session persistence guard
Keep two invariants covering empty create/cwd/save/fork, genuine content and existing-row metadata. Preserve original authorship and avoid source-only legacy pruning: an empty ACP row does not prove its owner is dead. Native ACP wire plus a local streaming model fixture verifies the first turn and nonempty fork remain durable.
2026-09-07 04:46:04 -07:00
Willhong 64fda8c73f fix(acp): stop persisting sessions that never received a prompt
Fixes #104724

The ACP wire contract has session/new as a separate round trip from
session/prompt precisely so a client can open a session before it knows a
prompt is coming, and at least one shipping client opens sessions it will
never prompt: bb discovers a model catalog by spawning a throwaway
`hermes acp`, sending initialize + session/new, reading
NewSessionResponse.models, and killing the process. Its discovery cache
TTL is 60s, so an open editor re-probes continuously.

_persist() created the state.db row the moment a session was created, so
every such probe left a permanent message_count=0 shell. Measured on one
workstation: 116 accumulated, appearing on a time cadence rather than a
per-conversation one (26 real ACP user turns vs 13 shells in a day; exactly
120.0-minute spacing overnight with zero user activity).

The shells are indistinguishable from real chats in the session list and
`hermes sessions prune` cannot remove them: the rows are never ended, so
ended_at stays NULL and prune skips them by design, leaving
`hermes sessions delete <id>` one id at a time as the only remedy.

Defer row creation until the session has history. Nothing is lost for a
genuine conversation: AIAgent._ensure_db_session() creates the row on the
first turn and the post-prompt save_session() lands the ACP metadata on
top, which makes the create-time write redundant for every session except
the one case that should not be recorded at all.

Gate on state.history rather than message_count so fork_session, which
deep-copies a non-empty history into a fresh id, still persists at once.

Verified by differential execution of an identical probe against both
trees: on 693641aa8b an unprompted session/new adds a row, with this
change it adds none, and a session that does receive a prompt persists
in both. tests/acp: 138 passed. Reverting only the source change leaves
the two new assertions failing, confirming they exercise the fix.
2026-09-07 04:46:04 -07:00
Teknium 0ee524a3d6 test(acp): reproduce empty session persistence on the native wire 2026-09-07 04:46:04 -07:00
Teknium 9da8df8d26 fix(prompt): preserve shared project prefixes across worktrees
Slim salvage of #104688: place project context before workspace state and
keep cwd outside the stable prefix. Put runtime hints behind a final
renderer-owned boundary so quoted operator, memory, plugin and embedder
examples cannot override the persisted runtime cwd or identity fields.

Retain legacy unmarked prompt validation, add two invariant tests and a
credential-free real-AIAgent/git-worktree replay harness. No provider
cache-hit or billing measurements are claimed.

Co-authored-by: joaomarcos <joaomarcosdias444@gmail.com>
Co-authored-by: HexLab98 <liruixinch@outlook.com>
2026-09-07 04:45:48 -07:00
Teknium cfe07df09f test(agent): assert owner-scoped teardown instead of bulk cleanup 2026-09-07 04:38:59 -07:00
Teknium dca7a90cf8 fix(agent): reclaim background processes by execution owner
Track raw task identities across an agent's turns and match them against
process owner_task_id during close. Session IDs and shared terminal keys
are not process ownership, so the old bulk cleanup missed delegated work.
Preserve parent/sibling processes and consume teardown notifications.

Move task-resource cleanup into the lifecycle mixin, add real-process
isolation regressions, and document background process lifetime.
2026-09-07 04:38:59 -07:00
Teknium 22c5684b98 fix(agent): clear resumed stream wait status without synthetic reasoning 2026-09-07 02:37:13 -07:00
Teknium ba395af50f refactor(agent): isolate streaming wait monitor phase 2026-09-07 02:37:13 -07:00
Teknium 77f79ae831 fix(agent): keep active Codex reasoning out of wait warnings
Use request-local stream silence for the waiting notice, preserving quiet
activity heartbeats and all existing watchdog policies. Distinguish a stream
that stopped from a request with no response, and clear this request's notice
on the next poll when events resume. Existing fresh first-event retry phases
also reset the display; recovery deadlines explicitly use total call elapsed.

Add two invariant tests (eight cases), proven red on main, plus EN/ZH docs.
Local SDK SSE through classic CLI callbacks in a PTY verifies active reasoning,
true silence, and an already-visible warning clearing on resumed reasoning.

Related: #92657 addresses repeated waiting notices; its phase deduplication
still labels active streams as no response and is not incorporated here.
2026-09-07 02:37:13 -07:00
Teknium 47d73afeb1 refactor(agent): isolate non-stream request polling 2026-09-07 02:37:13 -07:00
Teknium 7874ef9f62 test: keep title workers out of persistence fixtures 2026-09-07 02:30:04 -07:00
Teknium a5d3bb4211 test(agent): isolate compression timeout phases under load 2026-09-07 02:30:04 -07:00
Teknium d8a07768c5 fix(cli): keypad Alt+Enter inserts a newline instead of escape text
Slim rework of despotak's modified-keypad fix in #97290. Mirror existing
non-keypad mappings for modified keypad keys, including lock-state variants,
so Alt+keypad Enter reaches the existing newline handler rather than leaking
[57414;3u into the draft. Preserve installed twin mappings before consulting
pending aliases, matching first-writer-wins registration.

Replace the source PR's keyed branch ladder with a format table and verify
parser parity plus real buffer insertion with two invariant tests. Document
keypad multiline support in English and Chinese.

Live PTY: the exact doubled leak after a real collapsed paste reproduces on
main; all 21 editor cases pass with the fix, including ordinary Enter and
legacy Alt+Enter controls. Whitespace also reproduces on main: adjacent
characters are not the root cause.

Co-authored-by: Christos Despotakis <christos@despotak.is>
2026-09-07 02:09:34 -07:00
Teknium c4a5deeffa test: retry busy shared-metrics writes after contention 2026-09-07 01:37:43 -07:00
Teknium a688e7d5ff docs: clarify when delegated results belong in one group 2026-09-07 01:23:34 -07:00
Teknium 72719c7c1b fix: show task-first subagent completion notices in CLI and TUI 2026-09-07 01:23:34 -07:00
Teknium fc8d15d779 test: retain a reproducible CLI goal parity oracle 2026-09-07 00:51:41 -07:00
Teknium aacaeff42f fix: share goal controls while messaging turns are active 2026-09-07 00:51:41 -07:00