ask_operator tool: non-blocking; operator answer arrives as helper event
new mcp tool on the manager surface that queues a question on the
dashboard and returns the question id immediately. operator submits an
answer via /answer-question/<id>; the dashboard fires
HelperEvent::OperatorAnswered { id, question, answer } into the manager
inbox so the next turn picks it up.
also: fix async-form button stuck on spinner after successful submit
(refreshState skipped re-rendering, so the button was never re-enabled).
This commit is contained in:
parent
abfd2cce4b
commit
2770630f33
17 changed files with 426 additions and 79 deletions
43
TODO.md
43
TODO.md
|
|
@ -33,43 +33,12 @@ Pick anything from here when relevant. Cross-cutting design notes live in
|
|||
|
||||
## Manager → operator question channel
|
||||
|
||||
- **`mcp__hyperhive__ask_operator(question, options?)` tool** on the manager
|
||||
MCP surface. The manager turn pauses; the question gets surfaced as a
|
||||
prominent prompt on the dashboard (its own section, or interleaved with
|
||||
the operator inbox); the operator's typed answer comes back as the tool
|
||||
result. Modelled after Claude Code's `AskUserQuestion` tool.
|
||||
|
||||
Design open questions:
|
||||
|
||||
- **Storage.** New sqlite table `operator_questions(id, asker, question,
|
||||
options_json, asked_at, answered_at, answer)` — or piggyback on the
|
||||
existing message broker with a new envelope kind. Probably a new
|
||||
table because the lifecycle (pending → answered) is different from
|
||||
fire-and-forget messages.
|
||||
|
||||
- **Waiting semantics.** The MCP tool call needs to block until
|
||||
answered. Two options:
|
||||
1. Long-poll from inside the tool handler (broker-style — broadcast
|
||||
on insert, await via `tokio::sync::broadcast`). Simple but the
|
||||
claude turn stays alive for the whole wait, eating context-window
|
||||
budget.
|
||||
2. Tool returns a `question_id` immediately; manager re-enters its
|
||||
inbox loop and a `HelperEvent::OperatorAnswered { id, answer }`
|
||||
wakes it. Cheaper context-wise but two-step.
|
||||
|
||||
- **Dashboard UX.** New "◆ M1ND H4S QU3STI0NS ◆" section at the top
|
||||
when any question is pending. Inline `<form>` with a textarea (or
|
||||
select if `options` were provided), POST `/api/answer-question`.
|
||||
State refresh + the live SSE stream notify the manager harness.
|
||||
|
||||
- **Sub-agent path.** Sub-agents don't get the tool — they message the
|
||||
manager and the manager decides whether to relay the question to the
|
||||
operator. The manager's system prompt already covers this.
|
||||
|
||||
- **Timeout / cancel.** Questions that sit pending too long: do they
|
||||
expire? Manager probably wants to know if the operator hasn't
|
||||
answered after some interval so it can fall back. Maybe a per-
|
||||
question `ttl_seconds`.
|
||||
- **TTL / cancel on `ask_operator`.** Questions today block forever; the
|
||||
manager turn stays alive until the operator answers. Add a per-question
|
||||
`ttl_seconds` (or a dashboard "cancel" button that resolves the question
|
||||
with a sentinel answer) so a long-idle question can time out and let the
|
||||
manager fall back. Wire the timeout into `OperatorQuestions::wait_answered`
|
||||
and surface remaining-time on the dashboard.
|
||||
|
||||
## Loop substance
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue