@harness-mix/cli 0.1.8 → 0.1.10

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 (156) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/NOTICE +12 -0
  3. package/README.md +74 -71
  4. package/docs/harness-management.md +1 -1
  5. package/docs/native-acp.md +7 -2
  6. package/docs/task-handoff-design.md +65 -0
  7. package/licenses/codex-styler-Apache-2.0.txt +193 -0
  8. package/licenses/heige-codex-skin-studio-MIT.txt +22 -0
  9. package/output/native-build/desktop-controller.mjs +3 -2
  10. package/output/native-build/renderer-extension.js +1350 -46
  11. package/package.json +189 -188
  12. package/scripts/acp-capabilities-probe.cjs +1 -0
  13. package/scripts/build-native.cjs +1 -1
  14. package/scripts/codex-adapter-test.cjs +44 -0
  15. package/scripts/collaboration-recovery-test.cjs +3 -0
  16. package/scripts/e2e-codex.cjs +3 -1
  17. package/scripts/e2e-native-acp.cjs +1 -1
  18. package/scripts/error-kind-test.cjs +107 -0
  19. package/scripts/handoff-checkpoint-test.cjs +38 -0
  20. package/scripts/integrations-test.cjs +5 -2
  21. package/scripts/kiro-cursor-adapters-test.cjs +18 -0
  22. package/scripts/native-acp-depth-test.cjs +1 -1
  23. package/scripts/native-config-test.cjs +32 -2
  24. package/scripts/native-protocol-test.cjs +60 -4
  25. package/scripts/native-ui-smoke.cjs +127 -2
  26. package/scripts/pets-test.cjs +146 -0
  27. package/scripts/switch-harness-test.cjs +79 -12
  28. package/scripts/tool-concurrency-regression-test.cjs +8 -6
  29. package/src/assets/icons/cline-color.svg +1 -0
  30. package/src/assets/skins/codex-styler/ASSET_LICENSES.md +36 -0
  31. package/src/assets/skins/codex-styler/NOTICE +11 -0
  32. package/src/assets/skins/codex-styler/themes/gilded-grandeur/hero.webp +0 -0
  33. package/src/assets/skins/codex-styler/themes/merry-big-top/hero.webp +0 -0
  34. package/src/assets/skins/codex-styler/themes/nocturne-studio/hero.webp +0 -0
  35. package/src/assets/skins/codex-styler/themes/quiet-garden/hero.webp +0 -0
  36. package/src/assets/skins/heige/ASSET_PROVENANCE.md +56 -0
  37. package/src/assets/skins/heige/NOTICE.md +13 -0
  38. package/src/assets/skins/heige/themes/dalao-dianyan/hero.webp +0 -0
  39. package/src/assets/skins/heige/themes/dalao-dianyan/theme.json +14 -0
  40. package/src/assets/skins/heige/themes/deepspace-dawn/hero.webp +0 -0
  41. package/src/assets/skins/heige/themes/deepspace-dawn/theme.json +14 -0
  42. package/src/assets/skins/heige/themes/deepspace-star/hero.webp +0 -0
  43. package/src/assets/skins/heige/themes/deepspace-star/theme.json +14 -0
  44. package/src/assets/skins/heige/themes/dragonball-nimbus/hero.webp +0 -0
  45. package/src/assets/skins/heige/themes/dragonball-nimbus/theme.json +16 -0
  46. package/src/assets/skins/heige/themes/dragonball-super-saiyan/hero.webp +0 -0
  47. package/src/assets/skins/heige/themes/dragonball-super-saiyan/theme.json +16 -0
  48. package/src/assets/skins/heige/themes/genshin-dawn/hero.webp +0 -0
  49. package/src/assets/skins/heige/themes/genshin-dawn/theme.json +14 -0
  50. package/src/assets/skins/heige/themes/genshin-night/hero.webp +0 -0
  51. package/src/assets/skins/heige/themes/genshin-night/theme.json +14 -0
  52. package/src/assets/skins/heige/themes/miku-488137/hero.webp +0 -0
  53. package/src/assets/skins/heige/themes/miku-488137/logo.webp +0 -0
  54. package/src/assets/skins/heige/themes/miku-488137/polaroid.webp +0 -0
  55. package/src/assets/skins/heige/themes/miku-488137/theme.json +16 -0
  56. package/src/assets/skins/heige/themes/naruto-hokage/hero.webp +0 -0
  57. package/src/assets/skins/heige/themes/naruto-hokage/theme.json +14 -0
  58. package/src/assets/skins/heige/themes/naruto-sasuke/hero.webp +0 -0
  59. package/src/assets/skins/heige/themes/naruto-sasuke/theme.json +14 -0
  60. package/src/assets/skins/heige/themes/wuthering-echo/hero.webp +0 -0
  61. package/src/assets/skins/heige/themes/wuthering-echo/theme.json +14 -0
  62. package/src/assets/skins/heige/themes/wuthering-tide/hero.webp +0 -0
  63. package/src/assets/skins/heige/themes/wuthering-tide/theme.json +14 -0
  64. package/src/main/adapters/cline.js +22 -0
  65. package/src/main/adapters/codex.js +78 -8
  66. package/src/main/adapters/index.js +2 -1
  67. package/src/main/adapters/native-acp-command.js +1 -0
  68. package/src/main/harness-adapter/error-kind.js +82 -0
  69. package/src/main/harness-adapter/event-normalizer.js +7 -1
  70. package/src/main/host/core-session.js +1 -1
  71. package/src/main/host/handoff-access.js +23 -4
  72. package/src/main/host/handoff-checkpoints.js +9 -2
  73. package/src/main/host/handoff-tools.js +3 -0
  74. package/src/main/host/handoff.js +11 -2
  75. package/src/main/host/runtime.js +272 -163
  76. package/src/main/host/session-history.js +54 -15
  77. package/src/main/native/icons.js +3 -2
  78. package/src/main/native/launcher.js +49 -2
  79. package/src/main/native/pets.js +395 -0
  80. package/src/main/native/protocol.js +197 -55
  81. package/src/main/protocol-core/projector.js +44 -41
  82. package/src/native-ui/desktop-control/dist/production-controller.d.ts.map +1 -1
  83. package/src/native-ui/desktop-control/dist/production-controller.js +1 -0
  84. package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -1
  85. package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +4 -4
  86. package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -1
  87. package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
  88. package/src/native-ui/desktop-control/src/production-controller.ts +1 -0
  89. package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +4 -4
  90. package/src/native-ui/desktop-control/test/production-controller.test.ts +1 -0
  91. package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +7 -0
  92. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +5 -2
  93. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
  94. package/src/native-ui/renderer-extension/dist/types/renderer-agent-icon.d.ts.map +1 -1
  95. package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
  96. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts +1 -0
  97. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
  98. package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
  99. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +2 -0
  100. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
  101. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +2 -1
  102. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
  103. package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -1
  104. package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
  105. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +1 -1
  106. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -1
  107. package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
  108. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +5 -3
  109. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
  110. package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts +5 -0
  111. package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts.map +1 -0
  112. package/src/native-ui/renderer-extension/dist/types/settings/pets-client.d.ts +57 -0
  113. package/src/native-ui/renderer-extension/dist/types/settings/pets-client.d.ts.map +1 -0
  114. package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -1
  115. package/src/native-ui/renderer-extension/dist/types/settings/skin-market.d.ts +4 -0
  116. package/src/native-ui/renderer-extension/dist/types/settings/skin-market.d.ts.map +1 -0
  117. package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts +31 -0
  118. package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts.map +1 -0
  119. package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
  120. package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -1
  121. package/src/native-ui/renderer-extension/package.json +1 -1
  122. package/src/native-ui/renderer-extension/src/agent-selection-state.ts +61 -14
  123. package/src/native-ui/renderer-extension/src/assets.d.ts +5 -0
  124. package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +5 -4
  125. package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +1 -0
  126. package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +78 -33
  127. package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +2 -2
  128. package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +2 -0
  129. package/src/native-ui/renderer-extension/src/renderer-model-client.ts +6 -0
  130. package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +5 -0
  131. package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +1 -0
  132. package/src/native-ui/renderer-extension/src/settings/connections-page.ts +2 -0
  133. package/src/native-ui/renderer-extension/src/settings/icons.ts +15 -6
  134. package/src/native-ui/renderer-extension/src/settings/localization.ts +6 -2
  135. package/src/native-ui/renderer-extension/src/settings/pages.ts +72 -61
  136. package/src/native-ui/renderer-extension/src/settings/pet-market.css +310 -0
  137. package/src/native-ui/renderer-extension/src/settings/pet-market.ts +447 -0
  138. package/src/native-ui/renderer-extension/src/settings/pets-client.ts +74 -0
  139. package/src/native-ui/renderer-extension/src/settings/shell.css +27 -8
  140. package/src/native-ui/renderer-extension/src/settings/shell.ts +3 -1
  141. package/src/native-ui/renderer-extension/src/settings/skin-market.css +213 -0
  142. package/src/native-ui/renderer-extension/src/settings/skin-market.ts +174 -0
  143. package/src/native-ui/renderer-extension/src/settings/skin-runtime.ts +630 -0
  144. package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +2 -1
  145. package/src/native-ui/renderer-extension/test/agent-selection-state.test.ts +58 -0
  146. package/src/native-ui/renderer-extension/test/renderer-binding-probe.test.ts +12 -0
  147. package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +1 -0
  148. package/src/native-ui/renderer-extension/test/settings/localization.test.ts +12 -1
  149. package/src/native-ui/renderer-extension/test/settings/shell.test.ts +6 -0
  150. package/src/native-ui/renderer-extension/test/settings/skin-runtime.test.ts +162 -0
  151. package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts +7 -0
  152. package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts.map +1 -1
  153. package/src/native-ui/shared-contracts/dist/thread-harness-switch.js +3 -0
  154. package/src/native-ui/shared-contracts/dist/thread-harness-switch.js.map +1 -1
  155. package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
  156. package/src/native-ui/shared-contracts/src/thread-harness-switch.ts +3 -0
