feat(coding-agent): update AgentSession for steer()/followUp() API

- Rename queueMessage to steer(), add followUp()
- Split _pendingMessages into _steeringMessages and _followUpMessages
- Update sendHookMessage to accept deliverAs option
- Rename hasQueuedMessages to hasPendingMessages
- Rename queuedMessageCount to pendingMessageCount
- Update clearQueue() return type to { steering, followUp }
- Update UI to show steering vs follow-up messages differently

WIP: settings-manager, sdk, interactive-mode, rpc-mode still need updates
This commit is contained in:
Mario Zechner 2026-01-02 23:24:27 +01:00
parent a980998464
commit 58c423ba36
10 changed files with 121 additions and 66 deletions

View file

@ -404,7 +404,7 @@ export class InteractiveMode {
sendMessageHandler: (message, triggerTurn) => {
const wasStreaming = this.session.isStreaming;
this.session
.sendHookMessage(message, triggerTurn)
.sendHookMessage(message, { triggerTurn })
.then(() => {
// For non-streaming cases with display=true, update UI
// (streaming cases update via message_end event)
@ -486,7 +486,7 @@ export class InteractiveMode {
abort: () => {
this.session.abort();
},
hasQueuedMessages: () => this.session.queuedMessageCount > 0,
hasPendingMessages: () => this.session.pendingMessageCount > 0,
uiContext,
hasUI: true,
});
@ -522,7 +522,7 @@ export class InteractiveMode {
modelRegistry: this.session.modelRegistry,
model: this.session.model,
isIdle: () => !this.session.isStreaming,
hasQueuedMessages: () => this.session.queuedMessageCount > 0,
hasPendingMessages: () => this.session.pendingMessageCount > 0,
abort: () => {
this.session.abort();
},
@ -737,8 +737,9 @@ export class InteractiveMode {
this.editor.onEscape = () => {
if (this.loadingAnimation) {
// Abort and restore queued messages to editor
const queuedMessages = this.session.clearQueue();
const queuedText = queuedMessages.join("\n\n");
const { steering, followUp } = this.session.clearQueue();
const allQueued = [...steering, ...followUp];
const queuedText = allQueued.join("\n\n");
const currentText = this.editor.getText();
const combinedText = [queuedText, currentText].filter((t) => t.trim()).join("\n\n");
this.editor.setText(combinedText);
@ -1599,12 +1600,17 @@ export class InteractiveMode {
private updatePendingMessagesDisplay(): void {
this.pendingMessagesContainer.clear();
const queuedMessages = this.session.getQueuedMessages();
if (queuedMessages.length > 0) {
const steeringMessages = this.session.getSteeringMessages();
const followUpMessages = this.session.getFollowUpMessages();
if (steeringMessages.length > 0 || followUpMessages.length > 0) {
this.pendingMessagesContainer.addChild(new Spacer(1));
for (const message of queuedMessages) {
const queuedText = theme.fg("dim", `Queued: ${message}`);
this.pendingMessagesContainer.addChild(new TruncatedText(queuedText, 1, 0));
for (const message of steeringMessages) {
const text = theme.fg("dim", `Steering: ${message}`);
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
}
for (const message of followUpMessages) {
const text = theme.fg("dim", `Follow-up: ${message}`);
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
}
}
}

View file

@ -33,7 +33,7 @@ export async function runPrintMode(
hookRunner.initialize({
getModel: () => session.model,
sendMessageHandler: (message, triggerTurn) => {
session.sendHookMessage(message, triggerTurn).catch((e) => {
session.sendHookMessage(message, { triggerTurn }).catch((e) => {
console.error(`Hook sendMessage failed: ${e instanceof Error ? e.message : String(e)}`);
});
},
@ -64,7 +64,7 @@ export async function runPrintMode(
modelRegistry: session.modelRegistry,
model: session.model,
isIdle: () => !session.isStreaming,
hasQueuedMessages: () => session.queuedMessageCount > 0,
hasPendingMessages: () => session.pendingMessageCount > 0,
abort: () => {
session.abort();
},

View file

@ -182,7 +182,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
hookRunner.initialize({
getModel: () => session.agent.state.model,
sendMessageHandler: (message, triggerTurn) => {
session.sendHookMessage(message, triggerTurn).catch((e) => {
session.sendHookMessage(message, { triggerTurn }).catch((e) => {
output(error(undefined, "hook_send", e.message));
});
},
@ -216,7 +216,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
modelRegistry: session.modelRegistry,
model: session.model,
isIdle: () => !session.isStreaming,
hasQueuedMessages: () => session.queuedMessageCount > 0,
hasPendingMessages: () => session.pendingMessageCount > 0,
abort: () => {
session.abort();
},
@ -284,7 +284,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
sessionId: session.sessionId,
autoCompactionEnabled: session.autoCompactionEnabled,
messageCount: session.messages.length,
queuedMessageCount: session.queuedMessageCount,
pendingMessageCount: session.pendingMessageCount,
};
return success(id, "get_state", state);
}

View file

@ -74,7 +74,7 @@ export interface RpcSessionState {
sessionId: string;
autoCompactionEnabled: boolean;
messageCount: number;
queuedMessageCount: number;
pendingMessageCount: number;
}
// ============================================================================