Commit ee01b34
authored
corp-ship combat wake (#416)
* fix: stop start_task silently auto-steering on a busy ship
`start_task` on a ship with an occupied task slot used to route the new
request into the running task's `_steer_existing_task` path and return
`success: true, steered: true`. Two failure modes followed:
- Distinct follow-up intents (e.g. "buy a probe and an Atlas" on the
single personal-ship slot) collapsed into a steer of an unrelated
in-flight task. The task agent had no obligation to act on the steer;
the new request was silently dropped while the voice agent reported
success to the commander.
- The shared task_id in the response caused the LLM to double-count a
single `task.completed` as completion of both intents.
The busy branch now returns a structured `ship_busy` result (mirrors the
server-side `ship_busy` 409 shape) with `current_task_id`,
`current_task_type`, `current_task_description`, and a
`suggested_action` hint. The voice agent prompt and the `start_task`
tool schema are updated to teach the LLM to either call `steer_task`
silently for refinements, tell the commander to wait for
`task.completed` for separate actions, or ask when ambiguous.
`_handle_start_task_tool` now surfaces failure results minimally (no
event injection, no forced response cycle) so the default
`result_callback` triggers a follow-up inference — mirrors the
established `_handle_stop_task_tool` failure pattern. The success
branch's dead `steered`-event handling is removed.
BYOA `register_active` now carries `task_description` so the same busy
helper works for in-process TaskAgents and remote BYOA agents.
Tests rewritten: the prior busy-ship test asserted auto-steer; it now
asserts the `ship_busy` contract and that no `BusSteerTaskMessage`
fires. The wrapper failure test now asserts the new minimal-surface
pattern.
* make steer_task survive the closing-task race + prioritize the steer
Two complementary changes that make explicit steer_task calls reliable
in the situations where the previous commit's ship_busy contract sends
the LLM down that path:
1. Closing-state pre-flight in `_steer_existing_task`. When the target
in-process TaskAgent has already set `_task_finished` /
`_finish_emitted` / `_cancelled`, the orchestrator returns
`{error: "task_closing", retry_with: "start_task"}` instead of
firing a steer that would race the terminal turn and silently drop.
`_handle_steer_task_tool` surfaces this result without a forced
response cycle so the voice LLM chains a fresh start_task in the
same turn — the user hears one bot response, not a "steer sent"
ack followed by silence. Personal and corp in-process ships go
through the same code path; BYOA agents skip the check and rely on
the existing ship_busy retry on the follow-up start_task.
2. Priority-wrap on TaskAgent steer injection. `_inject_steering` now
wraps the steer text with a short `<priority>` directive before
adding it to LLM context. The TaskAgent's original task description
is itself a user message (system prompt is generic), so without the
wrap the steer was just another peer instruction with no priority
signal. With the wrap the LLM treats the steer as outranking the
original. Orchestrator side drops its now-redundant
`"Steering instruction: "` text prefix; the structured wrap is the
canonical signal.
Tests: new closing-state pre-flight test + new silent-task_closing
wrapper test; existing steer-success test updated to assert the dropped
prefix; existing _inject_steering test strengthened to assert the
priority wrap is applied to the LLM context message.
* restore auto-steer on busy ship; keep closing-state guard + priority wrap
Walking back the no-auto-steer contract from the previous two commits.
Convenient refinement UX wins out — the LLM should be able to issue a
follow-up instruction via `start_task` without an extra round-trip — and
the recovery for cross-intent steers (Atlas-class bugs) moves into the
voice agent's task.completed handling: the agent reads the completion
message and re-issues `start_task` for any unfulfilled intent.
What's kept from the prior commits:
- Closing-state pre-flight in `_steer_existing_task`. If the active task
has already called `finished`, the steer would race the terminal turn
and silently drop. Explicit `steer_task` calls return `task_closing`
with a retry directive; auto-steer calls from `start_task`'s busy
branch translate this to `ship_busy` so the voice LLM chains a fresh
start_task. Personal and corp in-process ships symmetric; BYOA skips
the check (no local liveness state).
- TaskAgent `_inject_steering` `<priority>` wrap. The TaskAgent's
original task description is a user message (system prompt is
generic), so the wrap is what gives the steer override semantics.
- Orchestrator's `"Steering instruction: "` text prefix stays removed
— the `<priority>` wrap is the canonical signal.
What changes back to main's behavior:
- `_handle_start_task` busy branch calls `_steer_existing_task` again
(with the closing-state translation above).
- `_handle_start_task_tool` restores the `steered` branch so
auto-steered results emit `task.steered` events.
- `start_task` tool schema goes back to describing auto-steer with a
new caveat: READ the task.completed message; re-issue for any intent
that wasn't fulfilled.
- voice_agent.md prompt updated to match.
Cleanup:
- Removed unused `_active_task_description_for` helper (no longer needed
since busy branch doesn't synthesize a ship_busy payload with task
description on every call).
- Removed `task_description` kwarg from BYOACoordinator.register_active
(added in the prior commit for the now-removed helper).
Tests:
- Rewrote `test_start_task_busy_ship_*` back to assert auto-steer fires
(it's the original `test_start_task_busy_byoa_ship_steers_existing_bus_agent`).
- New `test_start_task_busy_closing_task_returns_ship_busy_for_chained_start`
covers the closing-state translation path.
- Restored `test_start_task_tool_steered_result_queues_steered_event`
(now exercising the live code path again).
- Updated `test_start_task_tool_failure_surfaces_result_without_event_injection`
to reflect the new ship_busy payload shape (no current_task_description).
* inline wait+retry on closing-state race; trim voice agent prompt edits
Two corrections to the previous commit:
1. `start_task` handles the closing-state race purely in code now —
the LLM never sees ship_busy / task_closing for it. The public
`_handle_start_task` calls a renamed `_handle_start_task_attempt`;
when the attempt bubbles up `task_closing` (auto-steer detected
the target's terminal turn), the wrapper waits up to
`settings.TASK_STEER_CLOSING_WAIT_SECONDS` (default 5s) via a
small `_wait_for_ship_release` poll, then retries the attempt
cleanly. On wait timeout, returns a standard failure result
(`error: "task_closing_timeout"`) which the existing wrapper
surfaces to the LLM with a message. No new infrastructure —
just poll `_locked_ships` until released.
2. Voice agent prompt edits are back to a minimal delta vs main —
one sentence acknowledging that start_task on a busy slot
auto-steers (priority-wrapped), and one sentence telling the
agent to read the task.completed message and mention any
unfulfilled steered intent to the commander (don't auto-re-issue;
the commander can ask if they still want it).
Explicit `steer_task` on a closing task still returns
`task_closing` to the LLM — different semantics (commander asked to
modify the running task, not start a new one), so the LLM-visible
retry directive is right there.
Tests: split the prior closing-state test into two — one for
`_handle_start_task_attempt` bubbling task_closing internally, one
for `_handle_start_task` doing the wait+retry. New timeout-path
test covers the failure surface. Wrapper failure test updated to
the new payload shape (`task_closing_timeout` instead of
`ship_busy`).
* use task.steered event xml shape for steer injection
Replace the ad-hoc <priority>...</priority> wrap from the previous
commit with the same <event name="..."> shape the TaskAgent already
reads for task.progress, task.cancelled, etc. Body starts with
"User has steered your task:" so the LLM has a clear directive to
treat this as overriding its current plan.
Before:
<priority>Override your current plan with the instruction below.</priority>
{text}
After:
<event name="task.steered">
User has steered your task: {text}
</event>
Test + CHANGELOG updated to match.
* split steer injection into event header + raw user message
Previous commit had the steer text duplicated inside the
<event name="task.steered"> body. Instead, inject as two messages:
1. <event name="task.steered">
User has steered your task. Override and prioritize your current
plan with the instruction below.
</event>
2. {steer text} (as a normal user message)
The event xml is the directive header that frames the next user
message; the steer text stands on its own. Two related tests updated
to assert add_message is called twice with the right shapes.
* collapse steer injection back to a single event-wrapped user message
Walking back the two-message split from the previous commit. One user
message, content is the task.steered event xml with the directive line
followed by the raw steer text — all inside the event tags. Matches how
task.progress and task.cancelled events the agent already reads embed
their summary text.
<event name="task.steered">
User has steered your task. Override and prioritize your current
plan with the instruction below.
{steer_text}
</event>
* trim behavioral instructions out of start_task tool schema
The "check what was done / mention unfulfilled intents to the commander"
guidance belongs in the voice agent prompt, not the tool schema. Tool
schemas describe what the tool does and what it returns; behavioral
guidance is prompt-level. Same guidance already lives in voice_agent.md.
* restored steering message
* Tighten task steering prompt
* Restore quiet start task failures
* Wake corp ships for combat
* Support BYOA combat wake hooks1 parent c9a6bd4 commit ee01b34
16 files changed
Lines changed: 909 additions & 68 deletions
File tree
- docs
- src/gradientbang
- runtime
- byoa
- subagents
- utils
- tests/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
10 | 15 | | |
11 | 16 | | |
12 | 17 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
64 | 86 | | |
65 | 87 | | |
66 | 88 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
| 123 | + | |
| 124 | + | |
123 | 125 | | |
124 | 126 | | |
125 | 127 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
94 | 115 | | |
95 | 116 | | |
96 | 117 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
126 | 140 | | |
127 | 141 | | |
128 | 142 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
20 | 26 | | |
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
24 | 30 | | |
| 31 | + | |
25 | 32 | | |
26 | 33 | | |
27 | 34 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
21 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
82 | 91 | | |
83 | 92 | | |
84 | 93 | | |
| |||
124 | 133 | | |
125 | 134 | | |
126 | 135 | | |
| 136 | + | |
127 | 137 | | |
128 | 138 | | |
129 | 139 | | |
| |||
153 | 163 | | |
154 | 164 | | |
155 | 165 | | |
| 166 | + | |
156 | 167 | | |
157 | 168 | | |
158 | 169 | | |
| |||
187 | 198 | | |
188 | 199 | | |
189 | 200 | | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
190 | 210 | | |
191 | 211 | | |
192 | 212 | | |
| |||
235 | 255 | | |
236 | 256 | | |
237 | 257 | | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
238 | 268 | | |
239 | 269 | | |
240 | 270 | | |
241 | | - | |
| 271 | + | |
242 | 272 | | |
243 | 273 | | |
244 | 274 | | |
| |||
272 | 302 | | |
273 | 303 | | |
274 | 304 | | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
275 | 327 | | |
276 | 328 | | |
277 | 329 | | |
| |||
306 | 358 | | |
307 | 359 | | |
308 | 360 | | |
| 361 | + | |
| 362 | + | |
309 | 363 | | |
310 | 364 | | |
311 | 365 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
| 69 | + | |
68 | 70 | | |
69 | 71 | | |
70 | 72 | | |
| |||
93 | 95 | | |
94 | 96 | | |
95 | 97 | | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
96 | 103 | | |
97 | 104 | | |
98 | 105 | | |
| |||
1743 | 1750 | | |
1744 | 1751 | | |
1745 | 1752 | | |
| 1753 | + | |
| 1754 | + | |
| 1755 | + | |
| 1756 | + | |
| 1757 | + | |
| 1758 | + | |
| 1759 | + | |
| 1760 | + | |
| 1761 | + | |
| 1762 | + | |
1746 | 1763 | | |
1747 | 1764 | | |
1748 | 1765 | | |
1749 | 1766 | | |
1750 | 1767 | | |
1751 | 1768 | | |
1752 | | - | |
1753 | | - | |
1754 | 1769 | | |
1755 | | - | |
| 1770 | + | |
1756 | 1771 | | |
1757 | 1772 | | |
1758 | 1773 | | |
| |||
1768 | 1783 | | |
1769 | 1784 | | |
1770 | 1785 | | |
1771 | | - | |
1772 | | - | |
1773 | | - | |
1774 | | - | |
1775 | | - | |
1776 | | - | |
| 1786 | + | |
| 1787 | + | |
| 1788 | + | |
| 1789 | + | |
| 1790 | + | |
| 1791 | + | |
| 1792 | + | |
| 1793 | + | |
| 1794 | + | |
| 1795 | + | |
| 1796 | + | |
| 1797 | + | |
| 1798 | + | |
| 1799 | + | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
| 1803 | + | |
| 1804 | + | |
| 1805 | + | |
| 1806 | + | |
| 1807 | + | |
| 1808 | + | |
| 1809 | + | |
| 1810 | + | |
| 1811 | + | |
| 1812 | + | |
| 1813 | + | |
| 1814 | + | |
| 1815 | + | |
| 1816 | + | |
| 1817 | + | |
| 1818 | + | |
| 1819 | + | |
| 1820 | + | |
| 1821 | + | |
| 1822 | + | |
| 1823 | + | |
| 1824 | + | |
| 1825 | + | |
| 1826 | + | |
| 1827 | + | |
| 1828 | + | |
| 1829 | + | |
| 1830 | + | |
| 1831 | + | |
| 1832 | + | |
| 1833 | + | |
| 1834 | + | |
| 1835 | + | |
| 1836 | + | |
| 1837 | + | |
| 1838 | + | |
| 1839 | + | |
| 1840 | + | |
| 1841 | + | |
| 1842 | + | |
| 1843 | + | |
| 1844 | + | |
| 1845 | + | |
| 1846 | + | |
1777 | 1847 | | |
1778 | 1848 | | |
1779 | 1849 | | |
| |||
0 commit comments