@harness-mix/cli 0.1.8 → 0.1.9

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 (132) hide show
  1. package/NOTICE +12 -0
  2. package/README.md +51 -43
  3. package/docs/harness-management.md +1 -1
  4. package/docs/native-acp.md +7 -2
  5. package/docs/task-handoff-design.md +65 -0
  6. package/licenses/codex-styler-Apache-2.0.txt +193 -0
  7. package/licenses/heige-codex-skin-studio-MIT.txt +22 -0
  8. package/output/native-build/desktop-controller.mjs +2 -1
  9. package/output/native-build/renderer-extension.js +651 -9
  10. package/package.json +188 -188
  11. package/scripts/acp-capabilities-probe.cjs +1 -0
  12. package/scripts/build-native.cjs +1 -1
  13. package/scripts/codex-adapter-test.cjs +44 -0
  14. package/scripts/collaboration-recovery-test.cjs +3 -0
  15. package/scripts/e2e-codex.cjs +3 -1
  16. package/scripts/e2e-native-acp.cjs +1 -1
  17. package/scripts/error-kind-test.cjs +107 -0
  18. package/scripts/handoff-checkpoint-test.cjs +38 -0
  19. package/scripts/integrations-test.cjs +5 -2
  20. package/scripts/kiro-cursor-adapters-test.cjs +18 -0
  21. package/scripts/native-acp-depth-test.cjs +1 -1
  22. package/scripts/native-protocol-test.cjs +60 -4
  23. package/scripts/native-ui-smoke.cjs +92 -2
  24. package/scripts/switch-harness-test.cjs +79 -12
  25. package/scripts/tool-concurrency-regression-test.cjs +8 -6
  26. package/src/assets/icons/cline-color.svg +1 -0
  27. package/src/assets/skins/codex-styler/ASSET_LICENSES.md +36 -0
  28. package/src/assets/skins/codex-styler/NOTICE +11 -0
  29. package/src/assets/skins/codex-styler/themes/gilded-grandeur/hero.webp +0 -0
  30. package/src/assets/skins/codex-styler/themes/merry-big-top/hero.webp +0 -0
  31. package/src/assets/skins/codex-styler/themes/nocturne-studio/hero.webp +0 -0
  32. package/src/assets/skins/codex-styler/themes/quiet-garden/hero.webp +0 -0
  33. package/src/assets/skins/heige/ASSET_PROVENANCE.md +56 -0
  34. package/src/assets/skins/heige/NOTICE.md +13 -0
  35. package/src/assets/skins/heige/themes/dalao-dianyan/hero.webp +0 -0
  36. package/src/assets/skins/heige/themes/dalao-dianyan/theme.json +14 -0
  37. package/src/assets/skins/heige/themes/deepspace-dawn/hero.webp +0 -0
  38. package/src/assets/skins/heige/themes/deepspace-dawn/theme.json +14 -0
  39. package/src/assets/skins/heige/themes/deepspace-star/hero.webp +0 -0
  40. package/src/assets/skins/heige/themes/deepspace-star/theme.json +14 -0
  41. package/src/assets/skins/heige/themes/dragonball-nimbus/hero.webp +0 -0
  42. package/src/assets/skins/heige/themes/dragonball-nimbus/theme.json +16 -0
  43. package/src/assets/skins/heige/themes/dragonball-super-saiyan/hero.webp +0 -0
  44. package/src/assets/skins/heige/themes/dragonball-super-saiyan/theme.json +16 -0
  45. package/src/assets/skins/heige/themes/genshin-dawn/hero.webp +0 -0
  46. package/src/assets/skins/heige/themes/genshin-dawn/theme.json +14 -0
  47. package/src/assets/skins/heige/themes/genshin-night/hero.webp +0 -0
  48. package/src/assets/skins/heige/themes/genshin-night/theme.json +14 -0
  49. package/src/assets/skins/heige/themes/miku-488137/hero.webp +0 -0
  50. package/src/assets/skins/heige/themes/miku-488137/logo.webp +0 -0
  51. package/src/assets/skins/heige/themes/miku-488137/polaroid.webp +0 -0
  52. package/src/assets/skins/heige/themes/miku-488137/theme.json +16 -0
  53. package/src/assets/skins/heige/themes/naruto-hokage/hero.webp +0 -0
  54. package/src/assets/skins/heige/themes/naruto-hokage/theme.json +14 -0
  55. package/src/assets/skins/heige/themes/naruto-sasuke/hero.webp +0 -0
  56. package/src/assets/skins/heige/themes/naruto-sasuke/theme.json +14 -0
  57. package/src/assets/skins/heige/themes/wuthering-echo/hero.webp +0 -0
  58. package/src/assets/skins/heige/themes/wuthering-echo/theme.json +14 -0
  59. package/src/assets/skins/heige/themes/wuthering-tide/hero.webp +0 -0
  60. package/src/assets/skins/heige/themes/wuthering-tide/theme.json +14 -0
  61. package/src/main/adapters/cline.js +22 -0
  62. package/src/main/adapters/codex.js +78 -8
  63. package/src/main/adapters/index.js +2 -1
  64. package/src/main/adapters/native-acp-command.js +1 -0
  65. package/src/main/harness-adapter/error-kind.js +82 -0
  66. package/src/main/harness-adapter/event-normalizer.js +7 -1
  67. package/src/main/host/core-session.js +1 -1
  68. package/src/main/host/handoff-access.js +23 -4
  69. package/src/main/host/handoff-checkpoints.js +9 -2
  70. package/src/main/host/handoff-tools.js +3 -0
  71. package/src/main/host/handoff.js +11 -2
  72. package/src/main/host/runtime.js +272 -163
  73. package/src/main/host/session-history.js +54 -15
  74. package/src/main/native/icons.js +3 -2
  75. package/src/main/native/pets.js +234 -0
  76. package/src/main/native/protocol.js +197 -56
  77. package/src/main/protocol-core/projector.js +44 -41
  78. package/src/native-ui/desktop-control/dist/production-controller.d.ts.map +1 -1
  79. package/src/native-ui/desktop-control/dist/production-controller.js +1 -0
  80. package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -1
  81. package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
  82. package/src/native-ui/desktop-control/src/production-controller.ts +1 -0
  83. package/src/native-ui/desktop-control/test/production-controller.test.ts +1 -0
  84. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +4 -2
  85. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
  86. package/src/native-ui/renderer-extension/dist/types/renderer-agent-icon.d.ts.map +1 -1
  87. package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
  88. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
  89. package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
  90. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
  91. package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -1
  92. package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
  93. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +1 -1
  94. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -1
  95. package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
  96. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +1 -1
  97. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
  98. package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -1
  99. package/src/native-ui/renderer-extension/dist/types/settings/skin-market.d.ts +4 -0
  100. package/src/native-ui/renderer-extension/dist/types/settings/skin-market.d.ts.map +1 -0
  101. package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts +31 -0
  102. package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts.map +1 -0
  103. package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
  104. package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -1
  105. package/src/native-ui/renderer-extension/package.json +1 -1
  106. package/src/native-ui/renderer-extension/src/agent-selection-state.ts +16 -0
  107. package/src/native-ui/renderer-extension/src/assets.d.ts +5 -0
  108. package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +5 -4
  109. package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +1 -0
  110. package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +9 -6
  111. package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +2 -2
  112. package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +2 -0
  113. package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +2 -0
  114. package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +1 -0
  115. package/src/native-ui/renderer-extension/src/settings/connections-page.ts +2 -0
  116. package/src/native-ui/renderer-extension/src/settings/icons.ts +6 -0
  117. package/src/native-ui/renderer-extension/src/settings/localization.ts +4 -2
  118. package/src/native-ui/renderer-extension/src/settings/pages.ts +5 -2
  119. package/src/native-ui/renderer-extension/src/settings/shell.ts +3 -2
  120. package/src/native-ui/renderer-extension/src/settings/skin-market.css +213 -0
  121. package/src/native-ui/renderer-extension/src/settings/skin-market.ts +174 -0
  122. package/src/native-ui/renderer-extension/src/settings/skin-runtime.ts +399 -0
  123. package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +2 -1
  124. package/src/native-ui/renderer-extension/test/settings/localization.test.ts +11 -1
  125. package/src/native-ui/renderer-extension/test/settings/shell.test.ts +3 -0
  126. package/src/native-ui/renderer-extension/test/settings/skin-runtime.test.ts +136 -0
  127. package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts +7 -0
  128. package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts.map +1 -1
  129. package/src/native-ui/shared-contracts/dist/thread-harness-switch.js +3 -0
  130. package/src/native-ui/shared-contracts/dist/thread-harness-switch.js.map +1 -1
  131. package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
  132. package/src/native-ui/shared-contracts/src/thread-harness-switch.ts +3 -0