@@ -109,6 +109,7 @@ const externalHarnessIds = {
109
109
  zcode: harnessIdSchema.parse("zcode"),
110
110
  trae: harnessIdSchema.parse("trae"),
111
111
  'cursor-cli': harnessIdSchema.parse("cursor-cli"),
112
+ cline: harnessIdSchema.parse("cline"),
112
113
  'codex-harness': harnessIdSchema.parse('codex-harness'),
113
114
  } as const;
114
115
 
@@ -128,6 +129,7 @@ const externalAgents: readonly ExternalRendererAgent[] = [
128
129
  "zcode",
129
130
  "trae",
130
131
  "cursor-cli",
132
+ "cline",
131
133
  'codex-harness',
132
134
  ];
133
135
  type HarnessAvailability = Partial<Record<ExternalRendererAgent, RendererAgentAvailability>>;
@@ -503,7 +505,7 @@ export function restoredThreadOwnership(inspection: ThreadInspection): RestoredT
503
505
  ...(permissionModeId ? { permissionModeId } : {}),
504
506
  };
505
507
  }
506
- if (inspection.harnessId === "openclaw" || inspection.harnessId === "hermes" || inspection.harnessId === 'codex-harness' || inspection.harnessId === 'qoder' || inspection.harnessId === 'codebuddy' || inspection.harnessId === 'zcode' || inspection.harnessId === 'trae' || inspection.harnessId === 'cursor-cli') {
508
+ if (inspection.harnessId === "openclaw" || inspection.harnessId === "hermes" || inspection.harnessId === 'codex-harness' || inspection.harnessId === 'qoder' || inspection.harnessId === 'codebuddy' || inspection.harnessId === 'zcode' || inspection.harnessId === 'trae' || inspection.harnessId === 'cursor-cli' || inspection.harnessId === 'cline') {
507
509
  const harnessId = inspection.harnessId;
508
510
  const route = decodeHarnessPluginRoute(inspection.transportModelId);
509
511
  if (!route || route.harnessId !== harnessId) {
@@ -568,7 +570,7 @@ export function shouldTransferComposerState(
568
570
  );
569
571
  }
570
572
 
571
- export function isLateConversationTarget(
573
+ export function isLateConversationTarget(
572
574
  mountedTarget: readonly unknown[] | null,
573
575
  currentTarget: readonly unknown[] | null,
574
576
  ): boolean {
@@ -580,19 +582,43 @@ export function isLateConversationTarget(
580
582
  mountedTarget.length !== currentTarget.length ||
581
583
  mountedTarget.some((value, index) => value !== currentTarget[index])
582
584
  );
583
- }
584
-
585
- export function lateConversationTargetResolution(
585
+ }
586
+
587
+ export function composerTargetResolution(
588
+ mountedTarget: readonly unknown[] | null,
589
+ currentTarget: readonly unknown[] | null,
590
+ sourcePhase: ComposerAgentPhase,
591
+ submissionPending = false,
592
+ ): "none" | "transfer" | "inspect" | "restore-draft" {
593
+ if (!currentTarget) return "none";
594
+ if (
595
+ mountedTarget &&
596
+ mountedTarget.length === currentTarget.length &&
597
+ mountedTarget.every((value, index) => value === currentTarget[index])
598
+ ) {
599
+ return "none";
600
+ }
601
+ if (currentTarget[0] === "default") return "restore-draft";
602
+ if (currentTarget[0] !== "conversation") return "none";
603
+ return mountedTarget?.[0] === "default" && (sourcePhase === "locked" || submissionPending)
604
+ ? "transfer"
605
+ : "inspect";
606
+ }
607
+
608
+ export function lateConversationTargetResolution(
586
609
  mountedTarget: readonly unknown[] | null,
587
610
  currentTarget: readonly unknown[] | null,
588
611
  sourcePhase: ComposerAgentPhase,
589
612
  submissionPending = false,
590
- ): "none" | "transfer" | "inspect" {
591
- if (!isLateConversationTarget(mountedTarget, currentTarget)) return "none";
592
- return mountedTarget?.[0] === "default" && (sourcePhase === "locked" || submissionPending)
593
- ? "transfer"
594
- : "inspect";
595
- }
613
+ ): "none" | "transfer" | "inspect" {
614
+ const resolution = composerTargetResolution(
615
+ mountedTarget,
616
+ currentTarget,
617
+ sourcePhase,
618
+ submissionPending,
619
+ );
620
+ return resolution === "restore-draft" ? "none" : resolution;
621
+ }
596
622
 
597
623
  export function scopedComposerTarget(
598
624
  target: readonly unknown[] | null,
@@ -748,11 +774,11 @@ export function installRendererBindingProbe(
748
774
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
749
775
  getUpdateClient: () => modelControl,
750
776
  getAccountClient: () => modelControl,
751
- getConnectionDiagnostics: () => connectionDiagnostics,
752
- getStorageClient: () => {
753
- const client = modelClientForHost('local');
754
- return client?.inspectStorage && client.optimizeStorage ? { inspectStorage: () => client.inspectStorage!(), optimizeStorage: () => client.optimizeStorage!() } : null;
755
- },
777
+ getConnectionDiagnostics: () => connectionDiagnostics,
778
+ getStorageClient: () => {
779
+ const client = modelClientForHost('local');
780
+ return client?.inspectStorage && client.optimizeStorage ? { inspectStorage: () => client.inspectStorage!(), optimizeStorage: () => client.optimizeStorage!() } : null;
781
+ },
756
782
  getIntegrationsClient: () => {
757
783
  const client = modelClientForHost('local');
758
784
  if (!client?.integrationCatalog || !client.listIntegrations || !client.saveMcp || !client.removeMcp || !client.changeSkill) return null;
@@ -770,6 +796,10 @@ export function installRendererBindingProbe(
770
796
  importHarnessSession: (input) => importSession(input),
771
797
  };
772
798
  },
799
+ getPetClient: () => {
800
+ const client = modelClientForHost("local");
801
+ return client?.petsClient ?? null;
802
+ },
773
803
  openImportedThread: (threadId, signal) =>
774
804
  openRendererThread(threadId, { hostId: "local", signal }),
775
805
  onLocaleChange() {
@@ -803,6 +833,7 @@ export function installRendererBindingProbe(
803
833
  zcode: undefined,
804
834
  trae: undefined,
805
835
  'cursor-cli': undefined,
836
+ cline: undefined,
806
837
  'codex-harness': undefined,
807
838
  },
808
839
  webUi: Object.fromEntries(
@@ -1346,25 +1377,30 @@ export function installRendererBindingProbe(
1346
1377
  }
1347
1378
  };
1348
1379
 
1349
- const refreshMountedConversationTarget = (mounted: MountedComposer): boolean => {
1350
- const currentTarget = findComposerModelTarget(mounted.composer);
1351
- const resolution = lateConversationTargetResolution(
1352
- mounted.modelTarget,
1353
- currentTarget,
1380
+ const refreshMountedComposerTarget = (mounted: MountedComposer): boolean => {
1381
+ const currentTarget = findComposerModelTarget(mounted.composer);
1382
+ const resolution = composerTargetResolution(
1383
+ mounted.modelTarget,
1384
+ currentTarget,
1354
1385
  controller.get(mounted.composer).phase,
1355
1386
  controller.isSubmissionPending(mounted.composer),
1356
1387
  );
1357
1388
  if (resolution === "none") return false;
1358
1389
 
1359
- const previousTarget = mounted.modelTarget;
1360
- const nextHostId = activeModelHostId() ?? mounted.hostId;
1390
+ const nextHostId = activeModelHostId() ?? mounted.hostId;
1361
1391
  const nextControllerTarget = controllerTarget(currentTarget, nextHostId);
1362
1392
  mounted.modelTarget = currentTarget;
1363
1393
  mounted.hostId = nextHostId;
1364
- const rebound =
1365
- resolution === "transfer"
1366
- ? controller.transfer(mounted.composer, mounted.composer, nextControllerTarget)
1367
- : controller.rebindConversation(mounted.composer, nextControllerTarget) !== null;
1394
+ const rebound =
1395
+ resolution === "transfer"
1396
+ ? controller.transfer(mounted.composer, mounted.composer, nextControllerTarget)
1397
+ : controller.rebindTarget(
1398
+ mounted.composer,
1399
+ nextControllerTarget,
1400
+ currentTarget?.[0] === "default"
1401
+ ? readNewThreadAgentPreference(enabledAgentSet)
1402
+ : undefined,
1403
+ ) !== null;
1368
1404
  if (!rebound) {
1369
1405
  mounted.ownershipStatus = "error";
1370
1406
  renderMounted(mounted);
@@ -1376,18 +1412,27 @@ export function installRendererBindingProbe(
1376
1412
  if (shouldRetryExternalThreadUsage(controller.get(mounted.composer).agent, null, null)) {
1377
1413
  scheduleThreadUsageRefresh(mounted);
1378
1414
  }
1379
- } else {
1380
- mounted.composerId = controller.get(mounted.composer).composerId;
1415
+ } else {
1416
+ mounted.composerId = controller.get(mounted.composer).composerId;
1381
1417
  mounted.modelView = { status: "idle" };
1382
1418
  mounted.permissionModeView = { status: "idle" };
1383
1419
  mounted.threadConfiguration = undefined;
1384
- mounted.ownershipStatus = "loading";
1420
+ mounted.ownershipStatus =
1421
+ currentTarget?.[0] === "conversation" ? "loading" : "not-required";
1385
1422
  mounted.usage = null;
1386
1423
  mounted.accountCredits = null;
1387
1424
  mounted.usageRequestGeneration += 1;
1388
1425
  usageRefreshAttempts.delete(mounted.composer);
1389
- if (previousTarget?.[0] === "conversation") renderMounted(mounted);
1390
- void loadThreadOwnership(mounted);
1426
+ renderMounted(mounted);
1427
+ if (currentTarget?.[0] === "conversation") {
1428
+ void loadThreadOwnership(mounted);
1429
+ } else {
1430
+ applyComposerAgent(mounted.composer);
1431
+ const state = controller.get(mounted.composer);
1432
+ if (state.agent !== "codex") void loadExternalCatalog(mounted);
1433
+ void refreshDraftCodexUsage(mounted);
1434
+ void refreshCommands(mounted);
1435
+ }
1391
1436
  }
1392
1437
  sidebarAgentIcons.refresh();
1393
1438
  return true;
@@ -2669,7 +2714,7 @@ export function installRendererBindingProbe(
2669
2714
  const state = controller.get(composer);
2670
2715
  const hideCodexControls = controller.isSwitching(composer) || state.agent !== "codex";
2671
2716
  reconcileComposerNativeControls(mounted.control, hideCodexControls, hideCodexControls);
2672
- if (refreshTargets) refreshMountedConversationTarget(mounted);
2717
+ if (refreshTargets) refreshMountedComposerTarget(mounted);
2673
2718
  }
2674
2719
  for (const editor of document.querySelectorAll(EDITOR_SELECTOR)) {
2675
2720
  const composer = composerForEditor(editor);
@@ -39,7 +39,7 @@ export function rendererHarnessHandoffMessages(locale: RendererSettingsLocale) {
39
39
  includeLabel: "将要交接",
40
40
  includeOptions: { conversation: "近期对话", plan: "当前计划", evidence: "测试与工具证据", files: "文件变更", unresolved: "未解决问题" },
41
41
  notePlaceholder: "例如:按上面的方案继续实现,先核对当前文件状态和未完成步骤",
42
- context: "下一条消息会自动携带近期对话、最新方案、计划和变更文件。",
42
+ context: "确认后会先连接目标 Harness,失败将自动恢复。下一条消息会携带已选的近期对话、最新方案、计划和变更文件。",
43
43
  running: "若当前回合仍在运行,请先停止或等待完成。",
44
44
  cancel: "取消",
45
45
  confirm: "确认接力",
@@ -54,7 +54,7 @@ export function rendererHarnessHandoffMessages(locale: RendererSettingsLocale) {
54
54
  includeLabel: "Include",
55
55
  includeOptions: { conversation: "Recent conversation", plan: "Current plan", evidence: "Test and tool evidence", files: "File changes", unresolved: "Open issues" },
56
56
  notePlaceholder: "For example: implement the plan above; verify the working tree and remaining steps first",
57
- context: "Your next message will include recent context, the latest solution, plan, and changed files.",
57
+ context: "The target Harness is connected before the switch commits; failures roll back automatically. Your next message carries the selected recent conversation, latest solution, plan, and changed files.",
58
58
  running: "If a turn is still running, stop it or wait for it to finish first.",
59
59
  cancel: "Cancel",
60
60
  confirm: "Hand off",
@@ -70,6 +70,7 @@ const AGENT_DESCRIPTIONS: Record<string, string> = {
70
70
  trae: 'Trae 原生协同',
71
71
  'kiro-cli': 'Kiro CLI 智能编程',
72
72
  'cursor-cli': 'Cursor CLI 原生协同',
73
+ cline: 'Cline 自主编程智能体',
73
74
  };
74
75
 
75
76
  const AGENT_COLORS: Record<string, string> = {
@@ -93,6 +94,7 @@ const AGENT_COLORS: Record<string, string> = {
93
94
  zcode: '#4f46e5',
94
95
  trae: '#0284c7',
95
96
  'cursor-cli': '#475569',
97
+ cline: '#be185d',
96
98
  };
97
99
 
98
100
  function getAgentColor(agentId: string): string {
@@ -105,6 +105,10 @@ import {
105
105
  createRendererSessionImportClient,
106
106
  type RendererSessionImportClient,
107
107
  } from "./renderer-session-import-client.js";
108
+ import {
109
+ createRendererPetsClient,
110
+ type RendererPetsClient,
111
+ } from "./settings/pets-client.js";
108
112
 
109
113
  export const HARNESS_INSPECT_METHOD = "codexhost/harness/inspect";
110
114
  export const HARNESS_INSTALL_METHOD = "codexhost/harness/install";
@@ -174,6 +178,7 @@ function notificationTarget(manager: RequestManagerCandidate): RequestManagerCan
174
178
  }
175
179
 
176
180
  export interface RendererModelClient extends Partial<RendererSessionImportClient>, Partial<RendererIntegrationsClient> {
181
+ petsClient?: RendererPetsClient;
177
182
  inspectStorage?(): Promise<import('./settings/storage-page.js').RendererStorageInspection>;
178
183
  optimizeStorage?(): Promise<{ before: import('./settings/storage-page.js').RendererStorageInspection; after: import('./settings/storage-page.js').RendererStorageInspection }>;
179
184
  listCollaborationAgents?(): Promise<Array<{ id: string; name: string; available: boolean; lead: boolean }>>;
@@ -343,6 +348,7 @@ export function createRendererModelClient(
343
348
  ...createRendererSessionImportClient(async (method, params) =>
344
349
  manager.sendRequest(method, params),
345
350
  ),
351
+ petsClient: createRendererPetsClient((method, params) => manager.sendRequest(method, params)),
346
352
  async forkThread(input: ExternalThreadForkParams): Promise<ExternalThreadForkResult> {
347
353
  const params = externalThreadForkParamsSchema.parse(input);
348
354
  const result = await manager.sendRequest(THREAD_FORK_METHOD, params);
@@ -10,6 +10,7 @@ import {
10
10
  type RendererCodexAccountClient,
11
11
  type RendererUpdateClient,
12
12
  type RendererStorageClient,
13
+ type RendererPetsClient,
13
14
  } from "./settings/pages.js";
14
15
  import type {
15
16
  RendererSessionImportClient,
@@ -20,6 +21,7 @@ import {
20
21
  installRendererSettingsHeaderTrigger,
21
22
  type RendererSettingsHeaderTriggerControl,
22
23
  } from "./settings/trigger.js";
24
+ import { restoreRendererSkin } from "./settings/skin-runtime.js";
23
25
 
24
26
  const UPDATE_CHECK_TIMEOUT_MS = 5_000;
25
27
  const UPDATE_RETRY_DELAYS_MS = [1_000, 3_000, 10_000, 30_000] as const;
@@ -31,6 +33,7 @@ export interface RendererSettingsLifecycleOptions {
31
33
  getAccountClient?(): RendererCodexAccountClient | null;
32
34
  getSessionImportClient?(): RendererSessionImportClient | null;
33
35
  getStorageClient?(): RendererStorageClient | null;
36
+ getPetClient?(): RendererPetsClient | null;
34
37
  openImportedThread?: RendererImportedThreadOpener;
35
38
  onLocaleChange?(locale: RendererSettingsLocale): void;
36
39
  }
@@ -45,6 +48,7 @@ export function installRendererSettingsLifecycle(
45
48
  ownerWindow: Window = window,
46
49
  options: RendererSettingsLifecycleOptions = {},
47
50
  ): RendererSettingsLifecycleControl {
51
+ restoreRendererSkin(ownerWindow);
48
52
  const lifecycleController = new AbortController();
49
53
  let locale = resolveRendererSettingsLocale(ownerWindow.navigator.languages);
50
54
  let shell: RendererSettingsShell | null = null;
@@ -79,6 +83,7 @@ export function installRendererSettingsLifecycle(
79
83
  },
80
84
  options.getIntegrationsClient,
81
85
  options.getStorageClient,
86
+ options.getPetClient,
82
87
  );
83
88
  const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document);
84
89
  const nextTrigger = installRendererSettingsHeaderTrigger({
@@ -147,6 +147,7 @@ export function rendererAgentForThreadOwnership(
147
147
  if (ownership.harnessId === "zcode") return "zcode";
148
148
  if (ownership.harnessId === "trae") return "trae";
149
149
  if (ownership.harnessId === "cursor-cli") return "cursor-cli";
150
+ if (ownership.harnessId === "cline") return "cline";
150
151
  if (ownership.harnessId === 'codex-harness') return 'codex-harness';
151
152
  return null;
152
153
  }
@@ -31,6 +31,7 @@ export const HARNESS_INSTALL_COMMANDS: Readonly<Partial<Record<ExternalRendererA
31
31
  antigravity: { command: "npm install -g @google/antigravity-cli" },
32
32
  openclaw: { command: "npm install -g openclaw" },
33
33
  hermes: { command: "pip install hermes-agent" },
34
+ cline: { command: "npm install -g cline" },
34
35
  "codex-harness": { command: "npm install -g @openai/codex" },
35
36
  });
36
37
 
@@ -50,6 +51,7 @@ const HARNESS_INSTALL_URLS: Readonly<Record<ExternalRendererAgent, string>> = Ob
50
51
  zcode: "https://zcode.z.ai/",
51
52
  trae: "https://www.trae.ai/",
52
53
  'cursor-cli': "https://cursor.com/docs/cli/installation",
54
+ cline: "https://docs.cline.bot/usage/cli-overview",
53
55
  'codex-harness': 'https://developers.openai.com/codex/',
54
56
  });
55
57
 
@@ -31,8 +31,11 @@ import Search from "lucide/dist/esm/icons/search.mjs";
31
31
  import CircleHelp from "lucide/dist/esm/icons/circle-question-mark.mjs";
32
32
  import X from "lucide/dist/esm/icons/x.mjs";
33
33
  import Users from "lucide/dist/esm/icons/users.mjs";
34
- import Plus from "lucide/dist/esm/icons/plus.mjs";
35
- import Database from 'lucide/dist/esm/icons/database.mjs';
34
+ import Plus from "lucide/dist/esm/icons/plus.mjs";
35
+ import Database from 'lucide/dist/esm/icons/database.mjs';
36
+ import Palette from "lucide/dist/esm/icons/palette.mjs";
37
+ import ShieldCheck from "lucide/dist/esm/icons/shield-check.mjs";
38
+ import PawPrint from "lucide/dist/esm/icons/paw-print.mjs";
36
39
  import harnessMixLogoUrl from "../../../../assets/brand-harness-mix.png";
37
40
 
38
41
  export const RENDERER_SETTINGS_ICON_NAMES = [
@@ -68,8 +71,11 @@ export const RENDERER_SETTINGS_ICON_NAMES = [
68
71
  "trash",
69
72
  "terminal",
70
73
  "search",
71
- "help",
72
- "storage",
74
+ "help",
75
+ "palette",
76
+ "shield",
77
+ "storage",
78
+ "pets",
73
79
  ] as const;
74
80
 
75
81
  export type RendererSettingsIconName = (typeof RENDERER_SETTINGS_ICON_NAMES)[number];
@@ -107,8 +113,11 @@ const iconNodes = {
107
113
  trash: Trash,
108
114
  terminal: Terminal,
109
115
  search: Search,
110
- help: CircleHelp,
111
- storage: Database,
116
+ help: CircleHelp,
117
+ palette: Palette,
118
+ shield: ShieldCheck,
119
+ storage: Database,
120
+ pets: PawPrint,
112
121
  } satisfies Record<RendererSettingsIconName, IconNode>;
113
122
 
114
123
  export function isRendererSettingsIconName(value: string): value is RendererSettingsIconName {
@@ -515,7 +515,9 @@ const ENGLISH_MESSAGES: RendererSettingsMessages = Object.freeze({
515
515
  skills: 'Skills',
516
516
  "session-import": "Session Import",
517
517
  storage: "Storage & verification",
518
- updates: "Updates",
518
+ skins: "Skins",
519
+ pets: "Pets",
520
+ updates: "Updates",
519
521
  about: "About",
520
522
  }),
521
523
  });
@@ -773,7 +775,9 @@ const CHINESE_MESSAGES: RendererSettingsMessages = Object.freeze({
773
775
  skills: 'Skills',
774
776
  "session-import": "会话导入",
775
777
  storage: "存储与验证",
776
- updates: "更新",
778
+ skins: "皮肤",
779
+ pets: "桌宠",
780
+ updates: "更新",
777
781
  about: "关于",
778
782
  }),
779
783
  });
@@ -26,11 +26,14 @@ import {
26
26
  type RendererSessionImportClient,
27
27
  type RendererImportedThreadOpener,
28
28
  } from "./session-import-page.js";
29
- import { createReleaseNotesElement } from "./release-notes.js";
30
- import { createMcpSettingsPage, createSkillsSettingsPage } from './integrations-page.js';
31
- import type { RendererIntegrationsClient } from '../renderer-integrations-client.js';
32
- import { createAccountsSettingsPage, type RendererCodexAccountClient } from "./accounts-page.js";
33
- import { createStorageSettingsPage, type RendererStorageClient } from './storage-page.js';
29
+ import { createReleaseNotesElement } from "./release-notes.js";
30
+ import { createMcpSettingsPage, createSkillsSettingsPage } from './integrations-page.js';
31
+ import type { RendererIntegrationsClient } from '../renderer-integrations-client.js';
32
+ import { createAccountsSettingsPage, type RendererCodexAccountClient } from "./accounts-page.js";
33
+ import { createStorageSettingsPage, type RendererStorageClient } from './storage-page.js';
34
+ import { createSkinSettingsPage } from "./skin-market.js";
35
+ import { createPetSettingsPage } from "./pet-market.js";
36
+ import type { RendererPetsClient } from "./pets-client.js";
34
37
 
35
38
  export type {
36
39
  RendererConnectionAgentSnapshot,
@@ -74,23 +77,25 @@ function windowsInstallerDownloadUrl(window: Window | null | undefined, version:
74
77
 
75
78
  export const DEFAULT_RENDERER_SETTINGS_PAGE_IDS = [
76
79
  "connections",
77
- "accounts",
78
- "mcp",
79
- "skills",
80
- "session-import",
81
- "storage",
80
+ "accounts",
81
+ "mcp",
82
+ "skills",
83
+ "session-import",
84
+ "storage",
85
+ "skins",
86
+ "pets",
82
87
  "updates",
83
88
  "about",
84
89
  ] as const;
85
90
 
86
91
  export type DefaultRendererSettingsPageId = (typeof DEFAULT_RENDERER_SETTINGS_PAGE_IDS)[number];
87
92
 
88
- export interface RendererUpdateClient {
89
- checkUpdate(): Promise<UpdateCheckResult>;
90
- startUpdate(): Promise<UpdateStartResult>;
91
- readUpdateStatus(): Promise<UpdateStatusResult>;
92
- readCurrentVersion?(): Promise<{ version: string }>;
93
- }
93
+ export interface RendererUpdateClient {
94
+ checkUpdate(): Promise<UpdateCheckResult>;
95
+ startUpdate(): Promise<UpdateStartResult>;
96
+ readUpdateStatus(): Promise<UpdateStatusResult>;
97
+ readCurrentVersion?(): Promise<{ version: string }>;
98
+ }
94
99
 
95
100
  function panelIconName(view: string): RendererSettingsIconName {
96
101
  if (view === "failed" || view === "error") return "alert";
@@ -161,10 +166,10 @@ function formatUpdateBytes(value: number): string {
161
166
  return `${scaled.toFixed(scaled >= 10 ? 0 : 1)} ${unit}`;
162
167
  }
163
168
 
164
- export function aboutPage(
165
- messages: RendererSettingsMessages,
166
- getClient: () => RendererUpdateClient | null = () => null,
167
- ): RendererSettingsPageDefinition {
169
+ export function aboutPage(
170
+ messages: RendererSettingsMessages,
171
+ getClient: () => RendererUpdateClient | null = () => null,
172
+ ): RendererSettingsPageDefinition {
168
173
  return Object.freeze({
169
174
  id: "about",
170
175
  label: messages.pageLabels.about,
@@ -187,7 +192,7 @@ export function aboutPage(
187
192
  versionRow.className = "settings-about-version-row";
188
193
  const versionBadge = document.createElement("span");
189
194
  versionBadge.className = "settings-about-version-badge";
190
- versionBadge.textContent = "v…";
195
+ versionBadge.textContent = "v…";
191
196
  const checkUpdateBtn = document.createElement("button");
192
197
  checkUpdateBtn.type = "button";
193
198
  checkUpdateBtn.className = "settings-command-button settings-command-button--secondary";
@@ -227,27 +232,27 @@ export function aboutPage(
227
232
  repositoryUrl,
228
233
  );
229
234
  repositorySection.append(openSource, repository);
230
- panel.append(product, tagline, versionRow, introduction, starCallout, repositorySection);
231
- context.content.append(heading, panel);
232
- const client = getClient();
233
- const readVersion = client?.readCurrentVersion
234
- ? () => client.readCurrentVersion!()
235
- : client
236
- ? () => client.checkUpdate().then(({ currentVersion }) => ({ version: currentVersion }))
237
- : null;
238
- if (readVersion) {
239
- void context.runLatest(readVersion, {
240
- success(result) {
241
- versionBadge.textContent = `v${result.version}`;
242
- },
243
- failure() {
244
- versionBadge.textContent = "v—";
245
- },
246
- });
247
- } else {
248
- versionBadge.textContent = "v—";
249
- }
250
- return undefined;
235
+ panel.append(product, tagline, versionRow, introduction, starCallout, repositorySection);
236
+ context.content.append(heading, panel);
237
+ const client = getClient();
238
+ const readVersion = client?.readCurrentVersion
239
+ ? () => client.readCurrentVersion!()
240
+ : client
241
+ ? () => client.checkUpdate().then(({ currentVersion }) => ({ version: currentVersion }))
242
+ : null;
243
+ if (readVersion) {
244
+ void context.runLatest(readVersion, {
245
+ success(result) {
246
+ versionBadge.textContent = `v${result.version}`;
247
+ },
248
+ failure() {
249
+ versionBadge.textContent = "v—";
250
+ },
251
+ });
252
+ } else {
253
+ versionBadge.textContent = "v—";
254
+ }
255
+ return undefined;
251
256
  },
252
257
  });
253
258
  }
@@ -625,20 +630,23 @@ export function createDefaultRendererSettingsPages(
625
630
  getDiagnostics: () => RendererConnectionDiagnostics | null = () => null,
626
631
  getAccountClient: () => RendererCodexAccountClient | null = () => null,
627
632
  getSessionImportClient: () => RendererSessionImportClient | null = () => null,
628
- openImportedThread: RendererImportedThreadOpener = () =>
629
- Promise.reject(new Error("Imported Thread navigation is unavailable")),
630
- getIntegrationsClient: () => RendererIntegrationsClient | null = () => null,
631
- getStorageClient: () => RendererStorageClient | null = () => null,
632
- ): readonly RendererSettingsPageDefinition[] {
633
+ openImportedThread: RendererImportedThreadOpener = () =>
634
+ Promise.reject(new Error("Imported Thread navigation is unavailable")),
635
+ getIntegrationsClient: () => RendererIntegrationsClient | null = () => null,
636
+ getStorageClient: () => RendererStorageClient | null = () => null,
637
+ getPetClient: () => RendererPetsClient | null = () => null,
638
+ ): readonly RendererSettingsPageDefinition[] {
633
639
  return Object.freeze([
634
640
  createConnectionsSettingsPage(messages, getDiagnostics),
635
- createAccountsSettingsPage(messages, getAccountClient),
636
- createMcpSettingsPage(messages, getIntegrationsClient),
637
- createSkillsSettingsPage(messages, getIntegrationsClient),
638
- createSessionImportSettingsPage(messages, getSessionImportClient, openImportedThread),
639
- createStorageSettingsPage(messages, getStorageClient),
641
+ createAccountsSettingsPage(messages, getAccountClient),
642
+ createMcpSettingsPage(messages, getIntegrationsClient),
643
+ createSkillsSettingsPage(messages, getIntegrationsClient),
644
+ createSessionImportSettingsPage(messages, getSessionImportClient, openImportedThread),
645
+ createStorageSettingsPage(messages, getStorageClient),
646
+ createSkinSettingsPage(messages),
647
+ createPetSettingsPage(messages, getPetClient),
640
648
  updatesPage(messages, getUpdateClient),
641
- aboutPage(messages, getUpdateClient),
649
+ aboutPage(messages, getUpdateClient),
642
650
  ]);
643
651
  }
644
652
 
@@ -648,9 +656,10 @@ export function createDefaultRendererSettingsRegistry(
648
656
  getDiagnostics: () => RendererConnectionDiagnostics | null = () => null,
649
657
  getAccountClient: () => RendererCodexAccountClient | null = () => null,
650
658
  getSessionImportClient: () => RendererSessionImportClient | null = () => null,
651
- openImportedThread?: RendererImportedThreadOpener,
652
- getIntegrationsClient?: () => RendererIntegrationsClient | null,
653
- getStorageClient?: () => RendererStorageClient | null,
659
+ openImportedThread?: RendererImportedThreadOpener,
660
+ getIntegrationsClient?: () => RendererIntegrationsClient | null,
661
+ getStorageClient?: () => RendererStorageClient | null,
662
+ getPetClient?: () => RendererPetsClient | null,
654
663
  ): RendererSettingsPageRegistry {
655
664
  return createRendererSettingsPageRegistry(
656
665
  createDefaultRendererSettingsPages(
@@ -659,12 +668,14 @@ export function createDefaultRendererSettingsRegistry(
659
668
  getDiagnostics,
660
669
  getAccountClient,
661
670
  getSessionImportClient,
662
- openImportedThread,
663
- getIntegrationsClient,
664
- getStorageClient,
671
+ openImportedThread,
672
+ getIntegrationsClient,
673
+ getStorageClient,
674
+ getPetClient,
665
675
  ),
666
676
  );
667
677
  }
668
678
 
669
- export type { RendererCodexAccountClient } from "./accounts-page.js";
670
- export type { RendererStorageClient, RendererStorageInspection } from './storage-page.js';
679
+ export type { RendererCodexAccountClient } from "./accounts-page.js";
680
+ export type { RendererStorageClient, RendererStorageInspection } from './storage-page.js';
681
+ export type { RendererPetsClient } from "./pets-client.js";