@zhin.js/adapter-sandbox 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,91 @@
1
1
  # @zhin.js/adapter-process
2
2
 
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 8740059: Standardize TypeScript capabilities on named module directories such as `commands/foo/index.ts`, `middlewares/audit/index.ts`, `handlers/message-receive/index.ts`, `pages/workroom/index.tsx`, and `mcps/filesystem/index.ts`. Only the fixed `index` entry is discovered; sibling files remain private helpers.
8
+
9
+ Command route segments come from directories, while `[name]`, `[[name]]`, `[...name]`, and `[[...name]]` directories declare dynamic parameters. Plugin owners do not enter the route unless their config explicitly sets `commandNamespace`; Endpoint `commandPrefix` remains platform-owned and defaults to an empty string.
10
+
11
+ Migrate the built-in adapters, plugins, examples, generators, migration tooling, hot reload classification, Agent authoring surfaces, documentation, and release artifacts to the explicit entry convention.
12
+
13
+ Make Tool ownership and progressive disclosure explicit across all four supported locations: plugin-public `tools/`, Agent-private `agents/<name>/tools/`, Skill-private `skills/<name>/tools/`, and Agent-Skill-private `agents/<name>/skills/<name>/tools/`. Move adapter and group-suite operations that require domain instructions into their owning Skills so `load_skill` is the only path that unlocks their schemas.
14
+
15
+ Remove the package-root `agent/` convention. Public capabilities now use named package-root directories, schedules use `schedules/<name>/index.ts` or `plugin.ts` injection, MCP connections use `mcps/<name>/index.ts`, Prompt Sections use `prompt-sections/<name>/index.ts`, Agent definitions use `agents/<name>/`, and permission vocabulary is published as `PERMITS.md`.
16
+
17
+ - 25a845a: Unify Skills on `skills/<name>/SKILL.md`, support Agent-private Skills and nested Skill-private Tools, publish and mount existing plugin Skills, add governed Skill metadata and Turn access filtering, unlock only already-admitted same-owner Tools, and make `on-risk` and `once` approval behavior precise.
18
+ - c6be75f: Require the canonical expanded Sandbox endpoint configuration. The protocol no longer reinterprets nested `endpoints` or reads endpoint identity and owner from `process.env`; the empty-endpoint local development path keeps explicit stable defaults.
19
+ - 698f16f: Replace ambiguous command approval modes with `ask`, `auto`, and `bypass`, add a fail-closed review Agent, and scope remembered human decisions to the current invocation, sender, or conversation.
20
+ - 5c3858e: Make `@zhin.js/im-contract` the explicit zero-dependency owner of transport identities, canonical segments, media guards, conversation facts, stores, and delivery contracts. Platform adapters now read media contracts from that foundation instead of reaching through Core.
21
+ - Updated dependencies [c861789]
22
+ - Updated dependencies [1414ccb]
23
+ - Updated dependencies [743d470]
24
+ - Updated dependencies [62dee52]
25
+ - Updated dependencies [cd54131]
26
+ - Updated dependencies [244e475]
27
+ - Updated dependencies [a9e4a40]
28
+ - Updated dependencies [5855db7]
29
+ - Updated dependencies [ec921d2]
30
+ - Updated dependencies [8740059]
31
+ - Updated dependencies [25a845a]
32
+ - Updated dependencies [103f2c3]
33
+ - Updated dependencies [9110ab8]
34
+ - Updated dependencies [cd2a888]
35
+ - Updated dependencies [eb3227a]
36
+ - Updated dependencies [b853dba]
37
+ - Updated dependencies [f474b1d]
38
+ - Updated dependencies [e561309]
39
+ - Updated dependencies [d4c6175]
40
+ - Updated dependencies [7a0e1ca]
41
+ - Updated dependencies [103b5d3]
42
+ - Updated dependencies [5a7a7f7]
43
+ - Updated dependencies [b076eae]
44
+ - Updated dependencies [73a24b7]
45
+ - Updated dependencies [2dbbc15]
46
+ - Updated dependencies [1cb1163]
47
+ - Updated dependencies [be3061e]
48
+ - Updated dependencies [75f8332]
49
+ - Updated dependencies [81935e2]
50
+ - Updated dependencies [f9ed01b]
51
+ - Updated dependencies [ac0ab50]
52
+ - Updated dependencies [5140ce1]
53
+ - Updated dependencies [522d75f]
54
+ - Updated dependencies [a7611b3]
55
+ - Updated dependencies [8823044]
56
+ - Updated dependencies [379439b]
57
+ - Updated dependencies [11c9352]
58
+ - Updated dependencies [6b70e46]
59
+ - Updated dependencies [135ac91]
60
+ - Updated dependencies [140cf0f]
61
+ - Updated dependencies [251e4d2]
62
+ - Updated dependencies [203ad34]
63
+ - Updated dependencies [e6c5113]
64
+ - Updated dependencies [e0f6478]
65
+ - Updated dependencies [baef56b]
66
+ - Updated dependencies [da0a8e3]
67
+ - Updated dependencies [2fd8017]
68
+ - Updated dependencies [5c3858e]
69
+ - Updated dependencies [507d602]
70
+ - Updated dependencies [df9f76b]
71
+ - @zhin.js/core@1.1.37
72
+ - @zhin.js/client@1.1.6
73
+ - @zhin.js/host-http@1.1.1
74
+ - @zhin.js/adapter@1.1.14
75
+ - @zhin.js/feature-kit@1.1.1
76
+ - @zhin.js/page@1.1.1
77
+ - @zhin.js/skill@1.1.1
78
+ - @zhin.js/logger@1.1.1
79
+ - @zhin.js/im-contract@1.1.1
80
+
81
+ ## 1.1.1
82
+
83
+ ### Patch Changes
84
+
85
+ - Updated dependencies [769e67f]
86
+ - @zhin.js/adapter@1.1.13
87
+ - @zhin.js/core@1.1.36
88
+
3
89
  ## 1.1.0
4
90
 
5
91
  ### Minor Changes
package/README.md CHANGED
@@ -24,10 +24,10 @@ Sandbox 不需要外部账号。只需由 `zhin runtime start` 装配 HTTP Host
24
24
 
25
25
  ### Plugin Runtime(新,`zhin runtime start`)
26
26
 
27
- - `@zhin.js/adapter` — 约定式 `adapters/sandbox.ts`
27
+ - `@zhin.js/adapter` — 约定式 `adapters/sandbox/index.ts`
28
28
  - `@zhin.js/host-http` — Root 提供的 `httpHostToken`(WebSocket `/sandbox` + Console HTTP)
29
29
  - `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
30
- - `@zhin.js/page` + `pages/index.tsx` — ADR 0046 约定页(`definePage`;路由 `/sandbox`)
30
+ - `@zhin.js/page` + `pages/index/index.tsx` — ADR 0046 约定页(`definePage`;路由 `/sandbox`)
31
31
 
32
32
  Root 在 `zhin runtime start` 时装载 `@zhin.js/host-http`、`ConsoleRuntime` 与
33
33
  `ClientBuildModuleRuntime`。打开 `http://<host>:<port>/console` 可浏览页面;Sandbox 页
@@ -46,7 +46,7 @@ Root 在 `zhin runtime start` 时装载 `@zhin.js/host-http`、`ConsoleRuntime`
46
46
 
47
47
  ## 配置
48
48
 
49
- **推荐(与 [minimal-bot](../../../examples/minimal-bot/) 一致)**:`plugins.sandbox.endpoints: []`,在 Remote Console 打开「沙盒」页时经 `/sandbox` WebSocket **自动创建** bot(如 `sandbox-xxxx`),无需在 yaml 里写 `context: sandbox`。
49
+ **推荐(与 [minimal-bot](../../../examples/minimal-bot/) 一致)**:`plugins.sandbox.endpoints: []`,运行时提供默认 `sandbox-bot` endpoint,无需额外账号配置。
50
50
 
51
51
  ```yaml
52
52
  # zhin.config.yml(Plugin Runtime)
@@ -61,8 +61,7 @@ plugins:
61
61
  plugins:
62
62
  sandbox:
63
63
  endpoints:
64
- - name: sandbox-bot
65
- context: sandbox
64
+ - id: sandbox-bot
66
65
  owner: sandbox-user
67
66
  ```
68
67
 
