@logicseek/hello-cc 1.0.5 → 1.1.0-rc.3

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.
Files changed (146) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/docs/codex-account.zh-CN.md +38 -0
  3. package/docs/codex-protocol.md +59 -0
  4. package/docs/commands.md +9 -0
  5. package/docs/commands.zh-CN.md +7 -0
  6. package/docs/macos-process-identity.md +2 -0
  7. package/docs/native.md +179 -14
  8. package/docs/native.zh-CN.md +136 -6
  9. package/docs/web-handoff.zh-CN.md +118 -8
  10. package/lib/cli/app.mjs +47 -4
  11. package/lib/cli/commands/coordination.mjs +14 -4
  12. package/lib/cli/commands/dsh.mjs +1 -1
  13. package/lib/cli/commands/gc.mjs +22 -15
  14. package/lib/cli/commands/hook.mjs +19 -3
  15. package/lib/cli/commands/install.mjs +80 -10
  16. package/lib/cli/commands/misc.mjs +36 -9
  17. package/lib/cli/commands/native.mjs +4 -38
  18. package/lib/cli/commands/peer.mjs +3 -0
  19. package/lib/cli/commands/run.mjs +13 -6
  20. package/lib/cli/commands/tmux.mjs +1 -2
  21. package/lib/cli-runtime.mjs +19 -2
  22. package/lib/core/coordination/results.mjs +27 -10
  23. package/lib/core/sessions/hook-root-identity.mjs +47 -0
  24. package/lib/core/sessions/launch.mjs +7 -1
  25. package/lib/db/connection.mjs +140 -42
  26. package/lib/db/schema.mjs +59 -0
  27. package/lib/diagnostics/codex-protocol.mjs +105 -0
  28. package/lib/guidance.mjs +21 -36
  29. package/lib/integrations/codex-account.mjs +143 -0
  30. package/lib/integrations/codex-interactions.mjs +13 -11
  31. package/lib/integrations/dsh-collaboration.mjs +6 -12
  32. package/lib/integrations/dsh-cordis.mjs +17 -13
  33. package/lib/integrations/dsh.mjs +16 -11
  34. package/lib/integrations/mcp-elicitation.mjs +116 -0
  35. package/lib/integrations/mcp-url-elicitation.mjs +25 -0
  36. package/lib/integrations/native/claude.mjs +24 -6
  37. package/lib/integrations/native/codex.mjs +38 -7
  38. package/lib/integrations/native/dsh-acp.mjs +67 -24
  39. package/lib/integrations/native/index.mjs +4 -1
  40. package/lib/integrations/native/interactions.mjs +2 -1
  41. package/lib/integrations/native/jsonrpc.mjs +37 -5
  42. package/lib/integrations/native/telemetry.mjs +30 -0
  43. package/lib/process/identity.mjs +4 -0
  44. package/lib/process/pinned-cwd.mjs +302 -0
  45. package/lib/process/pinned-guidance.mjs +138 -0
  46. package/lib/process/pinned-project-state.mjs +105 -0
  47. package/lib/process/selected-cwd-identity.mjs +95 -0
  48. package/lib/runtime/buffer-evidence.mjs +2 -1
  49. package/lib/runtime/client.mjs +9 -0
  50. package/lib/runtime/native/client.mjs +4 -1
  51. package/lib/runtime/native/history.mjs +140 -0
  52. package/lib/runtime/native/launcher.mjs +106 -0
  53. package/lib/runtime/native/service.mjs +64 -8
  54. package/lib/runtime/native/store.mjs +83 -58
  55. package/lib/runtime/paths.mjs +17 -2
  56. package/lib/runtime/private-file.mjs +76 -0
  57. package/lib/runtime/private-state.mjs +546 -0
  58. package/lib/runtime/project-context.mjs +2 -1
  59. package/lib/runtime/project-path.mjs +92 -20
  60. package/lib/runtime/project-trust.mjs +79 -0
  61. package/lib/runtime/projects.mjs +9 -3
  62. package/lib/runtime/state-migration.mjs +697 -0
  63. package/lib/runtime/state.mjs +69 -27
  64. package/lib/shared/automatic-result-key.mjs +6 -0
  65. package/lib/shared/json-file.mjs +55 -6
  66. package/lib/shared/redact.mjs +2 -1
  67. package/lib/ui/help.mjs +6 -4
  68. package/lib/web/agent-defaults.mjs +110 -0
  69. package/lib/web/auto-attach.mjs +3 -2
  70. package/lib/web/browser/contracts.d.ts +10 -0
  71. package/lib/web/browser/core.mjs +1997 -0
  72. package/lib/web/browser/project-data.mjs +158 -0
  73. package/lib/web/browser/session-sync.mjs +82 -0
  74. package/lib/web/browser-assets.mjs +19 -0
  75. package/lib/web/buffer-gc-runtime.mjs +2 -2
  76. package/lib/web/codex-app-server.mjs +111 -37
  77. package/lib/web/codex-sessions.mjs +61 -12
  78. package/lib/web/context-files.mjs +82 -0
  79. package/lib/web/cookie-auth.mjs +51 -3
  80. package/lib/web/external-sessions.mjs +76 -9
  81. package/lib/web/http-routes.mjs +238 -25
  82. package/lib/web/http.mjs +50 -4
  83. package/lib/web/liveness-reaper.mjs +2 -2
  84. package/lib/web/native-sessions.mjs +323 -14
  85. package/lib/web/project-contexts.mjs +236 -44
  86. package/lib/web/project-file-write.mjs +249 -0
  87. package/lib/web/project-files.mjs +268 -0
  88. package/lib/web/pty-sessions.mjs +11 -3
  89. package/lib/web/runtime-main.mjs +132 -80
  90. package/lib/web/session-serialize.mjs +87 -8
  91. package/lib/web/session-state-sync.mjs +98 -0
  92. package/lib/web/shutdown-diagnostics.mjs +38 -0
  93. package/lib/web/shutdown-response.mjs +21 -0
  94. package/lib/web/shutdown-state.mjs +91 -0
  95. package/lib/web/startup.mjs +103 -35
  96. package/lib/web/task-results.mjs +19 -19
  97. package/lib/web/tls.mjs +84 -33
  98. package/lib/web/tmux-sessions.mjs +73 -9
  99. package/lib/web/tmux-stream.mjs +36 -8
  100. package/lib/web/ui-agent-defaults.mjs +149 -0
  101. package/lib/web/ui-agent-start.mjs +330 -0
  102. package/lib/web/ui-codex-account.mjs +81 -0
  103. package/lib/web/ui-codex.mjs +105 -85
  104. package/lib/web/ui-command-palette.mjs +21 -5
  105. package/lib/web/ui-file-writes.mjs +220 -0
  106. package/lib/web/ui-files.mjs +231 -0
  107. package/lib/web/ui-handoff.mjs +10 -2
  108. package/lib/web/ui-history.mjs +175 -105
  109. package/lib/web/ui-i18n.mjs +280 -8
  110. package/lib/web/ui-interactions.mjs +107 -6
  111. package/lib/web/ui-native-timeline.mjs +326 -0
  112. package/lib/web/ui-native.mjs +93 -43
  113. package/lib/web/ui-review.mjs +5 -3
  114. package/lib/web/ui-safe-markdown.mjs +79 -0
  115. package/lib/web/ui-session-tools.mjs +224 -0
  116. package/lib/web/ui-template.mjs +29 -1968
  117. package/lib/web/ui-terminal-find.mjs +5 -3
  118. package/lib/web/ui-workbench.mjs +5 -3
  119. package/lib/web/ui-workspace.mjs +3 -0
  120. package/package.json +8 -2
  121. package/scripts/benchmark-session-sync.mjs +53 -0
  122. package/scripts/codex-protocol.mjs +88 -0
  123. package/scripts/dsh-approval-layers-acceptance.mjs +172 -0
  124. package/scripts/dsh-package-provenance.mjs +118 -0
  125. package/scripts/mcp-form-live-fixture.mjs +81 -0
  126. package/scripts/native-bounded-live-acceptance.mjs +193 -0
  127. package/scripts/native-fixture-root.mjs +49 -0
  128. package/scripts/native-installed-acceptance.mjs +611 -0
  129. package/scripts/native-stability-checks.mjs +40 -0
  130. package/scripts/regression-cookie-expiry.mjs +70 -0
  131. package/scripts/regression-http-transport.mjs +9 -0
  132. package/scripts/regression-terminal-marker.mjs +95 -0
  133. package/scripts/regression.mjs +481 -219
  134. package/scripts/shim-reentry-probe.mjs +105 -0
  135. package/scripts/web-acceptance-diagnostics.mjs +90 -0
  136. package/scripts/web-codex-account-acceptance.mjs +178 -0
  137. package/scripts/web-file-preview-checks.mjs +302 -0
  138. package/scripts/web-mcp-form-acceptance.mjs +134 -0
  139. package/scripts/web-mcp-form-live-acceptance.mjs +368 -0
  140. package/scripts/web-native-handoff-checks.mjs +128 -0
  141. package/scripts/web-native-interaction-acceptance.mjs +219 -18
  142. package/scripts/web-native-interaction-diagnostics.mjs +80 -0
  143. package/scripts/web-native-model-acceptance.mjs +22 -7
  144. package/scripts/web-session-tools-checks.mjs +112 -0
  145. package/scripts/web-workbench-acceptance.mjs +435 -0
  146. package/scripts/web-workbench-installed-acceptance.mjs +122 -0
