fix(tools,client): chain-mode resolution and a live preset label

modeFor read only the exact scope's layer, so a `code`-preset session
advertised the native catalog: the mode is declared on the preset's STANDING
scope, and the agent only parents to it. Nearest scope wins along the chain —
the mode decides what the model SEES, which is the class of fact the chain
inherits. Caught live (the model politely computed with bash and said
run_code was not in its list); the chain test pins it.

The client half of the label fix: the create echo and the session-added
frame's agentPreset now reach the session list (newest wins in the upsert —
every producer of the field reports the CURRENT composition), and a confirmed
blank-session switch publishes through the new ISessions.noteAgentPreset, so
the header label moves with the composition instead of waiting for a reload.
This commit is contained in:
Yichen Jiang
2026-08-08 20:19:00 +08:00
parent 3926e95c61
commit 66e9b01fbd
8 changed files with 81 additions and 2 deletions
@@ -98,6 +98,8 @@ export function apply(ctx: ClientContext): void {
blank: summary.blank,
...summary.agentPreset === undefined ? {} : { agentPreset: summary.agentPreset },
}
}, (sessionId, agentPreset) => {
scope.sessions.noteAgentPreset(sessionId as never, agentPreset)
})
const seatInjected = (): AgentPresetSeatInjected => ({
@@ -60,6 +60,12 @@ export class AgentPresetSeatController {
private readonly api: Pick<IApiClient, 'agentPresets'>,
/** The session the hero is about to hand over to, when there is one. */
private readonly currentSession: () => SeatSessionSummary | undefined,
/**
* Publish an applied switch into the session list, so the header label
* moves with the composition instead of waiting for the next full list
* refresh. Optional: a harness that renders no list omits it.
*/
private readonly onApplied?: (sessionId: string, agentPreset: string) => void,
) {}
private set(patch: Partial<AgentPresetSeatState>): void {
@@ -129,6 +135,7 @@ export class AgentPresetSeatController {
}
// Consumed: the next new session opens on the deployment default again.
this.set({ busy: false, current: response.result.value.agentPreset })
this.onApplied?.(session.id, response.result.value.agentPreset)
} catch (error) {
this.staged = undefined
this.set({ busy: false, error: messageOf(error), current: this.fallback })