feat: add browser workbar navigation controls
The Browser workbar previously showed the Agent-controlled page but did not let users navigate it directly. It now provides Back, Refresh or Stop Loading, an address field, and Go alongside the existing interaction and close actions. User and Agent operations share the same per-conversation browser session and serialization path. Loading, committed URL, and history state keep the toolbar accurate, while Stop Loading interrupts page navigation without closing the session or releasing Runtime context. Direct navigation continues to use the existing built-in-browser capability and URL policy. Closing the browser remains a distinct lifecycle action. Release note: 浏览器工作栏新增返回、刷新/停止加载、地址栏和前往操作;用户与 Agent 共用同一浏览器会话,停止加载不会关闭会话。
This commit is contained in:
@@ -1,11 +1,64 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
agentRequestSchema,
|
||||
browserLiveStateSchema,
|
||||
browserNavigateRequestSchema,
|
||||
browserStopLoadingRequestSchema,
|
||||
builtinEmbeddingConnectionId,
|
||||
legacyEmbeddingConnectionId,
|
||||
runtimeSettingsInputSchema
|
||||
} from './contracts'
|
||||
|
||||
describe('browser control contracts', () => {
|
||||
it('requires authoritative toolbar metadata and accepts committed URLs up to 8192 characters', () => {
|
||||
const base = {
|
||||
conversationId: 'conversation',
|
||||
status: 'ready' as const,
|
||||
sessionActive: true,
|
||||
isLoading: false,
|
||||
canGoBack: true,
|
||||
updatedAt: 1
|
||||
}
|
||||
expect(
|
||||
browserLiveStateSchema.safeParse({
|
||||
...base,
|
||||
url: `https://example.com/${'a'.repeat(8_172)}`
|
||||
}).success
|
||||
).toBe(true)
|
||||
expect(
|
||||
browserLiveStateSchema.safeParse({
|
||||
conversationId: 'conversation',
|
||||
status: 'ready',
|
||||
updatedAt: 1
|
||||
}).success
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps browser navigation and stop-loading requests narrow', () => {
|
||||
expect(
|
||||
browserNavigateRequestSchema.parse({
|
||||
conversationId: 'conversation',
|
||||
url: 'https://example.com/'
|
||||
})
|
||||
).toEqual({
|
||||
conversationId: 'conversation',
|
||||
url: 'https://example.com/'
|
||||
})
|
||||
expect(
|
||||
browserNavigateRequestSchema.safeParse({
|
||||
conversationId: 'conversation',
|
||||
url: 'https://example.com/',
|
||||
rawElectron: true
|
||||
}).success
|
||||
).toBe(false)
|
||||
expect(
|
||||
browserStopLoadingRequestSchema.safeParse({
|
||||
conversationId: 'conversation'
|
||||
}).success
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const baseInput = {
|
||||
provider: 'model' as const,
|
||||
modelBaseUrl: 'https://bigtoken.ai',
|
||||
|
||||
+18
-1
@@ -1346,7 +1346,10 @@ export const browserLiveStateSchema = z
|
||||
'failed',
|
||||
'stopped'
|
||||
]),
|
||||
url: z.string().max(2_048).optional(),
|
||||
sessionActive: z.boolean(),
|
||||
isLoading: z.boolean(),
|
||||
canGoBack: z.boolean(),
|
||||
url: z.string().max(8_192).optional(),
|
||||
frameDataUrl: z
|
||||
.string()
|
||||
.max(400_000)
|
||||
@@ -1369,6 +1372,16 @@ export const browserStopRequestSchema = z
|
||||
.strict()
|
||||
|
||||
export const browserInteractRequestSchema = browserStopRequestSchema
|
||||
export const browserBackRequestSchema = browserStopRequestSchema
|
||||
export const browserReloadRequestSchema = browserStopRequestSchema
|
||||
export const browserStopLoadingRequestSchema = browserStopRequestSchema
|
||||
|
||||
export const browserNavigateRequestSchema = z
|
||||
.object({
|
||||
conversationId: conversationIdSchema,
|
||||
url: z.string().min(1).max(8_192)
|
||||
})
|
||||
.strict()
|
||||
|
||||
export const knowledgeIdSchema = z.string().uuid()
|
||||
export const knowledgeCreateSchema = z
|
||||
@@ -1554,6 +1567,10 @@ export type DesktopApi = {
|
||||
onEvent: (listener: (event: AgentEvent) => void) => () => void
|
||||
}
|
||||
browser: {
|
||||
navigate: (conversationId: string, url: string) => Promise<void>
|
||||
back: (conversationId: string) => Promise<void>
|
||||
reload: (conversationId: string) => Promise<void>
|
||||
stopLoading: (conversationId: string) => Promise<void>
|
||||
interact: (conversationId: string) => Promise<void>
|
||||
stop: (conversationId: string) => Promise<void>
|
||||
onState: (listener: (state: BrowserLiveState) => void) => () => void
|
||||
|
||||
@@ -20,6 +20,10 @@ export const ipcChannels = {
|
||||
agentQuestionRespond: 'agent:question:respond',
|
||||
agentCompactConversation: 'agent:context:compact',
|
||||
agentEvent: 'agent:event',
|
||||
browserNavigate: 'browser:navigate',
|
||||
browserBack: 'browser:back',
|
||||
browserReload: 'browser:reload',
|
||||
browserStopLoading: 'browser:stop-loading',
|
||||
browserInteract: 'browser:interact',
|
||||
browserStop: 'browser:stop',
|
||||
browserState: 'browser:state',
|
||||
|
||||
Reference in New Issue
Block a user