@zhin.js/adapter-sandbox 1.1.4 → 1.1.6

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,52 @@
1
1
  # @zhin.js/adapter-process
2
2
 
3
+ ## 1.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - badcc1f: Unify lazy JSX and HTML delivery across commands, inbound middleware and registered components. Remove the former JSX/template component runtimes, respect declared adapter HTML policies, and preserve author/generation ownership for automatic and explicit replies.
8
+
9
+ Add the optional @zhin.js/components library with composable JSXRenderable display props, shared themes, symmetric spacing and local style/text overrides. Include tables, lists, Markdown with highlighted code blocks, and display-only checkbox, radio, switch and button states; provide a reusable preview gallery. Update scaffolds, examples and public authoring documentation together.
10
+
11
+ Add optional official Tailwind static-utility compilation to inline styles. Consolidate raster rendering on @pixel.js/shotium, remove the Satori image toolkit, and align raster format contracts with actual PNG/JPEG/WebP results.
12
+
13
+ Reject cascade-dependent Tailwind values including unset, track the installed Tailwind version in the bundled theme provenance, and preserve both repository and upstream MIT attribution. Keep optional gallery dependency failures visible and document asynchronous Tailwind initialization.
14
+
15
+ - Updated dependencies [badcc1f]
16
+ - @zhin.js/core@1.1.41
17
+ - @zhin.js/feature-kit@1.1.3
18
+ - @zhin.js/adapter@1.1.17
19
+ - @zhin.js/page@1.1.4
20
+ - @zhin.js/skill@1.1.4
21
+ - @zhin.js/host-http@1.1.4
22
+ - @zhin.js/client@1.1.9
23
+
24
+ ## 1.1.5
25
+
26
+ ### Patch Changes
27
+
28
+ - b9b3953: Fix adapter connection races, transport health reporting, candidate rollback and delivery acknowledgement boundaries. Separate generation admission from locally observed connectivity, reject unconfirmed message receipts, and mark uncertain delivery results without recommending automatic replay.
29
+
30
+ Class and compact endpoints should expose a `transportState` getter to report physical connection or local listener health. Unobserved endpoints now report an unknown transport and remain offline in diagnostics. Platform stability tiers remain unchanged until real account acceptance evidence is available.
31
+
32
+ - Updated dependencies [b9b3953]
33
+ - Updated dependencies [b9b3953]
34
+ - Updated dependencies [b9b3953]
35
+ - Updated dependencies [b9b3953]
36
+ - Updated dependencies [b9b3953]
37
+ - Updated dependencies [b9b3953]
38
+ - Updated dependencies [b9b3953]
39
+ - Updated dependencies [b9b3953]
40
+ - Updated dependencies [b9b3953]
41
+ - Updated dependencies [b9b3953]
42
+ - Updated dependencies [b9b3953]
43
+ - @zhin.js/adapter@1.1.16
44
+ - @zhin.js/im-contract@1.1.3
45
+ - @zhin.js/core@1.1.40
46
+ - @zhin.js/host-http@1.1.3
47
+ - @zhin.js/skill@1.1.3
48
+ - @zhin.js/client@1.1.8
49
+
3
50
  ## 1.1.4
4
51
 
5
52
  ### Patch Changes
package/README.md CHANGED
@@ -44,6 +44,8 @@ Root 在 `zhin runtime start` 时装载 `@zhin.js/host-http`、`ConsoleRuntime`
44
44
 
45
45
  出站 wire 只做 JSON 封装;旧 `segment-mapper`(canonical segments)归一化上移到 gateway/core 渲染链。
46
46
 
47
+ Sandbox 声明 `html: direct`,聊天页在隔离 iframe 中展示 canonical `html`,因此 JSX 卡片无需截图引擎即可预览。允许内联样式和 data/blob 图片;组件脚本、事件属性、外部资源、表单提交和父页面访问均被限制。高度按内容调整,超过 1200px 可在框内滚动。
48
+
47
49
  ## 配置
48
50
 
49
51
  **推荐(与 [minimal-bot](../../../examples/minimal-bot/) 一致)**:`plugins.sandbox.endpoints: []`,运行时提供默认 `sandbox-bot` endpoint,无需额外账号配置。
