Merge pull request #114 from alibaba/feat/user-takeover
feat: remove `pause`
This commit is contained in:
@@ -12,7 +12,7 @@ import type { PageAgentConfig } from './config'
|
|||||||
import { MAX_STEPS } from './config/constants'
|
import { MAX_STEPS } from './config/constants'
|
||||||
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
|
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
|
||||||
import { tools } from './tools'
|
import { tools } from './tools'
|
||||||
import { normalizeResponse, trimLines, uid, waitUntil } from './utils'
|
import { normalizeResponse, trimLines, uid } from './utils'
|
||||||
import { assert } from './utils/assert'
|
import { assert } from './utils/assert'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,7 +104,6 @@ export class PageAgent extends EventTarget {
|
|||||||
id = uid()
|
id = uid()
|
||||||
panel: Panel
|
panel: Panel
|
||||||
tools: typeof tools
|
tools: typeof tools
|
||||||
paused = false
|
|
||||||
disposed = false
|
disposed = false
|
||||||
task = ''
|
task = ''
|
||||||
taskId = ''
|
taskId = ''
|
||||||
@@ -138,11 +137,6 @@ export class PageAgent extends EventTarget {
|
|||||||
language: this.config.language,
|
language: this.config.language,
|
||||||
onExecuteTask: (task) => this.execute(task),
|
onExecuteTask: (task) => this.execute(task),
|
||||||
onStop: () => this.dispose(),
|
onStop: () => this.dispose(),
|
||||||
onPauseToggle: () => {
|
|
||||||
this.paused = !this.paused
|
|
||||||
return this.paused
|
|
||||||
},
|
|
||||||
getPaused: () => this.paused,
|
|
||||||
})
|
})
|
||||||
this.tools = new Map(tools)
|
this.tools = new Map(tools)
|
||||||
|
|
||||||
@@ -190,6 +184,7 @@ export class PageAgent extends EventTarget {
|
|||||||
*/
|
*/
|
||||||
pushObservation(content: string): void {
|
pushObservation(content: string): void {
|
||||||
this.history.push({ type: 'observation', content })
|
this.history.push({ type: 'observation', content })
|
||||||
|
this.panel.update({ type: 'observation', content })
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(task: string): Promise<ExecutionResult> {
|
async execute(task: string): Promise<ExecutionResult> {
|
||||||
@@ -237,8 +232,6 @@ export class PageAgent extends EventTarget {
|
|||||||
|
|
||||||
// abort
|
// abort
|
||||||
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
||||||
// pause
|
|
||||||
await waitUntil(() => !this.paused)
|
|
||||||
|
|
||||||
// Update status to thinking
|
// Update status to thinking
|
||||||
console.log(chalk.blue('Thinking...'))
|
console.log(chalk.blue('Thinking...'))
|
||||||
@@ -362,8 +355,6 @@ export class PageAgent extends EventTarget {
|
|||||||
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
||||||
// abort
|
// abort
|
||||||
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
if (this.#abortController.signal.aborted) throw new Error('AbortError')
|
||||||
// pause
|
|
||||||
await waitUntil(() => !this.paused)
|
|
||||||
|
|
||||||
console.log(chalk.blue.bold('MacroTool execute'), input)
|
console.log(chalk.blue.bold('MacroTool execute'), input)
|
||||||
const action = input.action
|
const action = input.action
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ export interface PageAgentEventMap {
|
|||||||
// PageAgent status events
|
// PageAgent status events
|
||||||
// 'agent:execute': { params: { task: string } }
|
// 'agent:execute': { params: { task: string } }
|
||||||
// 'agent:done': { params: { text: string; success: boolean } }
|
// 'agent:done': { params: { text: string; success: boolean } }
|
||||||
// 'agent:paused': { params: undefined }
|
|
||||||
// 'agent:resumed': { params: undefined }
|
|
||||||
// 'agent:disposed': { params: undefined }
|
// 'agent:disposed': { params: undefined }
|
||||||
// 'agent:error': { params: { error: string | Error } }
|
// 'agent:error': { params: { error: string | Error } }
|
||||||
|
|
||||||
|
|||||||
@@ -209,18 +209,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pauseButton {
|
|
||||||
font-weight: 600;
|
|
||||||
&.paused {
|
|
||||||
background: rgba(34, 197, 94, 0.2); /* 绿色背景表示可以继续 */
|
|
||||||
color: rgb(34, 197, 94);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(34, 197, 94, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stopButton {
|
.stopButton {
|
||||||
background: rgba(239, 68, 68, 0.2);
|
background: rgba(239, 68, 68, 0.2);
|
||||||
color: rgb(255, 41, 41);
|
color: rgb(255, 41, 41);
|
||||||
@@ -364,6 +352,11 @@
|
|||||||
background: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));
|
background: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.observation {
|
||||||
|
border-left-color: rgb(147, 51, 234);
|
||||||
|
background: linear-gradient(135deg, rgba(147, 51, 234, 0.1), rgba(147, 51, 234, 0.05));
|
||||||
|
}
|
||||||
|
|
||||||
/* 突出显示 done 成功结果 */
|
/* 突出显示 done 成功结果 */
|
||||||
&.doneSuccess {
|
&.doneSuccess {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ export interface PanelConfig {
|
|||||||
language?: SupportedLanguage
|
language?: SupportedLanguage
|
||||||
onExecuteTask: (task: string) => void
|
onExecuteTask: (task: string) => void
|
||||||
onStop: () => void
|
onStop: () => void
|
||||||
onPauseToggle: () => boolean // returns new paused state
|
|
||||||
getPaused: () => boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +27,7 @@ export type PanelUpdate =
|
|||||||
| { type: 'completed' }
|
| { type: 'completed' }
|
||||||
| { type: 'toolExecuting'; toolName: string; args: any }
|
| { type: 'toolExecuting'; toolName: string; args: any }
|
||||||
| { type: 'toolCompleted'; toolName: string; args: any; result?: string; duration?: number }
|
| { type: 'toolCompleted'; toolName: string; args: any; result?: string; duration?: number }
|
||||||
|
| { type: 'observation'; content: string }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent control panel
|
* Agent control panel
|
||||||
@@ -39,7 +38,6 @@ export class Panel {
|
|||||||
#statusText: HTMLElement
|
#statusText: HTMLElement
|
||||||
#historySection: HTMLElement
|
#historySection: HTMLElement
|
||||||
#expandButton: HTMLElement
|
#expandButton: HTMLElement
|
||||||
#pauseButton: HTMLElement
|
|
||||||
#stopButton: HTMLElement
|
#stopButton: HTMLElement
|
||||||
#inputSection: HTMLElement
|
#inputSection: HTMLElement
|
||||||
#taskInput: HTMLInputElement
|
#taskInput: HTMLInputElement
|
||||||
@@ -66,7 +64,6 @@ export class Panel {
|
|||||||
this.#statusText = this.#wrapper.querySelector(`.${styles.statusText}`)!
|
this.#statusText = this.#wrapper.querySelector(`.${styles.statusText}`)!
|
||||||
this.#historySection = this.#wrapper.querySelector(`.${styles.historySection}`)!
|
this.#historySection = this.#wrapper.querySelector(`.${styles.historySection}`)!
|
||||||
this.#expandButton = this.#wrapper.querySelector(`.${styles.expandButton}`)!
|
this.#expandButton = this.#wrapper.querySelector(`.${styles.expandButton}`)!
|
||||||
this.#pauseButton = this.#wrapper.querySelector(`.${styles.pauseButton}`)!
|
|
||||||
this.#stopButton = this.#wrapper.querySelector(`.${styles.stopButton}`)!
|
this.#stopButton = this.#wrapper.querySelector(`.${styles.stopButton}`)!
|
||||||
this.#inputSection = this.#wrapper.querySelector(`.${styles.inputSectionWrapper}`)!
|
this.#inputSection = this.#wrapper.querySelector(`.${styles.inputSectionWrapper}`)!
|
||||||
this.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!
|
this.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!
|
||||||
@@ -120,11 +117,6 @@ export class Panel {
|
|||||||
this.#updateStatusIndicator('thinking')
|
this.#updateStatusIndicator('thinking')
|
||||||
this.#updateHistory()
|
this.#updateHistory()
|
||||||
this.#collapse()
|
this.#collapse()
|
||||||
// Reset pause state via callback
|
|
||||||
if (this.#config.getPaused()) {
|
|
||||||
this.#config.onPauseToggle()
|
|
||||||
}
|
|
||||||
this.#updatePauseButton()
|
|
||||||
// Reset user input state
|
// Reset user input state
|
||||||
this.#isWaitingForUserAnswer = false
|
this.#isWaitingForUserAnswer = false
|
||||||
this.#userAnswerResolver = null
|
this.#userAnswerResolver = null
|
||||||
@@ -205,6 +197,8 @@ export class Panel {
|
|||||||
duration: data.duration,
|
duration: data.duration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'observation':
|
||||||
|
return { type: 'observation', displayText: data.content }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,39 +271,6 @@ export class Panel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle pause state
|
|
||||||
*/
|
|
||||||
#togglePause(): void {
|
|
||||||
const paused = this.#config.onPauseToggle()
|
|
||||||
this.#updatePauseButton()
|
|
||||||
|
|
||||||
// Update status display
|
|
||||||
if (paused) {
|
|
||||||
this.#statusText.textContent = this.#i18n.t('ui.panel.paused')
|
|
||||||
this.#updateStatusIndicator('thinking')
|
|
||||||
} else {
|
|
||||||
this.#statusText.textContent = this.#i18n.t('ui.panel.continueExecution')
|
|
||||||
this.#updateStatusIndicator('tool_executing')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update pause button state
|
|
||||||
*/
|
|
||||||
#updatePauseButton(): void {
|
|
||||||
const paused = this.#config.getPaused()
|
|
||||||
if (paused) {
|
|
||||||
this.#pauseButton.textContent = '▶'
|
|
||||||
this.#pauseButton.title = this.#i18n.t('ui.panel.continue')
|
|
||||||
this.#pauseButton.classList.add(styles.paused)
|
|
||||||
} else {
|
|
||||||
this.#pauseButton.textContent = '⏸︎'
|
|
||||||
this.#pauseButton.title = this.#i18n.t('ui.panel.pause')
|
|
||||||
this.#pauseButton.classList.remove(styles.paused)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop Agent
|
* Stop Agent
|
||||||
*/
|
*/
|
||||||
@@ -426,9 +387,6 @@ export class Panel {
|
|||||||
<button class="${styles.controlButton} ${styles.expandButton}" title="${this.#i18n.t('ui.panel.expand')}">
|
<button class="${styles.controlButton} ${styles.expandButton}" title="${this.#i18n.t('ui.panel.expand')}">
|
||||||
▼
|
▼
|
||||||
</button>
|
</button>
|
||||||
<button class="${styles.controlButton} ${styles.pauseButton}" title="${this.#i18n.t('ui.panel.pause')}">
|
|
||||||
⏸︎
|
|
||||||
</button>
|
|
||||||
<button class="${styles.controlButton} ${styles.stopButton}" title="${this.#i18n.t('ui.panel.stop')}">
|
<button class="${styles.controlButton} ${styles.stopButton}" title="${this.#i18n.t('ui.panel.stop')}">
|
||||||
X
|
X
|
||||||
</button>
|
</button>
|
||||||
@@ -466,12 +424,6 @@ export class Panel {
|
|||||||
this.#toggle()
|
this.#toggle()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Pause/continue button
|
|
||||||
this.#pauseButton.addEventListener('click', (e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this.#togglePause()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Stop button
|
// Stop button
|
||||||
this.#stopButton.addEventListener('click', (e) => {
|
this.#stopButton.addEventListener('click', (e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -632,7 +584,7 @@ export class Panel {
|
|||||||
typeClass = styles.error
|
typeClass = styles.error
|
||||||
statusIcon = '❌'
|
statusIcon = '❌'
|
||||||
} else if (step.type === 'tool_executing') {
|
} else if (step.type === 'tool_executing') {
|
||||||
statusIcon = '⚙️'
|
statusIcon = '🔨'
|
||||||
} else if (step.type === 'output') {
|
} else if (step.type === 'output') {
|
||||||
typeClass = styles.output
|
typeClass = styles.output
|
||||||
statusIcon = '🤖'
|
statusIcon = '🤖'
|
||||||
@@ -642,6 +594,9 @@ export class Panel {
|
|||||||
} else if (step.type === 'retry') {
|
} else if (step.type === 'retry') {
|
||||||
typeClass = styles.retry
|
typeClass = styles.retry
|
||||||
statusIcon = '🔄'
|
statusIcon = '🔄'
|
||||||
|
} else if (step.type === 'observation') {
|
||||||
|
typeClass = styles.observation
|
||||||
|
statusIcon = '👁️'
|
||||||
} else {
|
} else {
|
||||||
statusIcon = '🧠'
|
statusIcon = '🧠'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,15 @@ export interface Step {
|
|||||||
id: string
|
id: string
|
||||||
stepNumber: number
|
stepNumber: number
|
||||||
timestamp: Date
|
timestamp: Date
|
||||||
type: 'thinking' | 'tool_executing' | 'completed' | 'error' | 'output' | 'input' | 'retry'
|
type:
|
||||||
|
| 'thinking'
|
||||||
|
| 'tool_executing'
|
||||||
|
| 'completed'
|
||||||
|
| 'error'
|
||||||
|
| 'output'
|
||||||
|
| 'input'
|
||||||
|
| 'retry'
|
||||||
|
| 'observation'
|
||||||
|
|
||||||
// Tool execution related
|
// Tool execution related
|
||||||
toolName?: string
|
toolName?: string
|
||||||
@@ -18,7 +26,7 @@ export interface Step {
|
|||||||
duration?: number
|
duration?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AgentStatus = 'idle' | 'running' | 'paused' | 'completed' | 'error'
|
export type AgentStatus = 'idle' | 'running' | 'completed' | 'error'
|
||||||
|
|
||||||
export class UIState {
|
export class UIState {
|
||||||
private steps: Step[] = []
|
private steps: Step[] = []
|
||||||
|
|||||||
@@ -4,17 +4,13 @@ const enUS = {
|
|||||||
panel: {
|
panel: {
|
||||||
ready: 'Ready',
|
ready: 'Ready',
|
||||||
thinking: 'Thinking...',
|
thinking: 'Thinking...',
|
||||||
paused: 'Paused',
|
|
||||||
taskInput: 'Enter new task, describe steps in detail, press Enter to submit',
|
taskInput: 'Enter new task, describe steps in detail, press Enter to submit',
|
||||||
userAnswerPrompt: 'Please answer the question above, press Enter to submit',
|
userAnswerPrompt: 'Please answer the question above, press Enter to submit',
|
||||||
taskTerminated: 'Task terminated',
|
taskTerminated: 'Task terminated',
|
||||||
taskCompleted: 'Task completed',
|
taskCompleted: 'Task completed',
|
||||||
continueExecution: 'Continue execution',
|
|
||||||
userAnswer: 'User answer: {{input}}',
|
userAnswer: 'User answer: {{input}}',
|
||||||
question: 'Question: {{question}}',
|
question: 'Question: {{question}}',
|
||||||
waitingPlaceholder: 'Waiting for task to start...',
|
waitingPlaceholder: 'Waiting for task to start...',
|
||||||
pause: 'Pause',
|
|
||||||
continue: 'Continue',
|
|
||||||
stop: 'Stop',
|
stop: 'Stop',
|
||||||
expand: 'Expand history',
|
expand: 'Expand history',
|
||||||
collapse: 'Collapse history',
|
collapse: 'Collapse history',
|
||||||
@@ -54,17 +50,13 @@ const zhCN = {
|
|||||||
panel: {
|
panel: {
|
||||||
ready: '准备就绪',
|
ready: '准备就绪',
|
||||||
thinking: '正在思考...',
|
thinking: '正在思考...',
|
||||||
paused: '暂停中,稍后',
|
|
||||||
taskInput: '输入新任务,详细描述步骤,回车提交',
|
taskInput: '输入新任务,详细描述步骤,回车提交',
|
||||||
userAnswerPrompt: '请回答上面问题,回车提交',
|
userAnswerPrompt: '请回答上面问题,回车提交',
|
||||||
taskTerminated: '任务已终止',
|
taskTerminated: '任务已终止',
|
||||||
taskCompleted: '任务结束',
|
taskCompleted: '任务结束',
|
||||||
continueExecution: '继续执行',
|
|
||||||
userAnswer: '用户回答: {{input}}',
|
userAnswer: '用户回答: {{input}}',
|
||||||
question: '询问: {{question}}',
|
question: '询问: {{question}}',
|
||||||
waitingPlaceholder: '等待任务开始...',
|
waitingPlaceholder: '等待任务开始...',
|
||||||
pause: '暂停',
|
|
||||||
continue: '继续',
|
|
||||||
stop: '终止',
|
stop: '终止',
|
||||||
expand: '展开历史',
|
expand: '展开历史',
|
||||||
collapse: '收起历史',
|
collapse: '收起历史',
|
||||||
|
|||||||
Reference in New Issue
Block a user