Skip to content

Feat/turn handle - #164

Merged
stippi merged 6 commits into
mainfrom
feat/turn-handle
Jul 16, 2026
Merged

Feat/turn handle#164
stippi merged 6 commits into
mainfrom
feat/turn-handle

Conversation

@stippi

@stippi stippi commented Jul 15, 2026

Copy link
Copy Markdown
Owner

No description provided.

stippi added 6 commits July 15, 2026 23:12
- wait types migrate together with the goal state machine (the controller's
  Wait decision folds out of it); stores/probes/sweeps stay host concerns
- explicit migration order: turn handle + evidence first, then goal/wait
  types with the store traits (so PAL's planned transactional repository is
  written against them once), then run convergence
- the turn handle is not the send-or-queue channel dispatch; a merged
  operation would need an explicit Queued outcome
- RunSpec budgets are owner policy (PAL children run to completion by
  design); the run/attempt split is what ChildRun still lacks
- durable approvals: PAL builds action intents P0 in pal_core first, the
  upstream generalization is a deliberate second migration
start_turn_if_idle is the typed sibling of try_send_user_message_if_idle
(ROADMAP 'Replace event inference with an exact turn handle'): it starts a
turn only on an idle session, atomically inside the service actor, and
returns a TurnHandle identifying exactly that turn. The handle resolves
once with a bounded TurnOutcome — final narration, tool lifecycle records,
resource writes, token/tool/wall-time usage, and whether a queued user
message was absorbed mid-run — collected by a TurnRecorder teed
synchronously into the session's event publisher, so outcomes never depend
on a broadcast subscriber keeping up. An aborted agent task resolves the
outcome as failed via the recorder's Drop instead of losing it; the token
delta is read from the state the run persisted, not from fire-and-forget
metadata events.

Also:
- AgentRuntimeOptions.llm_client_factory: injectable LLM construction per
  run (scripted providers for tests and fault-injection harnesses)
- compaction: a model unknown to models.json (playback, injected factory)
  disables automatic compaction for the run instead of failing the turn
The generic half of PAL's durable-goal feature moves upstream, per the
ROADMAP's 'Now: converge goals' slice: Goal/GoalState/CompletionContract/
Budget/Subgoal, the attempt/evidence ledger with claim+revision atomicity,
the deterministic controller fold (apply_evaluation → Continue/Wait/
AwaitInput/Blocked/Done/Failed), typed wait barriers (WaitKind, the armed →
satisfied/timed-out/cancelled machine, clock-only predicates), the
GoalEvaluator/WaitProbe seams, the bounded LLM evaluator over an injected
provider, and goal-turn framing. All 78 deterministic tests moved with it
and run without an LLM or frontend.

Host-neutral owner binding: SessionKey generalizes to OwnerKey (a plain
hierarchical string key; PAL re-exports it as its lane key), the goals/waits
owner field deserializes legacy 'lane'-keyed JSON via a serde alias.

GoalRepository/WaitRepository traits capture the store surface: the bundled
JSON stores (explicit path, no global config) implement them today; PAL's
planned transactional repository implements the same traits instead of
migrating persistence twice.

PAL keeps everything deployment-shaped: lane binding, gateway passes
(goal_pass/wait_pass), startup sweeps, durable probes, delivery.
PAL's existing goals.json/waits.json key the owner as 'lane'; the serde
alias keeps them readable after the OwnerKey generalization (the initial
mechanical rename had rewritten the alias string itself).
The evaluator prompt listed 'a reply from the user must arrive' under the
generic waiting verdict while also offering needs_input, without explaining
the mechanically different consequences: waiting+human_input parks the goal
Waiting and the user's NEXT message on the channel wakes it automatically;
needs_input parks it Blocked until someone deliberately resumes it.

The prompt now states the consequence on both bullets and gives the choice
rule: human_input only for a just-asked concrete question whose next reply
is expected to be the answer; needs_input for decisions that must not
auto-continue on an arbitrary message.
@stippi
stippi merged commit 051f88e into main Jul 16, 2026
5 checks passed
@stippi
stippi deleted the feat/turn-handle branch July 16, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant