You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(goal): let the user arm a Goal from the composer (#3199)
A Goal could only be armed by the model, from inside a Turn, with the GoalSet tool. There was no Host operation for it and no control anywhere in the product: the user could stop a Goal but never start one.
`goal.arm` joins `goal.query` and `goal.control` as a real Host operation. `HostGoalCoordinator#arm` creates the Goal through the same `GoalManager` the tool uses, under the same Session admission gate, with the same durable record, and refuses a Session that already has an unfinished one. The two budget ceilings and the token floor move to `@maka/core/goal`, so the protocol codec and the GoalSet tool schema validate against one number rather than two copies of it. Remote owners are granted the operation at the same tier as `goal.control`: they already send Turns, and the model arms its own Goal inside one, so refusing would remove only the explicit path the user can see and stop. The IPC handler takes the Session from the scoped channel rather than the renderer's frame, and normalizes the budgets without clamping them. The + menu gains one row that opens a dialog for the condition and the two budgets, and explains itself when a Goal is already running or a Turn is in flight.
Arming starts nothing, which the Goal itself has to remember. A Goal the model sets drives from the moment it exists; an armed one waits for a Turn to take hold of it, and `active` covers both. `armedAt` records that wait, and the two events that end it clear it — a carried Turn settling into a continuation, and the user resuming. `isDrivingGoal` reads that one field, so a restart puts back only a drive the Goal already has, while Resume delivers the immediate continuation its control promises.
Epoch 28 is retired: `goal.arm` is a new wire operation, and an older Host decodes it as unknown and tears the connection down instead of refusing the pair up front.
Reviewed-by: M4n5ter
Reviewed-by: hqhq1025
Generated-by: Claude Code
0 commit comments