Client side of the mod-answer contract (the endpoint and the payload are defined in the lich repository; this plugin only implements them).
Makes a lich subagent worker (a session opened with lich open --subagent,
which is what agent-cards opens for an Agent call) answer the
task it was handed the way a native subagent does: the final message of its
turn is its result. lich hands such a worker its task with no ticket and no
reply instructions, and answers the errand with what this mod posts.
| client | Claude Code | Codex | Antigravity | opencode | omp | Crush |
|---|---|---|---|---|---|---|
hooks/worker-answer.js |
mod, registered by hooks/lich.js, the modules entry |
none: no mod system | none: no mod system | none: no mod system | none: no mod system | none: no mod system |
| event | sent |
|---|---|
classic.Stop, then turn.complete{reason=answer} |
POST /mod/answer with {"session_id", "text"} |
classic.Stop with a blank message, then turn.complete{reason=answer} |
POST /mod/answer with {"session_id", "unanswered": "blank"} |
- Only a worker answers. It reads
LICH_SUBAGENT_DEPTHinsession.start, which lich sets on every Claude Code session it starts, and reports only when it is an integer above0: a session openedn--subagentlevels down carriesn, any other0. Under a lich older than that variable it readsLICH_SUBAGENT_CARDSinstead and reports only when it isoff, which that lich sets on every--subagentsession. A session the user turned subagent cards off for carries it too; lich ignores a report from a session with no subagent errand open. - A turn that handed work to the background does not answer. Its main-loop
classic.Stoplists what is still running inbackground_tasks(a shell, a subagent, a monitor, a workflow); Claude Code resumes in a new turn when that work finishes, and that turn'sStop, with nothing listed, is the one that answers. - A blank turn says so. One whose final message is blank posts
unanswered: "blank", so the caller hears the errand ended instead of waiting on it, and only when the turn's ownclassic.Stoplists nothing running. The plugin reports blank turns only. - An aborted turn does not report: its user took the worker over, and the turn they start next is the one that answers.
- A refused or failed turn does not report either, though the contract has
unanswered: "refusal"and"error"for them. Measured on Claude Code 2.1.296, both end throughStopFailure(a refusal with errorinvalid_request) and thenturn.complete, with noStop, and neither carriesbackground_tasks, nor does any mod engine call list them: the mod cannot tell a failed turn with nothing running from one whose background work resumes it later and answers then, so a report would answer the errand twice. Both are pending until Claude Code exposes that list on the failure. turn.completeis matched once perreason(answer,refusal,error,aborted), which keeps the hooks beside mod-control's ownturn.completehook: Claude Code refuses one plugin's second hook on an event with no matcher. Every one of them drops theStopit read.- A subagent's turn inside the worker does not answer.
classic.Stopfires on the main loop only, and aturn.completethat carries anagentIdis skipped. - A Stop answers only for its own turn. The text is posted when the
completion's
answerequals the Stop'slast_assistant_message, a blank Stop only beside a blankanswer, and the Stop is dropped once a completion read it: an aborted turn fires no Stop, and must not inherit the last one. - The text is cut at 16,000 UTF-16 units and marked
[truncated], as anask's answer is. - The turn is never held up. The answer is posted beside the chain; one that fails is dropped, never retried.
- Outside lich, and in a non-interactive run, it never reports.
Measured on Claude Code 2.1.289: classic.Stop fires on the main loop before
turn.complete, its last_assistant_message equals the completion's answer,
and its background_tasks is [] when nothing runs. classic.SubagentStop
fires for engine forks too, and a background subagent lists itself as still
running there, which is why only the main loop's Stop is read. A worker that
asks with AskUserQuestion blocks with no Stop until someone answers in its
card.