@@ -28,6 +28,7 @@ const PACKAGE_JSON_PATH = path.join(REPO_ROOT, 'package.json');
28
28
 
29
29
  const ALIASES = { workbuddy: 'codebuddy', 'claude-code': 'claude', 'deepseek-harness': 'dsh', 'codex-harness': 'codex' };
30
30
  const externalId = id => ({ workbuddy: 'codebuddy', claude: 'claude-code', dsh: 'deepseek-harness', codex: 'codex-harness' }[id] || id);
31
+ const { errorActions } = require('../harness-adapter/error-kind');
31
32
  const modelRef = model => ({ id: Buffer.from(JSON.stringify({ id: model.id, provider: model.provider })).toString('base64url') });
32
33
  const routeModel = harnessId => ['pi', 'claude-code', 'deepseek-harness', 'antigravity', 'omp', 'opencode', 'grok'].includes(harnessId)
33
34
  ? `codexhost/${harnessId}-native`
@@ -47,6 +48,7 @@ const HARNESS_INSTALL_COMMANDS = {
47
48
  antigravity: { win32: 'npm install -g @google/antigravity-cli', default: 'npm install -g @google/antigravity-cli' },
48
49
  openclaw: { win32: 'npm install -g openclaw', default: 'npm install -g openclaw' },
49
50
  hermes: { win32: 'pip install hermes-agent', default: 'pip3 install hermes-agent' },
51
+ cline: { win32: 'npm install -g cline', default: 'npm install -g cline' },
50
52
  };
51
53
 
