Commit graph

2093 commits

Author SHA1 Message Date
aqilaziz
1733cb3a13 feat(kanban): configure worktree paths and branches
Salvages #26496 by @aqilaziz. Adds branch_name column + CLI flag so
tasks with workspace_kind='worktree' can pin a target branch on
create. Schema migration added to _migrate_add_optional_columns.

- Task.branch_name field + DB column + migration
- create_task accepts branch_name kwarg
- hermes kanban create --branch <name> flag
- kanban show output includes 'Branch: <name>' when set

Cherry-picked the substantive commit (a7558cf27); the PR's tip was
an unrelated service-path-dirs commit. Resolved 2 INSERT-column-list
and show-output conflicts alongside main's session_id and
max_runtime_seconds additions; kept all three.
2026-05-18 21:33:08 -07:00
Teknium
53cf82a1ea
fix(kanban): remove orphan conflict markers from kanban.py (#28459)
PR #28454 (salvage of #26745, workflow filter) merged with leftover
git conflict markers in hermes_cli/kanban.py at three sites:
- _task_to_dict() (session_id alongside workflow_template_id/current_step_key)
- p_list parser (--sort alongside --workflow-template-id/--step-key)
- _cmd_list (order_by alongside the new filter kwargs)

Cleans up the markers and keeps both halves at each site.

Resolves a self-introduced regression.
2026-05-18 21:29:31 -07:00
Teknium
1a883b421f
fix(kanban): remove orphan conflict markers from config.py (#28458)
PR #28452 (salvage of #23790, stale detection) merged with leftover
git conflict markers in hermes_cli/config.py around the
`dispatch_stale_timeout_seconds` config block, breaking config import
and any code path that loads it. Cleans up the markers and keeps both
config blocks (worker log rotation/orchestrator + stale detection).

Resolves a self-introduced regression.
2026-05-18 21:27:58 -07:00
SerenityTn
1a5172742e feat(kanban): show dashboard cron jobs across profiles
Salvages #27568 by @SerenityTn. Dashboard cron page now lists cron
jobs from all profiles, with profile-aware filter UI and storage
routing. Includes test coverage for cross-profile listing, mutation,
deletion, and validation.

Also fixes orphan conflict markers in config.py left by an earlier
salvage merge (kanban.dispatch_stale_timeout_seconds was double-nested
in HEAD/PR markers from #28452 salvage of #23790).
2026-05-18 21:26:45 -07:00
fardoche6
264e85b3dd feat(kanban): add respawn guard to block repeat worker storms
Salvages #27484 by @fardoche6. Adds a respawn guard that skips worker
spawn for tasks where:
- a recent run already succeeded (recent_success — within guard window)
- the previous run hit a quota/auth error (blocker_auth, also auto-blocks)
- a recent task comment includes a GitHub PR URL (active_pr)

The guard prevents repeat worker storms on the same bug/task. Includes
the contributor's review-findings fixup (regex hardening, observability,
auth coverage).

Resolved a small DispatchResult conflict alongside main's 'stale' field;
kept both. Authorship preserved via rebase merge.
2026-05-18 21:24:19 -07:00
nehaaprasaad
341912c224 feat(kanban): filter tasks by workflow fields and runs by status/outcome
Salvages #26745 by @nehaaprasaad. Exposes filtering for the existing
workflow_template_id and current_step_key columns:

- list_tasks() accepts workflow_template_id and current_step_key kwargs
- 'hermes kanban list' adds matching CLI flags
- dashboard plugin_api also exposes the filters

Resolved a small conflict in list_tasks signature alongside main's
session_id and order_by additions; combined all three into the single
filter list.
2026-05-18 21:22:32 -07:00
thewillhuang
e286e68756 feat(kanban): stale detection for running tasks in dispatcher
Salvages #23790 by @thewillhuang. Adds detect_stale_running() to
the dispatcher cycle. Running tasks that have been started for longer
than dispatch_stale_timeout_seconds (default 14400 = 4h) without a
heartbeat in the last hour are auto-reclaimed to ready.

- New config kanban.dispatch_stale_timeout_seconds (default 14400, 0 disables)
- New 'stale' field on DispatchResult
- detect_stale_running() in kanban_db.py with heartbeat freshness check
- Records outcome='stale' on run close + 'stale' event; ticks failure counter
- Wires config through gateway embedded dispatcher
- Updates _cmd_dispatch verbose/JSON output and daemon logging

Resolved test-file end-of-file conflict by appending both halves.
2026-05-18 21:20:56 -07:00
thewillhuang
f55d94a1e0 feat(kanban): wire dispatcher to dispatch review agents from review column
Salvages #23772 by @thewillhuang. Adds 'review' as a valid kanban task
status and extends dispatch_once to monitor the review column as a
second dispatch source (in addition to the existing ready column).

- Adds 'review' to VALID_STATUSES
- Adds claim_review_task() — atomically transitions review → running
- Adds has_spawnable_review() — health telemetry mirror
- Extends dispatch_once with a review column dispatch loop
- Review agents get 'sdlc-review' skill auto-loaded

Resolved 2 conflicts (VALID_STATUSES merge with main's 'scheduled' state,
test file additions). Adapted claim_review_task to main's
ttl_seconds: Optional[int] = None convention (matches claim_task).
2026-05-18 21:19:51 -07:00
awizemann
31fe229039 feat(kanban): stamp originating ACP session_id on tasks
Salvages #23208 by @awizemann. Tracks which chat session created a
kanban task so clients can render a per-session board without falling
back to tenant + time-window heuristics.

- Schema: tasks gains nullable session_id TEXT column with index
  (additive migration in _migrate_add_optional_columns).
- ACP: server.py exposes the originating session id via HERMES_SESSION_ID
  with save/restore around the agent loop.
- Tool: kanban_create reads HERMES_SESSION_ID (with explicit override).
- CLI: 'hermes kanban list --session <id>' filter; JSON output exposes
  session_id.
2026-05-18 21:15:21 -07:00
Niraven
3ee7a5546d feat(cli): add kanban swarm topology helper
Salvages #26791 by @Niraven. Adds 'hermes kanban swarm' to create a
durable Kanban Swarm v1 graph: a completed root/blackboard card,
parallel worker cards, a verifier gated on all workers, and a
synthesizer gated on the verifier. Stores shared swarm blackboard
updates as structured JSON comments on the root card.

Self-contained: new hermes_cli/kanban_swarm.py module + CLI wiring +
unit tests.
2026-05-18 21:10:12 -07:00
loicnico96
79f6654d16 feat(kanban): surface per-task model_override in show + tool output
Salvages #26897 by @loicnico96. The per-task model_override DB column
already exists on main, but it wasn't exposed in user-facing surfaces.
This adds:
- 'kanban show' prints 'model: <name>' when model_override is set
- kanban_show / kanban_list tool responses include the model_override field

Original branch was stale (PR was authored against an older field name
'model'); applied the substantive surface exposure manually using the
current 'model_override' field name.
2026-05-18 21:09:02 -07:00
LizerAIDev
a846e500b0 feat(kanban): add --sort option to 'hermes kanban list'
Salvages #25745 by @LizerAIDev. Adds --sort {created,created-desc,
priority,priority-desc,status,assignee,title,updated} to 'hermes kanban
list'. Validated against VALID_SORT_ORDERS map; invalid values raise
ValueError. Default behaviour (priority DESC, created ASC) is unchanged
when --sort is omitted.
2026-05-18 20:58:43 -07:00
Bartok9
365da2d2df fix: 4 small surgical bugs
Salvages #23302 by @Bartok9. Four independent one-area fixes:

1. kanban boards delete alias now hard-deletes (not archives) — the
   alias didn't carry --delete, so getattr(args, 'delete', False)
   returned False. Detect boards_action=='delete' explicitly.
2. Gateway auto-title failures no longer leak as user-visible
   warnings — debug-log only since they're not actionable.
3. Background process completion notification snaps truncation to
   the next newline boundary, prepends a marker when content is
   dropped.
4. _cprint() schedules the run_in_terminal coroutine via
   asyncio.ensure_future so output isn't silently dropped from
   background threads (fixes #23185 Bug A). Skips the
   double-print fallback that would fire for mock paths.
2026-05-18 20:54:52 -07:00
SimbaKingjoe
5fdcfd851f feat(kanban): add max_in_progress config to cap concurrent running tasks
Salvages #22981 by @SimbaKingjoe. Adds 'kanban.max_in_progress' config
that caps simultaneously running tasks. When the board already has N
running, dispatcher skips spawning so slow workers (local LLMs,
resource-constrained hosts) don't pile up and time out.

Threads through dispatch_once(max_in_progress=) and gateway dispatcher
config parsing with validation (warns on invalid/below-1 values).
2026-05-18 20:50:13 -07:00
LeonSGP43
a94ddd8073 fix(kanban): honor severity thresholds in diagnostics
Salvages #26431 by @LeonSGP43. Dashboard plugin_api list_diagnostics
was using exact-match (severity == filter), so '--severity warning'
hid 'error' and 'critical' diagnostics. Adds severity_at_or_above()
helper to kanban_diagnostics and uses it in the dashboard endpoint
(CLI already used SEVERITY_ORDER comparison correctly).
2026-05-18 20:47:01 -07:00
LeonJS
9f008bcd5c fix(kanban): release scratch workspace and tmux session on task completion
Salvages #27369 by @LeonJS. complete_task() now calls _cleanup_workspace()
and _cleanup_worker_tmux() after marking a task complete.

Scratch workspaces (used by swarm agents) accumulate on disk — hundreds
of MB per task, never released. Stale tmux sessions from completed
agents also persist indefinitely.

Both gates are safe:
- workspace_kind == 'scratch' gate preserves user worktree/dir workspaces
- tmux #{pane_dead} == 1 gate only kills sessions where the worker has
  already exited
- best-effort: cleanup failures never block task completion
2026-05-18 20:45:29 -07:00
shunsuke-hikiyama
fb96208892 feat(kanban): add initial-status for human-ops cards
Salvages #27526 by @shunsuke-hikiyama. Adds an --initial-status flag
(running|blocked, default running) to 'kanban create', threaded through
kanban_db.create_task() and the kanban_create tool schema. 'blocked'
parks the task directly in the blocked column for R3 human-ops review,
skipping the brief running-to-blocked transition.

Dropped the unrelated 'add' alias, WIFEXITED Windows compat, and
slash-handler error formatting changes that were bundled in the
original PR — those should ship as their own focused changes if still
wanted.
2026-05-18 20:44:02 -07:00
uzunkuyruk
666b66a066 fix(oneshot): pass fallback_providers from profile config to AIAgent
Salvages #23368 by @uzunkuyruk. Oneshot workers (e.g. kanban workers
spawned via 'hermes -p <profile> chat -q ...') were not honouring the
profile's fallback_providers / fallback_model chain because oneshot.py
never read the config and never passed fallback_model= to AIAgent.

Reads cfg.get('fallback_providers') (new list format) or
cfg.get('fallback_model') (legacy single-dict) with the same
normalization cli.py applies, then forwards as fallback_model=_fb.
2026-05-18 20:37:23 -07:00
oemtalks
b9d38a56dd fix(kanban): don't crash dispatched workers when kanban-worker skill is absent
Salvages #27372 by @oemtalks. The dispatcher unconditionally injected
`--skills kanban-worker` into every worker spawn, but worker profiles
sometimes don't have that bundled skill in their skills dir, which is
fatal at CLI startup (`ValueError: Unknown skill(s): kanban-worker`).

Adds `_kanban_worker_skill_available(hermes_home)` and only injects the
flag when the skill resolves. The MANDATORY lifecycle still ships via
KANBAN_GUIDANCE in the system prompt, so omitting the flag is safe.
2026-05-18 20:32:20 -07:00
Ade5954
0392cf53b5 fix(kanban): close sqlite connection on init failure to prevent fd leak
Salvages #28301 by @Ade5954. If WAL setup, PRAGMA application, or schema
init raises after sqlite3.connect() succeeds, the new connection was
leaking. Wrap the body in try/except so the connection is closed before
the exception propagates.
2026-05-18 20:30:56 -07:00
DoGMaTiiC
4da4133d34 fix: assign single-task kanban decompositions 2026-05-18 20:26:02 -07:00
roycepersonalassistant
6c4f11c64a fix: show scheduled kanban tasks in dashboard 2026-05-18 20:25:45 -07:00
ACR27
a5c2836b07 feat(kanban): allow trimmed task comments
SS-1647 live SHIP validation: real code + tests for kanban comment --max-len.
2026-05-18 20:25:29 -07:00
hanzckernel
5d079fee17 fix: harden Kanban worker Hermes command resolution 2026-05-18 20:25:09 -07:00
ht1072
0b547aea03 fix(kanban): make legacy task migration idempotent
(cherry picked from commit 293f1c3a7241b0117669e049d9aa746c9645ac90)
2026-05-18 20:24:53 -07:00
zccyman
fe5e0bf5a3 feat(kanban): add board-level default workdir (#25430) 2026-05-18 20:24:04 -07:00
LeonSGP43
8bfb456948 fix(kanban): pass accept-hooks to worker chat subprocess 2026-05-18 20:23:47 -07:00
LeonSGP43
0f620138b0 fix(kanban): make claim ttl configurable
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-18 20:23:31 -07:00
Interstellar-code
d8ad431de8 fix(kanban): task_age() tolerates ISO-8601 timestamps
Prevents ValueError crash in dashboard get_board() when a task has
an ISO timestamp (e.g. "2026-05-10T15:00:00Z") instead of a unix epoch
int. Adds _to_epoch() helper that normalises both formats.
2026-05-18 20:18:04 -07:00
psionic73
ca8126bd53 fix(kanban): serialize DB initialization 2026-05-18 20:17:48 -07:00
soynchux
9281599b6f fix(kanban): align board_exists with board discovery rules 2026-05-18 20:17:10 -07:00
bradhallett
de9bcfc6a0 fix(kanban): fingerprint crash errors to prevent fleet-wide retry exhaustion
When a systemic failure (provider outage, auth expiry, OOM) crashes
multiple workers simultaneously, detect_crashed_workers increments
each task failure counter independently. The circuit breaker only
trips after N × failure_limit retries across the fleet.

Fingerprint crash errors by normalizing host-specific details (PIDs,
timestamps). When 3+ tasks crash with the same fingerprint in a
single detection cycle, immediately trip the circuit breaker
(failure_limit=1) instead of waiting for repeated failures.

Isolated crashes (unique fingerprints) retain their normal retry
budget. Protocol violations continue to trip immediately.

Includes regression tests for systemic and isolated crash paths.
2026-05-18 20:16:50 -07:00
bradhallett
f042931852 fix(kanban): reset failure counters on unblock_task
When a task is manually unblocked (blocked → ready/todo), the
consecutive_failures counter and last_failure_error were left intact.
The next failure would immediately re-trip the circuit breaker because
the counter was still at or above the failure limit.

Reset both fields on unblock so the task gets a fresh retry budget.

Includes a regression test that verifies counters are zeroed.
2026-05-18 20:16:32 -07:00
sprmn24
5db0d72c90 fix(kanban): use 'is not None' check for max_runtime_seconds in create_task
max_runtime_seconds=0 was being silently coerced to None due to a falsy
check (if max_runtime_seconds). Zero is a valid value that causes the
dispatcher to immediately time out a task. The adjacent max_retries
parameter already used the correct 'is not None' pattern.

Fixes the inconsistency by aligning max_runtime_seconds with max_retries.
2026-05-18 20:16:15 -07:00
bradhallett
40c1decb3b fix(kanban): promote blocked tasks when parent dependencies complete
recompute_ready only scanned 'todo' tasks for promotion, ignoring
'blocked' tasks entirely. When a task was blocked (e.g. by the circuit
breaker) and its parent dependencies later completed, the task stayed
stuck in 'blocked' forever unless manually unblocked.

Now recompute_ready also scans 'blocked' tasks. When all parents are
done/archived, the blocked task is promoted to 'ready' with failure
counters reset — equivalent to an automatic unblock.

Includes a regression test for the blocked-parent-done promotion path.
2026-05-18 20:15:55 -07:00
Que0x
bc961c13f3 fix(kanban): sync slash subcommands with live parser 2026-05-18 20:15:38 -07:00
argabor
f149e1e567 fix(cli): make kanban specify max_tokens configurable 2026-05-18 20:15:20 -07:00
Zyrixtrex
b7ea62e5d3 fix(kanban): promote dependents when a parent is archived 2026-05-18 20:15:03 -07:00
Zyrixtrex
326c15d955 fix(kanban): preserve notifier_profile for dashboard home subscriptions 2026-05-18 20:14:45 -07:00
QuenVix
afae2dd9ec fix(kanban): keep board-management commands independent from board override 2026-05-18 20:14:27 -07:00
QuenVix
8a64e1580b fix(kanban): ignore stale HERMES_KANBAN_BOARD for removed boards 2026-05-18 20:14:10 -07:00
ms-alan
97ac94fe56 fix(kanban): seed bundled skills (e.g. kanban-worker) on kanban init
Closes #23725
2026-05-18 20:13:52 -07:00
momowind
4519d2b476 fix(web): add Cache-Control: no-store to plugin static file serving
Prevents browser caching of stale dashboard plugin JS files that may
contain bugs already fixed upstream (e.g. COLUMN_LABEL undefined).
2026-05-18 20:13:35 -07:00
briandevans
d62964cdfa fix(kanban): clear _INITIALIZED_PATHS in remove_board so recycled DBs re-init schema
Archiving or deleting a board via remove_board() leaves the path's
"schema already initialized" entry in the module-level cache. A
concurrent connect(board=<slug>) call (e.g. the dashboard event-stream
poll loop) then:

  1. resolves the same kanban.db path,
  2. recreates the directory + an empty sqlite file because
     connect() does mkdir(parents=True, exist_ok=True),
  3. skips the CREATE TABLE pass because the cache entry says the
     schema is already in place,
  4. errors on the next read with `no such table: task_events`.

Drop the cache entry before mutating the filesystem so the fresh file
gets a proper schema init on next connect(). Applies to both
archive=True (rename) and archive=False (rmtree) branches.

Fixes #23833.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:13:17 -07:00
hongchen1993
f01ee0b575 feat: per-task model override for kanban workers
- Add model_override field to Task class and tasks schema
- Add migration for existing databases
- Spawn worker with -m model when model_override is set
2026-05-18 20:12:28 -07:00
xxxigm
cafbc9a734 feat(cli): wire --manual-paste into `hermes auth add and hermes model`
Register the new ``--manual-paste`` flag on both entry points and
thread it through to the xAI loopback login:

* ``hermes auth add xai-oauth --manual-paste`` — pool-add path,
  forwarded inside ``auth_commands.handle_auth_add``.
* ``hermes model --manual-paste`` — model-picker path, forwarded
  by ``_model_flow_xai_oauth`` into the synthetic ``argparse.Namespace``
  it passes to ``_login_xai_oauth``.  The picker also now forwards
  ``--no-browser`` and ``--timeout`` for consistency (previously
  hardcoded to defaults regardless of CLI flags).

Help text on both flags points at #26923 and names the
browser-only remote consoles (Cloud Shell, Codespaces, EC2
Instance Connect) so users searching ``hermes --help`` can find
the workaround.
2026-05-18 20:10:52 -07:00
xxxigm
5a5c265bcf fix(oauth): add manual-paste fallback for browser-only remote consoles
xAI Grok OAuth (and Spotify) use a loopback redirect to
``http://127.0.0.1:<port>/callback`` to capture the authorization
code. That works when the browser and Hermes run on the same
machine, and the SSH tunnel recipe handles the regular remote
case. It breaks completely on **browser-only remote consoles**
(GCP Cloud Shell, GitHub Codespaces, AWS EC2 Instance Connect,
Gitpod, Replit, …) where the user has a browser but no real SSH
client to forward a port — the redirect to 127.0.0.1 on the
remote VM simply isn't reachable from the laptop, and there's
nothing the existing flow can do about it (#26923).

This commit adds the foundation for a manual-paste fallback:

* ``_is_remote_session`` now also recognises Cloud Shell,
  Codespaces, Gitpod, Replit, StackBlitz (in addition to SSH),
  so the existing tunnel hint at least fires in those
  environments.
* ``_parse_pasted_callback`` accepts any of: a full
  ``http(s)://...?code=...&state=...`` URL, a bare ``?code=...``
  query string, a bare ``code=...&state=...`` fragment, or a
  bare opaque code value.  Returns the same dict shape the HTTP
  callback handler produces, so the caller's state / error
  validation works unchanged (no CSRF bypass).
* ``_prompt_manual_callback_paste`` reads stdin with a clear
  multi-line explanation of what's happening and what to paste.
* ``_xai_oauth_loopback_login`` gains a ``manual_paste`` kwarg
  that skips the HTTP listener entirely.  The redirect_uri,
  PKCE verifier, state, and nonce are byte-identical to the
  loopback path so xAI's token endpoint can't tell the
  difference at the protocol level.
* ``_print_loopback_ssh_hint`` now also mentions
  ``--manual-paste`` so users without a real SSH client see a
  path forward instead of a dead-end tunnel recipe.
* ``_login_xai_oauth`` threads ``args.manual_paste`` into the
  loopback helper.
2026-05-18 20:10:52 -07:00
yannsunn
1d6f3753de feat(proxy): add xai upstream adapter for Grok via OAuth 2026-05-18 20:09:32 -07:00
Beandon13
bde6313e34 feat(kanban): archive --rm to hard-delete archived tasks
Salvages #19964 by @Beandon13. Adds `hermes kanban archive --rm` to
permanently remove already-archived tasks with cascading cleanup of
links, comments, events, runs, and notify-subs. Safety guard: only
archived tasks can be deleted; active/blocked/done must be archived
first.

Cherry-picked from #19964 onto current main (severe stale base, applied
manually to preserve substance only).
2026-05-18 20:09:26 -07:00
xxxigm
60ef368792 fix(xai-oauth): split 403 (tier/entitlement) from 400/401 in token endpoint
xAI's token endpoint returns HTTP 403 to the OAuth grant when the
account isn't on the allowlist for API access (e.g. standard
SuperGrok subscribers — see #26847). Treating it like a stale-token
400/401 made ``format_auth_error`` append "Run ``hermes model`` to
re-authenticate", which is misleading because re-login can't change
xAI's tier decision.

Split 403 off in both ``refresh_xai_oauth_pure`` and the loopback
login token exchange:

* New error code ``xai_oauth_tier_denied`` with ``relogin_required=False``
* Message explains the entitlement gate and points at the
  ``XAI_API_KEY`` + ``provider: xai`` fallback
* 400/401 still set ``relogin_required=True`` as before
* 5xx still set ``relogin_required=False`` as before
2026-05-18 20:08:09 -07:00