Commit Graph

13 Commits

Author SHA1 Message Date
kshitijk4poor cdd58810a4 fix(plugins): keep one worker per callback while a timed-out worker is still running
Gating hook callbacks by call identity lets two concurrent calls of the
same tool both run their hooks, but it also let a fresh tool_call_id pass
the gate once the 60 s suppression window lapsed even though the previous
worker for that callback never returned. A hung plugin then leaked one
daemon thread per minute for the life of the process; on the old
coarse-keyed gate it leaked exactly one.

Track abandoned-but-running workers per callback: the timeout branch
records the gate key, the worker's own release discards it, and the gate
treats any non-empty abandoned set as "still running" for that callback.
Healthy callbacks keep distinct-id concurrency; hung ones are back to
at most one outstanding worker.
2026-09-15 10:48:49 +05:30
deadczarvc 4121aa295a fix(plugins): gate hook callbacks by call identity, not by tool name alone
Concurrent invocations of the same tool in one session collapsed into a single
busy key (hook_name, id(cb)): the second invocation was reported as 'still
running' and dropped. For pre_tool_call a drop is a fail-closed block, so the
gate silenced itself on an ordinary, healthy callback.

Measured on a busy profile: 3574 skip lines and 0 timeout lines in one hour —
every skip was the 'while still running' branch, i.e. pure key collision, not
slowness.

The gate now keys on the call identity that is already in the payload
(tool_call_id, else turn_id, else none — the last case behaves exactly as
before). Suppression stays keyed coarsely on (hook_name, id(cb)): a hung
callback is a fact about the callback, so its back-off must not be diluted
per call.

Refs #98382. Independent of #107894 (that one releases the slot on timeout;
this one stops healthy concurrency from colliding).

(cherry picked from commit 53b3dacd008418fcdf5fa6dfcadde575a35a776e)
2026-09-15 10:48:49 +05:30
teknium1 24444e52eb fix(plugins): await async hook callbacks instead of collecting bare coroutines
Slash-command handlers gained loop-safe awaiting in ca9a61ae38, but
`PluginManager.invoke_hook` still called `async def` hook callbacks directly:
the coroutine object was appended to the results (so `pre_llm_call` context
injection silently did nothing) and Python warned "coroutine was never
awaited". `_invoke_hook_callback` now routes every return through
`resolve_plugin_command_result`, which covers both the direct and the
timeout-bounded paths and is safe under the gateway's running loop.

Fixes #12449 (remaining hook half). Salvage of #63240 by @Bartok9, applied
one layer down so the bounded-worker path is covered too.

Co-authored-by: Bartok9 <Bartok9@users.noreply.github.com>
2026-09-12 08:26:48 -07:00
kshitij a188c84871 refactor(plugins): share the hook-token release between runner and start-failure paths
The identity-guarded token pop appeared twice (the runner's finally and the
new worker-start except); a future edit to one copy would silently reintroduce
the sticky-token class. Hoist into a local closure called from both sites, and
extend the _run_hook_callback_bounded docstring with the new skip reason.
Behavior-preserving follow-up on #104651.
2026-09-07 13:11:36 +05:30
Bruce Xu 5f561efe67 fix(plugins): recover when hook worker cannot start 2026-09-07 13:11:36 +05:30
Teknium e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium d127749b23 refactor(hermes_cli): inline single-use plumbing helpers (portable manifest read, pulled-revision record, entrypoint listing, timeout block) 2026-09-02 22:24:36 -07:00
Teknium 761686347c refactor(hermes_cli): hug closing brackets in plugin CLI modules (AST-identical) 2026-09-02 21:10:03 -07:00
Teknium b10a148053 refactor(hermes_cli): AST-neutral statement packing across plugin CLI modules 2026-09-02 20:37:05 -07:00
Teknium 53d082c678 refactor(hermes_cli): compact plugins_dispatch constants and dispatch bodies 2026-09-02 20:24:45 -07:00
Teknium 7f02cb7e1e refactor(cli/plugins): trim re-exports to referenced names; drop isort blanks after in-function imports 2026-09-02 15:57:40 -07:00
Teknium d7c64815a7 refactor(cli/plugins): repack multi-line statements to <=100 cols (AST-identical) across plugins*.py 2026-09-02 15:53:53 -07:00
Teknium f0cfbb33e1 refactor(cli/plugins): move hook/event-bus/prompt-section dispatch into PluginDispatchMixin (plugins_dispatch.py) 2026-09-02 15:41:50 -07:00