52
54
  const HARNESS_AUTH_INFO = {
@@ -66,6 +68,7 @@ const HARNESS_AUTH_INFO = {
66
68
  hermes: { name: 'Hermes', plan: 'Nous Hermes', label: 'Hermes Agent 配置', loginCommand: null, configHint: '~/.hermes/ 配置文件或各模型 API Key' },
67
69
  zcode: { name: 'ZCode', plan: 'ZCode AI', label: 'ZCode 账号与配置', loginCommand: null, configHint: 'ZCode 客户端或配置文件' },
68
70
  trae: { name: 'Trae', plan: 'Trae AI', label: 'Trae 账号与配置', loginCommand: null, configHint: 'Trae 客户端登录状态' },
71
+ cline: { name: 'Cline', plan: 'Cline Providers', label: 'Cline CLI 认证', loginCommand: 'cline auth', configHint: '命令行 cline auth 或 ~/.cline/data 配置' },
69
72
  };
70
73
 
71
74
  const MODEL_REF_ID = /^[A-Za-z0-9._~-]{1,512}$/;
@@ -150,6 +153,52 @@ function unifiedDiff(change, kindType) {
150
153
  return [`diff --git a/${file} b/${file}`, ...(/^---\s/m.test(body) ? [] : headers), body].filter(Boolean).join('\n');
151
154
  }
152
155
 
156
+ // 各 harness 终端工具的命名集合;命中即把该工具调用投影为原生 commandExecution,
157
+ // 让 Desktop 显示「正在运行/运行了命令」而不是笼统的「已使用 Harness Mix 集成」。
158
+ const SHELL_TOOL_TITLE = /bash|shell|terminal|console|exec|powershell|pwsh|\bcmd\b|command|命令|终端/i;
159
+
160
+ function shellCommandText(item) {
161
+ if (!SHELL_TOOL_TITLE.test(String(item.title || ''))) return null;
162
+ const input = item.input;
163
+ if (typeof input !== 'string' || !input.trim()) return String(item.title || 'command');
164
+ try {
165
+ const parsed = JSON.parse(input);
166
+ if (parsed && typeof parsed === 'object') {
167
+ const cmd = parsed.command ?? parsed.cmd ?? parsed.script ?? parsed.input;
168
+ if (typeof cmd === 'string' && cmd.trim()) return cmd;
169
+ }
170
+ } catch {}
171
+ return input;
172
+ }
173
+
174
+ // 官方 app-server 由 Rust 把命令解析成 commandActions;这里只对无引号/管道/重定向的
175
+ // 简单单命令做保守分类(读取/搜索/列目录),其余一律 unknown,不臆造命令行为。
176
+ function classifyCommandAction(command) {
177
+ if (/["'`|;&<>()[\]{}$]/.test(command)) return { type: 'unknown', command };
178
+ const tokens = command.trim().split(/\s+/).filter(Boolean);
179
+ const exe = (tokens[0] || '').toLowerCase().replace(/^.*[\\/]/, '').replace(/\.exe$/, '');
180
+ const args = tokens.slice(1).filter(t => !t.startsWith('-'));
181
+ if (['cat', 'head', 'tail', 'type', 'get-content', 'more'].includes(exe) && args.length === 1)
182
+ return { type: 'read', command, name: args[0].split(/[\\/]/).pop(), path: args[0] };
183
+ if (['rg', 'grep', 'egrep', 'fgrep', 'findstr'].includes(exe) && args.length >= 1)
184
+ return { type: 'search', command, query: args[0] };
185
+ if (['ls', 'dir', 'tree'].includes(exe))
186
+ return { type: 'listFiles', command, ...(args[0] ? { path: args[0] } : {}) };
187
+ return { type: 'unknown', command };
188
+ }
189
+
190
+ // Desktop 权限菜单/回合参数中的权限字段(thread/settings/update 与 turn/start 用 sandboxPolicy,
191
+ // thread/start 用 sandbox),统一存为 turnPermissions { approvalPolicy, approvalsReviewer, sandboxPolicy, permissions }
192
+ function pickTurnPermissions(params = {}) {
193
+ const picked = {};
194
+ if (typeof params.approvalPolicy === 'string') picked.approvalPolicy = params.approvalPolicy;
195
+ if (typeof params.approvalsReviewer === 'string') picked.approvalsReviewer = params.approvalsReviewer;
196
+ const sandbox = params.sandboxPolicy ?? params.sandbox;
197
+ if (sandbox && typeof sandbox === 'object') picked.sandboxPolicy = sandbox;
198
+ if (typeof params.permissions === 'string') picked.permissions = params.permissions;
199
+ return Object.keys(picked).length ? picked : null;
200
+ }
201
+
153
202
  function projectItem(item) {
154
203
  const base = { id: item.id };
155
204
  if (item.type === 'user_message') return { ...base, type: 'userMessage', content: [
@@ -175,23 +224,33 @@ function projectItem(item) {
175
224
  agentsStates: job.child_thread_id ? { [job.child_thread_id]: { status,
176
225
  message: job.attention?.message || job.attention?.title || job.result || job.error || null } } : {} };
177
226
  }
178
- if (item.type === 'tool_call') return { ...base, type: 'mcpToolCall', server: 'harness-mix', tool: item.title || 'tool',
179
- arguments: item.input || {}, status: terminal(item.status) ? (item.state === 'error' ? 'failed' : 'completed') : 'inProgress',
180
- result: item.output ? { content: [{ type: 'text', text: String(item.output) }], structuredContent: null } : null,
181
- error: item.state === 'error' ? { message: String(item.output || item.detail || 'Tool failed') } : null, durationMs: null };
227
+ if (item.type === 'tool_call') {
228
+ const status = terminal(item.status) ? (item.state === 'error' ? 'failed' : 'completed') : 'inProgress';
229
+ const command = shellCommandText(item);
230
+ if (command != null) {
231
+ // 退出码未知不臆造(exitCode: null);durationMs 来自 Core 真实时间戳
232
+ return { ...base, type: 'commandExecution', command, cwd: null, status,
233
+ commandActions: [classifyCommandAction(command)],
234
+ aggregatedOutput: item.output ? String(item.output) : null,
235
+ exitCode: null, durationMs: terminal(item.status) ? Math.max(0, (item.updatedAt ?? 0) - (item.createdAt ?? 0)) : null, processId: null };
236
+ }
237
+ return { ...base, type: 'dynamicToolCall', namespace: 'harness-mix', tool: item.title || 'tool',
238
+ arguments: item.input || {}, contentItems: item.output ? [{ type: 'inputText', text: String(item.output) }] : null,
239
+ status, success: terminal(item.status) ? item.state !== 'error' : null };
240
+ }
182
241
  if (item.type === 'file_change') return { ...base, type: 'fileChange', status: terminal(item.status) ? 'completed' : 'inProgress',
183
242
  changes: (item.changes || [item]).map(change => {
184
243
  const kind = change.changeType === 'deleted' ? { type: 'delete' } : change.changeType === 'added' ? { type: 'add' } : { type: 'update', move_path: null };
185
244
  return { path: change.path || change.file || '', kind, diff: unifiedDiff(change, kind.type) };
186
245
  }) };
187
- if (item.type === 'context_compaction' || item.type === 'contextCompaction') return { ...base, type: 'contextCompaction' };
188
- if (item.type === 'verification_report') {
189
- const report = item.report ?? {};
190
- const failed = report.status === 'failed';
191
- return { ...base, type: 'mcpToolCall', server: 'harness-mix', tool: 'verification_gate',
192
- arguments: { mode: report.mode ?? 'off' }, result: { content: [{ type: 'text', text: JSON.stringify(report) }], structuredContent: report },
193
- status: failed ? 'failed' : 'completed', error: failed ? { message: 'Verification gate failed' } : null, durationMs: Math.max(0, (report.completedAt ?? 0) - (report.startedAt ?? 0)) };
194
- }
246
+ if (item.type === 'context_compaction' || item.type === 'contextCompaction') return { ...base, type: 'contextCompaction' };
247
+ if (item.type === 'verification_report') {
248
+ const report = item.report ?? {};
249
+ const failed = report.status === 'failed';
250
+ return { ...base, type: 'mcpToolCall', server: 'harness-mix', tool: 'verification_gate',
251
+ arguments: { mode: report.mode ?? 'off' }, result: { content: [{ type: 'text', text: JSON.stringify(report) }], structuredContent: report },
252
+ status: failed ? 'failed' : 'completed', error: failed ? { message: 'Verification gate failed' } : null, durationMs: Math.max(0, (report.completedAt ?? 0) - (report.startedAt ?? 0)) };
253
+ }
195
254
  return null;
196
255
  }
197
256
 
@@ -206,10 +265,14 @@ class NativeProtocol {
206
265
  emit,
207
266
  });
208
267
  this.approvals = new Map();
268
+ this.pets = require('./pets').createPetMarket();
209
269
  this.published = new Map();
210
270
  this.steering = new Map(); // threadId -> in-flight steer promise
211
271
  this.steerReceipts = new Map(); // `${threadId}\0${clientUserMessageId}` -> bounded delivery receipt
212
272
  this.queues = new Map(); // threadId -> Array<{ id, input, clientUserMessageId, createdAt }>
273
+ this.queueNotifications = new Map(); // response-first queue notifications, coalesced per thread
274
+ this.queueStarts = new Set(); // queued submission ids currently being accepted by a native harness
275
+ this.closed = false;
213
276
  this.unsubscribe = runtime.core.subscribe(({ event, projected }) => this.onCore(event, projected));
214
277
  // Host 侧新建的线程(协作子任务等)也要通知 Desktop 侧栏,与 thread/start 同一契约
215
278
  this.unsubscribeRuntime = runtime.subscribe(event => {
@@ -223,21 +286,41 @@ class NativeProtocol {
223
286
  if (!this.queues.has(threadId)) this.queues.set(threadId, []);
224
287
  return this.queues.get(threadId);
225
288
  }
226
- emitQueueChanged(threadId) {
227
- this.emit({ method: 'thread/queue/changed', params: { threadId } });
289
+ emitQueueChanged(threadId) {
290
+ // Desktop mutates its local queue cache after the request promise resolves. Emitting
291
+ // synchronously lets its refresh replace that cache first; an edit then holds the old
292
+ // message id and fails with "queued follow-up no longer exists". Match app-server
293
+ // ordering: return the mutation response first, then notify this and other windows.
294
+ if (this.closed || this.queueNotifications.has(threadId)) return;
295
+ const timer = setTimeout(() => {
296
+ this.queueNotifications.delete(threadId);
297
+ this.emit({ method: 'thread/queue/changed', params: { threadId } });
298
+ }, 0);
299
+ timer.unref?.();
300
+ this.queueNotifications.set(threadId, timer);
301
+ }
302
+ thread(id) {
303
+ if (!id) return undefined;
304
+ return this.runtime.threads.some(t => t.id === id) ? this.runtime.getThread(id) : undefined;
228
305
  }
229
- thread(id) {
230
- if (!id) return undefined;
231
- return this.runtime.threads.some(t => t.id === id) ? this.runtime.getThread(id) : undefined;
232
- }
233
306
  owns(id) { return Boolean(this.thread(id)); }
234
- turn(turn) { return { id: turn.id, status: turnStatus(turn.status), error: turn.error ? { message: String(turn.error), codexErrorInfo: null, additionalDetails: null } : null,
235
- items: this.runtime.core.getItemsForTurn(turn.id).map(projectItem).filter(Boolean) }; }
307
+ turn(turn) {
308
+ // 官方 Turn 合约的时间字段:startedAt/completedAt(epoch 秒,Desktop 恢复线程时经
309
+ // kBt(x*1e3) 还原)与 durationMs(turn/completed 处理器直接采用)。缺失时 Desktop
310
+ // 无法合成 worked-for 计时项,完成回合只回退显示“已使用 Harness Mix 集成”。
311
+ const startedAt = turn.startedAt ?? turn.createdAt ?? null;
312
+ const completedAt = turn.completedAt ?? null;
313
+ return { id: turn.id, status: turnStatus(turn.status), error: turn.error ? { message: String(turn.error), codexErrorInfo: turn.codexErrorInfo ?? null, additionalDetails: null } : null,
314
+ startedAt: startedAt != null ? Math.floor(startedAt / 1000) : null,
315
+ completedAt: completedAt != null ? Math.floor(completedAt / 1000) : null,
316
+ durationMs: startedAt != null && completedAt != null ? Math.max(0, completedAt - startedAt) : null,
317
+ items: this.runtime.core.getItemsForTurn(turn.id).map(projectItem).filter(Boolean) };
318
+ }
236
319
  projectThread(thread, includeTurns = true) {
237
320
  // Projection shape mirrors the upstream codexhost external-thread contract: every
238
321
  // field the Desktop sidebar/composer reads must be present with the same defaults.
239
322
  const updatedAt = Math.floor((thread.updatedAt || thread.createdAt) / 1000);
240
- return { id: thread.id, preview: thread.messages?.find(m => m.role === 'user')?.text || thread.preview || thread.title,
323
+ return { id: thread.id, preview: thread.messages?.find(m => m.role === 'user')?.text || thread.preview || thread.title,
241
324
  ephemeral: thread.ephemeral === true, modelProvider: 'codexhost', model: routeModel(externalId(thread.harnessId)), reasoningEffort: null,
242
325
  section: thread.section ?? null, sectionEnteredAt: thread.sectionEnteredAt ?? null, projectId: thread.projectId ?? null,
243
326
  createdAt: Math.floor(thread.createdAt / 1000),
@@ -249,7 +332,14 @@ class NativeProtocol {
249
332
  sessionId: thread.id, forkedFromId: thread.forkedFrom ?? null, parentThreadId: thread.parentThreadId ?? null,
250
333
  // 跨 Harness 原地切换血缘:链上每条是某 Harness 曾用的原生会话引用(切回可 resume)
251
334
  harnessChain: (thread.harnessChain ?? []).map(e => ({ harnessId: externalId(e.harnessId), at: e.at })),
252
- pendingHarnessSwitch: thread.pendingHandoff ? { fromHarnessId: externalId(thread.pendingHandoff.fromHarnessId), note: thread.pendingHandoff.note ?? null } : null,
335
+ pendingHarnessSwitch: thread.pendingHandoff ? {
336
+ checkpointId: thread.pendingHandoff.checkpointId,
337
+ fromHarnessId: externalId(thread.pendingHandoff.fromHarnessId),
338
+ toHarnessId: externalId(thread.pendingHandoff.toHarnessId || thread.harnessId),
339
+ phase: thread.pendingHandoff.phase || 'ready',
340
+ intent: thread.pendingHandoff.intent || 'continue',
341
+ note: thread.pendingHandoff.note ?? null,
342
+ } : null,
253
343
  canAcceptDirectInput: true, historyMode: 'legacy', isPinned: false, extra: null,
254
344
  isolation: thread.isolation ?? 'shared',
255
345
  workspace: thread.workspace ? { mode: thread.workspace.mode, branch: thread.workspace.branch, root: thread.workspace.root } : null,
@@ -350,7 +440,24 @@ class NativeProtocol {
350
440
  if (method === 'codexhost/harness/session-import/list') return this.runtime.history.list(params);
351
441
  if (method === 'codexhost/harness/session-import/import') return this.runtime.history.import(params);
352
442
  if (method === 'codexhost/collaboration/agents') return [...this.runtime.adapters.values()].map(a => ({ id: externalId(a.manifest.id), name: a.manifest.name, available: !!this.runtime.status[a.manifest.id]?.available, lead: a.manifest.capabilities?.collaborationTools === true }));
443
+ // 桌宠市场:官方预载(Codex 安装包 asar 提取)与 ~/.codex/pets 安装管理
444
+ if (method === 'codexhost/pets/catalog') return this.pets.catalog();
445
+ if (method === 'codexhost/pets/preview') return this.pets.preview(params);
446
+ if (method === 'codexhost/pets/install') return this.pets.install(params);
447
+ if (method === 'codexhost/pets/uninstall') return this.pets.uninstall(params);
353
448
  const thread = this.thread(params.threadId);
449
+ // 失败回合分类查询:Renderer 据此在输入框下方渲染只真正帮得上忙的动作按钮
450
+ if (method === 'codexhost/harness/turn-error') {
451
+ if (!thread) throw new Error('Unknown thread');
452
+ const kind = thread.errorKind ?? null;
453
+ const meta = HARNESS_AUTH_INFO[externalId(thread.harnessId)] || HARNESS_AUTH_INFO[thread.harnessId];
454
+ return {
455
+ threadId: thread.id,
456
+ error: thread.error ?? null,
457
+ errorKind: kind,
458
+ actions: kind ? errorActions(kind, { canLogin: Boolean(meta?.loginCommand) }) : [],
459
+ };
460
+ }
354
461
  if (method === 'harness-mix/runtime/inspect') return {
355
462
  owner: 'harness-mix',
356
463
  runtime: 'src/main/host/runtime.js',
@@ -575,9 +682,9 @@ class NativeProtocol {
575
682
  }
576
683
  return { accounts };
577
684
  }
578
- if (method === 'codexhost/harness/plugins/list') return { plugins: this.runtime.snapshot().adapters.map(a => ({ id: externalId(a.id), name: a.id === 'codex' ? 'Codex(协作)' : a.name, version: '0.1.0', icon: `data:image/svg+xml;base64,${Buffer.from(getHarnessSvg(a.id)).toString('base64')}` })) };
579
- if (method === 'codexhost/storage/inspect') return this.runtime.inspectStorage();
580
- if (method === 'codexhost/storage/optimize') return this.runtime.optimizeStorage();
685
+ if (method === 'codexhost/harness/plugins/list') return { plugins: this.runtime.snapshot().adapters.map(a => ({ id: externalId(a.id), name: a.id === 'codex' ? 'Codex(协作)' : a.name, version: '0.1.0', icon: `data:image/svg+xml;base64,${Buffer.from(getHarnessSvg(a.id)).toString('base64')}` })) };
686
+ if (method === 'codexhost/storage/inspect') return this.runtime.inspectStorage();
687
+ if (method === 'codexhost/storage/optimize') return this.runtime.optimizeStorage();
581
688
  if (method === 'codexhost/harness/inspect') return this.inspect(params.harnessId);
582
689
  if (method === 'codexhost/harness/install') {
583
690
  const local = ALIASES[params.harnessId] || params.harnessId;
@@ -648,6 +755,7 @@ class NativeProtocol {
648
755
  model: selectedModel, thinking: effectiveRoute.thinkingOptionId, permissionMode: effectiveRoute.permissionModeId,
649
756
  ...(accountContext || {}),
650
757
  ...(isWorktree ? { worktree: true } : {}),
758
+ ...(pickTurnPermissions(params) ? { turnPermissions: pickTurnPermissions(params) } : {}),
651
759
  } });
652
760
  if (created.error) throw new Error(created.error);
653
761
  const result = { thread: this.projectThread(created), model: params.model, modelProvider: 'harness-mix', cwd: created.cwd,
@@ -658,10 +766,10 @@ class NativeProtocol {
658
766
  if (method === 'codexhost/thread/workspace/review') return this.runtime.reviewThreadWorkspace(params.threadId);
659
767
  if (method === 'codexhost/thread/workspace/apply') return this.runtime.applyThreadWorkspace(params.threadId, params.digest);
660
768
  if (method === 'codexhost/thread/workspace/discard') return this.runtime.discardThreadWorkspace(params.threadId);
661
- if (method === 'codexhost/thread/workspace/push') return this.runtime.pushThreadWorkspace(params.threadId, params);
662
- if (method === 'codexhost/thread/verification/get') return this.runtime.verificationState(params.threadId);
663
- if (method === 'codexhost/thread/verification/configure') return this.runtime.configureVerification(params.threadId, params.policy);
664
- if (method === 'codexhost/thread/verification/run') return this.runtime.runVerification(params.threadId);
769
+ if (method === 'codexhost/thread/workspace/push') return this.runtime.pushThreadWorkspace(params.threadId, params);
770
+ if (method === 'codexhost/thread/verification/get') return this.runtime.verificationState(params.threadId);
771
+ if (method === 'codexhost/thread/verification/configure') return this.runtime.configureVerification(params.threadId, params.policy);
772
+ if (method === 'codexhost/thread/verification/run') return this.runtime.runVerification(params.threadId);
665
773
  if (!thread) {
666
774
  if (method.startsWith('codexhost/')) throw new Error(`Harness Mix does not implement ${method}`);
667
775
  return undefined;
@@ -729,22 +837,16 @@ class NativeProtocol {
729
837
  ? queue.findIndex(item => item.id === params.queuedSubmissionId)
730
838
  : 0;
731
839
  if (idx === -1 || !queue[idx]) throw new Error('Queued submission not found');
732
- const [submission] = queue.splice(idx, 1);
733
- this.emitQueueChanged(thread.id);
734
- const { text, attachments } = await prepareInput(submission.input, thread.cwd);
735
- if (attachments.length && !this.runtime.getCapabilities(thread.harnessId).conversation.attachments) {
736
- throw new Error('当前 Harness 不支持图片附件');
737
- }
738
- if (this.runtime.execution.isRunning(thread.id)) {
739
- // 正在执行中:打断当前回合并立即执行该排队消息
740
- const turnId = await this.steerExclusive(thread, thread.currentTurn?.id, text, attachments);
741
- const started = this.runtime.core.getTurn(turnId) || thread.currentTurn;
742
- return { turn: this.turn(started) };
743
- }
744
- const turn = await this.startNativeTurn(thread, text, attachments);
745
- return { turn: this.turn(turn) };
840
+ return { turn: this.turn(await this.startQueuedSubmission(thread, queue[idx])) };
746
841
  }
747
842
  if (method === 'thread/metadata/update') return { thread: this.projectThread(await this.runtime.updateThreadMetadata(thread.id, params.gitInfo)) };
843
+ // Desktop 权限菜单(请求批准/帮我批准/完全访问)推送的线程级设置:原样存储并持久化,
844
+ // 后续回合经 codex 适配器转发到原生 app-server,让显示选择与实际生效一致
845
+ if (method === 'thread/settings/update') {
846
+ const picked = pickTurnPermissions(params);
847
+ if (picked) await this.runtime.setOptions(thread.id, { turnPermissions: { ...(thread.options?.turnPermissions ?? {}), ...picked } });
848
+ return {};
849
+ }
748
850
  if (method === 'thread/section/move') {
749
851
  if (params.sectionId !== null && (typeof params.sectionId !== 'string' || !params.sectionId)) throw new Error('Invalid sectionId');
750
852
  let section = null;
@@ -761,10 +863,17 @@ class NativeProtocol {
761
863
  await this.runtime.setThreadSection(thread.id, section, params.beforeThreadId);
762
864
  return {};
763
865
  }
764
- if (method === 'thread/resume') return { thread: this.projectThread(thread), model: routeModel(externalId(thread.harnessId)), modelProvider: 'harness-mix', cwd: thread.cwd, approvalPolicy: 'on-request', sandbox: { type: 'workspaceWrite', writableRoots: [thread.cwd], networkAccess: false, excludeTmpdirEnvVar: false, excludeSlashTmp: false }, reasoningEffort: null };
866
+ if (method === 'thread/resume') {
867
+ // 回显线程实际生效的权限(Desktop 据此渲染 composer 权限指示),而不是硬编码默认值
868
+ const perms = thread.options?.turnPermissions ?? null;
869
+ return { thread: this.projectThread(thread), model: routeModel(externalId(thread.harnessId)), modelProvider: 'harness-mix', cwd: thread.cwd,
870
+ approvalPolicy: perms?.approvalPolicy ?? 'on-request', approvalsReviewer: perms?.approvalsReviewer ?? null,
871
+ sandbox: perms?.sandboxPolicy ?? { type: 'workspaceWrite', writableRoots: [thread.cwd], networkAccess: false, excludeTmpdirEnvVar: false, excludeSlashTmp: false }, reasoningEffort: null };
872
+ }
765
873
  if (method === 'turn/start') {
766
874
  const { text, attachments } = await prepareInput(params.input, thread.cwd);
767
- return { turn: this.turn(await this.startNativeTurn(thread, text, attachments)) };
875
+ // Desktop fallback 路径在每个回合都携带完整权限参数;缺失时由适配器回退到线程级设置
876
+ return { turn: this.turn(await this.startNativeTurn(thread, text, attachments, undefined, pickTurnPermissions(params))) };
768
877
  }
769
878
  // External steering: cancel the active Turn, wait for it to fully settle, then start
770
879
  // the new input as a real new Turn. Never guess a stale target, never auto-start on
@@ -851,12 +960,12 @@ class NativeProtocol {
851
960
  }
852
961
  // 原地切换 Harness:会话历史保留,下条消息携带一次性上下文信封(/switch 指令的 RPC 等价物)
853
962
  if (method === 'codexhost/thread/harness/switch') {
854
- await this.runtime.switchHarness(thread.id, ALIASES[params.harnessId] || params.harnessId, {
963
+ const result = await this.runtime.switchHarness(thread.id, ALIASES[params.harnessId] || params.harnessId, {
855
964
  note: typeof params.note === 'string' ? params.note : undefined,
856
965
  intent: params.intent,
857
966
  includes: params.includes,
858
967
  });
859
- return { threadId: thread.id, checkpointId: thread.pendingHandoff.checkpointId };
968
+ return { ...result, fromHarnessId: externalId(result.fromHarnessId), toHarnessId: externalId(result.toHarnessId) };
860
969
  }
861
970
  if (method === 'thread/fork' || method === 'codexhost/thread/fork') {
862
971
  if (params.ephemeral || params.threadSource || params.excludeTurns) {
@@ -889,10 +998,10 @@ class NativeProtocol {
889
998
  const converted = projectItem(item);
890
999
  if (converted) {
891
1000
  const previous = this.published.get(item.id);
892
- if (!previous) notify('item/started', { item: converted });
1001
+ if (!previous) notify('item/started', { item: converted, startedAtMs: item.createdAt ?? Date.now() });
893
1002
  if (item.type === 'agent_message' && item.content?.length > (previous?.content?.length || 0)) notify('item/agentMessage/delta', { itemId: item.id, delta: item.content.slice(previous?.content?.length || 0) });
894
1003
  if (item.type === 'reasoning' && item.content?.length > (previous?.content?.length || 0)) notify('item/reasoning/summaryTextDelta', { itemId: item.id, summaryIndex: 0, delta: item.content.slice(previous?.content?.length || 0) });
895
- if (terminal(item.status) && (!terminal(previous?.status) || ['file_change', 'tool_call'].includes(item.type) && JSON.stringify(converted) !== JSON.stringify(projectItem(previous)))) notify('item/completed', { item: converted });
1004
+ if (terminal(item.status) && (!terminal(previous?.status) || ['file_change', 'tool_call'].includes(item.type) && JSON.stringify(converted) !== JSON.stringify(projectItem(previous)))) notify('item/completed', { item: converted, completedAtMs: item.updatedAt ?? item.createdAt ?? Date.now() });
896
1005
  this.published.set(item.id, structuredClone(item));
897
1006
  if (this.published.size > 200) {
898
1007
  const oldest = this.published.keys().next().value;
@@ -926,11 +1035,8 @@ class NativeProtocol {
926
1035
  const queue = this.getQueue(threadId);
927
1036
  const currentThread = this.thread(threadId);
928
1037
  if (queue.length > 0 && currentThread && !this.runtime.execution.isRunning(threadId) && !this.steering.has(threadId) && !this.runtime.sending?.has(threadId)) {
929
- const [submission] = queue.splice(0, 1);
930
- this.emitQueueChanged(threadId);
931
1038
  try {
932
- const { text, attachments } = await prepareInput(submission.input, currentThread.cwd);
933
- await this.startNativeTurn(currentThread, text, attachments);
1039
+ await this.startQueuedSubmission(currentThread, queue[0]);
934
1040
  } catch (err) {
935
1041
  console.error(`Failed to auto-drain queued submission for thread ${threadId}:`, err);
936
1042
  }
@@ -939,13 +1045,41 @@ class NativeProtocol {
939
1045
  }
940
1046
  }
941
1047
  }
942
- async startNativeTurn(thread, text, attachments, commandId) {
1048
+ async startQueuedSubmission(thread, submission) {
1049
+ if (this.queueStarts.has(submission.id)) throw new Error('Queued submission is already starting');
1050
+ this.queueStarts.add(submission.id);
1051
+ try {
1052
+ // Keep the item addressable until the native harness has accepted a real Turn. If
1053
+ // input preparation or turn startup fails, Desktop can still edit/retry/delete it.
1054
+ const { text, attachments } = await prepareInput(submission.input, thread.cwd);
1055
+ if (attachments.length && !this.runtime.getCapabilities(thread.harnessId).conversation.attachments) {
1056
+ throw new Error('当前 Harness 不支持图片附件');
1057
+ }
1058
+ let started;
1059
+ if (this.runtime.execution.isRunning(thread.id)) {
1060
+ const turnId = await this.steerExclusive(thread, thread.currentTurn?.id, text, attachments);
1061
+ started = this.runtime.core.getTurn(turnId) || thread.currentTurn;
1062
+ } else {
1063
+ started = await this.startNativeTurn(thread, text, attachments);
1064
+ }
1065
+ const queue = this.getQueue(thread.id);
1066
+ const idx = queue.findIndex(item => item.id === submission.id);
1067
+ if (idx !== -1) {
1068
+ queue.splice(idx, 1);
1069
+ this.emitQueueChanged(thread.id);
1070
+ }
1071
+ return started;
1072
+ } finally {
1073
+ this.queueStarts.delete(submission.id);
1074
+ }
1075
+ }
1076
+ async startNativeTurn(thread, text, attachments, commandId, turnPermissions) {
943
1077
  const before = thread.currentTurn?.id;
944
1078
  for (let i = 0; i < 100 && this.runtime.sending?.has(thread.id) && !this.runtime.execution.isRunning(thread.id); i++) {
945
1079
  await new Promise(resolve => setTimeout(resolve, 20));
946
1080
  }
947
1081
  let failure;
948
- const running = (commandId ? this.runtime.executeCommand(thread.id, commandId) : this.runtime.send(thread.id, text, { attachments })).catch(error => { failure = error; });
1082
+ const running = (commandId ? this.runtime.executeCommand(thread.id, commandId) : this.runtime.send(thread.id, text, { attachments, turnPermissions })).catch(error => { failure = error; });
949
1083
  for (let attempt = 0; attempt < 600 && thread.currentTurn?.id === before && !failure; attempt++) await new Promise(resolve => setTimeout(resolve, 50));
950
1084
  if (failure) throw failure;
951
1085
  if (!thread.currentTurn || thread.currentTurn.id === before) throw new Error('Native turn did not start');
@@ -1008,6 +1142,13 @@ class NativeProtocol {
1008
1142
  this.approvals.delete(message.id);
1009
1143
  return true;
1010
1144
  }
1011
- close() { this.codexAccounts.close(); this.unsubscribe(); this.unsubscribeRuntime(); }
1145
+ close() {
1146
+ this.closed = true;
1147
+ this.codexAccounts.close();
1148
+ this.unsubscribe();
1149
+ this.unsubscribeRuntime();
1150
+ for (const timer of this.queueNotifications.values()) clearTimeout(timer);
1151
+ this.queueNotifications.clear();
1152
+ }
1012
1153
  }
1013
1154
  module.exports = { NativeProtocol, decodeRoute, projectItem, externalId, routeModel };
@@ -17,23 +17,23 @@ class Projector {
17
17
  if (existingTurn && existingTurn.threadId !== event.threadId) throw new Error('event belongs to another thread');
18
18
  if (existingTurn && TERMINAL_TURN_STATUS.includes(existingTurn.status) && event.type.startsWith('item.')) return {};
19
19
  if (existingTurn && typeof event.sequence === 'number') existingTurn.lastSequence = Math.max(existingTurn.lastSequence, event.sequence);
20
- switch (event.type) {
21
- case 'thread.rolledBack': {
22
- const turns = this.turns.turnsForThread(event.threadId);
23
- const count = event.payload.numTurns;
24
- if (!Number.isSafeInteger(count) || count < 1 || count > turns.length || turns.some(t => !TERMINAL_TURN_STATUS.includes(t.status))) throw new Error('Invalid rollback boundary');
25
- const removed = new Set(turns.slice(-count).map(t => t.id));
26
- for (const id of removed) this.turns.turns.delete(id);
27
- for (const [id, item] of this.items) if (removed.has(item.turnId)) this.items.delete(id);
28
- const thread = this.threads.get(event.threadId);
29
- thread.usage = {};
30
- for (const turn of turns.slice(0, -count)) {
31
- turn.nativeTurnRef = { ...turn.nativeTurnRef, sessionId: event.payload.sessionId };
32
- const map = event.payload.checkpointMap;
33
- if (map && turn.nativeTurnRef.checkpointId) turn.nativeTurnRef.checkpointId = map[turn.nativeTurnRef.checkpointId];
34
- }
35
- return { thread: this.threads.update(event.threadId, { status: 'idle', activeTurnId: null, nativeSessionRef: { sessionId: event.payload.sessionId } }, event.timestamp) };
36
- }
20
+ switch (event.type) {
21
+ case 'thread.rolledBack': {
22
+ const turns = this.turns.turnsForThread(event.threadId);
23
+ const count = event.payload.numTurns;
24
+ if (!Number.isSafeInteger(count) || count < 1 || count > turns.length || turns.some(t => !TERMINAL_TURN_STATUS.includes(t.status))) throw new Error('Invalid rollback boundary');
25
+ const removed = new Set(turns.slice(-count).map(t => t.id));
26
+ for (const id of removed) this.turns.turns.delete(id);
27
+ for (const [id, item] of this.items) if (removed.has(item.turnId)) this.items.delete(id);
28
+ const thread = this.threads.get(event.threadId);
29
+ thread.usage = {};
30
+ for (const turn of turns.slice(0, -count)) {
31
+ turn.nativeTurnRef = { ...turn.nativeTurnRef, sessionId: event.payload.sessionId };
32
+ const map = event.payload.checkpointMap;
33
+ if (map && turn.nativeTurnRef.checkpointId) turn.nativeTurnRef.checkpointId = map[turn.nativeTurnRef.checkpointId];
34
+ }
35
+ return { thread: this.threads.update(event.threadId, { status: 'idle', activeTurnId: null, nativeSessionRef: { sessionId: event.payload.sessionId } }, event.timestamp) };
36
+ }
37
37
  case 'thread.created': {
38
38
  const thread = this.threads.create({ id: event.threadId, ...event.payload }, event.timestamp);
39
39
  return { thread };
@@ -83,28 +83,28 @@ class Projector {
83
83
  item.updatedAt = event.timestamp;
84
84
  return { item };
85
85
  }
86
- case 'usage.updated': {
86
+ case 'usage.updated': {
87
87
  const item = this.#upsertUsageItem(event);
88
88
  const thread = this.threads.update(event.threadId, { usage: event.payload }, event.timestamp);
89
- return { item, thread };
90
- }
91
- case 'verification.updated': {
92
- const turn = this.turns.get(event.turnId);
93
- if (!turn) return {};
94
- const itemId = event.itemId ?? `verification_${turn.id}`;
95
- let item = this.items.get(itemId);
96
- if (!item) {
97
- item = createItem({ id: itemId, threadId: event.threadId, turnId: turn.id,
98
- type: 'verification_report', status: 'completed', report: structuredClone(event.payload.report) }, event.timestamp);
99
- this.items.set(item.id, item);
100
- if (!turn.itemIds.includes(item.id)) turn.itemIds.push(item.id);
101
- } else {
102
- item.report = structuredClone(event.payload.report);
103
- item.status = 'completed';
104
- item.updatedAt = event.timestamp;
105
- }
106
- return { item };
107
- }
89
+ return { item, thread };
90
+ }
91
+ case 'verification.updated': {
92
+ const turn = this.turns.get(event.turnId);
93
+ if (!turn) return {};
94
+ const itemId = event.itemId ?? `verification_${turn.id}`;
95
+ let item = this.items.get(itemId);
96
+ if (!item) {
97
+ item = createItem({ id: itemId, threadId: event.threadId, turnId: turn.id,
98
+ type: 'verification_report', status: 'completed', report: structuredClone(event.payload.report) }, event.timestamp);
99
+ this.items.set(item.id, item);
100
+ if (!turn.itemIds.includes(item.id)) turn.itemIds.push(item.id);
101
+ } else {
102
+ item.report = structuredClone(event.payload.report);
103
+ item.status = 'completed';
104
+ item.updatedAt = event.timestamp;
105
+ }
106
+ return { item };
107
+ }
108
108
  default:
109
109
  return {};
110
110
  }
@@ -119,9 +119,9 @@ class Projector {
119
119
  return this.turns.create({ id: event.turnId ?? `turn_${event.eventId}`, threadId: event.threadId, nativeTurnRef: event.nativeRef }, event.timestamp);
120
120
  }
121
121
 
122
- #settleTurn(event, kind) {
123
- const turn = this.#ensureTurn(event);
124
- if (event.nativeRef) turn.nativeTurnRef = { ...turn.nativeTurnRef, ...event.nativeRef };
122
+ #settleTurn(event, kind) {
123
+ const turn = this.#ensureTurn(event);
124
+ if (event.nativeRef) turn.nativeTurnRef = { ...turn.nativeTurnRef, ...event.nativeRef };
125
125
  if (typeof event.sequence === 'number') turn.lastSequence = event.sequence;
126
126
  // 首次结算生效:Turn 已进入终态后,迟到的重复结算事件为幂等噪声(如 cancel 后
127
127
  // 原生流仍送达 agent_settled / result),不改变 Turn 生命周期。
@@ -133,7 +133,10 @@ class Projector {
133
133
  } else if (kind === 'cancelled') {
134
134
  this.turns.cancel(turn.id, {}, event.timestamp);
135
135
  } else {
136
- this.turns.fail(turn.id, event.payload?.message ?? event.payload?.error, {}, event.timestamp);
136
+ this.turns.fail(turn.id, event.payload?.message ?? event.payload?.error, {
137
+ ...(event.payload?.errorKind ? { errorKind: event.payload.errorKind } : {}),
138
+ ...(event.payload?.codexErrorInfo ? { codexErrorInfo: event.payload.codexErrorInfo } : {}),
139
+ }, event.timestamp);
137
140
  }
138
141
  // Turn 结算:所有未终态 Item 一并收尾(语义等同 legacy finishMessage)
139
142
  this.#finalizeOpenItems(turn, event.timestamp);
@@ -1 +1 @@
1
- {"version":3,"file":"production-controller.d.ts","sourceRoot":"","sources":["../src/production-controller.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,0BAA0B,EAC/B,KAAK,sCAAsC,EAC5C,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,mCAAmC,CAAC;AAE3C,MAAM,WAAW,wBAAwB;IACvC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,CAAC,CAAC;IACjB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,EAAE,CAAC;CACZ;AAED,MAAM,WAAW,6BAA6B;IAC5C,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,CAAC,OAAO,EAAE;QACf,mBAAmB,EAAE,MAAM,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;QACjC,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACvC,qBAAqB,CACnB,OAAO,EAAE,sCAAsC,GAC9C,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACvC,KAAK,CAAC,SAAS,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,GAAG,CAAC,IAAI,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAqBD,wBAAgB,mCAAmC,CAAC,SAAS,EAAE,0BAA0B,GAAG,MAAM,CAejG;AA8BD,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,wBAAwB,CAwE1B;AAmCD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,wBAAwB,EACjC,MAAM,EAAE,WAAW,EACnB,YAAY,GAAE,6BAAmD,GAChE,OAAO,CAAC,IAAI,CAAC,CA6Hf"}
1
+ {"version":3,"file":"production-controller.d.ts","sourceRoot":"","sources":["../src/production-controller.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,0BAA0B,EAC/B,KAAK,sCAAsC,EAC5C,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,mCAAmC,CAAC;AAE3C,MAAM,WAAW,wBAAwB;IACvC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,CAAC,CAAC;IACjB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,EAAE,CAAC;CACZ;AAED,MAAM,WAAW,6BAA6B;IAC5C,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,CAAC,OAAO,EAAE;QACf,mBAAmB,EAAE,MAAM,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;QACjC,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACvC,qBAAqB,CACnB,OAAO,EAAE,sCAAsC,GAC9C,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACvC,KAAK,CAAC,SAAS,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,GAAG,CAAC,IAAI,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAqBD,wBAAgB,mCAAmC,CAAC,SAAS,EAAE,0BAA0B,GAAG,MAAM,CAejG;AA8BD,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,wBAAwB,CAwE1B;AAmCD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,wBAAwB,EACjC,MAAM,EAAE,WAAW,EACnB,YAAY,GAAE,6BAAmD,GAChE,OAAO,CAAC,IAAI,CAAC,CA8Hf"}
@@ -206,6 +206,7 @@ export async function runDesktopController(options, signal, dependencies = defau
206
206
  "codex-harness",
207
207
  "zcode",
208
208
  "trae",
209
+ "cline",
209
210
  ],
210
211
  timeoutMs: PRODUCTION_INSTALL_TIMEOUT_MS,
211
212
  }, dependencies);
@@ -1 +1 @@
1
- {"version":3,"file":"production-controller.js","sourceRoot":"","sources":["../src/production-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"production-controller.js","sourceRoot":"","sources":["../src/production-controller.ts"],"names":[],"mappings":"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"}