refactor(core)!: rework agent run lifecycle and status semantics
BREAKING CHANGE: stop() is now async and resolves after the run fully settles; status decouples from task outcome (new 'stopped' state, LLM self-reported failure now ends as 'completed'). Lifecycle hooks re-throw instead of being folded into the result; agent errors go to history. Adds agent.lastResult.
This commit is contained in:
@@ -100,10 +100,10 @@ export class Panel {
|
||||
#handleStatusChange(): void {
|
||||
const status = this.#agent.status
|
||||
|
||||
// Map agent status to UI indicator type
|
||||
const indicatorType =
|
||||
status === 'running' ? 'thinking' : status === 'idle' ? 'thinking' : status
|
||||
this.#updateStatusIndicator(indicatorType)
|
||||
// Map agent status to UI indicator. A `completed` run whose result reports
|
||||
// failure shows as error; other statuses map to their own indicator.
|
||||
const failed = status === 'completed' && this.#agent.lastResult?.success === false
|
||||
this.#updateStatusIndicator(failed ? 'error' : status)
|
||||
|
||||
// Morph action button: running = stop (■), not running = close (X)
|
||||
if (status === 'running') {
|
||||
@@ -121,7 +121,7 @@ export class Panel {
|
||||
}
|
||||
|
||||
// Handle completion
|
||||
if (status === 'completed' || status === 'error') {
|
||||
if (status === 'completed' || status === 'error' || status === 'stopped') {
|
||||
if (!this.#isExpanded) {
|
||||
this.#expand()
|
||||
}
|
||||
@@ -376,7 +376,7 @@ export class Panel {
|
||||
}
|
||||
|
||||
const status = this.#agent.status
|
||||
const isTaskEnded = status === 'completed' || status === 'error'
|
||||
const isTaskEnded = status === 'completed' || status === 'error' || status === 'stopped'
|
||||
|
||||
// Only show input area after task completion if configured to do so
|
||||
if (isTaskEnded) {
|
||||
@@ -559,13 +559,23 @@ export class Panel {
|
||||
}
|
||||
|
||||
#updateStatusIndicator(
|
||||
type: 'thinking' | 'executing' | 'executed' | 'retrying' | 'completed' | 'error'
|
||||
type:
|
||||
| 'idle'
|
||||
| 'running'
|
||||
| 'thinking'
|
||||
| 'executing'
|
||||
| 'executed'
|
||||
| 'retrying'
|
||||
| 'completed'
|
||||
| 'error'
|
||||
| 'stopped'
|
||||
): void {
|
||||
// Clear all status classes
|
||||
// `running` animates like thinking; `idle`/`stopped` use the neutral base.
|
||||
const variant = type === 'running' ? 'thinking' : type
|
||||
this.#indicator.className = styles.indicator
|
||||
|
||||
// Add corresponding status class
|
||||
this.#indicator.classList.add(styles[type])
|
||||
if (variant !== 'idle' && variant !== 'stopped') {
|
||||
this.#indicator.classList.add(styles[variant])
|
||||
}
|
||||
}
|
||||
|
||||
#scrollToBottom(): void {
|
||||
|
||||
@@ -22,14 +22,17 @@ export type AgentActivity =
|
||||
* This enables decoupling and allows any agent implementation to work with Panel.
|
||||
*
|
||||
* Events:
|
||||
* - 'statuschange': Agent status changed (idle/running/completed/error)
|
||||
* - 'statuschange': Agent status changed
|
||||
* - 'historychange': Historical events updated (persisted)
|
||||
* - 'activity': Transient activity for immediate UI feedback (thinking/executing/etc)
|
||||
* - 'dispose': Agent is being disposed
|
||||
*/
|
||||
export interface PanelAgentAdapter extends EventTarget {
|
||||
/** Current agent status */
|
||||
readonly status: 'idle' | 'running' | 'completed' | 'error'
|
||||
readonly status: 'idle' | 'running' | 'completed' | 'error' | 'stopped'
|
||||
|
||||
/** Result of the most recent run, or `null` before the first run completes */
|
||||
readonly lastResult: { success: boolean } | null
|
||||
|
||||
/** History of agent events */
|
||||
readonly history: readonly {
|
||||
@@ -71,7 +74,7 @@ export interface PanelAgentAdapter extends EventTarget {
|
||||
execute(task: string): Promise<unknown>
|
||||
|
||||
/** Stop the current task (agent remains reusable) */
|
||||
stop(): void
|
||||
stop(): Promise<void>
|
||||
|
||||
/** Dispose the agent (terminal, cannot be reused) */
|
||||
dispose(): void
|
||||
|
||||
Reference in New Issue
Block a user