@@ -12,6 +12,7 @@ export default defineAdapter({
12
12
  // Console UI 直连 URL 媒体、直接渲染 base64 内联媒体;path 由端点读盘
13
13
  // 物化为 base64 内联(sandbox 无平台上传通道)。交互段由 Console 原生承载。
14
14
  segments: {
15
+ html: 'direct',
15
16
  outboundMedia: ['url', 'base64', 'path'],
16
17
  interactive: 'native',
17
18
  },
@@ -17,6 +17,7 @@ export default defineAdapter<SandboxEndpointConfig>({
17
17
  // Console UI 直连 URL 媒体、直接渲染 base64 内联媒体;path 由端点读盘
18
18
  // 物化为 base64 内联(sandbox 无平台上传通道)。交互段由 Console 原生承载。
19
19
  segments: {
20
+ html: 'direct',
20
21
  outboundMedia: ['url', 'base64', 'path'],
21
22
  interactive: 'native',
22
23
  },
package/lib/endpoint.d.ts CHANGED
@@ -33,6 +33,8 @@ export declare class SandboxWsEndpoint extends Endpoint<SandboxClient> {
33
33
  constructor(options: SandboxEndpointOptions);
34
34
  /** Live endpoint id (config `id`) — Console endpoint.list/resolve uses it. */
35
35
  get name(): string;
36
+ /** The Sandbox transport is the mounted local listener, not a remote account. */
37
+ get transportState(): 'open' | 'idle';
36
38
  start(): void;
37
39
  open(): void;
38
40
  close(): void;
package/lib/endpoint.js CHANGED
@@ -94,6 +94,10 @@ export class SandboxWsEndpoint extends Endpoint {
94
94
  get name() {
95
95
  return this.#options.defaults.id;
96
96
  }
97
+ /** The Sandbox transport is the mounted local listener, not a remote account. */
98
+ get transportState() {
99
+ return this.#started && this.#wsHandleRelease ? 'open' : 'idle';
100
+ }
97
101
  start() {
98
102
  if (this.#started)
99
103
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-sandbox",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Zhin.js Sandbox adapter for Plugin Runtime (WebSocket /sandbox)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -48,16 +48,16 @@
48
48
  "dependencies": {
49
49
  "lucide-react": "^0.525.0",
50
50
  "react": "^19.2.8",
51
- "@zhin.js/adapter": "1.1.15",
52
- "@zhin.js/client": "1.1.7",
51
+ "@zhin.js/adapter": "1.1.17",
52
+ "@zhin.js/client": "1.1.9",
53
53
  "@zhin.js/console-contract": "1.1.1",
54
- "@zhin.js/core": "1.1.39",
55
- "@zhin.js/feature-kit": "1.1.2",
56
- "@zhin.js/host-http": "1.1.2",
57
- "@zhin.js/im-contract": "1.1.2",
54
+ "@zhin.js/core": "1.1.41",
55
+ "@zhin.js/feature-kit": "1.1.3",
56
+ "@zhin.js/host-http": "1.1.4",
57
+ "@zhin.js/im-contract": "1.1.3",
58
58
  "@zhin.js/logger": "1.1.1",
59
- "@zhin.js/page": "1.1.3",
60
- "@zhin.js/skill": "1.1.2"
59
+ "@zhin.js/page": "1.1.4",
60
+ "@zhin.js/skill": "1.1.4"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/react": "^19.2.18",
@@ -65,18 +65,18 @@
65
65
  "typescript": "^6.0.3",
66
66
  "vitest": "^4.1.10",
67
67
  "ws": "^8.21.1",
68
- "@zhin.js/pagemanager": "1.1.3",
69
- "@zhin.js/runtime": "1.1.3",
70
- "zhin.js": "1.1.4"
68
+ "@zhin.js/pagemanager": "1.1.5",
69
+ "@zhin.js/runtime": "1.1.5",
70
+ "zhin.js": "1.1.6"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "react": "^19.0.0",
74
- "@zhin.js/adapter": "^1.1.15",
74
+ "@zhin.js/adapter": "^1.1.17",
75
75
  "@zhin.js/console-contract": "^1.1.1",
76
- "@zhin.js/core": "^1.1.39",
77
- "@zhin.js/host-http": "^1.1.2",
78
- "@zhin.js/page": "^1.1.3",
79
- "zhin.js": "^1.1.4"
76
+ "@zhin.js/core": "^1.1.41",
77
+ "@zhin.js/host-http": "^1.1.4",
78
+ "@zhin.js/page": "^1.1.4",
79
+ "zhin.js": "^1.1.6"
80
80
  },
81
81
  "peerDependenciesMeta": {
82
82
  "react": {
@@ -0,0 +1,33 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React, { useEffect, useRef, useState } from 'react';
4
+ /** HTML stays in an opaque-origin frame; only the size reporter can run scripts. */
5
+ export default function HtmlMessage({ html }) {
6
+ const frame = useRef(null);
7
+ const [nonce] = useState(createHtmlMessageNonce);
8
+ const [height, setHeight] = useState(240);
9
+ useEffect(() => {
10
+ const onSize = (event) => {
11
+ if (event.source !== frame.current?.contentWindow
12
+ || event.data?.type !== 'sandbox-html-size'
13
+ || event.data?.nonce !== nonce)
14
+ return;
15
+ const value = event.data.height;
16
+ if (typeof value === 'number' && Number.isFinite(value)) {
17
+ setHeight(Math.ceil(Math.min(1200, Math.max(32, value))));
18
+ }
19
+ };
20
+ window.addEventListener('message', onSize);
21
+ return () => window.removeEventListener('message', onSize);
22
+ }, [nonce]);
23
+ const srcDoc = createHtmlMessageDocument(html, nonce);
24
+ return _jsx("iframe", { ref: frame, title: "HTML \u6D88\u606F", sandbox: "allow-scripts", srcDoc: srcDoc, className: "block w-full min-w-0 border-0 rounded-lg my-1 bg-transparent", style: { height } });
25
+ }
26
+ /** getRandomValues is available on ordinary HTTP LAN pages as well as HTTPS. */
27
+ export function createHtmlMessageNonce() {
28
+ return Array.from(crypto.getRandomValues(new Uint8Array(16)), byte => byte.toString(16).padStart(2, '0')).join('');
29
+ }
30
+ /** The trusted reporter precedes raw markup so an unfinished tag cannot swallow it. */
31
+ export function createHtmlMessageDocument(html, nonce) {
32
+ return `<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: blob:; font-src data:; style-src 'unsafe-inline'; script-src 'nonce-${nonce}'; base-uri 'none'; form-action 'none'"><style>html,body{margin:0;padding:0;min-height:0}body{display:flow-root;overflow-wrap:anywhere}img{max-width:100%}</style></head><body><script nonce="${nonce}">const report=()=>parent.postMessage({type:'sandbox-html-size',nonce:'${nonce}',height:document.body.getBoundingClientRect().height},'*');new ResizeObserver(report).observe(document.body);report();<\/script>${html}</body></html>`;
33
+ }
@@ -0,0 +1,44 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+
3
+ /** HTML stays in an opaque-origin frame; only the size reporter can run scripts. */
4
+ export default function HtmlMessage({ html }: { readonly html: string }) {
5
+ const frame = useRef<HTMLIFrameElement>(null);
6
+ const [nonce] = useState(createHtmlMessageNonce);
7
+ const [height, setHeight] = useState(240);
8
+
9
+ useEffect(() => {
10
+ const onSize = (event: MessageEvent) => {
11
+ if (event.source !== frame.current?.contentWindow
12
+ || event.data?.type !== 'sandbox-html-size'
13
+ || event.data?.nonce !== nonce) return;
14
+ const value = event.data.height;
15
+ if (typeof value === 'number' && Number.isFinite(value)) {
16
+ setHeight(Math.ceil(Math.min(1200, Math.max(32, value))));
17
+ }
18
+ };
19
+ window.addEventListener('message', onSize);
20
+ return () => window.removeEventListener('message', onSize);
21
+ }, [nonce]);
22
+
23
+ const srcDoc = createHtmlMessageDocument(html, nonce);
24
+
25
+ return <iframe
26
+ ref={frame}
27
+ title="HTML 消息"
28
+ sandbox="allow-scripts"
29
+ srcDoc={srcDoc}
30
+ className="block w-full min-w-0 border-0 rounded-lg my-1 bg-transparent"
31
+ style={{ height }}
32
+ />;
33
+ }
34
+
35
+ /** getRandomValues is available on ordinary HTTP LAN pages as well as HTTPS. */
36
+ export function createHtmlMessageNonce(): string {
37
+ return Array.from(crypto.getRandomValues(new Uint8Array(16)),
38
+ byte => byte.toString(16).padStart(2, '0')).join('');
39
+ }
40
+
41
+ /** The trusted reporter precedes raw markup so an unfinished tag cannot swallow it. */
42
+ export function createHtmlMessageDocument(html: string, nonce: string): string {
43
+ return `<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: blob:; font-src data:; style-src 'unsafe-inline'; script-src 'nonce-${nonce}'; base-uri 'none'; form-action 'none'"><style>html,body{margin:0;padding:0;min-height:0}body{display:flow-root;overflow-wrap:anywhere}img{max-width:100%}</style></head><body><script nonce="${nonce}">const report=()=>parent.postMessage({type:'sandbox-html-size',nonce:'${nonce}',height:document.body.getBoundingClientRect().height},'*');new ResizeObserver(report).observe(document.body);report();<\/script>${html}</body></html>`;
44
+ }
@@ -5,6 +5,7 @@ import { cn, CodeBlock, MarkdownContent, resolveMediaSrc, pickMediaRawUrl, } fro
5
5
  import { buildSandboxWebSocketUrl } from './sandboxTransport';
6
6
  import { User, Bot, Users, Trash2, Send, Hash, MessageSquare, Wifi, WifiOff, Smile, Image, X, Check, Info, Search, Video, Music, Plus, PanelRight, ExternalLink, RefreshCw, Sparkles, Activity, Wrench, Coins, CircleAlert, Gauge, SlidersHorizontal, FolderOpen, ShieldCheck, Network, Square, RotateCcw, FileDiff, Terminal, FlaskConical, ChevronDown, FileDown, ListChecks, } from 'lucide-react';
7
7
  import RichTextEditor, {} from './RichTextEditor';
8
+ import HtmlMessage from './HtmlMessage.js';
8
9
  import { agentStudioPath, buildAgentRunReport, buildSandboxSessionKey, cancelAgentTask, deriveAgentRunSteps, deriveTaskRuns, deriveWorkbenchArtifacts, fetchAgentTrace, loadCachedAgentTrace, mergeTraceSnapshot, presentTraceEvent, summarizeTrace, saveCachedAgentTrace, } from './agentTrace.js';
9
10
  import { createDefaultPlaygroundState, loadPlaygroundState, savePlaygroundState, } from './playgroundState.js';
10
11
  export default function Sandbox() {
@@ -359,6 +360,8 @@ export default function Sandbox() {
359
360
  case 'markdown':
360
361
  case 'md':
361
362
  return _jsx(MarkdownContent, { text: String(d.text ?? d.content ?? ''), className: isSent ? 'zhin-markdown--inverse' : undefined }, index);
363
+ case 'html':
364
+ return _jsx(HtmlMessage, { html: String(d.html ?? '') }, index);
362
365
  case 'code':
363
366
  return _jsx(CodeBlock, { code: String(d.code ?? d.text ?? d.content ?? ''), language: String(d.language ?? d.lang ?? '') }, index);
364
367
  case 'mention':
@@ -18,6 +18,7 @@ import {
18
18
  FileDown, ListChecks,
19
19
  } from 'lucide-react';
20
20
  import RichTextEditor, { type RichTextEditorRef } from './RichTextEditor';
21
+ import HtmlMessage from './HtmlMessage.js';
21
22
  import {
22
23
  agentStudioPath,
23
24
  buildAgentRunReport,
@@ -409,6 +410,8 @@ export default function Sandbox() {
409
410
  case 'markdown':
410
411
  case 'md':
411
412
  return <MarkdownContent key={index} text={String(d.text ?? d.content ?? '')} className={isSent ? 'zhin-markdown--inverse' : undefined} />
413
+ case 'html':
414
+ return <HtmlMessage key={index} html={String(d.html ?? '')} />
412
415
  case 'code':
413
416
  return <CodeBlock key={index} code={String(d.code ?? d.text ?? d.content ?? '')} language={String(d.language ?? d.lang ?? '')} />
414
417
  case 'mention':
package/src/endpoint.ts CHANGED
@@ -155,6 +155,11 @@ export class SandboxWsEndpoint extends Endpoint<SandboxClient> {
155
155
  return this.#options.defaults.id;
156
156
  }
157
157
 
158
+ /** The Sandbox transport is the mounted local listener, not a remote account. */
159
+ get transportState(): 'open' | 'idle' {
160
+ return this.#started && this.#wsHandleRelease ? 'open' : 'idle';
161
+ }
162
+
158
163
  start(): void {
159
164
  if (this.#started) return;
160
165
  this.#started = true;