@@ -72,7 +71,7 @@ plugins:
72
71
  2. 打开 **[Remote Console](https://console.zhin.dev)**,API Base 与 Host 地址一致,Token 与 `http.token` / `HTTP_TOKEN` 一致
73
72
  3. 在 Console **沙盒** 页连接后发送消息进行测试
74
73
 
75
- 每个浏览器客户端连接后创建 Sandbox Bot(无 yaml 固定名时为 `sandbox-xxxx`)。
74
+ 浏览器客户端连接到默认 `sandbox-bot`,也可以通过多个显式 endpoint 使用不同 WebSocket 路径。
76
75
 
77
76
  通过 `Router.ws("/sandbox")`(插件 `useContext("router")` 自动挂载)建立连接。
78
77
 
@@ -97,7 +96,7 @@ Sandbox 使用 JSON 消息格式:
97
96
 
98
97
  ## AI 工具
99
98
 
100
- 技能说明见 `agent/skills/sandbox.md`(本地沙箱调试约束)。
99
+ 技能说明见 `skills/sandbox/SKILL.md`(本地沙箱调试约束)。
101
100
 
102
101
 
103
102
  ## 故障排查
@@ -1,12 +1,12 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  /**
3
- * Convention entry: discover `adapters/sandbox.ts` → defineAdapter.
3
+ * Convention entry: discover `adapters/sandbox/index.ts` → defineAdapter.
4
4
  */
5
5
  import { defineAdapter } from 'zhin.js/adapter';
6
6
  import { httpHostToken } from '@zhin.js/host-http';
7
- import { SandboxWsEndpoint } from "../lib/endpoint.js";
8
- import { resolveSandboxEndpoint, } from "../lib/protocol.js";
9
- export { SandboxWsEndpoint } from "../lib/endpoint.js";
7
+ import { SandboxWsEndpoint } from "../../lib/endpoint.js";
8
+ import { resolveSandboxEndpoint, } from "../../lib/protocol.js";
9
+ export { SandboxWsEndpoint } from "../../lib/endpoint.js";
10
10
  export default defineAdapter({
11
11
  capabilities: ['inbound', 'outbound'],
12
12
  // Console UI 直连 URL 媒体、直接渲染 base64 内联媒体;path 由端点读盘
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Convention entry: discover `adapters/sandbox.ts` → defineAdapter.
2
+ * Convention entry: discover `adapters/sandbox/index.ts` → defineAdapter.
3
3
  */
4
4
  import { defineAdapter } from 'zhin.js/adapter';
5
5
  import { httpHostToken } from '@zhin.js/host-http';
6
- import { SandboxWsEndpoint } from '../src/endpoint.js';
6
+ import { SandboxWsEndpoint } from '../../src/endpoint.js';
7
7
  import {
8
8
  resolveSandboxEndpoint,
9
- type SandboxAdapterConfig,
10
- } from '../src/protocol.js';
9
+ type SandboxEndpointConfig,
10
+ } from '../../src/protocol.js';
11
11
 
12
- export { SandboxWsEndpoint } from '../src/endpoint.js';
13
- export type { SandboxEndpointOptions } from '../src/endpoint.js';
12
+ export { SandboxWsEndpoint } from '../../src/endpoint.js';
13
+ export type { SandboxEndpointOptions } from '../../src/endpoint.js';
14
14
 
15
- export default defineAdapter<SandboxAdapterConfig>({
15
+ export default defineAdapter<SandboxEndpointConfig>({
16
16
  capabilities: ['inbound', 'outbound'],
17
17
  // Console UI 直连 URL 媒体、直接渲染 base64 内联媒体;path 由端点读盘
18
18
  // 物化为 base64 内联(sandbox 无平台上传通道)。交互段由 Console 原生承载。
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { bindSandboxWsSocket, formatSandboxOutbound, normalizeSandboxOutboundSegments, parseSandboxWsPayload, resolveSandboxEndpoint, sandboxInboundConversation, whenWsOpen, type MessageElement, type MessageType, type ResolvedSandboxBot, type SandboxAdapterConfig, type SandboxWsSocket, } from './protocol.js';
1
+ export { bindSandboxWsSocket, formatSandboxOutbound, normalizeSandboxOutboundSegments, parseSandboxWsPayload, resolveSandboxEndpoint, sandboxInboundConversation, whenWsOpen, type MessageElement, type MessageType, type ResolvedSandboxBot, type SandboxEndpointConfig, type SandboxWsSocket, } from './protocol.js';
2
2
  export { SandboxClient, sandboxClient, type SandboxClientEventMap, type SandboxClientConnection, } from './client.js';
3
3
  export { SandboxWsEndpoint, type SandboxEndpointOptions, } from './endpoint.js';
package/lib/protocol.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Sandbox WebSocket wire protocol helpers (no legacy Adapter/Endpoint). */
2
- import type { ConversationRef } from '@zhin.js/im-contract';
2
+ import { type ConversationRef } from '@zhin.js/im-contract';
3
3
  import { type SandboxAgentRunConfig } from './run-config.js';
4
4
  export type MessageType = 'private' | 'group' | 'guild' | 'direct' | 'channel';
5
5
  export interface MessageElement {
@@ -20,19 +20,12 @@ export type ResolvedSandboxBot = {
20
20
  readonly owner: string;
21
21
  readonly randomNamePerConnection: boolean;
22
22
  };
23
- export interface SandboxAdapterConfig {
24
- /** Runtime expands `endpoints[i]` onto the top level — prefer these. */
25
- readonly context?: string;
23
+ /** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
24
+ export interface SandboxEndpointConfig {
26
25
  readonly id?: string;
27
26
  readonly owner?: string;
28
- /** Legacy shape: endpoint entries nested under `endpoints[]`. */
29
- readonly endpoints?: ReadonlyArray<{
30
- readonly context?: string;
31
- readonly id?: string;
32
- readonly owner?: string;
33
- }>;
34
27
  }
35
- export declare function resolveSandboxEndpoint(appConfig: SandboxAdapterConfig): ResolvedSandboxBot;
28
+ export declare function resolveSandboxEndpoint(config: SandboxEndpointConfig): ResolvedSandboxBot;
36
29
  export declare function bindSandboxWsSocket(ws: SandboxWsSocket, handlers: {
37
30
  onMessage: (raw: string) => void;
38
31
  onClose: () => void;
package/lib/protocol.js CHANGED
@@ -1,21 +1,12 @@
1
1
  /** Sandbox WebSocket wire protocol helpers (no legacy Adapter/Endpoint). */
2
2
  import { readFileSync } from 'node:fs';
3
- import { isMediaRef } from '@zhin.js/core';
3
+ import { isMediaRef } from '@zhin.js/im-contract';
4
4
  import { formatCompact, getLogger } from '@zhin.js/logger';
5
5
  import { normalizeSandboxAgentRunConfig, } from './run-config.js';
6
6
  const logger = getLogger('sandbox');
7
- export function resolveSandboxEndpoint(appConfig) {
8
- const entry = appConfig.endpoints?.find((item) => item.context === 'sandbox');
9
- const fixedName = typeof appConfig.id === 'string' && appConfig.id
10
- ? appConfig.id
11
- : typeof entry?.id === 'string' && entry.id
12
- ? entry.id
13
- : undefined;
14
- const id = fixedName || process.env.SANDBOX_BOT_NAME || 'sandbox-bot';
15
- const owner = (typeof appConfig.owner === 'string' && appConfig.owner)
16
- || (typeof entry?.owner === 'string' && entry.owner)
17
- || process.env.SANDBOX_BOT_OWNER
18
- || 'sandbox-user';
7
+ export function resolveSandboxEndpoint(config) {
8
+ const id = optionalEndpointField(config.id) ?? 'sandbox-bot';
9
+ const owner = optionalEndpointField(config.owner) ?? 'sandbox-user';
19
10
  return {
20
11
  context: 'sandbox',
21
12
  id,
@@ -26,6 +17,9 @@ export function resolveSandboxEndpoint(appConfig) {
26
17
  randomNamePerConnection: false,
27
18
  };
28
19
  }
20
+ function optionalEndpointField(value) {
21
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
22
+ }
29
23
  export function bindSandboxWsSocket(ws, handlers) {
30
24
  if (typeof ws.on === 'function') {
31
25
  const onMessage = (...args) => {
@@ -1,5 +1,5 @@
1
1
  export type SandboxSafetyMode = 'read-only' | 'workspace-write' | 'danger-full-access';
2
- export type SandboxApprovalMode = 'ask' | 'deny' | 'allow';
2
+ export type SandboxApprovalMode = 'ask' | 'auto' | 'bypass';
3
3
  export interface SandboxAgentRunConfig {
4
4
  readonly workingDirectory: string;
5
5
  readonly safetyMode: SandboxSafetyMode;
package/lib/run-config.js CHANGED
@@ -5,7 +5,7 @@ export const DEFAULT_SANDBOX_AGENT_RUN_CONFIG = Object.freeze({
5
5
  networkAccess: false,
6
6
  });
7
7
  const SAFETY_MODES = new Set(['read-only', 'workspace-write', 'danger-full-access']);
8
- const APPROVAL_MODES = new Set(['ask', 'deny', 'allow']);
8
+ const APPROVAL_MODES = new Set(['ask', 'auto', 'bypass']);
9
9
  export function normalizeSandboxAgentRunConfig(value) {
10
10
  if (!value || typeof value !== 'object' || Array.isArray(value))
11
11
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-sandbox",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Zhin.js Sandbox adapter for Plugin Runtime (WebSocket /sandbox)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -20,7 +20,7 @@
20
20
  "src",
21
21
  "lib",
22
22
  "dist",
23
- "agent",
23
+ "skills",
24
24
  "README.md",
25
25
  "CHANGELOG.md"
26
26
  ],
@@ -48,15 +48,16 @@
48
48
  "dependencies": {
49
49
  "lucide-react": "^0.525.0",
50
50
  "react": "^19.2.8",
51
- "@zhin.js/adapter": "1.1.12",
52
- "@zhin.js/client": "1.1.5",
51
+ "@zhin.js/adapter": "1.1.14",
52
+ "@zhin.js/client": "1.1.6",
53
53
  "@zhin.js/console-contract": "1.1.0",
54
- "@zhin.js/core": "1.1.35",
55
- "@zhin.js/feature-kit": "1.1.0",
56
- "@zhin.js/host-http": "1.1.0",
57
- "@zhin.js/im-contract": "1.1.0",
58
- "@zhin.js/logger": "1.1.0",
59
- "@zhin.js/page": "1.1.0"
54
+ "@zhin.js/core": "1.1.37",
55
+ "@zhin.js/feature-kit": "1.1.1",
56
+ "@zhin.js/host-http": "1.1.1",
57
+ "@zhin.js/im-contract": "1.1.1",
58
+ "@zhin.js/logger": "1.1.1",
59
+ "@zhin.js/page": "1.1.1",
60
+ "@zhin.js/skill": "1.1.1"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@types/react": "^19.2.18",
@@ -64,18 +65,18 @@
64
65
  "typescript": "^6.0.3",
65
66
  "vitest": "^4.1.10",
66
67
  "ws": "^8.21.1",
67
- "@zhin.js/pagemanager": "1.1.0",
68
- "@zhin.js/runtime": "1.1.0",
69
- "zhin.js": "1.1.0"
68
+ "@zhin.js/pagemanager": "1.1.1",
69
+ "@zhin.js/runtime": "1.1.1",
70
+ "zhin.js": "1.1.2"
70
71
  },
71
72
  "peerDependencies": {
72
73
  "react": "^19.0.0",
73
- "@zhin.js/adapter": "^1.1.12",
74
+ "@zhin.js/adapter": "^1.1.14",
74
75
  "@zhin.js/console-contract": "^1.1.0",
75
- "@zhin.js/core": "^1.1.35",
76
- "@zhin.js/host-http": "^1.1.0",
77
- "@zhin.js/page": "^1.1.0",
78
- "zhin.js": "^1.1.0"
76
+ "@zhin.js/core": "^1.1.37",
77
+ "@zhin.js/host-http": "^1.1.1",
78
+ "@zhin.js/page": "^1.1.1",
79
+ "zhin.js": "^1.1.2"
79
80
  },
80
81
  "peerDependenciesMeta": {
81
82
  "react": {
@@ -103,6 +104,10 @@
103
104
  {
104
105
  "package": "@zhin.js/page",
105
106
  "api": "^1.0.0"
107
+ },
108
+ {
109
+ "package": "@zhin.js/skill",
110
+ "api": "^1.0.0"
106
111
  }
107
112
  ],
108
113
  "plugins": []
@@ -554,7 +554,7 @@ export default function Sandbox() {
554
554
  addSession(); }, placeholder: "\u4F1A\u8BDD\u540D\u79F0", autoFocus: true }), _jsx("div", { className: "agent-playground-scope-picker", role: "group", "aria-label": "\u6D88\u606F\u4F5C\u7528\u57DF", children: ['private', 'group', 'channel'].map((scope) => (_jsx("button", { type: "button", "aria-pressed": newSessionScope === scope, className: cn(newSessionScope === scope && 'active'), onClick: () => setNewSessionScope(scope), children: scope === 'private' ? '单用户' : scope === 'group' ? '群组' : '频道' }, scope))) }), _jsxs("div", { className: "agent-playground-new-actions", children: [_jsx("button", { type: "button", onClick: () => setShowNewSession(false), children: "\u53D6\u6D88" }), _jsx("button", { type: "button", className: "primary", onClick: addSession, children: "\u521B\u5EFA" })] })] })) : (_jsxs("button", { type: "button", className: "agent-playground-add", onClick: () => setShowNewSession(true), children: [_jsx(Plus, { size: 15 }), "\u65B0\u5EFA\u6D4B\u8BD5\u4F1A\u8BDD"] })) }), _jsxs("div", { className: "agent-playground-endpoint", children: [_jsx("span", { children: persistenceStatus === 'saved' ? '会话已持久化' : '会话保存失败' }), _jsx("strong", { children: endpointId })] })] }), showChannelList && _jsx("div", { className: "channel-overlay", onClick: () => setShowChannelList(false) }), _jsxs("main", { className: "chat-area agent-playground-main", children: [_jsxs("header", { className: "agent-playground-runbar", children: [_jsxs("div", { className: "agent-playground-run-identity", children: [_jsx("span", { className: "agent-playground-run-icon", children: getChannelIcon(activeChannel.type) }), _jsxs("div", { children: [_jsxs("div", { className: "agent-playground-run-title", children: [_jsx("h1", { children: activeChannel.name }), _jsx("span", { children: scopeLabel })] }), _jsx("code", { children: sessionKey })] })] }), _jsxs("div", { className: "agent-playground-run-actions", children: [_jsxs("span", { className: cn("agent-playground-run-state", currentTask?.status === 'running' && "is-running", currentTask?.status === 'failed' && 'has-problem'), children: [currentTask?.status === 'running' ? _jsx(Activity, { size: 14 }) : _jsx(Gauge, { size: 14 }), currentTask ? taskStatusLabel(currentTask.status) : `${channelMessages.length} 条消息`] }), currentTask?.status === 'running' ? (_jsxs("button", { type: "button", className: "agent-playground-stop", onClick: () => void stopActiveTask(), disabled: stoppingTask, "aria-label": "\u505C\u6B62\u5F53\u524D\u4EFB\u52A1", children: [_jsx(Square, { size: 13 }), stoppingTask ? '停止中' : '停止'] })) : lastUserMessage ? (_jsxs("button", { type: "button", onClick: retryLastTask, "aria-label": "\u91CD\u65B0\u8FD0\u884C\u4E0A\u4E00\u4E2A\u4EFB\u52A1", children: [_jsx(RotateCcw, { size: 14 }), "\u91CD\u8BD5"] })) : null, _jsxs("a", { href: agentStudioPath(sessionKey), children: [_jsx(ExternalLink, { size: 14 }), "Agent Studio"] }), _jsx("button", { type: "button", className: cn(showRunSettings && 'active'), "aria-expanded": showRunSettings, onClick: () => setShowRunSettings((visible) => !visible), "aria-label": "\u914D\u7F6E\u8FD0\u884C\u73AF\u5883", children: _jsx(SlidersHorizontal, { size: 16 }) }), _jsx("button", { type: "button", "aria-expanded": showInspector, onClick: () => setShowInspector(!showInspector), "aria-label": "\u5207\u6362\u8FD0\u884C\u68C0\u67E5\u5668", children: _jsx(PanelRight, { size: 16 }) }), _jsx("button", { type: "button", onClick: () => setConfirmClear(true), "aria-label": "\u6E05\u7A7A\u5F53\u524D\u4F1A\u8BDD", children: _jsx(Trash2, { size: 15 }) })] })] }), confirmClear && (_jsxs("div", { className: "agent-playground-confirm", role: "alert", children: [_jsxs("div", { children: [_jsx("strong", { children: "\u6E05\u7A7A\u5F53\u524D\u6D4B\u8BD5\u8BB0\u5F55\uFF1F" }), _jsxs("span", { children: ["\u53EA\u79FB\u9664\u201C", activeChannel.name, "\u201D\u5728\u6D4F\u89C8\u5668\u4E2D\u7684\u6D88\u606F\uFF0C\u4E0D\u5F71\u54CD Agent \u4F1A\u8BDD\u5B58\u50A8\u3002"] })] }), _jsx("button", { type: "button", onClick: () => setConfirmClear(false), children: "\u53D6\u6D88" }), _jsx("button", { type: "button", className: "danger", onClick: clearMessages, children: "\u786E\u8BA4\u6E05\u7A7A" })] })), transportNotice && (_jsxs("div", { className: "agent-playground-confirm", role: "status", children: [_jsxs("div", { children: [_jsx("strong", { children: "\u8FD0\u884C\u6682\u4E0D\u53EF\u7528" }), _jsx("span", { children: transportNotice })] }), _jsx("button", { type: "button", onClick: () => setTransportNotice(null), children: "\u77E5\u9053\u4E86" })] })), showRunSettings && (_jsxs("section", { className: "agent-playground-run-settings", "aria-label": "\u8FD0\u884C\u914D\u7F6E", children: [_jsxs("label", { className: "agent-playground-directory-field", children: [_jsxs("span", { children: [_jsx(FolderOpen, { size: 14 }), "\u5DE5\u4F5C\u76EE\u5F55"] }), _jsx("input", { value: activeChannel.runConfig.workingDirectory, onChange: (event) => updateRunConfig({ workingDirectory: event.target.value }), placeholder: "\u4F7F\u7528 Host \u9879\u76EE\u76EE\u5F55", spellCheck: false })] }), _jsxs("label", { children: [_jsxs("span", { children: [_jsx(ShieldCheck, { size: 14 }), "\u5B89\u5168\u7B56\u7565"] }), _jsxs("select", { value: activeChannel.runConfig.safetyMode, onChange: (event) => {
555
555
  const safetyMode = event.target.value;
556
556
  updateRunConfig({ safetyMode, ...(safetyMode === 'danger-full-access' ? { networkAccess: true } : {}) });
557
- }, children: [_jsx("option", { value: "read-only", children: "\u53EA\u8BFB" }), _jsx("option", { value: "workspace-write", children: "\u5DE5\u4F5C\u533A\u5199\u5165" }), _jsx("option", { value: "danger-full-access", children: "\u5B8C\u5168\u8BBF\u95EE" })] })] }), _jsxs("label", { children: [_jsx("span", { children: "\u5BA1\u6279\u7B56\u7565" }), _jsxs("select", { value: activeChannel.runConfig.safetyMode === 'read-only' ? 'deny' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'allow' : activeChannel.runConfig.approvalMode, disabled: activeChannel.runConfig.safetyMode !== 'workspace-write', onChange: (event) => updateRunConfig({ approvalMode: event.target.value }), children: [_jsx("option", { value: "ask", children: "\u6309\u9700\u786E\u8BA4" }), _jsx("option", { value: "deny", children: "\u81EA\u52A8\u62D2\u7EDD" }), _jsx("option", { value: "allow", children: "\u81EA\u52A8\u5141\u8BB8" })] })] }), _jsxs("label", { className: "agent-playground-network-toggle", children: [_jsxs("span", { children: [_jsx(Network, { size: 14 }), "\u7F51\u7EDC\u8BBF\u95EE"] }), _jsx("input", { type: "checkbox", checked: activeChannel.runConfig.safetyMode === 'danger-full-access' || activeChannel.runConfig.networkAccess, disabled: activeChannel.runConfig.safetyMode === 'danger-full-access', onChange: (event) => updateRunConfig({ networkAccess: event.target.checked }) }), _jsx("i", { "aria-hidden": "true" })] }), activeChannel.runConfig.safetyMode === 'danger-full-access' && (_jsxs("p", { children: [_jsx(CircleAlert, { size: 14 }), "\u5B8C\u5168\u8BBF\u95EE\u5141\u8BB8 Agent \u64CD\u4F5C\u5DE5\u4F5C\u76EE\u5F55\u4E4B\u5916\u7684\u6587\u4EF6\u5E76\u8BBF\u95EE\u7F51\u7EDC\uFF0C\u8BF7\u4EC5\u7528\u4E8E\u53EF\u4FE1\u4EFB\u52A1\u3002"] })), activeChannel.runConfig.safetyMode !== 'danger-full-access' && shellIsolation?.available === false && (_jsxs("p", { children: [_jsx(CircleAlert, { size: 14 }), "\u5B89\u5168 Shell \u9700\u8981\u53EF\u7528\u7684 Docker daemon\uFF1B\u5F53\u524D\u4EC5\u6587\u4EF6\u5DE5\u5177\u53EF\u8FD0\u884C\u3002", shellIsolation.message] }))] })), _jsx("section", { className: "agent-playground-conversation", "aria-label": "Agent \u5BF9\u8BDD", children: _jsx("div", { className: "agent-playground-message-scroll", children: channelMessages.length === 0 ? (_jsxs("div", { className: "agent-playground-empty", children: [_jsx("span", { className: "agent-playground-empty-mark", children: _jsx(Sparkles, { size: 24 }) }), _jsxs("div", { children: [_jsx("h2", { children: "\u5F00\u59CB\u4E00\u6B21\u53EF\u89C2\u5BDF\u7684 Agent \u8FD0\u884C" }), _jsx("p", { children: "\u53D1\u9001\u4EFB\u52A1\u540E\uFF0C\u56DE\u590D\u3001\u5DE5\u5177\u8C03\u7528\u3001Token \u4E0E\u5F02\u5E38\u4F1A\u5728\u540C\u4E00\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u4E2D\u5173\u8054\u3002" })] }), _jsxs("div", { className: "agent-playground-prompts", children: [_jsx("button", { type: "button", onClick: () => runPrompt('介绍当前 Agent 可以使用的能力,并给出三个具体示例。'), children: "\u63A2\u7D22\u53EF\u7528\u80FD\u529B" }), _jsx("button", { type: "button", onClick: () => runPrompt('用 Markdown 表格总结当前运行环境,并附上一段 TypeScript 示例代码。'), children: "\u6D4B\u8BD5\u5BCC\u6587\u672C\u8F93\u51FA" }), _jsx("button", { type: "button", onClick: () => runPrompt('分析一个任务从推理、工具调用到最终回复的完整执行路径。'), children: "\u89C2\u5BDF\u6267\u884C\u8DEF\u5F84" })] })] })) : (_jsxs("div", { className: "agent-playground-message-list", children: [channelMessages.map((msg) => {
557
+ }, children: [_jsx("option", { value: "read-only", children: "\u53EA\u8BFB" }), _jsx("option", { value: "workspace-write", children: "\u5DE5\u4F5C\u533A\u5199\u5165" }), _jsx("option", { value: "danger-full-access", children: "\u5B8C\u5168\u8BBF\u95EE" })] })] }), _jsxs("label", { children: [_jsx("span", { children: "\u5BA1\u6279\u7B56\u7565" }), _jsxs("select", { value: activeChannel.runConfig.safetyMode === 'read-only' ? 'auto' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'bypass' : activeChannel.runConfig.approvalMode, disabled: activeChannel.runConfig.safetyMode !== 'workspace-write', onChange: (event) => updateRunConfig({ approvalMode: event.target.value }), children: [_jsx("option", { value: "ask", children: "\u6309\u9700\u786E\u8BA4" }), _jsx("option", { value: "auto", children: "\u5BA1\u6838 Agent \u81EA\u52A8\u5224\u65AD" }), _jsx("option", { value: "bypass", children: "\u7ED5\u8FC7\u5BA1\u6279" })] })] }), _jsxs("label", { className: "agent-playground-network-toggle", children: [_jsxs("span", { children: [_jsx(Network, { size: 14 }), "\u7F51\u7EDC\u8BBF\u95EE"] }), _jsx("input", { type: "checkbox", checked: activeChannel.runConfig.safetyMode === 'danger-full-access' || activeChannel.runConfig.networkAccess, disabled: activeChannel.runConfig.safetyMode === 'danger-full-access', onChange: (event) => updateRunConfig({ networkAccess: event.target.checked }) }), _jsx("i", { "aria-hidden": "true" })] }), activeChannel.runConfig.safetyMode === 'danger-full-access' && (_jsxs("p", { children: [_jsx(CircleAlert, { size: 14 }), "\u5B8C\u5168\u8BBF\u95EE\u5141\u8BB8 Agent \u64CD\u4F5C\u5DE5\u4F5C\u76EE\u5F55\u4E4B\u5916\u7684\u6587\u4EF6\u5E76\u8BBF\u95EE\u7F51\u7EDC\uFF0C\u8BF7\u4EC5\u7528\u4E8E\u53EF\u4FE1\u4EFB\u52A1\u3002"] })), activeChannel.runConfig.safetyMode !== 'danger-full-access' && shellIsolation?.available === false && (_jsxs("p", { children: [_jsx(CircleAlert, { size: 14 }), "\u5B89\u5168 Shell \u9700\u8981\u53EF\u7528\u7684 Docker daemon\uFF1B\u5F53\u524D\u4EC5\u6587\u4EF6\u5DE5\u5177\u53EF\u8FD0\u884C\u3002", shellIsolation.message] }))] })), _jsx("section", { className: "agent-playground-conversation", "aria-label": "Agent \u5BF9\u8BDD", children: _jsx("div", { className: "agent-playground-message-scroll", children: channelMessages.length === 0 ? (_jsxs("div", { className: "agent-playground-empty", children: [_jsx("span", { className: "agent-playground-empty-mark", children: _jsx(Sparkles, { size: 24 }) }), _jsxs("div", { children: [_jsx("h2", { children: "\u5F00\u59CB\u4E00\u6B21\u53EF\u89C2\u5BDF\u7684 Agent \u8FD0\u884C" }), _jsx("p", { children: "\u53D1\u9001\u4EFB\u52A1\u540E\uFF0C\u56DE\u590D\u3001\u5DE5\u5177\u8C03\u7528\u3001Token \u4E0E\u5F02\u5E38\u4F1A\u5728\u540C\u4E00\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u4E2D\u5173\u8054\u3002" })] }), _jsxs("div", { className: "agent-playground-prompts", children: [_jsx("button", { type: "button", onClick: () => runPrompt('介绍当前 Agent 可以使用的能力,并给出三个具体示例。'), children: "\u63A2\u7D22\u53EF\u7528\u80FD\u529B" }), _jsx("button", { type: "button", onClick: () => runPrompt('用 Markdown 表格总结当前运行环境,并附上一段 TypeScript 示例代码。'), children: "\u6D4B\u8BD5\u5BCC\u6587\u672C\u8F93\u51FA" }), _jsx("button", { type: "button", onClick: () => runPrompt('分析一个任务从推理、工具调用到最终回复的完整执行路径。'), children: "\u89C2\u5BDF\u6267\u884C\u8DEF\u5F84" })] })] })) : (_jsxs("div", { className: "agent-playground-message-list", children: [channelMessages.map((msg) => {
558
558
  const isApproval = msg.type === 'received' && msg.content.some((segment) => segment.type === 'keyboard');
559
559
  return (_jsxs(React.Fragment, { children: [_jsxs("article", { className: cn("agent-playground-message", msg.type === 'sent' ? "is-user" : "is-agent", isApproval && "is-approval"), children: [_jsx("span", { className: "agent-playground-message-avatar", children: msg.type === 'received' ? _jsx(Bot, { size: 16 }) : _jsx(User, { size: 16 }) }), _jsxs("div", { className: "agent-playground-message-main", children: [_jsxs("div", { className: "agent-playground-message-meta", children: [_jsx("strong", { children: isApproval ? '需要你的确认' : msg.type === 'received' ? endpointId : '你' }), isApproval && _jsx("span", { children: "approval required" }), _jsx("time", { children: new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) })] }), _jsx("div", { className: "agent-playground-message-content", children: renderMessageSegments(msg.content, msg.type === 'sent', msg.id, msg.interactionResolved === true) })] })] }), currentTaskMessage?.id === msg.id && inlineRunCard] }, msg.id));
560
560
  }), !currentTaskMessage && inlineRunCard, _jsx("div", { ref: messagesEndRef })] })) }) }), _jsxs("section", { className: "agent-playground-composer", "aria-label": "\u4EFB\u52A1\u8F93\u5165", children: [_jsxs("div", { className: "flex gap-2 items-center flex-wrap", children: [_jsx("button", { type: "button", className: cn("h-8 w-8 rounded-md flex items-center justify-center border transition-colors", showFacePicker ? "bg-primary text-primary-foreground" : "hover:bg-accent"), onClick: () => { setShowFacePicker(!showFacePicker); setMediaPanel(null); }, title: "\u63D2\u5165\u8868\u60C5", children: _jsx(Smile, { size: 16 }) }), _jsx("button", { type: "button", className: cn("h-8 w-8 rounded-md flex items-center justify-center border transition-colors", mediaPanel === 'image' ? "bg-primary text-primary-foreground" : "hover:bg-accent"), onClick: () => { setMediaPanel((p) => (p === 'image' ? null : 'image')); setShowFacePicker(false); }, title: "\u63D2\u5165\u56FE\u7247 URL", children: _jsx(Image, { size: 16 }) }), _jsx("button", { type: "button", className: cn("h-8 w-8 rounded-md flex items-center justify-center border transition-colors", mediaPanel === 'video' ? "bg-primary text-primary-foreground" : "hover:bg-accent"), onClick: () => { setMediaPanel((p) => (p === 'video' ? null : 'video')); setShowFacePicker(false); }, title: "\u63D2\u5165\u89C6\u9891 URL", children: _jsx(Video, { size: 16 }) }), _jsx("button", { type: "button", className: cn("h-8 w-8 rounded-md flex items-center justify-center border transition-colors", mediaPanel === 'audio' ? "bg-primary text-primary-foreground" : "hover:bg-accent"), onClick: () => { setMediaPanel((p) => (p === 'audio' ? null : 'audio')); setShowFacePicker(false); }, title: "\u63D2\u5165\u97F3\u9891 URL", children: _jsx(Music, { size: 16 }) }), _jsx("div", { className: "flex-1 min-w-[1rem]" }), _jsxs("div", { className: "sandbox-composer-tabs", role: "tablist", "aria-label": "\u6D88\u606F\u7F16\u8F91\u6A21\u5F0F", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": composerMode === 'write', className: cn(composerMode === 'write' && 'active'), onClick: () => setComposerMode('write'), children: "\u7F16\u5199" }), _jsx("button", { type: "button", role: "tab", "aria-selected": composerMode === 'preview', className: cn(composerMode === 'preview' && 'active'), onClick: () => setComposerMode('preview'), children: "\u9884\u89C8" })] }), inputText && (_jsx("button", { className: "h-8 w-8 rounded-md flex items-center justify-center hover:bg-accent transition-colors", onClick: () => { editorRef.current?.clear(); setInputText(''); setPreviewSegments([]); setComposerMode('write'); }, "aria-label": "\u6E05\u7A7A\u8F93\u5165", children: _jsx(X, { size: 16 }) }))] }), showFacePicker && (_jsxs("div", { className: "p-3 rounded-md border bg-muted/30 max-h-64 overflow-y-auto space-y-2", children: [_jsx("input", { value: faceSearchQuery, onChange: (e) => setFaceSearchQuery(e.target.value), placeholder: "\u641C\u7D22\u8868\u60C5...", className: "w-full h-8 rounded-md border bg-transparent px-2 text-sm" }), _jsx("div", { className: "grid grid-cols-8 gap-1", children: filteredFaces.slice(0, 80).map((face) => (_jsx("button", { onClick: () => insertFace(face.id), title: face.name, className: "w-10 h-10 rounded-md border flex items-center justify-center hover:bg-accent transition-colors", children: _jsx("img", { src: `https://face.viki.moe/apng/${face.id}.png`, alt: face.name, className: "w-8 h-8" }) }, face.id))) }), filteredFaces.length === 0 && (_jsxs("div", { className: "flex flex-col items-center gap-2 py-4", children: [_jsx(Search, { size: 32, className: "text-muted-foreground/30" }), _jsx("span", { className: "text-sm text-muted-foreground", children: "\u672A\u627E\u5230\u5339\u914D\u7684\u8868\u60C5" })] }))] })), mediaPanel && (_jsxs("div", { className: "p-3 rounded-md border bg-muted/30 space-y-2", children: [_jsxs("p", { className: "text-xs text-muted-foreground", children: [mediaPanel === 'image' && '支持 http(s) 图片链接或 data URL', mediaPanel === 'video' && '支持浏览器可解码的视频直链(如 .mp4、.webm)', mediaPanel === 'audio' && '支持 .mp3、.ogg、.wav 等音频直链'] }), _jsx("input", { value: mediaUrl, onChange: (e) => setMediaUrl(e.target.value), placeholder: mediaPanel === 'image' ? '图片 URL…' : mediaPanel === 'video' ? '视频 URL…' : '音频 URL…', className: "w-full h-8 rounded-md border border-input bg-background px-2 text-sm", onKeyDown: (e) => { if (e.key === 'Enter') {
@@ -563,7 +563,7 @@ export default function Sandbox() {
563
563
  } } }), _jsxs("button", { type: "button", className: "inline-flex items-center gap-1 h-8 px-3 rounded-md bg-primary text-primary-foreground text-sm disabled:opacity-50", onClick: commitMediaUrl, disabled: !mediaUrl.trim(), children: [_jsx(Check, { size: 14 }), " \u63D2\u5165\u5230\u8F93\u5165\u6846"] })] })), _jsxs("div", { className: "flex gap-2 items-start", children: [_jsxs("div", { className: "flex-1 relative", children: [_jsx("div", { className: composerMode === 'write' ? 'block' : 'hidden', children: _jsx(RichTextEditor, { ref: editorRef, placeholder: `向 ${activeChannel.name} 发送消息,支持 Markdown...`, onSend: handleSendMessage, onChange: handleEditorChange, onAtTrigger: handleAtTrigger, minHeight: "44px", maxHeight: "200px" }) }), composerMode === 'preview' && (_jsx("div", { className: "sandbox-markdown-preview", role: "tabpanel", children: inputText.trim()
564
564
  ? _jsx(MarkdownContent, { text: inputText })
565
565
  : _jsx("span", { className: "sandbox-markdown-preview-empty", children: "\u8F93\u5165 Markdown \u540E\u53EF\u5728\u8FD9\u91CC\u68C0\u67E5\u6700\u7EC8\u6548\u679C" }) })), atPopoverPosition && (_jsx("div", { className: "absolute z-50 rounded-lg border bg-popover shadow-md min-w-60 max-h-72 overflow-y-auto p-1", style: { top: `${atPopoverPosition.top}px`, left: `${atPopoverPosition.left}px` }, children: filteredAtSuggestions.length > 0 ? filteredAtSuggestions.map((user) => (_jsxs("div", { className: "flex items-center gap-2 p-2 rounded-md cursor-pointer hover:bg-accent transition-colors", onClick: () => selectAtUser(user), children: [_jsx(User, { size: 16, className: "text-muted-foreground" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium", children: user.name }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["ID: ", user.id] })] })] }, user.id))) : (_jsxs("div", { className: "flex flex-col items-center gap-2 p-4", children: [_jsx(Search, { size: 20, className: "text-muted-foreground/50" }), _jsx("span", { className: "text-xs text-muted-foreground", children: "\u672A\u627E\u5230\u5339\u914D\u7684\u7528\u6237" })] })) }))] }), _jsxs("button", { className: "inline-flex items-center gap-1.5 h-10 px-4 rounded-md bg-primary text-primary-foreground text-sm font-medium disabled:opacity-50 transition-colors hover:bg-primary/90", onClick: () => { const c = editorRef.current?.getContent(); if (c)
566
- handleSendMessage(c.text, c.segments); }, disabled: !canExecute || !hasRenderableSegments(previewSegments), children: [_jsx(Send, { size: 16 }), " \u53D1\u9001"] })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap text-xs text-muted-foreground", children: [_jsx(Info, { size: 12 }), " \u5FEB\u6377\u64CD\u4F5C:", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "Enter" }), " \u53D1\u9001", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "Shift+Enter" }), " \u6362\u884C", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "```ts" }), " \u4EE3\u7801\u5757", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "**\u6587\u672C**" }), " \u52A0\u7C97", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "[@\u540D\u79F0]" }), " @\u67D0\u4EBA", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "[video:URL]" }), _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "[audio:URL]" })] })] })] }), _jsxs("aside", { className: cn("agent-playground-inspector", showInspector && "show"), "aria-label": "\u8FD0\u884C\u68C0\u67E5\u5668", children: [_jsxs("div", { className: "agent-playground-inspector-head", children: [_jsxs("div", { children: [_jsx("span", { children: "Run inspector" }), _jsx("h2", { children: "\u8FD0\u884C\u68C0\u67E5\u5668" })] }), _jsxs("div", { children: [_jsx("button", { type: "button", onClick: () => void loadTrace(), "aria-label": "\u5237\u65B0 Agent Trace", disabled: traceLoading, children: _jsx(RefreshCw, { size: 15, className: cn(traceLoading && 'animate-spin') }) }), _jsx("button", { type: "button", className: "agent-playground-inspector-close", onClick: () => setShowInspector(false), "aria-label": "\u5173\u95ED\u8FD0\u884C\u68C0\u67E5\u5668", children: _jsx(X, { size: 15 }) })] })] }), _jsxs("div", { className: "agent-playground-inspector-tabs", role: "tablist", "aria-label": "\u68C0\u67E5\u5668\u89C6\u56FE", children: [_jsxs("button", { type: "button", role: "tab", "aria-selected": inspectorView === 'runs', className: cn(inspectorView === 'runs' && 'active'), onClick: () => setInspectorView('runs'), children: [_jsx(Activity, { size: 13 }), "\u4EFB\u52A1"] }), _jsxs("button", { type: "button", role: "tab", "aria-selected": inspectorView === 'artifacts', className: cn(inspectorView === 'artifacts' && 'active'), onClick: () => setInspectorView('artifacts'), children: [_jsx(FileDiff, { size: 13 }), "\u53D8\u66F4\u4E0E\u4EA7\u7269 ", _jsx("span", { children: workbenchArtifacts.length })] })] }), _jsxs("div", { className: cn("agent-playground-inspector-view", inspectorView !== 'runs' && 'hidden'), children: [_jsxs("section", { className: "agent-playground-inspector-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u8FD0\u884C\u6982\u89C8" }), _jsx("small", { children: traceSummary.activeTurns > 0 ? 'live' : 'idle' })] }), _jsxs("div", { className: "agent-playground-metrics", children: [_jsxs("div", { children: [_jsx(Activity, {}), _jsx("strong", { children: traceSummary.eventCount.toLocaleString() }), _jsx("span", { children: "\u4E8B\u4EF6" })] }), _jsxs("div", { children: [_jsx(Wrench, {}), _jsx("strong", { children: traceSummary.toolCount.toLocaleString() }), _jsx("span", { children: "\u5DE5\u5177" })] }), _jsxs("div", { children: [_jsx(Coins, {}), _jsx("strong", { children: traceSummary.tokenCount.toLocaleString() }), _jsx("span", { children: "Token" })] }), _jsxs("div", { className: cn(traceSummary.problemCount > 0 && 'has-problem'), children: [_jsx(CircleAlert, {}), _jsx("strong", { children: traceSummary.problemCount }), _jsx("span", { children: "\u5F02\u5E38" })] })] })] }), _jsxs("section", { className: "agent-playground-inspector-section", children: [_jsx("div", { className: "agent-playground-inspector-title", children: _jsx("span", { children: "\u4E0A\u4E0B\u6587" }) }), _jsxs("dl", { className: "agent-playground-context-list", children: [_jsxs("div", { children: [_jsx("dt", { children: "Endpoint" }), _jsx("dd", { children: endpointId })] }), _jsxs("div", { children: [_jsx("dt", { children: "Scope" }), _jsx("dd", { children: activeChannel.type })] }), _jsxs("div", { children: [_jsx("dt", { children: "Scene" }), _jsx("dd", { children: activeChannel.id })] }), _jsxs("div", { children: [_jsx("dt", { children: "Workdir" }), _jsx("dd", { title: activeChannel.runConfig.workingDirectory, children: activeChannel.runConfig.workingDirectory || 'Host project root' })] }), _jsxs("div", { children: [_jsx("dt", { children: "Security" }), _jsx("dd", { children: activeChannel.runConfig.safetyMode })] }), _jsxs("div", { children: [_jsx("dt", { children: "Approval" }), _jsx("dd", { children: activeChannel.runConfig.safetyMode === 'read-only' ? 'deny' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'allow' : activeChannel.runConfig.approvalMode })] }), _jsxs("div", { children: [_jsx("dt", { children: "Network" }), _jsx("dd", { children: activeChannel.runConfig.networkAccess ? 'enabled' : 'disabled' })] }), _jsxs("div", { children: [_jsx("dt", { children: "Isolation" }), _jsx("dd", { className: cn(shellIsolation?.available && 'is-online'), children: shellIsolation ? `${shellIsolation.provider}: ${shellIsolation.available ? 'ready' : 'unavailable'}` : 'checking' })] }), _jsxs("div", { children: [_jsx("dt", { children: "Transport" }), _jsx("dd", { className: cn(connected && 'is-online'), children: connected ? 'WebSocket online' : 'reconnecting' })] })] })] }), _jsxs("section", { className: "agent-playground-inspector-section agent-playground-task-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u4EFB\u52A1\u5386\u53F2" }), _jsxs("small", { children: [taskRuns.length, " runs"] })] }), taskRuns.length > 0 ? (_jsx("div", { className: "agent-playground-task-list", children: taskRuns.slice(0, 8).map((run) => (_jsxs("article", { className: cn(`is-${run.status}`), children: [_jsx("i", {}), _jsxs("div", { children: [_jsx("strong", { children: taskStatusLabel(run.status) }), _jsx("code", { children: run.turnId.slice(0, 10) })] }), _jsxs("dl", { children: [_jsxs("div", { children: [_jsx("dt", { children: "\u8017\u65F6" }), _jsx("dd", { children: run.durationMs === undefined ? '进行中' : `${run.durationMs.toLocaleString()} ms` })] }), _jsxs("div", { children: [_jsx("dt", { children: "\u5DE5\u5177" }), _jsx("dd", { children: run.toolCount })] }), _jsxs("div", { children: [_jsx("dt", { children: "Token" }), _jsx("dd", { children: run.tokenCount.toLocaleString() })] })] })] }, run.id))) })) : _jsxs("div", { className: "agent-playground-artifact-empty", children: [_jsx(Activity, { size: 18 }), _jsx("span", { children: "\u8FD0\u884C\u4EFB\u52A1\u540E\u4F1A\u5728\u8FD9\u91CC\u5F62\u6210\u53EF\u56DE\u6EAF\u8BB0\u5F55" })] })] }), _jsxs("section", { className: "agent-playground-inspector-section agent-playground-trace-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u6700\u8FD1\u6267\u884C" }), _jsx("small", { children: "2s sync" })] }), traceLoading && !trace ? (_jsxs("div", { className: "agent-playground-trace-loading", children: [_jsx("i", {}), _jsx("i", {}), _jsx("i", {})] })) : traceNotice && !trace?.events.length ? (_jsxs("div", { className: "agent-playground-trace-notice", children: [_jsx(CircleAlert, { size: 16 }), _jsx("span", { children: traceNotice })] })) : recentTraceEvents.length ? (_jsx("div", { className: "agent-playground-trace-list", children: recentTraceEvents.map((event) => {
566
+ handleSendMessage(c.text, c.segments); }, disabled: !canExecute || !hasRenderableSegments(previewSegments), children: [_jsx(Send, { size: 16 }), " \u53D1\u9001"] })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap text-xs text-muted-foreground", children: [_jsx(Info, { size: 12 }), " \u5FEB\u6377\u64CD\u4F5C:", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "Enter" }), " \u53D1\u9001", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "Shift+Enter" }), " \u6362\u884C", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "```ts" }), " \u4EE3\u7801\u5757", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "**\u6587\u672C**" }), " \u52A0\u7C97", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "[@\u540D\u79F0]" }), " @\u67D0\u4EBA", _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "[video:URL]" }), _jsx("span", { className: "px-1 py-0.5 rounded border text-[10px]", children: "[audio:URL]" })] })] })] }), _jsxs("aside", { className: cn("agent-playground-inspector", showInspector && "show"), "aria-label": "\u8FD0\u884C\u68C0\u67E5\u5668", children: [_jsxs("div", { className: "agent-playground-inspector-head", children: [_jsxs("div", { children: [_jsx("span", { children: "Run inspector" }), _jsx("h2", { children: "\u8FD0\u884C\u68C0\u67E5\u5668" })] }), _jsxs("div", { children: [_jsx("button", { type: "button", onClick: () => void loadTrace(), "aria-label": "\u5237\u65B0 Agent Trace", disabled: traceLoading, children: _jsx(RefreshCw, { size: 15, className: cn(traceLoading && 'animate-spin') }) }), _jsx("button", { type: "button", className: "agent-playground-inspector-close", onClick: () => setShowInspector(false), "aria-label": "\u5173\u95ED\u8FD0\u884C\u68C0\u67E5\u5668", children: _jsx(X, { size: 15 }) })] })] }), _jsxs("div", { className: "agent-playground-inspector-tabs", role: "tablist", "aria-label": "\u68C0\u67E5\u5668\u89C6\u56FE", children: [_jsxs("button", { type: "button", role: "tab", "aria-selected": inspectorView === 'runs', className: cn(inspectorView === 'runs' && 'active'), onClick: () => setInspectorView('runs'), children: [_jsx(Activity, { size: 13 }), "\u4EFB\u52A1"] }), _jsxs("button", { type: "button", role: "tab", "aria-selected": inspectorView === 'artifacts', className: cn(inspectorView === 'artifacts' && 'active'), onClick: () => setInspectorView('artifacts'), children: [_jsx(FileDiff, { size: 13 }), "\u53D8\u66F4\u4E0E\u4EA7\u7269 ", _jsx("span", { children: workbenchArtifacts.length })] })] }), _jsxs("div", { className: cn("agent-playground-inspector-view", inspectorView !== 'runs' && 'hidden'), children: [_jsxs("section", { className: "agent-playground-inspector-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u8FD0\u884C\u6982\u89C8" }), _jsx("small", { children: traceSummary.activeTurns > 0 ? 'live' : 'idle' })] }), _jsxs("div", { className: "agent-playground-metrics", children: [_jsxs("div", { children: [_jsx(Activity, {}), _jsx("strong", { children: traceSummary.eventCount.toLocaleString() }), _jsx("span", { children: "\u4E8B\u4EF6" })] }), _jsxs("div", { children: [_jsx(Wrench, {}), _jsx("strong", { children: traceSummary.toolCount.toLocaleString() }), _jsx("span", { children: "\u5DE5\u5177" })] }), _jsxs("div", { children: [_jsx(Coins, {}), _jsx("strong", { children: traceSummary.tokenCount.toLocaleString() }), _jsx("span", { children: "Token" })] }), _jsxs("div", { className: cn(traceSummary.problemCount > 0 && 'has-problem'), children: [_jsx(CircleAlert, {}), _jsx("strong", { children: traceSummary.problemCount }), _jsx("span", { children: "\u5F02\u5E38" })] })] })] }), _jsxs("section", { className: "agent-playground-inspector-section", children: [_jsx("div", { className: "agent-playground-inspector-title", children: _jsx("span", { children: "\u4E0A\u4E0B\u6587" }) }), _jsxs("dl", { className: "agent-playground-context-list", children: [_jsxs("div", { children: [_jsx("dt", { children: "Endpoint" }), _jsx("dd", { children: endpointId })] }), _jsxs("div", { children: [_jsx("dt", { children: "Scope" }), _jsx("dd", { children: activeChannel.type })] }), _jsxs("div", { children: [_jsx("dt", { children: "Scene" }), _jsx("dd", { children: activeChannel.id })] }), _jsxs("div", { children: [_jsx("dt", { children: "Workdir" }), _jsx("dd", { title: activeChannel.runConfig.workingDirectory, children: activeChannel.runConfig.workingDirectory || 'Host project root' })] }), _jsxs("div", { children: [_jsx("dt", { children: "Security" }), _jsx("dd", { children: activeChannel.runConfig.safetyMode })] }), _jsxs("div", { children: [_jsx("dt", { children: "Approval" }), _jsx("dd", { children: activeChannel.runConfig.safetyMode === 'read-only' ? 'auto' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'bypass' : activeChannel.runConfig.approvalMode })] }), _jsxs("div", { children: [_jsx("dt", { children: "Network" }), _jsx("dd", { children: activeChannel.runConfig.networkAccess ? 'enabled' : 'disabled' })] }), _jsxs("div", { children: [_jsx("dt", { children: "Isolation" }), _jsx("dd", { className: cn(shellIsolation?.available && 'is-online'), children: shellIsolation ? `${shellIsolation.provider}: ${shellIsolation.available ? 'ready' : 'unavailable'}` : 'checking' })] }), _jsxs("div", { children: [_jsx("dt", { children: "Transport" }), _jsx("dd", { className: cn(connected && 'is-online'), children: connected ? 'WebSocket online' : 'reconnecting' })] })] })] }), _jsxs("section", { className: "agent-playground-inspector-section agent-playground-task-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u4EFB\u52A1\u5386\u53F2" }), _jsxs("small", { children: [taskRuns.length, " runs"] })] }), taskRuns.length > 0 ? (_jsx("div", { className: "agent-playground-task-list", children: taskRuns.slice(0, 8).map((run) => (_jsxs("article", { className: cn(`is-${run.status}`), children: [_jsx("i", {}), _jsxs("div", { children: [_jsx("strong", { children: taskStatusLabel(run.status) }), _jsx("code", { children: run.turnId.slice(0, 10) })] }), _jsxs("dl", { children: [_jsxs("div", { children: [_jsx("dt", { children: "\u8017\u65F6" }), _jsx("dd", { children: run.durationMs === undefined ? '进行中' : `${run.durationMs.toLocaleString()} ms` })] }), _jsxs("div", { children: [_jsx("dt", { children: "\u5DE5\u5177" }), _jsx("dd", { children: run.toolCount })] }), _jsxs("div", { children: [_jsx("dt", { children: "Token" }), _jsx("dd", { children: run.tokenCount.toLocaleString() })] })] })] }, run.id))) })) : _jsxs("div", { className: "agent-playground-artifact-empty", children: [_jsx(Activity, { size: 18 }), _jsx("span", { children: "\u8FD0\u884C\u4EFB\u52A1\u540E\u4F1A\u5728\u8FD9\u91CC\u5F62\u6210\u53EF\u56DE\u6EAF\u8BB0\u5F55" })] })] }), _jsxs("section", { className: "agent-playground-inspector-section agent-playground-trace-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u6700\u8FD1\u6267\u884C" }), _jsx("small", { children: "2s sync" })] }), traceLoading && !trace ? (_jsxs("div", { className: "agent-playground-trace-loading", children: [_jsx("i", {}), _jsx("i", {}), _jsx("i", {})] })) : traceNotice && !trace?.events.length ? (_jsxs("div", { className: "agent-playground-trace-notice", children: [_jsx(CircleAlert, { size: 16 }), _jsx("span", { children: traceNotice })] })) : recentTraceEvents.length ? (_jsx("div", { className: "agent-playground-trace-list", children: recentTraceEvents.map((event) => {
567
567
  const item = presentTraceEvent(event);
568
568
  return (_jsxs("div", { className: cn("agent-playground-trace-item", `is-${item.tone}`), children: [_jsx("span", { className: "agent-playground-trace-dot" }), _jsxs("div", { children: [_jsx("strong", { children: item.title }), item.detail ? _jsx("small", { children: item.detail }) : null] }), _jsx("time", { children: new Date(event.recordedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) })] }, event.sequence));
569
569
  }) })) : (_jsxs("div", { className: "agent-playground-trace-empty", children: [_jsx(Activity, { size: 18 }), _jsx("span", { children: "\u53D1\u9001\u4EFB\u52A1\u540E\u663E\u793A\u63A8\u7406\u4E0E\u5DE5\u5177\u8F68\u8FF9" })] }))] })] }), _jsx("div", { className: cn("agent-playground-inspector-view", inspectorView !== 'artifacts' && 'hidden'), children: _jsxs("section", { className: "agent-playground-inspector-section agent-playground-artifact-section", children: [_jsxs("div", { className: "agent-playground-inspector-title", children: [_jsx("span", { children: "\u6587\u4EF6\u4E0E\u547D\u4EE4\u4EA7\u7269" }), _jsxs("small", { children: [workbenchArtifacts.length, " items"] })] }), workbenchArtifacts.length > 0 ? (_jsx("div", { className: "agent-playground-artifact-list", children: workbenchArtifacts.map((artifact) => {
@@ -42,7 +42,7 @@ import {
42
42
  type PlaygroundMessage,
43
43
  type PlaygroundSession,
44
44
  } from './playgroundState.js';
45
- import type { SandboxAgentRunConfig } from '../src/run-config.js';
45
+ import type { SandboxAgentRunConfig } from '../../src/run-config.js';
46
46
 
47
47
  type Message = PlaygroundMessage
48
48
  type Channel = PlaygroundSession
@@ -823,13 +823,13 @@ export default function Sandbox() {
823
823
  <label>
824
824
  <span>审批策略</span>
825
825
  <select
826
- value={activeChannel.runConfig.safetyMode === 'read-only' ? 'deny' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'allow' : activeChannel.runConfig.approvalMode}
826
+ value={activeChannel.runConfig.safetyMode === 'read-only' ? 'auto' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'bypass' : activeChannel.runConfig.approvalMode}
827
827
  disabled={activeChannel.runConfig.safetyMode !== 'workspace-write'}
828
828
  onChange={(event) => updateRunConfig({ approvalMode: event.target.value as SandboxAgentRunConfig['approvalMode'] })}
829
829
  >
830
830
  <option value="ask">按需确认</option>
831
- <option value="deny">自动拒绝</option>
832
- <option value="allow">自动允许</option>
831
+ <option value="auto">审核 Agent 自动判断</option>
832
+ <option value="bypass">绕过审批</option>
833
833
  </select>
834
834
  </label>
835
835
  <label className="agent-playground-network-toggle">
@@ -1060,7 +1060,7 @@ export default function Sandbox() {
1060
1060
  <div><dt>Scene</dt><dd>{activeChannel.id}</dd></div>
1061
1061
  <div><dt>Workdir</dt><dd title={activeChannel.runConfig.workingDirectory}>{activeChannel.runConfig.workingDirectory || 'Host project root'}</dd></div>
1062
1062
  <div><dt>Security</dt><dd>{activeChannel.runConfig.safetyMode}</dd></div>
1063
- <div><dt>Approval</dt><dd>{activeChannel.runConfig.safetyMode === 'read-only' ? 'deny' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'allow' : activeChannel.runConfig.approvalMode}</dd></div>
1063
+ <div><dt>Approval</dt><dd>{activeChannel.runConfig.safetyMode === 'read-only' ? 'auto' : activeChannel.runConfig.safetyMode === 'danger-full-access' ? 'bypass' : activeChannel.runConfig.approvalMode}</dd></div>
1064
1064
  <div><dt>Network</dt><dd>{activeChannel.runConfig.networkAccess ? 'enabled' : 'disabled'}</dd></div>
1065
1065
  <div><dt>Isolation</dt><dd className={cn(shellIsolation?.available && 'is-online')}>{shellIsolation ? `${shellIsolation.provider}: ${shellIsolation.available ? 'ready' : 'unavailable'}` : 'checking'}</dd></div>
1066
1066
  <div><dt>Transport</dt><dd className={cn(connected && 'is-online')}>{connected ? 'WebSocket online' : 'reconnecting'}</dd></div>
@@ -9,7 +9,7 @@ export const meta = definePage({
9
9
  });
10
10
  /**
11
11
  * Convention page entry (ADR 0046).
12
- * `pages/index.tsx` → `/sandbox` (plugin path; no `/p-` leaf).
12
+ * `pages/index/index.tsx` → `/sandbox` (plugin path; no `/p-` leaf).
13
13
  * Agent workbench for testing scoped conversations, rich messages and execution traces.
14
14
  * WebSocket targets Host `/sandbox` via zhin_api_base + token (see sandboxTransport.ts).
15
15
  */
@@ -9,7 +9,7 @@ export const meta = definePage({
9
9
 
10
10
  /**
11
11
  * Convention page entry (ADR 0046).
12
- * `pages/index.tsx` → `/sandbox` (plugin path; no `/p-` leaf).
12
+ * `pages/index/index.tsx` → `/sandbox` (plugin path; no `/p-` leaf).
13
13
  * Agent workbench for testing scoped conversations, rich messages and execution traces.
14
14
  * WebSocket targets Host `/sandbox` via zhin_api_base + token (see sandboxTransport.ts).
15
15
  */
@@ -1,5 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { DEFAULT_SANDBOX_AGENT_RUN_CONFIG, normalizeSandboxAgentRunConfig, } from "../lib/run-config.js";
2
+ import { DEFAULT_SANDBOX_AGENT_RUN_CONFIG, normalizeSandboxAgentRunConfig, } from "../../lib/run-config.js";
3
3
  export const PLAYGROUND_STORAGE_KEY = 'zhin.sandbox.agent-playground.v1';
4
4
  export function createDefaultPlaygroundState(workingDirectory = '') {
5
5
  const runConfig = (safetyMode) => ({
@@ -4,7 +4,7 @@ import {
4
4
  normalizeSandboxAgentRunConfig,
5
5
  type SandboxAgentRunConfig,
6
6
  type SandboxSafetyMode,
7
- } from '../src/run-config.js';
7
+ } from '../../src/run-config.js';
8
8
 
9
9
  export const PLAYGROUND_STORAGE_KEY = 'zhin.sandbox.agent-playground.v1';
10
10
 
package/schema.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
27
27
  "items": {
28
28
  "type": "object",
29
- "additionalProperties": true,
29
+ "additionalProperties": false,
30
30
  "properties": {
31
31
  "master": {
32
32
  "type": [
@@ -46,14 +46,13 @@
46
46
  },
47
47
  "description": "本 endpoint 的 trusted 列表"
48
48
  },
49
- "context": {
50
- "type": "string",
51
- "description": "Sandbox context identifier"
52
- },
53
49
  "owner": {
54
50
  "type": "string",
55
51
  "description": "Sandbox owner user ID"
56
52
  },
53
+ "commandPrefix": {
54
+ "type": "string"
55
+ },
57
56
  "id": {
58
57
  "type": "string",
59
58
  "description": "Sandbox bot name"
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@ export {
9
9
  type MessageElement,
10
10
  type MessageType,
11
11
  type ResolvedSandboxBot,
12
- type SandboxAdapterConfig,
12
+ type SandboxEndpointConfig,
13
13
  type SandboxWsSocket,
14
14
  } from './protocol.js';
15
15