{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "agent-playground", "title": "Agent Playground", "description": "Complete transient Chat, Work, and Coding playground with connection, Persona and Skill selection, workspace context, approvals, terminal history, and lazy injected just-bash", "dependencies": ["@wener/ai@^0.1.21", "@wener/common@^3.0.0", "daisyui", "lucide-react"], "registryDependencies": [ "https://ui-components.wener.me/r/agent-ai-sdk.json", "https://ui-components.wener.me/r/agent-work.json", "https://ui-components.wener.me/r/agent-coding.json" ], "files": [ { "path": "registry/default/blocks/agent-playground/playground-runtime.ts", "content": "import type { Persona } from '@wener/ai/agent/persona';\nimport type { ToolSet } from 'ai';\nimport {\n\ttype AgentCommandExecutor,\n\ttype AgentCommandRuntime,\n\ttype AgentTerminalResult,\n\ttype CreateAgentCodingToolsOptions,\n\tcreateAgentCodingTools,\n} from '../agent-coding';\nimport {\n\ttype AgentWorkspace,\n\ttype AgentWorkspaceContext,\n\tcomposeAgentInstructions,\n\tcreateAgentWorkspaceReadTools,\n} from '../agent-work';\n\nexport type AgentPlaygroundMode = 'chat' | 'coding' | 'work';\n\nexport const AgentRuntimeIdentityChangedCancelReason = 'agent-runtime-identity-changed';\n\nexport type AgentPlaygroundRuntimeOptions = {\n\tcoding?: Omit;\n\tcommandRuntime?: AgentCommandRuntime;\n\tcontext?: AgentWorkspaceContext;\n\tmode: AgentPlaygroundMode;\n\tpersona?: Persona;\n\tworkspace: AgentWorkspace;\n};\n\nexport type AgentPlaygroundRuntimeConfiguration = {\n\tinstructions: string;\n\ttools?: ToolSet;\n};\n\nexport function createAgentPlaygroundRuntimeConfiguration({\n\tcoding,\n\tcommandRuntime,\n\tcontext,\n\tmode,\n\tpersona,\n\tworkspace,\n}: AgentPlaygroundRuntimeOptions): AgentPlaygroundRuntimeConfiguration {\n\tconst instructions = composeAgentInstructions({\n\t\tcontext: mode === 'chat' ? undefined : context,\n\t\tpersona,\n\t}).instructions;\n\tif (mode === 'chat') return { instructions };\n\tconst readonlyTools = createAgentWorkspaceReadTools(workspace);\n\tif (mode === 'work') return { instructions, tools: readonlyTools };\n\tif (!commandRuntime) return { instructions, tools: readonlyTools };\n\treturn {\n\t\tinstructions,\n\t\ttools: {\n\t\t\t...readonlyTools,\n\t\t\t...createAgentCodingTools({ ...coding, runtime: commandRuntime, workspace }),\n\t\t},\n\t};\n}\n\nexport function isUnsafeAgentTerminalResult(result: AgentTerminalResult): boolean {\n\treturn result.poisoned === true || result.settled === false || result.mutationMayContinue === true;\n}\n\nexport function createUnavailableAgentExecutor(message: string): AgentCommandExecutor {\n\treturn {\n\t\tasync execute() {\n\t\t\treturn {\n\t\t\t\tdurationMs: 0,\n\t\t\t\texitCode: 127,\n\t\t\t\tsettled: true,\n\t\t\t\tstderr: `${message}\\n`,\n\t\t\t\tstdout: '',\n\t\t\t\ttruncated: false,\n\t\t\t};\n\t\t},\n\t};\n}\n", "type": "registry:lib", "target": "@components/blocks/agent-playground/playground-runtime.ts" }, { "path": "registry/default/blocks/agent-playground/use-command-runtime-transition.ts", "content": "'use client';\n\nimport { type MutableRefObject, useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport type { AgentCommandRuntime } from '../agent-coding';\nimport { AgentRuntimeIdentityChangedCancelReason } from './playground-runtime';\n\nexport function useCommandRuntimeTransition(\n\tcandidate: AgentCommandRuntime,\n\tidentity: unknown,\n\tpoisonedRef: MutableRefObject,\n) {\n\tconst [commandRuntime, setCommandRuntime] = useState(candidate);\n\tconst [poisoned, setPoisonedState] = useState(false);\n\tconst [revision, setRevision] = useState(0);\n\tconst activeRuntime = useRef(commandRuntime);\n\tconst generation = useRef(0);\n\tconst setPoisoned = useCallback(\n\t\t(value: boolean) => {\n\t\t\tpoisonedRef.current = value;\n\t\t\tsetPoisonedState(value);\n\t\t},\n\t\t[poisonedRef],\n\t);\n\n\tuseLayoutEffect(() => {\n\t\tlet current = true;\n\t\tconst previousRuntime = activeRuntime.current;\n\t\tconst runtimeReplaced = previousRuntime !== candidate;\n\t\tconst transitionGeneration = ++generation.current;\n\t\tconst previousState = previousRuntime.getState();\n\t\tif (previousState.running || previousState.mutationMayContinue || previousState.poisoned) setPoisoned(true);\n\t\tvoid previousRuntime.cancelCurrentAndWait(AgentRuntimeIdentityChangedCancelReason).then((settledState) => {\n\t\t\tif (!current || transitionGeneration !== generation.current) return;\n\t\t\tif (!runtimeReplaced) {\n\t\t\t\tsetPoisoned(settledState.poisoned);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (settledState.mutationMayContinue) {\n\t\t\t\tsetPoisoned(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tactiveRuntime.current = candidate;\n\t\t\tsetCommandRuntime(candidate);\n\t\t\tsetPoisoned(candidate.getState().poisoned);\n\t\t});\n\t\tsetRevision((value) => value + 1);\n\t\treturn () => {\n\t\t\tcurrent = false;\n\t\t\tpoisonedRef.current = true;\n\t\t\tactiveRuntime.current.cancelCurrent(AgentRuntimeIdentityChangedCancelReason);\n\t\t};\n\t}, [candidate, identity, poisonedRef, setPoisoned]);\n\n\treturn { commandRuntime, generation, poisoned, revision, setPoisoned };\n}\n", "type": "registry:hook", "target": "@components/blocks/agent-playground/use-command-runtime-transition.ts" }, { "path": "registry/default/blocks/agent-playground/use-just-bash-executor.ts", "content": "'use client';\n\nimport {\n\tcreateJustBashAgentSession,\n\tcreateJustBashWorkspaceFromLoader,\n\ttype JustBashModuleLoader,\n} from '@wener/common/fs/just-bash';\nimport { useEffect, useState } from 'react';\nimport { type AgentCommandExecutor, createJustBashCommandExecutor } from '../agent-coding';\nimport type { AgentWorkspace } from '../agent-work';\n\nexport type JustBashExecutorState = {\n\terror?: Error;\n\texecutor?: AgentCommandExecutor;\n\tstatus: 'disabled' | 'error' | 'idle' | 'loading' | 'ready';\n};\n\nexport function useJustBashExecutor(options: {\n\tenabled: boolean;\n\tloader?: JustBashModuleLoader;\n\tonWorkspaceChanged: () => void;\n\tworkspace: AgentWorkspace;\n}): JustBashExecutorState {\n\tconst { enabled, loader, onWorkspaceChanged, workspace } = options;\n\tconst [state, setState] = useState({ status: loader ? 'idle' : 'disabled' });\n\tuseEffect(() => {\n\t\tlet current = true;\n\t\tif (!loader) {\n\t\t\tsetState({ status: 'disabled' });\n\t\t\treturn () => {\n\t\t\t\tcurrent = false;\n\t\t\t};\n\t\t}\n\t\tif (!enabled) {\n\t\t\tsetState({ status: 'idle' });\n\t\t\treturn () => {\n\t\t\t\tcurrent = false;\n\t\t\t};\n\t\t}\n\t\tsetState({ status: 'loading' });\n\t\tvoid createJustBashWorkspaceFromLoader(loader, {\n\t\t\tcwd: '/workspace',\n\t\t\tfs: workspace.fileSystem,\n\t\t\tfsRoot: workspace.rootPath,\n\t\t\tonWorkspaceChanged,\n\t\t\tworkspaceRoot: '/workspace',\n\t\t}).then(\n\t\t\t(bashWorkspace) => {\n\t\t\t\tif (!current) return;\n\t\t\t\tconst session = createJustBashAgentSession({ workspace: bashWorkspace });\n\t\t\t\tsetState({ executor: createJustBashCommandExecutor(session), status: 'ready' });\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (current) setState({ error: error instanceof Error ? error : new Error(String(error)), status: 'error' });\n\t\t\t},\n\t\t);\n\t\treturn () => {\n\t\t\tcurrent = false;\n\t\t};\n\t}, [enabled, loader, onWorkspaceChanged, workspace.fileSystem, workspace.id, workspace.rootPath]);\n\treturn state;\n}\n", "type": "registry:hook", "target": "@components/blocks/agent-playground/use-just-bash-executor.ts" }, { "path": "registry/default/blocks/agent-playground/playground-controls.tsx", "content": "'use client';\n\nimport type { Persona } from '@wener/ai/agent/persona';\nimport { getSkillIdentity, type Skill } from '@wener/ai/agent/skill';\nimport { Code2, MessageSquare, RefreshCw, Settings2, Wrench } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport type { AgentPlaygroundMode } from './playground-runtime';\n\nexport type AgentPlaygroundControlMessages = {\n\tchatMode: string;\n\tcodingMode: string;\n\tcontextError: string;\n\tcontextIdle: string;\n\tcontextLoading: string;\n\tcontextReady: string;\n\tnoPersona: string;\n\tnoSkills: string;\n\tpersona: string;\n\trefreshWorkspace: string;\n\tskills: string;\n\tworkMode: string;\n};\n\nexport const defaultAgentPlaygroundControlMessages: AgentPlaygroundControlMessages = {\n\tchatMode: '对话',\n\tcodingMode: '编码',\n\tcontextError: '上下文错误',\n\tcontextIdle: '对话模式不读取工作区',\n\tcontextLoading: '正在加载上下文',\n\tcontextReady: '上下文就绪',\n\tnoPersona: '无',\n\tnoSkills: '无可用 Skill',\n\tpersona: 'Persona',\n\trefreshWorkspace: '刷新工作区',\n\tskills: 'Skills',\n\tworkMode: '工作',\n};\n\nexport function getAgentSkillIdentity(skill: Pick): string {\n\treturn getSkillIdentity(skill);\n}\n\nexport type AgentPlaygroundControlsProps = {\n\tcontextStatus: 'error' | 'idle' | 'loading' | 'ready';\n\tmessages?: Partial;\n\tmode: AgentPlaygroundMode;\n\tonModeChange: (mode: AgentPlaygroundMode) => void;\n\tonPersonaChange: (id: string) => void;\n\tonRefreshWorkspace: () => void;\n\tonSkillChange: (identity: string, selected: boolean) => void;\n\tpersonas: readonly Persona[];\n\tselectedPersonaId: string;\n\tselectedSkillIdentities: ReadonlySet;\n\tskills: readonly Skill[];\n};\n\nexport function AgentPlaygroundControls({\n\tcontextStatus,\n\tmessages,\n\tmode,\n\tonModeChange,\n\tonPersonaChange,\n\tonRefreshWorkspace,\n\tonSkillChange,\n\tpersonas,\n\tselectedPersonaId,\n\tselectedSkillIdentities,\n\tskills,\n}: AgentPlaygroundControlsProps) {\n\tconst copy = { ...defaultAgentPlaygroundControlMessages, ...messages };\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\tAgent 模式\n\t\t\t\t} onClick={() => onModeChange('chat')}>\n\t\t\t\t\t{copy.chatMode}\n\t\t\t\t\n\t\t\t\t} onClick={() => onModeChange('work')}>\n\t\t\t\t\t{copy.workMode}\n\t\t\t\t\n\t\t\t\t} onClick={() => onModeChange('coding')}>\n\t\t\t\t\t{copy.codingMode}\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{skills.length === 0 ? (\n\t\t\t\t\t\t

{copy.noSkills}

\n\t\t\t\t\t) : (\n\t\t\t\t\t\tskills.map((skill) => {\n\t\t\t\t\t\t\tconst identity = getAgentSkillIdentity(skill);\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t onSkillChange(identity, event.currentTarget.checked)}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{skill.name}\n\t\t\t\t\t\t\t\t\t\t{skill.description}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t
\n\t\t\t\n\t\t\t\t{contextStatus === 'error'\n\t\t\t\t\t? copy.contextError\n\t\t\t\t\t: contextStatus === 'loading'\n\t\t\t\t\t\t? copy.contextLoading\n\t\t\t\t\t\t: contextStatus === 'idle'\n\t\t\t\t\t\t\t? copy.contextIdle\n\t\t\t\t\t\t\t: copy.contextReady}\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t);\n}\n\nfunction ModeButton({\n\tactive,\n\tchildren,\n\ticon,\n\tonClick,\n}: {\n\tactive: boolean;\n\tchildren: string;\n\ticon: import('react').ReactNode;\n\tonClick: () => void;\n}) {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t{children}\n\t\t\n\t);\n}\n", "type": "registry:component", "target": "@components/blocks/agent-playground/playground-controls.tsx" }, { "path": "registry/default/blocks/agent-playground/tool-approval-renderer.tsx", "content": "'use client';\n\nimport { Check, X } from 'lucide-react';\nimport { useRef, useState } from 'react';\nimport type { AiSdkAgentToolRenderContext } from '../agent-ai-sdk';\n\nexport type AgentPlaygroundApprovalMessages = {\n\tapprove: string;\n\tdeny: string;\n\tfailed: string;\n\tpending: string;\n\tresponded: string;\n\ttitle: string;\n};\n\nexport const defaultAgentPlaygroundApprovalMessages: AgentPlaygroundApprovalMessages = {\n\tapprove: '批准',\n\tdeny: '拒绝',\n\tfailed: '提交审批失败,请重试。',\n\tpending: '正在提交审批…',\n\tresponded: '审批已提交',\n\ttitle: '工具执行审批',\n};\n\nexport function AgentPlaygroundApproval({\n\tcontext,\n\tmessages,\n}: {\n\tcontext: AiSdkAgentToolRenderContext;\n\tmessages?: Partial;\n}) {\n\tconst part = readRecord(context.part);\n\tconst approval = readRecord(part?.approval);\n\tconst id = typeof approval?.id === 'string' ? approval.id : undefined;\n\tconst state = typeof part?.state === 'string' ? part.state : undefined;\n\tconst name = typeof part?.type === 'string' ? part.type.replace(/^tool-/u, '') : 'tool';\n\tif (!id || (state !== 'approval-requested' && state !== 'approval-responded')) return null;\n\treturn (\n\t\t\n\t);\n}\n\nfunction AgentPlaygroundApprovalRequest({\n\tcontext,\n\tid,\n\tinput,\n\tmessages,\n\tname,\n}: {\n\tcontext: AiSdkAgentToolRenderContext;\n\tid: string;\n\tinput: unknown;\n\tmessages?: Partial;\n\tname: string;\n}) {\n\tconst copy = { ...defaultAgentPlaygroundApprovalMessages, ...messages };\n\tconst submitted = useRef(false);\n\tconst [pending, setPending] = useState(false);\n\tconst [responded, setResponded] = useState(false);\n\tconst [error, setError] = useState(false);\n\tconst respond = async (approved: boolean) => {\n\t\tif (submitted.current || pending || responded) return;\n\t\tsubmitted.current = true;\n\t\tsetPending(true);\n\t\tsetError(false);\n\t\ttry {\n\t\t\tawait context.respondToApproval({ approved, id, reason: approved ? undefined : '用户拒绝' });\n\t\t\tsetResponded(true);\n\t\t} catch {\n\t\t\tsubmitted.current = false;\n\t\t\tsetError(true);\n\t\t} finally {\n\t\t\tsetPending(false);\n\t\t}\n\t};\n\treturn (\n\t\t\n\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t{copy.title}:{name}\n\t\t\t\t

\n\t\t\t\t\n\t\t\t\t\t{responded ? copy.responded : copy.pending.replace('正在提交', '等待')}\n\t\t\t\t\n\t\t\t
\n\t\t\t{input !== undefined ? (\n\t\t\t\t
\n\t\t\t\t\t{safeJson(input)}\n\t\t\t\t
\n\t\t\t) : null}\n\t\t\t
\n\t\t\t\t{pending ? {copy.pending} : null}\n\t\t\t\t{error ? (\n\t\t\t\t\t\n\t\t\t\t\t\t{copy.failed}\n\t\t\t\t\t\n\t\t\t\t) : null}\n\t\t\t\t void respond(false)}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\n\t);\n}\n\nexport function renderAgentPlaygroundApproval(\n\tcontext: AiSdkAgentToolRenderContext,\n\tmessages?: Partial,\n) {\n\tconst part = readRecord(context.part);\n\treturn part?.state === 'approval-requested' || part?.state === 'approval-responded' ? (\n\t\t\n\t) : undefined;\n}\n\nfunction readRecord(value: unknown): Record | undefined {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n\t\t? (value as Record)\n\t\t: undefined;\n}\n\nfunction safeJson(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value, null, 2).slice(0, 16 * 1024);\n\t} catch {\n\t\treturn '[无法显示输入]';\n\t}\n}\n", "type": "registry:component", "target": "@components/blocks/agent-playground/tool-approval-renderer.tsx" }, { "path": "registry/default/blocks/agent-playground/agent-playground.tsx", "content": "'use client';\n\nimport type { Persona } from '@wener/ai/agent/persona';\nimport type { Skill } from '@wener/ai/agent/skill';\nimport type { IFileSystem } from '@wener/common/fs';\nimport type { JustBashModuleLoader } from '@wener/common/fs/just-bash';\nimport type { ComponentPropsWithRef } from 'react';\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport { cn } from '@/lib/utils';\nimport {\n\tAiSdkAgentChat,\n\ttype OpenAICompatibleConnectionConfig,\n\ttype OpenAICompatibleConnectionDraft,\n\tOpenAICompatibleConnectionEditor,\n\ttype OpenAICompatibleConnectionEditorMessages,\n} from '../agent-ai-sdk';\nimport { AgentChat } from '../agent-chat';\nimport {\n\ttype AgentActiveCommand,\n\tAgentCoding,\n\ttype AgentCodingMessages,\n\ttype AgentCommandExecutor,\n\ttype AgentTerminalEntry,\n\ttype AgentTerminalMessages,\n\ttype AgentTerminalResult,\n\tappendAgentTerminalEntry,\n\tappendCompletedAgentTerminalEntry,\n\tcompleteAgentTerminalEntry,\n\tcreateAgentCommandRuntime,\n} from '../agent-coding';\nimport {\n\tAgentWork,\n\ttype AgentWorkMessages,\n\ttype AgentWorkspace,\n\ttype AgentWorkspaceContextLimits,\n\tcreateAgentWorkspaceMutationGuard,\n\tuseAgentWorkspaceContext,\n} from '../agent-work';\nimport {\n\ttype AgentPlaygroundControlMessages,\n\tAgentPlaygroundControls,\n\tgetAgentSkillIdentity,\n} from './playground-controls';\nimport {\n\ttype AgentPlaygroundMode,\n\tcreateAgentPlaygroundRuntimeConfiguration,\n\tcreateUnavailableAgentExecutor,\n\tisUnsafeAgentTerminalResult,\n} from './playground-runtime';\nimport { type AgentPlaygroundApprovalMessages, renderAgentPlaygroundApproval } from './tool-approval-renderer';\nimport { useCommandRuntimeTransition } from './use-command-runtime-transition';\nimport { useJustBashExecutor } from './use-just-bash-executor';\n\nexport { AgentRuntimeIdentityChangedCancelReason } from './playground-runtime';\n\nconst blankConnection: OpenAICompatibleConnectionDraft = { apiKey: '', baseUrl: '', headers: {}, model: '' };\n\nexport type AgentPlaygroundMessages = {\n\tapproval?: Partial;\n\tchatTitle: string;\n\tcoding?: Partial;\n\tcodingTitle: string;\n\tconnectionApplied: string;\n\tconnectionEditor?: Partial;\n\tconnectionRequired: string;\n\tconnectionSettings: string;\n\tcontrols?: Partial;\n\texecutorHost: string;\n\texecutorMissing: string;\n\texecutorPoisoned: string;\n\tjustBashError: string;\n\tjustBashLoading: string;\n\tjustBashReady: string;\n\truntimeLabel: string;\n\tterminal?: Partial;\n\twork?: Partial;\n\tworkTitle: string;\n};\n\nexport const defaultAgentPlaygroundMessages: AgentPlaygroundMessages = {\n\tchatTitle: '对话',\n\tcodingTitle: '编码',\n\tconnectionApplied: '连接已应用',\n\tconnectionRequired: '请先应用连接配置',\n\tconnectionSettings: '连接设置',\n\texecutorHost: '宿主执行器',\n\texecutorMissing: '尚未配置命令执行器',\n\texecutorPoisoned: '已污染',\n\tjustBashError: 'just-bash 加载失败',\n\tjustBashLoading: '正在加载 just-bash',\n\tjustBashReady: 'just-bash 就绪',\n\truntimeLabel: '命令运行时:',\n\tworkTitle: '工作',\n};\n\nexport type AgentPlaygroundProps = ComponentPropsWithRef<'section'> & {\n\tagentsPath?: string;\n\tcommandCancelSettleGraceMs?: number;\n\tcontextLimits?: Partial;\n\texecutor?: AgentCommandExecutor;\n\tfetch?: typeof globalThis.fetch;\n\tfileSystem: IFileSystem;\n\tinitialConnection?: OpenAICompatibleConnectionDraft;\n\tinitialConnectionApplied?: boolean;\n\tinitialMode?: AgentPlaygroundMode;\n\tjustBashLoader?: JustBashModuleLoader;\n\tmessages?: Partial;\n\tpersonas: readonly Persona[];\n\trootPath: string;\n\tskills: readonly Skill[];\n\tworkspaceId: string;\n};\n\nexport function AgentPlayground({\n\tagentsPath,\n\tclassName,\n\tcommandCancelSettleGraceMs,\n\tcontextLimits,\n\texecutor: hostExecutor,\n\tfetch,\n\tfileSystem,\n\tinitialConnection = blankConnection,\n\tinitialConnectionApplied = false,\n\tinitialMode = 'work',\n\tjustBashLoader,\n\tmessages,\n\tpersonas,\n\trootPath,\n\tskills,\n\tworkspaceId,\n\t...props\n}: AgentPlaygroundProps) {\n\tconst copy = { ...defaultAgentPlaygroundMessages, ...messages };\n\tconst [mode, setMode] = useState(initialMode);\n\tconst [connectionDraft, setConnectionDraft] = useState(() =>\n\t\tcloneConnection(initialConnection),\n\t);\n\tconst [connection, setConnection] = useState(() =>\n\t\tinitialConnectionApplied ? cloneConnection(initialConnection) : undefined,\n\t);\n\tconst [connectionRevision, setConnectionRevision] = useState(0);\n\tconst [workspaceRevision, setWorkspaceRevision] = useState(0);\n\tconst [contextRevision, setContextRevision] = useState(0);\n\tconst [selectedPersonaId, setSelectedPersonaId] = useState(personas[0]?.id ?? '');\n\tconst [selectedSkillIdentities, setSelectedSkillIdentities] = useState>(\n\t\t() => new Set(skills.map(getAgentSkillIdentity)),\n\t);\n\tconst [terminalCommand, setTerminalCommand] = useState('');\n\tconst [terminalEntries, setTerminalEntries] = useState([]);\n\tconst [activeCommand, setActiveCommand] = useState();\n\tconst runtimePoisonedRef = useRef(false);\n\tconst guardedFileSystem = useMemo(\n\t\t() => createAgentWorkspaceMutationGuard(fileSystem, () => !runtimePoisonedRef.current),\n\t\t[fileSystem],\n\t);\n\tconst commandSequence = useRef(0);\n\tconst incrementWorkspaceRevision = useCallback(() => setWorkspaceRevision((value) => value + 1), []);\n\tconst refreshWorkspaceContext = useCallback(() => {\n\t\tsetWorkspaceRevision((value) => value + 1);\n\t\tsetContextRevision((value) => value + 1);\n\t}, []);\n\tconst runtimeWorkspace = useMemo(\n\t\t() => ({\n\t\t\tfileSystem: guardedFileSystem,\n\t\t\tid: workspaceId,\n\t\t\tlabel: workspaceId,\n\t\t\trootPath,\n\t\t}),\n\t\t[guardedFileSystem, rootPath, workspaceId],\n\t);\n\tconst workspace = useMemo(\n\t\t() => ({ ...runtimeWorkspace, revision: workspaceRevision }),\n\t\t[runtimeWorkspace, workspaceRevision],\n\t);\n\tconst contextWorkspace = useMemo(\n\t\t() => ({ ...runtimeWorkspace, revision: contextRevision }),\n\t\t[contextRevision, runtimeWorkspace],\n\t);\n\tconst selectedPersona = personas.find((persona) => persona.id === selectedPersonaId);\n\tconst selectedSkills = useMemo(\n\t\t() => skills.filter((skill) => selectedSkillIdentities.has(getAgentSkillIdentity(skill))),\n\t\t[selectedSkillIdentities, skills],\n\t);\n\tconst contextState = useAgentWorkspaceContext({\n\t\tagentsPath,\n\t\tenabled: mode !== 'chat',\n\t\tlimits: contextLimits,\n\t\tskills: selectedSkills,\n\t\tworkspace: contextWorkspace,\n\t});\n\tconst justBash = useJustBashExecutor({\n\t\tenabled: mode === 'coding' && !hostExecutor,\n\t\tloader: justBashLoader,\n\t\tonWorkspaceChanged: incrementWorkspaceRevision,\n\t\tworkspace: runtimeWorkspace,\n\t});\n\tconst unavailableExecutor = useMemo(\n\t\t() => createUnavailableAgentExecutor(copy.executorMissing),\n\t\t[copy.executorMissing],\n\t);\n\tconst executor = hostExecutor ?? justBash.executor ?? unavailableExecutor;\n\tconst candidateCommandRuntime = useMemo(\n\t\t() => createAgentCommandRuntime(executor, { cancelSettleGraceMs: commandCancelSettleGraceMs }),\n\t\t[commandCancelSettleGraceMs, executor],\n\t);\n\tconst commandRuntimeIdentity = useMemo(\n\t\t() => ({\n\t\t\tcontext: contextState.context,\n\t\t\tfileSystem,\n\t\t\tmode,\n\t\t\trootPath,\n\t\t\tselectedPersonaId,\n\t\t\tselectedSkillIdentities,\n\t\t\tworkspaceId,\n\t\t}),\n\t\t[contextState.context, fileSystem, mode, rootPath, selectedPersonaId, selectedSkillIdentities, workspaceId],\n\t);\n\tconst {\n\t\tcommandRuntime,\n\t\tgeneration: runtimeGeneration,\n\t\tpoisoned: runtimePoisoned,\n\t\trevision: commandRuntimeRevision,\n\t\tsetPoisoned: setRuntimePoisoned,\n\t} = useCommandRuntimeTransition(candidateCommandRuntime, commandRuntimeIdentity, runtimePoisonedRef);\n\tconst runtimeRevision = commandRuntimeRevision + connectionRevision;\n\tconst commandGenerations = useRef(new Map());\n\n\tconst commandStarted = useCallback(({ command, toolCallId }: { command: string; toolCallId: string }) => {\n\t\tconst startedAt = Date.now();\n\t\tcommandGenerations.current.set(toolCallId, runtimeGeneration.current);\n\t\tsetActiveCommand({ command, id: toolCallId, startedAt });\n\t\tsetTerminalEntries((current) =>\n\t\t\tappendAgentTerminalEntry(current, { command, id: toolCallId, startedAt, status: 'running' }),\n\t\t);\n\t}, []);\n\tconst commandFinished = useCallback(\n\t\t({\n\t\t\tacquired,\n\t\t\tcommand,\n\t\t\tresult,\n\t\t\ttoolCallId,\n\t\t}: {\n\t\t\tacquired: boolean;\n\t\t\tcommand: string;\n\t\t\tresult: AgentTerminalResult;\n\t\t\ttoolCallId: string;\n\t\t}) => {\n\t\t\tif (!acquired) {\n\t\t\t\tconst startedAt = Date.now();\n\t\t\t\tsetTerminalEntries((current) =>\n\t\t\t\t\tappendCompletedAgentTerminalEntry(current, { command, id: toolCallId, startedAt }, result),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst generation = commandGenerations.current.get(toolCallId);\n\t\t\tcommandGenerations.current.delete(toolCallId);\n\t\t\tsetTerminalEntries((current) => completeAgentTerminalEntry(current, toolCallId, result));\n\t\t\tsetActiveCommand((current) => (current?.id === toolCallId ? undefined : current));\n\t\t\tif (generation === runtimeGeneration.current && isUnsafeAgentTerminalResult(result)) setRuntimePoisoned(true);\n\t\t},\n\t\t[],\n\t);\n\tconst runtime = useMemo(\n\t\t() =>\n\t\t\tcreateAgentPlaygroundRuntimeConfiguration({\n\t\t\t\tcoding: {\n\t\t\t\t\tonCommandFinish: commandFinished,\n\t\t\t\t\tonCommandStart: commandStarted,\n\t\t\t\t\tonWorkspaceChanged: incrementWorkspaceRevision,\n\t\t\t\t},\n\t\t\t\tcommandRuntime,\n\t\t\t\tcontext: contextState.context,\n\t\t\t\tmode,\n\t\t\t\tpersona: selectedPersona,\n\t\t\t\tworkspace: runtimeWorkspace,\n\t\t\t}),\n\t\t[\n\t\t\tcommandFinished,\n\t\t\tcommandRuntime,\n\t\t\tcommandStarted,\n\t\t\tcontextState.context,\n\t\t\tincrementWorkspaceRevision,\n\t\t\tmode,\n\t\t\truntimeWorkspace,\n\t\t\tselectedPersona,\n\t\t],\n\t);\n\n\tconst runTerminalCommand = useCallback(\n\t\t(command: string) => {\n\t\t\tconst id = `terminal-${++commandSequence.current}`;\n\t\t\tconst startedAt = Date.now();\n\t\t\tlet acquired = false;\n\t\t\tlet generation: number | undefined;\n\t\t\tsetTerminalCommand('');\n\t\t\tvoid commandRuntime\n\t\t\t\t.execute(\n\t\t\t\t\t{ command },\n\t\t\t\t\t{\n\t\t\t\t\t\tonAcquired: () => {\n\t\t\t\t\t\t\tacquired = true;\n\t\t\t\t\t\t\tgeneration = runtimeGeneration.current;\n\t\t\t\t\t\t\tsetActiveCommand({ command, id, startedAt });\n\t\t\t\t\t\t\tsetTerminalEntries((current) =>\n\t\t\t\t\t\t\t\tappendAgentTerminalEntry(current, { command, id, startedAt, status: 'running' }),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t.then((result) => {\n\t\t\t\t\tif (acquired) {\n\t\t\t\t\t\tsetTerminalEntries((current) => completeAgentTerminalEntry(current, id, result));\n\t\t\t\t\t\tsetActiveCommand((current) => (current?.id === id ? undefined : current));\n\t\t\t\t\t\tif (generation === runtimeGeneration.current && isUnsafeAgentTerminalResult(result)) {\n\t\t\t\t\t\t\tsetRuntimePoisoned(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetTerminalEntries((current) =>\n\t\t\t\t\t\t\tappendCompletedAgentTerminalEntry(current, { command, id, startedAt }, result),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (generation === runtimeGeneration.current && result.workspaceChanged) incrementWorkspaceRevision();\n\t\t\t\t});\n\t\t},\n\t\t[commandRuntime, incrementWorkspaceRevision],\n\t);\n\tconst cancelCommand = useCallback(\n\t\t(id: string) => {\n\t\t\tif (activeCommand?.id === id) commandRuntime.cancelCurrent('用户取消');\n\t\t},\n\t\t[activeCommand?.id, commandRuntime],\n\t);\n\n\tconst modeTitle = mode === 'chat' ? copy.chatTitle : mode === 'work' ? copy.workTitle : copy.codingTitle;\n\tconst chat = connection ? (\n\t\t{copy.connectionApplied}}\n\t\t\tfetch={fetch}\n\t\t\theader={

{modeTitle}

}\n\t\t\tinstructions={runtime.instructions}\n\t\t\trenderTool={(context) => renderAgentPlaygroundApproval(context, copy.approval)}\n\t\t\ttools={runtime.tools}\n\t\t/>\n\t) : (\n\t\t{modeTitle}}\n\t\t\tstatus='ready'\n\t\t\ttext=''\n\t\t\tvalue={[]}\n\t\t\tonFilesChange={() => undefined}\n\t\t\tonSend={() => undefined}\n\t\t\tonTextChange={() => undefined}\n\t\t/>\n\t);\n\tconst work = (\n\t\t\n\t);\n\tconst coding = (\n\t\t\n\t);\n\n\treturn (\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{copy.connectionSettings}\n\t\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tsetConnection(cloneConnection(next));\n\t\t\t\t\t\tsetConnectionRevision((value) => value + 1);\n\t\t\t\t\t}}\n\t\t\t\t\tonDraftChange={setConnectionDraft}\n\t\t\t\t/>\n\t\t\t
\n\t\t\t\n\t\t\t\t\tsetSelectedSkillIdentities((current) => {\n\t\t\t\t\t\tconst next = new Set(current);\n\t\t\t\t\t\tif (selected) next.add(identity);\n\t\t\t\t\t\telse next.delete(identity);\n\t\t\t\t\t\treturn next;\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tpersonas={personas}\n\t\t\t\tselectedPersonaId={selectedPersonaId}\n\t\t\t\tselectedSkillIdentities={selectedSkillIdentities}\n\t\t\t\tskills={skills}\n\t\t\t/>\n\t\t\t{mode === 'coding' ? (\n\t\t\t\t
\n\t\t\t\t\t{copy.runtimeLabel}\n\t\t\t\t\t\n\t\t\t\t\t\t{runtimePoisoned\n\t\t\t\t\t\t\t? copy.executorPoisoned\n\t\t\t\t\t\t\t: hostExecutor\n\t\t\t\t\t\t\t\t? copy.executorHost\n\t\t\t\t\t\t\t\t: justBash.status === 'loading'\n\t\t\t\t\t\t\t\t\t? copy.justBashLoading\n\t\t\t\t\t\t\t\t\t: justBash.status === 'error'\n\t\t\t\t\t\t\t\t\t\t? copy.justBashError\n\t\t\t\t\t\t\t\t\t\t: justBash.status === 'ready'\n\t\t\t\t\t\t\t\t\t\t\t? copy.justBashReady\n\t\t\t\t\t\t\t\t\t\t\t: copy.executorMissing}\n\t\t\t\t\t\n\t\t\t\t\t{justBash.error ? (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{justBash.error.message}\n\t\t\t\t\t\t\n\t\t\t\t\t) : null}\n\t\t\t\t
\n\t\t\t) : null}\n\t\t\t
{mode === 'chat' ? chat : mode === 'work' ? work : coding}
\n\t\t\n\t);\n}\n\nfunction cloneConnection(connection: T): T {\n\treturn { ...connection, headers: connection.headers ? { ...connection.headers } : undefined };\n}\n", "type": "registry:component", "target": "@components/blocks/agent-playground/agent-playground.tsx" }, { "path": "registry/default/blocks/agent-playground/index.ts", "content": "export * from './agent-playground';\nexport * from './playground-controls';\nexport * from './playground-runtime';\nexport * from './tool-approval-renderer';\nexport * from './use-just-bash-executor';\n", "type": "registry:component", "target": "@components/blocks/agent-playground/index.ts" } ], "type": "registry:block" }