ask: rename ask_operator → ask + optional 'to' for agent-to-agent Q&A
This commit is contained in:
parent
87f8f8a123
commit
82b0877c47
21 changed files with 640 additions and 266 deletions
|
|
@ -248,16 +248,25 @@ package legitimacy, cheaper alternative, blast radius) before
|
|||
committing and calling `request_apply_commit`.
|
||||
|
||||
For ambiguous cases or anything that needs human signal, the
|
||||
manager calls `ask_operator(question, options?, multi?,
|
||||
ttl_seconds?)` — queues the question on the dashboard and returns
|
||||
the id immediately. The operator's answer arrives later as
|
||||
`HelperEvent::OperatorAnswered` in the manager inbox. Storage is
|
||||
`hive-c0re::operator_questions` (sqlite); the answer flow is:
|
||||
manager calls `ask(question, options?, multi?, ttl_seconds?, to?)` —
|
||||
queues the question and returns the id immediately. When `to` is
|
||||
omitted (or `"operator"`) the question shows up on the dashboard;
|
||||
when `to` is a sub-agent's name, the recipient receives a
|
||||
`HelperEvent::QuestionAsked` and answers via their own `answer`
|
||||
tool. Either way the answer arrives back as
|
||||
`HelperEvent::QuestionAnswered { id, question, answer, answerer }`
|
||||
in the asker's inbox. Storage is `hive-c0re::operator_questions`
|
||||
(sqlite) — same table, with a nullable `target` column
|
||||
(NULL = operator). Dispatch goes through
|
||||
`hive-c0re/src/questions.rs::{handle_ask, handle_answer}` so both
|
||||
the agent + manager surfaces stay aligned. The answer flow is:
|
||||
|
||||
```
|
||||
POST /answer-question/{id}
|
||||
→ OperatorQuestions::answer
|
||||
→ notify_manager(OperatorAnswered { id, question, answer })
|
||||
POST /answer-question/{id} agent: Answer { id, answer }
|
||||
→ OperatorQuestions::answer(_, _, "operator") → questions::handle_answer
|
||||
→ notify_agent(asker, QuestionAnswered { → OperatorQuestions::answer(_, _, agent)
|
||||
answerer: "operator", ... }) → notify_agent(asker, QuestionAnswered {
|
||||
answerer: agent, ... })
|
||||
```
|
||||
|
||||
Two more paths resolve a pending question with a sentinel answer:
|
||||
|
|
@ -301,9 +310,14 @@ regular claude turn so the manager can react. Variants
|
|||
- `NeedsUpdate { agent }` — sub-agent's recorded flake rev is
|
||||
stale. Manager calls `update(name)` to rebuild — idempotent,
|
||||
no approval required.
|
||||
- `OperatorAnswered { id, question, answer }` — dashboard
|
||||
`/answer-question/{id}` after the operator submits the answer
|
||||
form.
|
||||
- `QuestionAnswered { id, question, answer, answerer }` —
|
||||
dashboard `/answer-question/{id}` (answerer = `"operator"`),
|
||||
peer `Answer` request (answerer = agent name), or ttl watchdog
|
||||
expiry (answerer = `"ttl-watchdog"`, answer = `"[expired]"`).
|
||||
- `QuestionAsked { id, asker, question, options, multi }` —
|
||||
fired when an agent calls `Ask { to: Some(<this-agent>), ... }`.
|
||||
The recipient responds via `Answer { id, answer }` and the
|
||||
asker sees the matching `QuestionAnswered`.
|
||||
|
||||
To add a new event: new `HelperEvent` variant + call sites + update
|
||||
`prompts/manager.md` so the manager knows the new shape.
|
||||
|
|
|
|||
|
|
@ -12,10 +12,15 @@ Three tables, all in one file:
|
|||
`sender / recipient / body / sent_at / delivered_at`.
|
||||
- `approvals` — the queue. `agent / kind (apply_commit | spawn) /
|
||||
commit_ref / requested_at / status / resolved_at / note`.
|
||||
- `operator_questions` — `ask_operator` queue.
|
||||
- `operator_questions` — `ask` / `answer` queue (despite the
|
||||
file name, stores both operator-targeted + agent-to-agent
|
||||
questions since the `ask` rename).
|
||||
`asker / question / options_json / multi / asked_at /
|
||||
deadline_at (ttl) / answered_at / answer`. Migrated via
|
||||
`ALTER TABLE ADD COLUMN` against `pragma_table_info`.
|
||||
deadline_at (ttl) / answered_at / answer / target`. `target IS
|
||||
NULL` = operator path (dashboard); `target = '<agent>'` = peer
|
||||
Q&A (`HelperEvent::QuestionAsked` pushed into target's inbox,
|
||||
answered via `Answer` request). Migrated via `ALTER TABLE ADD
|
||||
COLUMN` against `pragma_table_info`.
|
||||
|
||||
Retention:
|
||||
|
||||
|
|
|
|||
|
|
@ -107,10 +107,18 @@ it as a stdio child via `--mcp-config`. The hyperhive socket name is
|
|||
"anything pending?" peek. Positive value parks the turn up
|
||||
to that many seconds (cap 180) — incoming messages wake
|
||||
instantly, otherwise returns empty at the timeout.
|
||||
- `ask_operator(question, options?, multi?, ttl_seconds?)` —
|
||||
surface a question on the dashboard. Same shape as the manager's;
|
||||
answer routes back to the asker's own inbox as
|
||||
`HelperEvent::OperatorAnswered` via `coord.notify_agent`.
|
||||
- `ask(question, options?, multi?, ttl_seconds?, to?)` —
|
||||
surface a structured question. Same shape as the manager's;
|
||||
recipient defaults to the operator (dashboard) but can be set
|
||||
to a peer agent name via `to: "<agent>"`. Answer routes back
|
||||
to the asker's own inbox as `HelperEvent::QuestionAnswered`
|
||||
via `coord.notify_agent`. For peer questions the recipient
|
||||
sees a `HelperEvent::QuestionAsked` event and replies with
|
||||
`answer(id, answer)`.
|
||||
- `answer(id, answer)` — respond to a `question_asked` event
|
||||
routed to this agent. Authorisation is strict: only the
|
||||
declared target (or the operator via the dashboard) can
|
||||
answer.
|
||||
|
||||
### Waking the agent from inside the container
|
||||
|
||||
|
|
@ -167,16 +175,22 @@ meta's.
|
|||
- `request_apply_commit(agent, commit_ref)` — submit a config
|
||||
change for any agent (`hm1nd` for the manager's own config) for
|
||||
operator approval.
|
||||
- `ask_operator(question, options?, multi?, ttl_seconds?)` —
|
||||
surface a question on the dashboard. Non-blocking — returns the
|
||||
queued question id; the operator's answer arrives later as
|
||||
`HelperEvent::OperatorAnswered` in the manager inbox. Options
|
||||
always render alongside a free-text fallback; `multi=true`
|
||||
renders options as checkboxes. `ttl_seconds` auto-cancels with
|
||||
answer `[expired]` after the deadline (useful for time-sensitive
|
||||
decisions that become moot if the operator hasn't responded).
|
||||
The operator can also manually cancel with `[cancelled]` via the
|
||||
dashboard.
|
||||
- `ask(question, options?, multi?, ttl_seconds?, to?)` —
|
||||
surface a structured question to the operator (default) or a
|
||||
sub-agent (`to: "<agent>"`). Non-blocking — returns the
|
||||
queued question id; the answer arrives later as
|
||||
`HelperEvent::QuestionAnswered { id, question, answer,
|
||||
answerer }` in the asker's inbox. Options always render
|
||||
alongside a free-text fallback; `multi=true` renders options
|
||||
as checkboxes. `ttl_seconds` auto-cancels with answer
|
||||
`[expired]` (and `answerer: "ttl-watchdog"`) after the
|
||||
deadline (useful for time-sensitive decisions that become moot
|
||||
if no one has responded). The operator can also manually
|
||||
cancel with `[cancelled]` via the dashboard.
|
||||
- `answer(id, answer)` — respond to a `question_asked` event
|
||||
that was routed to the manager (a sub-agent did
|
||||
`ask(to: "manager", ...)`). Surfaces in the asker's inbox as
|
||||
the same `question_answered` event.
|
||||
|
||||
The boundary: lifecycle ops on *existing* sub-agents
|
||||
(`kill`/`start`/`restart`) are at the manager's discretion — no
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ the previous process's socket release resolves itself.
|
|||
age + claude-creds badge). Two actions: `⊕ R3V1V3` (queues a
|
||||
Spawn approval; existing state is reused), `PURG3` (wipes
|
||||
state + applied dirs; `POST /purge-tombstone/{name}`).
|
||||
4. **M1ND H4S QU3STI0NS** — pending `ask_operator` questions
|
||||
4. **M1ND H4S QU3STI0NS** — pending operator-targeted `ask`
|
||||
questions, i.e. rows with `target IS NULL` (peer-to-peer
|
||||
questions live in the same table but never surface here)
|
||||
(amber pulsing border). Free-text fallback always rendered
|
||||
alongside any option list; `multi=true` renders options as
|
||||
checkboxes; submit merges selections + free text comma-joined.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue