Thread mode: coordinator/worker threads (plugin side) - #45
Open
promptclickrun wants to merge 2 commits into
Open
promptclickrun wants to merge 2 commits into
promptclickrun wants to merge 2 commits into
Conversation
Implements the plugin half of the thread-mode protocol contract (PROTOCOL.md): - Four loopdy tools: thread_spawn, thread_status, thread_collect, thread_note - Plugin state under threadmode:<coordinator_session_id> and threadmode:worker:<worker_session_id> keys - Additive pre_llm_call injection (coordinator + worker) and subagent_start/subagent_stop observers - App out-of-turn REST: POST /native/threads/flag, POST /native/threads/register, GET /native/threads/roster - 51 new tests in tests/test_thread_mode.py; full suite matches main baseline
Thread mode described manual worker creation as a normal path, which made the Threads surface feel like a setup form. Make the coordinator decide when parallel work helps, create and name workers itself, and return the assembled result in the ongoing chat.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Optional per-conversation "thread mode" (plugin side), modeled on Anthropic's redesigned Projects: one coordinator conversation fans scoped work out to parallel worker threads, then assembles the finished result. Implemented per the locked protocol contract in
PROTOCOL.md(verified against loopdy-plugin @ 83d05d2 and hermes-agent @ 287c56e9).Contract summary (PROTOCOL.md)
Tools (toolset
loopdy, inplugin.yamlprovides_tools):thread_spawn {threads: [{name, brief, context?}]}— launches in-process subagent workers viactx.subagent_lifecycle.launch(SubagentLaunchRequest(goal=brief, context=<worker discipline>, role="leaf", correlation_id=name, metadata={threadmode, thread})). Persists each thread record with statusspawningBEFORElaunch()(closes the pre_llm_call race). Outside a live turn it fails clean with: "thread_spawn needs a live coordinator turn (no active Hermes parent session). Create the worker from the Loopdy thread view instead." Never leaks tracebacks.thread_status {}— refreshes subagent-kind threads vialifecycle.status()(SubagentState mapped to the contract vocabulary), reports stored status for session-kind threads, returns the roster.thread_collect {thread_name}— subagent kind: non-blockinglifecycle.result(); parses the first## Resultheading section (falls back to full text). Session kind: best-effort read of the worker session's recent messages, else returns a hint to read the worker session directly.thread_note {thread_name?, note}— scratch notes, capped at 50 per thread / 2000 chars each; no thread_name = coordinator-level note.State (profile-scoped PluginState via
ctx.state):threadmode:<coordinator_session_id>— coordinator record {version, enabled, threads{name, brief, kind, status, subagent_id, subagent_session_id, handle, worker_session_id, notes, result, timestamps}}.threadmode:worker:<worker_session_id>— {coordinator_session_id, thread} index, written atsubagent_startand at REST/register.^[a-z0-9][a-z0-9_-]{0,63}$, unique per coordinator.REST (mounted in
dashboard/plugin_api.pyas/api/plugins/loopdy/native/threads/*):POST /flag {session_id, enabled}— app calls on every open/resume while its toggle is on.POST /register {coordinator_session_id, name, worker_session_id, brief}— 409 on duplicate name; registers session-backed workers (title convention"Thread: <name>").GET /roster?coordinator_session_id=…— coordinator record minus raw handle dict.Injection (additive
pre_llm_call, fail-silent, returns{"context": ...}):## Thread mode — coordinator(roster table, delegate/review guidance, memory discipline — ONLY the coordinator writesMEMORY.md).parent_session_idmatch or the worker index):## Your thread assignment(name, brief, scratch notes, sibling names+statuses, full worker discipline).subagent_startrecordschild_session_idand writes the worker index;subagent_stopmaps child_status (completed/interrupted/failed) to terminal thread statuses.Hard constraints honored: no Hermes changes, no plugin HTTP self-callbacks, no REST/fork for workers, no private data, focused diff.
Test results
tests/test_thread_mode.py: 51 tests, all green — four tools, state transitions, name validation, spawn-outside-turn clean error (realagent.subagent_lifecycleerror type),## Resultparsing, pre_llm_call coordinator/worker/index/fail-silent branches, subagent_start/stop observers, and the REST endpoints (real Hermes dashboard auth middleware + fixture provider).python -m unittest discover -s tests→ 949 tests; the failure/error set is byte-identical to the main-branch baseline (40 pre-existing, all environmental: missing optional services, git-fixture deps, timing-sensitive middleware tests). No regressions.Known gaps
platform == "loopdy"gate on the thread-mode pre_llm_call injection (the contract does not call for one; the discipline text references Loopdy-only tools).thread_collectdepends on a read-onlySessionDBread of the worker session; any failure falls back to the "read the worker session directly" hint by design.subagent_stophook payload carries nochild_subagent_id(onlychild_session_id+child_status); stop-matching uses thechild_session_idrecorded at start, per the contract's mapping rule.Future work (per contract §12)
The Anthropic-style project artifact/library layer (shared file library, generated-artifact accumulation) is explicitly out of scope here.
Conversation-first orchestration
Thread Mode follows the project-chat model: the user stays in one coordinator conversation and speaks normally. The coordinator decides when parallel work helps, chooses and launches worker threads without setup prompts, monitors them, collects results, and answers in the same chat. Simple work stays direct. Manual thread creation remains an optional fallback in the Threads rail.
Follow-up validation
python -m unittest tests.test_thread_mode→ 52 passed locally.