@@ -1,6 +1,89 @@
1
1
  // Shared interface copy for the console and its sign-in page.
2
2
  const coreTranslations = {
3
3
  en: {
4
+ 'files.discardChanged': 'The draft changed after confirmation and has been retained. Inspect the saved content before deciding again.',
5
+ 'files.mixedNewlines': 'This file has mixed line endings. Saving uses the editor’s LF line endings.',
6
+ "files.upload": "Upload file",
7
+ "files.chooseUpload": "Choose one file to upload",
8
+ "files.uploadName": "Target file name",
9
+ "files.uploadHelp": "One file, up to 10 MiB. Existing files will not be replaced.",
10
+ "files.checkUpload": "Check target file",
11
+ "files.uploadTarget": "Upload target:",
12
+ "files.edit": "Edit text",
13
+ "files.resumeDraft": "Open retained draft",
14
+ "files.editText": "File text",
15
+ "files.save": "Save file",
16
+ "files.keepDraft": "Back to preview",
17
+ "files.verifySave": "Read saved file",
18
+ "files.discardEdit": "Discard draft and reload",
19
+ "files.serverCopy": "Current saved content",
20
+ "files.draftHelp": "Drafts stay in this page when you switch files or close this window. Reloading the page can discard them.",
21
+ "files.editReady": "Edit the text, then save to update this project file.",
22
+ "files.unsaved": "Unsaved draft retained in this page.",
23
+ "files.textTooLarge": "Text must fit within 1 MiB of UTF-8 data.",
24
+ "files.saving": "Saving file…",
25
+ "files.saved": "File saved.",
26
+ "files.savedWithDraft": "Submitted text was saved. Your newer edits remain a draft.",
27
+ "files.editConflict": "The file changed or could not be replaced. Your draft is retained. Read the saved file before deciding whether to discard the draft.",
28
+ "files.saveUncertain": "The save result is unknown. Your draft is retained. Read the saved file to check before saving again.",
29
+ "files.saveFailed": "File was not saved. Your draft is retained.",
30
+ "files.savedVerified": "The saved file matches the submitted text.",
31
+ "files.readbackDifferent": "The saved file differs from the submitted text. Your draft is retained; inspect the saved content below. To edit the latest version, explicitly discard and reload.",
32
+ "files.discardConfirm": "Discard this draft and read the current saved file? Any unconfirmed write may already have changed the file.",
33
+ "files.badUploadName": "Enter a file name without directory separators or control characters.",
34
+ "files.uploadTooLarge": "Choose a single file no larger than 10 MiB.",
35
+ "files.uploading": "Uploading file…",
36
+ "files.uploadReady": "Upload was not started. Reopen its target directory to try again.",
37
+ "files.uploaded": "File uploaded.",
38
+ "files.uploadUncertain": "Upload result is unknown. Check the target file before retrying. Another upload will not be sent automatically.",
39
+ "files.uploadExists": "The target already exists. Choose another name; existing files are never replaced.",
40
+ "files.uploadFailed": "Upload failed.",
41
+ "files.uploadVerified": "The target size and SHA-256 match the selected file.",
42
+ "files.uploadDifferent": "The target has different content. It will not be replaced. Check the project file before choosing another target.",
43
+ "files.uploadAbsent": "The target was not found. Select the file if needed, then explicitly upload again.",
44
+ "agentDefaults.title": "Project Agent defaults",
45
+ "agentDefaults.help": "Saved for this project and used for new background Agents. Existing Agents, history restores, accounts and API keys are unchanged.",
46
+ "agentDefaults.provider": "Default Agent",
47
+ "agentDefaults.cwd": "Project relative working directory",
48
+ "agentDefaults.fieldsHelp": "Use . for the project directory. An empty model uses the provider default.",
49
+ "agentDefaults.reload": "Reload saved values",
50
+ "agentDefaults.reset": "Restore initial values",
51
+ "agentDefaults.save": "Save project defaults",
52
+ "agentDefaults.ready": "Project values are saved on the server. Changes require Save.",
53
+ "agentDefaults.loading": "Reading project defaults…",
54
+ "agentDefaults.projectLoading": "Waiting for the selected project…",
55
+ "agentDefaults.unsaved": "Unsaved project changes.",
56
+ "agentDefaults.saving": "Saving project defaults…",
57
+ "agentDefaults.saved": "Project defaults saved. They apply to the next new background Agent.",
58
+ "agentDefaults.savedWithEdits": "Submitted values were saved. Your newer edits are still unsaved.",
59
+ "agentDefaults.resetDraft": "Initial values are in the form. Save to apply them to this project.",
60
+ "agentDefaults.conflict": "Another client saved newer values. Your edits are retained. Reload saved values to discard these edits and read the latest revision.",
61
+ "agentDefaults.uncertain": "The save result is unknown. Your edits are retained. Reload saved values before saving again.",
62
+ "agentDefaults.loadFailed": "Could not read project defaults. Reload to try again.",
63
+ "agentDefaults.saveFailed": "Project defaults were not saved. Your edits are retained.",
64
+ "agent.defaultsLoading": "Reading this project’s Agent defaults…",
65
+ "agent.defaultsFailed": "Project defaults could not be read. The visible fields will be used; an empty model uses the provider default.",
66
+ "agent.defaultsRetry": "Reload defaults for unedited fields",
67
+ 'files.button': 'Files',
68
+ 'files.title': 'Project files',
69
+ 'files.readOnly': 'Preview, upload and edit project files. File contents are not sent to an Agent.',
70
+ 'files.path': 'Project relative directory',
71
+ 'files.go': 'Go',
72
+ 'files.up': 'Parent directory',
73
+ 'files.back': 'Back to files',
74
+ 'files.source': 'Source',
75
+ 'files.rendered': 'Preview',
76
+ 'files.download': 'Download PDF',
77
+ 'files.truncated': 'Only the first 1 MiB is shown. Truncated HTML is available as source only.',
78
+ 'files.htmlLimit': 'Static preview: scripts, network resources and navigation are disabled.',
79
+ 'files.pdfHelp': 'If your browser cannot display this PDF, download it to view locally.',
80
+ 'files.staticHtml': 'Static HTML preview',
81
+ 'files.unsupported': 'This file type or size cannot be previewed.',
82
+ 'files.loading': 'Loading…',
83
+ 'files.treeTruncated': 'Only the first entries are shown. Open a subdirectory or enter its path.',
84
+ 'files.empty': 'This directory is empty.',
85
+ 'files.select': 'Choose a file to preview.',
86
+ 'files.mainWindowRequired': 'Open project files from the main window for this project.',
4
87
  'workspace.split': 'Split',
5
88
  'workspace.panes': 'Session panes',
6
89
  'workspace.primary': 'Main',
@@ -53,7 +136,7 @@ const coreTranslations = {
53
136
  projectPath: 'Project path',
54
137
  projectPathPlaceholder: '/path/to/project',
55
138
  registerProject: 'Register Project',
56
- newSession: 'New session',
139
+ newSession: 'New Agent',
57
140
  dshLaunchHelp: 'For DeepSeek Harness, run hcc dsh web in the project. Its agents appear under Detected.',
58
141
  dshCoordinationHelp: 'Tasks, locks, and messages are shared with hello-cc. Chat, cancel, and resume this agent in the dsh Web UI.',
59
142
  start: 'Start',
@@ -76,8 +159,28 @@ const coreTranslations = {
76
159
  clearSessionFilters: 'Clear search and filters',
77
160
  sessionDetails: 'Session details',
78
161
  projectDialogHelp: 'Register a local project directory to view its sessions and shared state.',
79
- startDialogHelp: 'Choose a CLI and start a new session or resume existing history.',
162
+ startDialogHelp: 'Choose an agent and its working directory. It will keep running when this page closes.',
80
163
  startProject: 'Target project',
164
+ 'agent.provider': 'Agent',
165
+ 'agent.cwd': 'Working directory',
166
+ 'agent.cwdHelp': 'Use this project directory or a subdirectory within it.',
167
+ 'agent.model': 'Model (optional)',
168
+ 'agent.modelPlaceholder': 'Provider default',
169
+ 'agent.name': 'Name (optional)',
170
+ 'agent.nameHelp': 'Leave blank for an automatic name, or use letters, numbers, dots, hyphens and underscores. Start with a letter or number.',
171
+ 'agent.advanced': 'Advanced options',
172
+ 'agent.interface': 'Interface',
173
+ 'agent.background': 'Background Agent',
174
+ 'agent.configHelp': "Terminal CLI and App Server use the provider's existing model configuration.",
175
+ 'agent.badName': 'Use 1–128 letters, numbers, dots, hyphens or underscores, starting with a letter or number. The name all is reserved.',
176
+ 'agent.resumeRequired': 'Choose a saved session or enter its session ID.',
177
+ 'agent.openCreated': 'Open created Agent',
178
+ 'agent.createdRefreshFailed': 'Agent {name} was created, but the session list could not be refreshed. Retry opening it below; this will not create another Agent.',
179
+ 'agent.checkCreated': 'Check original Agent',
180
+ 'agent.creationUnconfirmed': 'Creation of Agent {name} is unconfirmed. Check the original Agent below; this will not create another worker.',
181
+ 'agent.awaitingDiscovery': 'The original Agent is not visible yet. Keep checking the list or inspect native status before starting another worker.',
182
+ 'agent.forgetRecovery': 'Reviewed; forget recovery record',
183
+ 'agent.forgetRecoveryConfirm': 'Have you checked the status of Agent {name}? Forgetting this record will let you create another Agent. It does not stop the original Agent.',
81
184
  createFirstSession: 'Start a session',
82
185
  sessionListSummary: '{count} matching sessions',
83
186
  view: 'View',
@@ -189,6 +292,89 @@ const coreTranslations = {
189
292
  detectedPeer: 'detected peer'
190
293
  },
191
294
  zh: {
295
+ 'files.discardChanged': '确认之后草稿发生变化,已保留新内容;请核对已保存文件后重新决定。',
296
+ 'files.mixedNewlines': '此文件使用混合换行符,保存时将统一为编辑器的 LF 换行。',
297
+ "files.upload": "上传文件",
298
+ "files.chooseUpload": "选择一个要上传的文件",
299
+ "files.uploadName": "目标文件名",
300
+ "files.uploadHelp": "单个文件,最大 10 MiB;不会覆盖已有文件。",
301
+ "files.checkUpload": "核对目标文件",
302
+ "files.uploadTarget": "上传目标:",
303
+ "files.edit": "编辑文本",
304
+ "files.resumeDraft": "打开保留草稿",
305
+ "files.editText": "文件文本",
306
+ "files.save": "保存文件",
307
+ "files.keepDraft": "返回预览",
308
+ "files.verifySave": "读取已保存文件",
309
+ "files.discardEdit": "放弃草稿并重载",
310
+ "files.serverCopy": "当前已保存内容",
311
+ "files.draftHelp": "切换文件或关闭此窗口时,草稿保留在当前页面;重新加载页面可能丢失草稿。",
312
+ "files.editReady": "编辑文本后点击保存,才会更新此项目文件。",
313
+ "files.unsaved": "未保存草稿已保留在当前页面。",
314
+ "files.textTooLarge": "文本的 UTF-8 编码大小不能超过 1 MiB。",
315
+ "files.saving": "正在保存文件…",
316
+ "files.saved": "文件已保存。",
317
+ "files.savedWithDraft": "提交的文本已保存,你之后的编辑仍是草稿。",
318
+ "files.editConflict": "文件已变化或无法替换,已保留草稿。请先读取已保存文件,再决定是否放弃草稿。",
319
+ "files.saveUncertain": "尚未确认保存结果,已保留草稿。再次保存前请先读取已保存文件核对。",
320
+ "files.saveFailed": "文件未保存,已保留草稿。",
321
+ "files.savedVerified": "已保存文件与提交的文本一致。",
322
+ "files.readbackDifferent": "已保存文件与提交的文本不同,已保留草稿;可查看下方已保存内容。若要编辑最新版本,请明确放弃草稿并重载。",
323
+ "files.discardConfirm": "放弃此草稿并读取当前已保存文件?尚未确认的写入可能已改变文件。",
324
+ "files.badUploadName": "请输入单一文件名,不含目录分隔符或控制字符。",
325
+ "files.uploadTooLarge": "请选择不超过 10 MiB 的单个文件。",
326
+ "files.uploading": "正在上传文件…",
327
+ "files.uploadReady": "尚未开始上传,请重新打开目标目录后重试。",
328
+ "files.uploaded": "文件已上传。",
329
+ "files.uploadUncertain": "尚未确认上传结果,请先核对目标文件再重试;不会自动重复上传。",
330
+ "files.uploadExists": "目标文件已存在,请选择其他文件名;不会覆盖已有文件。",
331
+ "files.uploadFailed": "上传失败。",
332
+ "files.uploadVerified": "目标文件大小与 SHA-256 均与所选文件一致。",
333
+ "files.uploadDifferent": "目标内容不同,不会覆盖;请先检查项目文件,再选择其他目标。",
334
+ "files.uploadAbsent": "未找到目标文件。如有需要,请重新选择文件后手动上传。",
335
+ "agentDefaults.title": "项目 Agent 默认配置",
336
+ "agentDefaults.help": "保存到当前项目,用于新建后台 Agent;不改变已有 Agent、历史恢复、账号或 API key。",
337
+ "agentDefaults.provider": "默认 Agent",
338
+ "agentDefaults.cwd": "项目相对工作目录",
339
+ "agentDefaults.fieldsHelp": "使用 . 表示项目根目录;模型留空表示使用提供商默认值。",
340
+ "agentDefaults.reload": "重新载入已保存值",
341
+ "agentDefaults.reset": "恢复初始值",
342
+ "agentDefaults.save": "保存项目默认配置",
343
+ "agentDefaults.ready": "配置保存在服务端;修改后需要点击保存。",
344
+ "agentDefaults.loading": "正在读取项目默认配置…",
345
+ "agentDefaults.projectLoading": "正在等待当前项目…",
346
+ "agentDefaults.unsaved": "项目配置有尚未保存的修改。",
347
+ "agentDefaults.saving": "正在保存项目默认配置…",
348
+ "agentDefaults.saved": "项目默认配置已保存,下次新建后台 Agent 时生效。",
349
+ "agentDefaults.savedWithEdits": "提交的配置已保存;你之后编辑的内容仍未保存。",
350
+ "agentDefaults.resetDraft": "表单已填入初始值,保存后才会应用到当前项目。",
351
+ "agentDefaults.conflict": "其他客户端已保存更新的配置。已保留你的修改;重新载入已保存值将丢弃当前修改并读取最新版本。",
352
+ "agentDefaults.uncertain": "尚未确认保存结果,已保留你的修改。请先重新载入已保存值,再进行保存。",
353
+ "agentDefaults.loadFailed": "无法读取项目默认配置,请重新载入重试。",
354
+ "agentDefaults.saveFailed": "项目默认配置未保存,已保留你的修改。",
355
+ "agent.defaultsLoading": "正在读取此项目的 Agent 默认配置…",
356
+ "agent.defaultsFailed": "未能读取项目默认配置,将使用表单中可见的值;模型为空时使用提供商默认值。",
357
+ "agent.defaultsRetry": "重新载入未编辑字段的默认值",
358
+ 'files.button': '文件',
359
+ 'files.title': '项目文件',
360
+ 'files.readOnly': '查看、上传和编辑项目文件,不会将文件内容发送给 Agent。',
361
+ 'files.path': '项目相对目录',
362
+ 'files.go': '前往',
363
+ 'files.up': '上一级',
364
+ 'files.back': '返回文件列表',
365
+ 'files.source': '源码',
366
+ 'files.rendered': '预览',
367
+ 'files.download': '下载 PDF',
368
+ 'files.truncated': '仅显示前 1 MiB 内容;截断的 HTML 仅提供源码。',
369
+ 'files.htmlLimit': '静态预览:已禁用脚本、网络资源和页面跳转。',
370
+ 'files.pdfHelp': '如果浏览器无法显示此 PDF,可下载后在本地查看。',
371
+ 'files.staticHtml': 'HTML 静态预览',
372
+ 'files.unsupported': '此文件类型或大小暂不支持预览。',
373
+ 'files.loading': '正在加载…',
374
+ 'files.treeTruncated': '仅显示部分条目,可打开子目录或输入其路径。',
375
+ 'files.empty': '此目录为空。',
376
+ 'files.select': '选择文件以预览内容。',
377
+ 'files.mainWindowRequired': '请在此项目的主窗口中打开项目文件。',
192
378
  'workspace.split': '分屏',
193
379
  'workspace.panes': '会话窗格',
194
380
  'workspace.primary': '主窗格',
@@ -241,7 +427,7 @@ const coreTranslations = {
241
427
  projectPath: '项目路径',
242
428
  projectPathPlaceholder: '/项目/路径',
243
429
  registerProject: '注册项目',
244
- newSession: '新会话',
430
+ newSession: '新建 Agent',
245
431
  dshLaunchHelp: 'DeepSeek Harness 请在项目内运行 hcc dsh web,其 Agent 会显示在检测会话中。',
246
432
  dshCoordinationHelp: 'hello-cc 共享任务、锁和消息。对话、取消及恢复该 Agent 请在 dsh Web 界面操作。',
247
433
  start: '启动',
@@ -264,8 +450,28 @@ const coreTranslations = {
264
450
  clearSessionFilters: '清除搜索与筛选',
265
451
  sessionDetails: '会话详情',
266
452
  projectDialogHelp: '注册本地项目目录,查看项目会话和共享状态。',
267
- startDialogHelp: '选择 CLI,启动新会话或恢复已有历史。',
453
+ startDialogHelp: '选择 Agent 和工作目录。关闭页面后,Agent 仍会继续运行。',
268
454
  startProject: '目标项目',
455
+ 'agent.provider': 'Agent',
456
+ 'agent.cwd': '工作目录',
457
+ 'agent.cwdHelp': '使用当前项目目录或项目内的子目录。',
458
+ 'agent.model': '模型(可选)',
459
+ 'agent.modelPlaceholder': '沿用 provider 默认模型',
460
+ 'agent.name': '名称(可选)',
461
+ 'agent.nameHelp': '留空自动生成;可使用英文字母、数字、点、连字符和下划线,须以字母或数字开头。',
462
+ 'agent.advanced': '高级选项',
463
+ 'agent.interface': '接入方式',
464
+ 'agent.background': '后台 Agent',
465
+ 'agent.configHelp': 'Terminal CLI 和 App Server 沿用各 provider 已有的模型配置。',
466
+ 'agent.badName': '名称须为 1–128 个英文字母、数字、点、连字符或下划线,并以字母或数字开头;all 为保留名称。',
467
+ 'agent.resumeRequired': '请选择已有会话或填写会话 ID。',
468
+ 'agent.openCreated': '打开已创建的 Agent',
469
+ 'agent.createdRefreshFailed': 'Agent {name} 已创建,但会话列表刷新失败。可在下方重试打开,不会再次创建 Agent。',
470
+ 'agent.checkCreated': '核查原 Agent',
471
+ 'agent.creationUnconfirmed': 'Agent {name} 的创建结果尚未确认。请在下方核查原 Agent,不会再次创建 worker。',
472
+ 'agent.awaitingDiscovery': '暂未发现原 Agent。请继续核查会话列表,或检查 native status 后再新建 worker。',
473
+ 'agent.forgetRecovery': '已核查,放弃恢复记录',
474
+ 'agent.forgetRecoveryConfirm': '确认已检查 Agent {name} 的状态?清除待核查记录后可以重新创建;此操作不会停止原 Agent。',
269
475
  createFirstSession: '启动一个会话',
270
476
  sessionListSummary: '{count} 个匹配会话',
271
477
  view: '视图',
@@ -381,13 +587,46 @@ const coreTranslations = {
381
587
 
382
588
  export const UI_TRANSLATIONS = Object.freeze({
383
589
  en: Object.freeze({
384
- 'history.title': 'Codex history',
590
+ 'history.title': 'History',
591
+ "history.source": "Source",
592
+ "history.projectLoading": "Waiting for the selected project to load…",
593
+ "history.native": "HCC retained history",
594
+ "history.codex": "Codex saved threads",
595
+ "history.allProviders": "All providers",
596
+ "history.nativeHelp": "Browse workers and events retained by HCC in this project. This does not search provider accounts or their complete history.",
597
+ "history.nativeConfirm": "I have checked that the original executor is stopped. Resume keeps this worker name and saved provider session, using a new executor.",
598
+ "history.nativeEmpty": "No matching workers recorded by HCC in this project.",
599
+ "history.previousEvents": "Previous events",
600
+ "history.nextEvents": "Next events",
601
+ "history.view": "View",
602
+ "history.output": "Recorded output",
603
+ "history.trace": "Event trace",
604
+ "history.receipts": "Recent delivery receipts",
605
+ "history.retention": "HCC retains approximately 2,000 events across all workers in this project. This page contains at most 100 events, not a full transcript; submitted prompts may be absent.",
606
+ "history.eventPage": "Page {page} · {count} events",
607
+ "history.noEvents": "No recorded output on this page. Event trace includes other retained events.",
608
+ "history.runtimeOffline": "The native service is unavailable. Showing retained history.",
609
+ "history.listTruncated": "Only the first 100 recorded workers are shown.",
610
+ "history.resumeUnavailable": "This recorded worker is not ready to resume.",
611
+ "history.nativeActive": "The original executor is running. Stop it and refresh this history before resuming.",
612
+ "history.resumeReason.active": "The original executor is still owned or running. Stop it and refresh this history.",
613
+ "history.resumeReason.not_closed": "The previous executor has not reached a confirmed closed state. Check its status before resuming.",
614
+ "history.resumeReason.no_session": "No saved provider session is available to resume.",
615
+ "history.resumeReason.owner_changed": "The recorded executor or provider session changed. Refresh and select the worker again.",
616
+ "history.resumeReason.provider_unsupported": "This provider does not support restoring this recorded worker.",
617
+ "history.resumeReason.capability_unknown": "Resume support was not confirmed for this worker.",
618
+ "history.resumeReason.cwd_unavailable": "The recorded working directory is unavailable.",
619
+ "agent.resumeTitle": "Resume recorded Agent",
620
+ "agent.resumeInfo": "Resume {name} using saved provider session {session}. The worker name and working directory are preserved.",
621
+ "agent.resumeUnconfirmed": "The result of resuming {name} is unconfirmed. Check for its new executor; this action will not send another resume request.",
622
+ "agent.resumeRefreshFailed": "The resume request for {name} succeeded, but opening its new executor is incomplete. Check the same operation again.",
385
623
  'history.help': 'Browse saved history, then explicitly resume or fork it in a separate executor.',
386
624
  'history.more': 'Load more',
387
625
  'history.confirm': 'I checked the original executor. Opening starts a separate executor; resuming the same thread requires stopping the original.',
388
626
  'history.resume': 'Resume in a new executor',
389
627
  'history.fork': 'Fork into a new executor',
390
628
  'history.empty': 'No saved Codex threads in this project.',
629
+ 'history.unverified': '{count} Codex history entries in the loaded results are hidden because their original directory identity cannot be verified. They cannot be read, resumed, or forked here until manually reviewed and associated.',
391
630
  'history.loading': 'Reading saved history…',
392
631
  'history.opening': 'Opening the selected history…',
393
632
  'history.noTurns': 'This saved thread has no recorded turns.',
@@ -468,7 +707,7 @@ export const UI_TRANSLATIONS = Object.freeze({
468
707
  'native.state.disconnected': 'Disconnected',
469
708
  'native.state.error': 'Error',
470
709
  'native.state.uncertain': 'Uncertain',
471
- 'aux.history': 'Codex history',
710
+ 'aux.history': 'History',
472
711
  'aux.review': 'Review results',
473
712
  'handoff.resumeConfirm': 'I have stopped the original executor. Resuming history starts a separate executor.',
474
713
  'handoff.transport': 'Codex interface',
@@ -610,13 +849,46 @@ export const UI_TRANSLATIONS = Object.freeze({
610
849
  'error.server_error': 'The server could not complete this request.'
611
850
  }),
612
851
  zh: Object.freeze({
613
- 'history.title': 'Codex 历史',
852
+ 'history.title': '历史',
853
+ "history.source": "历史来源",
854
+ "history.projectLoading": "正在等待当前项目加载…",
855
+ "history.native": "HCC 保留历史",
856
+ "history.codex": "Codex 已保存会话",
857
+ "history.allProviders": "所有提供商",
858
+ "history.nativeHelp": "浏览 HCC 在此项目中保留的 worker 与事件;不会搜索提供商账号或其完整历史。",
859
+ "history.nativeConfirm": "我已核对原执行器已停止;将保留此 worker 名称与提供商会话,通过新的执行器恢复。",
860
+ "history.nativeEmpty": "此项目暂无匹配的 HCC worker 记录。",
861
+ "history.previousEvents": "上一页事件",
862
+ "history.nextEvents": "下一页事件",
863
+ "history.view": "显示",
864
+ "history.output": "已记录输出",
865
+ "history.trace": "事件轨迹",
866
+ "history.receipts": "近期投递回执",
867
+ "history.retention": "HCC 在此项目全部 worker 间合计保留约 2,000 条事件,每页最多 100 条;这不是完整会话,可能不含提交的原文。",
868
+ "history.eventPage": "第 {page} 页 · {count} 条事件",
869
+ "history.noEvents": "此页暂无已记录的输出,可切换事件轨迹查看其他保留事件。",
870
+ "history.runtimeOffline": "Native 服务当前不可用,正在显示保留历史。",
871
+ "history.listTruncated": "仅显示前 100 条 worker 记录。",
872
+ "history.resumeUnavailable": "此 worker 记录暂时不具备恢复条件。",
873
+ "history.nativeActive": "原执行器仍在运行,请停止后刷新此历史再恢复。",
874
+ "history.resumeReason.active": "原执行器仍被持有或正在运行,请停止后刷新历史。",
875
+ "history.resumeReason.not_closed": "尚未确认原执行器已关闭,请先核对其状态。",
876
+ "history.resumeReason.no_session": "没有可恢复的已保存提供商会话。",
877
+ "history.resumeReason.owner_changed": "记录的执行器或提供商会话已变化,请刷新并重新选择。",
878
+ "history.resumeReason.provider_unsupported": "此提供商不支持恢复该 worker 记录。",
879
+ "history.resumeReason.capability_unknown": "尚未确认此 worker 支持恢复。",
880
+ "history.resumeReason.cwd_unavailable": "记录中的工作目录已不可用。",
881
+ "agent.resumeTitle": "恢复已记录的 Agent",
882
+ "agent.resumeInfo": "使用已保存的提供商会话 {session} 恢复 {name},保留 worker 名称和工作目录。",
883
+ "agent.resumeUnconfirmed": "尚未确认 {name} 的恢复结果;请检查新的执行器,此操作不会再次发送恢复请求。",
884
+ "agent.resumeRefreshFailed": "{name} 的恢复请求已成功,但尚未完成打开新的执行器。请再次检查同一操作。",
614
885
  'history.help': '浏览保存的历史,并显式选择恢复或分叉到独立执行器。',
615
886
  'history.more': '加载更多',
616
887
  'history.confirm': '我已核对原执行器状态,明确在独立执行器中打开;恢复同一会话前需停止原执行器。',
617
888
  'history.resume': '在独立执行器中恢复',
618
889
  'history.fork': '分叉到独立执行器',
619
890
  'history.empty': '此项目暂无保存的 Codex 会话。',
891
+ 'history.unverified': '已加载结果中有 {count} 条 Codex 历史缺少可信的原始目录身份,暂不显示内容,也不能在此读取、恢复或分叉;需经人工核验后关联。',
620
892
  'history.loading': '正在读取保存的历史…',
621
893
  'history.opening': '正在打开所选历史…',
622
894
  'history.noTurns': '此会话暂无已记录的轮次。',
@@ -697,7 +969,7 @@ export const UI_TRANSLATIONS = Object.freeze({
697
969
  'native.state.disconnected': '已断开',
698
970
  'native.state.error': '错误',
699
971
  'native.state.uncertain': '状态不确定',
700
- 'aux.history': 'Codex 历史',
972
+ 'aux.history': '历史',
701
973
  'aux.review': '结果审阅',
702
974
  'handoff.resumeConfirm': '我已停止原执行器;恢复历史会启动一个独立执行器。',
703
975
  'handoff.transport': 'Codex 接入方式',
@@ -1,7 +1,13 @@
1
+ import { createMcpFormValidator } from '../integrations/mcp-elicitation.mjs';
2
+ import { createMcpUrlValidator } from '../integrations/mcp-url-elicitation.mjs';
3
+
1
4
  // Shared form controls for Web-owned and independent native executors.
2
- export function interactionPanelScript() {
3
- return String.raw`(() => {
5
+ export function interactionPanelScript() { return '(' + installInteractionPanel.toString() + ')(' + createMcpFormValidator.toString() + ',' + createMcpUrlValidator.toString() + ');'; }
6
+
7
+ export function installInteractionPanel(createForms = createMcpFormValidator, createUrls = createMcpUrlValidator) {
8
+ (() => {
4
9
  if (window.hccInteractions) return;
10
+ const mcpForms = createForms(), mcpUrls = createUrls();
5
11
  const drafts = new Map(), expanded = new Map();
6
12
  let focus = null;
7
13
  function remember(container) {
@@ -12,7 +18,7 @@ export function interactionPanelScript() {
12
18
  });
13
19
  container.querySelectorAll('[data-interaction-details]').forEach(detail => expanded.set(detail.dataset.interactionDetails, detail.open));
14
20
  while (expanded.size > 256) expanded.delete(expanded.keys().next().value);
15
- while (drafts.size > 256) drafts.delete(drafts.keys().next().value);
21
+ while (drafts.size > 8192) drafts.delete(drafts.keys().next().value);
16
22
  }
17
23
  function restore(container) {
18
24
  const live = new Set();
@@ -31,17 +37,56 @@ export function interactionPanelScript() {
31
37
  });
32
38
  for (const key of expanded.keys()) if (!detailKeys.has(key)) expanded.delete(key);
33
39
  }
40
+ function requiresInput(request) {
41
+ return request.kind === 'userInput' || (request.kind === 'mcp' && request.params?.mode === 'form' && Object.keys(request.params.requestedSchema?.properties || {}).length > 0);
42
+ }
34
43
  function approvalBlocked(request) {
44
+ if (request.kind === 'mcp') { try { (isUrl(request) ? mcpUrls : mcpForms).describe(request.params); } catch (_) { return true; } }
35
45
  const tool = request.params?.toolCall;
36
46
  return Boolean(request.truncated || (request.method === 'session/request_permission' &&
37
47
  (!tool || tool.contextPending || tool.contextTruncated || !Object.hasOwn(tool, 'rawInput') ||
38
48
  !request.params.options?.some(option => option.kind === 'allow_once'))));
39
49
  }
50
+ function isUrl(request) { return request.kind === 'mcp' && request.params?.mode === 'url'; }
51
+ function acceptLabel(request, text) {
52
+ return isUrl(request) ? text('Open authorization page', '打开授权页面') : requiresInput(request) ? text('Send answers', '提交答复')
53
+ : request.kind === 'permissions' ? text('Grant selected permissions', '授予所选权限') : text('Approve once', '批准本次');
54
+ }
55
+ function previewParams(request) {
56
+ if (!isUrl(request)) return request.params || {};
57
+ let destination;
58
+ try { destination = mcpUrls.describe(request.params).origin; } catch (_) {}
59
+ return { mode: 'url', serverName: request.params?.serverName, destination };
60
+ }
61
+ function prepareUrl(request, decision, text) {
62
+ if (!isUrl(request) || decision !== 'accept') return null;
63
+ if (approvalBlocked(request)) throw new Error(text('This authorization request cannot be opened.', '当前授权请求不可打开。'));
64
+ const { href } = mcpUrls.describe(request.params);
65
+ // Reserve a tab within the explicit click; navigate only after the owning
66
+ // executor accepts the exact request. A stale lease leaves no auth page.
67
+ const page = window.open('about:blank', '_blank');
68
+ if (!page) throw new Error(text('Allow pop-ups for this site, then try again. The request has not been accepted.', '请允许本站弹出新标签页后重试;请求尚未接受。'));
69
+ let navigated = false;
70
+ try {
71
+ page.opener = null;
72
+ page.document.write('<meta name="referrer" content="no-referrer"><title>Authorization / 授权</title><p>Opening authorization page / 正在打开授权页面…</p>');
73
+ page.document.close();
74
+ } catch (error) { page.close(); throw error; }
75
+ return {
76
+ complete() {
77
+ if (page.closed) throw new Error(text('The request was accepted but the authorization tab was closed. Check the task result or request authorization again.', '请求已接受,但授权标签页已关闭。请查看任务结果或重新发起授权。'));
78
+ page.location.replace(href); navigated = true;
79
+ return text('Authorization page opened. Complete the steps there, then return to follow this task. Opening the page does not confirm authorization.', '授权页面已打开。请在该页面完成操作,再返回查看任务结果;打开页面不表示授权成功。');
80
+ },
81
+ cancel() { if (!navigated && !page.closed) page.close(); }
82
+ };
83
+ }
40
84
  function preview(request, key, esc, text) {
41
85
  const params = request.params || {}, tool = params.toolCall || {}, input = params.input || tool.rawInput || {};
42
86
  const rows = [], row = (label, value) => { if (typeof value === 'string' && value) rows.push('<dt>' + esc(label) + '</dt><dd>' + esc(value) + '</dd>'); };
43
87
  const name = params.tool || tool.title;
44
88
  row(text('Tool', '工具'), name);
89
+ row(text('MCP server', 'MCP 服务'), params.serverName);
45
90
  row(text('Command', '命令'), params.command || input.command);
46
91
  row(text('File', '文件'), input.file_path || input.path || params.path || params.grantRoot);
47
92
  row(text('Working directory', '工作目录'), params.cwd || input.cwd);
@@ -55,9 +100,46 @@ export function interactionPanelScript() {
55
100
  return (rows.length ? '<dl class="hcc-interaction-summary">' + rows.join('') + '</dl>' : '') + contentPreview
56
101
  + (missingContext ? '<p role="alert">' + esc(text('The operation input or one-time approval option is unavailable. Only rejection is available.', '操作输入或单次批准选项不可用,目前只能拒绝。')) + '</p>' : '')
57
102
  + (request.kind === 'approval' && !missingContext ? '<p>' + esc(text('Approval applies only to this operation. Later requests require another decision.', '仅批准本次操作,后续请求仍需单独确认。')) + '</p>' : '')
58
- + '<details class="hcc-interaction-details" data-interaction-details="' + esc(key) + '"' + (known ? '' : ' open') + '><summary>' + esc(text('Full operation details', '完整操作详情')) + '</summary><pre>' + esc(JSON.stringify(params, null, 2)) + '</pre></details>';
103
+ + '<details class="hcc-interaction-details" data-interaction-details="' + esc(key) + '"' + (known ? '' : ' open') + '><summary>' + esc(text('Full operation details', '完整操作详情')) + '</summary><pre>' + esc(JSON.stringify(previewParams(request), null, 2)) + '</pre></details>';
104
+ }
105
+ function mcpForm(request, key, esc, text) {
106
+ let fields;
107
+ try { ({ fields } = mcpForms.describe(request.params)); }
108
+ catch (_) { return '<p role="alert">' + esc(text('This MCP request mode or schema is unsupported. Decline or cancel this request.', '暂不支持此 MCP 请求模式或表单结构,请拒绝或取消请求。')) + '</p>'; }
109
+ if (!fields.length) return '';
110
+ const attr = suffix => ' data-interaction-field="' + esc(key + ':mcp:' + suffix) + '"';
111
+ const select = (i, choices, value) => '<select data-mcp-field="' + i + '"' + attr(i) + '><option value="">' + esc(text('Choose a value', '请选择')) + '</option>'
112
+ + choices.map((choice, n) => '<option value="' + n + '"' + (value === choice.value ? ' selected' : '') + '>' + esc(choice.label) + '</option>').join('') + '</select>';
113
+ return '<p>' + esc(text('Required fields are marked *. Optional fields are sent only when selected. Responses are not saved in browser storage.', '带 * 的为必填项;可选项仅在勾选填写后发送。答复不会保存到浏览器存储。')) + '</p>'
114
+ + fields.map((field, i) => {
115
+ const label = esc(field.title || field.key) + (field.required ? ' *' : '');
116
+ const optional = field.required ? '' : '<label><input type="checkbox" data-mcp-use="' + i + '"' + attr(i + ':use') + '> ' + esc(text('Provide this optional field', '填写此可选项')) + '</label>';
117
+ let control;
118
+ if (field.kind === 'enum') control = select(i, field.options, field.default);
119
+ else if (field.type === 'boolean') control = select(i, [{ value: true, label: text('Yes', '是') }, { value: false, label: text('No', '否') }], field.default);
120
+ else if (field.kind === 'multi') control = field.options.map((option, n) => '<label><input type="checkbox" data-mcp-field="' + i + '" data-mcp-option="' + n + '"' + attr(i + ':' + n)
121
+ + (field.default?.includes(option.value) ? ' checked' : '') + '> ' + esc(option.label) + '</label>').join('');
122
+ else control = '<input data-mcp-field="' + i + '"' + attr(i) + ' type="' + (['number', 'integer'].includes(field.type) ? 'number' : 'text') + '"'
123
+ + (field.type === 'integer' ? ' step="1"' : field.type === 'number' ? ' step="any"' : ' maxlength="8192"')
124
+ + (field.minimum != null ? ' min="' + esc(field.minimum) + '"' : '') + (field.maximum != null ? ' max="' + esc(field.maximum) + '"' : '')
125
+ + ' value="' + esc(field.default ?? '') + '" autocomplete="off">';
126
+ const limits = [field.format, field.minLength != null ? text('Min length: ', '最短长度:') + field.minLength : '', field.maxLength != null ? text('Max length: ', '最长长度:') + field.maxLength : '',
127
+ field.minimum != null ? text('Minimum: ', '最小值:') + field.minimum : '', field.maximum != null ? text('Maximum: ', '最大值:') + field.maximum : '',
128
+ field.minItems != null ? text('Min selections: ', '至少选择:') + field.minItems : '', field.maxItems != null ? text('Max selections: ', '最多选择:') + field.maxItems : ''].filter(Boolean).join(' · ');
129
+ return '<fieldset class="hcc-mcp-field"><legend>' + label + '</legend>' + (field.description ? '<p>' + esc(field.description) + '</p>' : '') + optional
130
+ + (field.kind === 'multi' ? control : '<label><span class="sr-only">' + label + '</span>' + control + '</label>') + (limits ? '<small>' + esc(limits) + '</small>' : '') + '</fieldset>';
131
+ }).join('');
59
132
  }
60
133
  function form(request, key, esc, text) {
134
+ if (isUrl(request)) {
135
+ let destination;
136
+ try { destination = mcpUrls.describe(request.params); }
137
+ catch (_) { return '<p role="alert">' + esc(text('This authorization URL or identity is invalid. Decline or cancel this request.', '授权链接或请求标识无效,请拒绝或取消请求。')) + '</p>'; }
138
+ return '<p class="hcc-url-destination">' + esc(text('Authorization destination: ', '授权目标:')) + '<strong>' + esc(destination.origin) + '</strong></p>'
139
+ + '<p>' + esc(text('Open this page only if you trust the MCP server and destination. Finish authorization there, then return to this task. Opening acknowledges the request; it does not confirm authorization.', '确认信任此 MCP 服务和目标后打开页面,在该页面完成授权,再返回本任务查看结果。打开仅表示接受请求,不表示授权成功。')) + '</p>'
140
+ + (destination.loopback ? '<p>' + esc(text('This address belongs to the executor computer. A browser on another device may not reach it; open this task on the executor computer to authorize.', '此地址位于执行器所在电脑。其他设备的浏览器可能无法访问,请在执行器所在电脑打开本任务完成授权。')) + '</p>' : '');
141
+ }
142
+ if (request.kind === 'mcp') return mcpForm(request, key, esc, text);
61
143
  const field = suffix => ' data-interaction-field="' + esc(key + ':' + suffix) + '"';
62
144
  if (request.kind === 'userInput') return (request.params?.questions || []).map((q, i) => {
63
145
  const label = '<label>' + esc(q.header || '') + '<p>' + esc(q.question || '') + '</p>';
@@ -80,6 +162,25 @@ export function interactionPanelScript() {
80
162
  }
81
163
  function payload(request, container, decision) {
82
164
  if (decision !== 'accept') return { decision };
165
+ if (isUrl(request)) { mcpUrls.describe(request.params); return { decision }; }
166
+ if (request.kind === 'mcp') {
167
+ const { fields } = mcpForms.describe(request.params), content = Object.create(null);
168
+ for (const [i, field] of fields.entries()) {
169
+ if (!field.required && !container?.querySelector('[data-mcp-use="' + i + '"]')?.checked) continue;
170
+ const controls = container?.querySelectorAll('[data-mcp-field="' + i + '"]');
171
+ if (!controls?.length) throw new Error('MCP form is unavailable / MCP 表单不可用');
172
+ const control = controls[0];
173
+ if (field.kind === 'multi') content[field.key] = Array.from(controls).filter(item => item.checked).map(item => field.options[Number(item.dataset.mcpOption)]?.value);
174
+ else if (field.kind === 'enum' || field.type === 'boolean') {
175
+ if (!/^\d+$/.test(control.value)) throw new Error('Choose a value / 请选择:' + field.key);
176
+ content[field.key] = field.kind === 'enum' ? field.options[Number(control.value)]?.value : control.value === '0' ? true : control.value === '1' ? false : undefined;
177
+ } else if (['number', 'integer'].includes(field.type)) {
178
+ if (!control.value.trim()) throw new Error('Enter a number / 请填写数值:' + field.key);
179
+ content[field.key] = Number(control.value);
180
+ } else content[field.key] = control.value;
181
+ }
182
+ return { decision, content: mcpForms.validate(request.params, content) };
183
+ }
83
184
  if (request.kind === 'userInput') {
84
185
  const answers = Object.create(null);
85
186
  for (const [i, q] of (request.params?.questions || []).entries()) {
@@ -107,6 +208,6 @@ export function interactionPanelScript() {
107
208
  return { decision, permissions, scope: container.querySelector('[data-scope]').value };
108
209
  }
109
210
  function forget(key) { for (const field of drafts.keys()) if (field.startsWith(key + ':')) drafts.delete(field); expanded.delete(key); }
110
- window.hccInteractions = { preview, approvalBlocked, form, payload, remember, restore, forget };
111
- })();`;
211
+ window.hccInteractions = { preview, previewParams, approvalBlocked, requiresInput, acceptLabel, prepareUrl, form, payload, remember, restore, forget };
212
+ })();
112
213
  }