@harness-mix/cli 0.1.9 → 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 (55) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +25 -30
  3. package/output/native-build/desktop-controller.mjs +1 -1
  4. package/output/native-build/renderer-extension.js +703 -41
  5. package/package.json +9 -8
  6. package/scripts/native-config-test.cjs +32 -2
  7. package/scripts/native-ui-smoke.cjs +39 -4
  8. package/scripts/pets-test.cjs +146 -0
  9. package/src/main/native/launcher.js +49 -2
  10. package/src/main/native/pets.js +176 -15
  11. package/src/main/native/protocol.js +9 -8
  12. package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +4 -4
  13. package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -1
  14. package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
  15. package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +4 -4
  16. package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +7 -0
  17. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +1 -0
  18. package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
  19. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts +1 -0
  20. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
  21. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +2 -0
  22. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
  23. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +2 -1
  24. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
  25. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +1 -1
  26. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -1
  27. package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
  28. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +5 -3
  29. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
  30. package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts +5 -0
  31. package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts.map +1 -0
  32. package/src/native-ui/renderer-extension/dist/types/settings/pets-client.d.ts +57 -0
  33. package/src/native-ui/renderer-extension/dist/types/settings/pets-client.d.ts.map +1 -0
  34. package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -1
  35. package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts.map +1 -1
  36. package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
  37. package/src/native-ui/renderer-extension/src/agent-selection-state.ts +45 -14
  38. package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +69 -27
  39. package/src/native-ui/renderer-extension/src/renderer-model-client.ts +6 -0
  40. package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +3 -0
  41. package/src/native-ui/renderer-extension/src/settings/icons.ts +15 -12
  42. package/src/native-ui/renderer-extension/src/settings/localization.ts +2 -0
  43. package/src/native-ui/renderer-extension/src/settings/pages.ts +74 -66
  44. package/src/native-ui/renderer-extension/src/settings/pet-market.css +310 -0
  45. package/src/native-ui/renderer-extension/src/settings/pet-market.ts +447 -0
  46. package/src/native-ui/renderer-extension/src/settings/pets-client.ts +74 -0
  47. package/src/native-ui/renderer-extension/src/settings/shell.css +27 -8
  48. package/src/native-ui/renderer-extension/src/settings/shell.ts +4 -3
  49. package/src/native-ui/renderer-extension/src/settings/skin-runtime.ts +234 -3
  50. package/src/native-ui/renderer-extension/test/agent-selection-state.test.ts +58 -0
  51. package/src/native-ui/renderer-extension/test/renderer-binding-probe.test.ts +12 -0
  52. package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +1 -0
  53. package/src/native-ui/renderer-extension/test/settings/localization.test.ts +1 -0
  54. package/src/native-ui/renderer-extension/test/settings/shell.test.ts +3 -0
  55. package/src/native-ui/renderer-extension/test/settings/skin-runtime.test.ts +28 -2
@@ -183,6 +183,8 @@ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] body::after {
183
183
  return `
184
184
  html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] {
185
185
  color-scheme: ${skin.dark ? "dark" : "light"};
186
+ --codex-titlebar-tint: transparent !important;
187
+ --header-tint: transparent !important;
186
188
  --color-text: ${text}; --color-text-primary: ${text}; --color-token-text-primary: ${text};
187
189
  --color-token-foreground: ${text}; --color-text-prose: ${text}; --color-text-emphasis: ${text};
188
190
  --color-text-primary-surface: ${text}; --color-text-primary-soft: ${text};
@@ -204,6 +206,7 @@ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] {
204
206
  --wb-text-primary: ${text}; --wb-text-secondary: ${secondaryText}; --wb-text-tertiary: ${tertiaryText};
205
207
  --color-control-thumb-foreground: ${text};
206
208
  --color-text-mode-toggle-inactive: ${secondaryText};
209
+ --color-text-user-message: ${text} !important;
207
210
  --color-surface: color-mix(in srgb, ${surface} 92%, transparent);
208
211
  --color-surface-secondary: color-mix(in srgb, ${surface} 88%, transparent);
209
212
  --color-surface-tertiary: color-mix(in srgb, ${surface} 94%, transparent);
@@ -225,6 +228,7 @@ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] {
225
228
  --color-background-secondary-soft-alpha: color-mix(in srgb, ${text} 7%, transparent);
226
229
  --color-background-secondary-soft-hover: color-mix(in srgb, ${text} 11%, transparent);
227
230
  --color-background-other-user-message: color-mix(in srgb, ${surface} 76%, transparent);
231
+ --color-background-user-message: color-mix(in srgb, ${surface} 92%, transparent) !important;
228
232
  --color-background-user-message-compact: color-mix(in srgb, ${surface} 88%, transparent);
229
233
  --color-border: color-mix(in srgb, ${text} 16%, transparent);
230
234
  --color-border-subtle: color-mix(in srgb, ${text} 8%, transparent);
@@ -329,19 +333,246 @@ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
329
333
  ) {
330
334
  box-sizing: border-box;
331
335
  color: ${text} !important;
332
- background: color-mix(in srgb, ${surface} 90%, transparent) !important;
336
+ background: color-mix(in srgb, ${surface} 96%, transparent) !important;
333
337
  border: 1px solid color-mix(in srgb, ${accent} 16%, transparent) !important;
334
338
  border-radius: 18px;
335
339
  padding: 14px 16px 12px;
336
340
  box-shadow: 0 8px 24px color-mix(in srgb, ${text} 8%, transparent) !important;
337
- backdrop-filter: none !important;
341
+ backdrop-filter: blur(16px) saturate(120%) !important;
342
+ }
343
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
344
+ table,
345
+ [data-markdown-table],
346
+ [data-markdown-table] > div,
347
+ .main-surface table,
348
+ main table,
349
+ article table,
350
+ [data-response-annotation-conversation] table
351
+ ) {
352
+ background-color: color-mix(in srgb, ${surface} 95%, transparent) !important;
353
+ backdrop-filter: blur(16px) saturate(120%) !important;
354
+ }
355
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
356
+ [data-markdown-table],
357
+ .main-surface table,
358
+ main table,
359
+ article table,
360
+ table
361
+ ) {
362
+ border: 1px solid color-mix(in srgb, ${accent} 24%, transparent) !important;
363
+ border-radius: 12px !important;
364
+ box-shadow: 0 4px 20px color-mix(in srgb, ${text} 8%, transparent) !important;
365
+ overflow: hidden !important;
366
+ }
367
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
368
+ table th,
369
+ [data-markdown-table] th,
370
+ [data-response-annotation-conversation] th
371
+ ) {
372
+ background-color: color-mix(in srgb, ${surface} 98%, transparent) !important;
373
+ border-bottom: 2px solid color-mix(in srgb, ${accent} 28%, transparent) !important;
374
+ color: ${text} !important;
375
+ font-weight: 600 !important;
376
+ padding: 8px 14px !important;
377
+ }
378
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
379
+ table td,
380
+ [data-markdown-table] td,
381
+ [data-response-annotation-conversation] td,
382
+ [data-response-annotation-conversation] :where(th, td)
383
+ ) {
384
+ border-bottom: 1px solid color-mix(in srgb, ${text} 12%, transparent) !important;
385
+ border-right: 1px solid color-mix(in srgb, ${text} 6%, transparent) !important;
386
+ color: ${text} !important;
387
+ padding: 8px 14px !important;
388
+ }
389
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
390
+ table tr:last-child td,
391
+ [data-markdown-table] tr:last-child td
392
+ ) {
393
+ border-bottom: none !important;
394
+ }
395
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
396
+ table tr:hover td,
397
+ [data-markdown-table] tr:hover td
398
+ ) {
399
+ background-color: color-mix(in srgb, ${accent} 10%, transparent) !important;
338
400
  }
339
401
  html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(.composer-surface-chrome, [data-user-message-bubble], [data-codex-approval-surface]) {
340
402
  color: ${text} !important;
341
- background: color-mix(in srgb, ${surface} 82%, transparent) !important;
403
+ background: color-mix(in srgb, ${surface} 92%, transparent) !important;
342
404
  border-color: color-mix(in srgb, ${accent} 24%, transparent) !important;
343
405
  box-shadow: 0 8px 24px color-mix(in srgb, ${accent} 12%, transparent) !important;
406
+ backdrop-filter: blur(16px) saturate(120%) !important;
407
+ }
408
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-user-message-bubble],
409
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-user-message-bubble] :where(
410
+ [data-markdown-text-tone="user-message"],
411
+ [data-markdown-han-text],
412
+ p,
413
+ span,
414
+ a,
415
+ code
416
+ ) {
417
+ color: ${text} !important;
418
+ }
419
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
420
+ header,
421
+ header > div,
422
+ [data-pip-obstacle="app-shell-header"],
423
+ [data-pip-obstacle="app-shell-header"] > div,
424
+ [class*="_ApplicationMenuTopBar_"],
425
+ [class*="_FloatingHeader_"],
426
+ [class*="_TitleBar_"],
427
+ [data-testid="app-shell-header-context-menu-surface"],
428
+ [data-app-shell-header-toolbar],
429
+ [data-app-shell-tab-row],
430
+ [data-app-shell-application-menu-bar]
431
+ ) {
432
+ background: transparent !important;
433
+ background-color: transparent !important;
434
+ border-color: transparent !important;
435
+ box-shadow: none !important;
436
+ }
437
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] header :where(.bg-surface, .bg-surface-secondary, .bg-surface-elevated):not([data-tab-id], [data-tab-id] *),
438
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-pip-obstacle="app-shell-header"] :where(.bg-surface, .bg-surface-secondary, .bg-surface-elevated):not([data-tab-id], [data-tab-id] *) {
439
+ background: transparent !important;
440
+ background-color: transparent !important;
441
+ }
442
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-main-content-top-fade] {
443
+ background: none !important;
444
+ background-image: none !important;
445
+ opacity: 0 !important;
446
+ }
447
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > div:first-child {
448
+ background: transparent !important;
449
+ background-color: transparent !important;
450
+ border: 0 !important;
451
+ border-radius: 0 !important;
452
+ box-shadow: none !important;
344
453
  backdrop-filter: none !important;
454
+ color: color-mix(in srgb, ${text} 82%, ${accent}) !important;
455
+ font-size: 13px !important;
456
+ font-weight: 500 !important;
457
+ text-shadow: 0 1px 2px color-mix(in srgb, ${surface} 72%, transparent) !important;
458
+ }
459
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > div:first-child > div {
460
+ background: transparent !important;
461
+ background-color: transparent !important;
462
+ }
463
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > div:first-child button {
464
+ background: transparent !important;
465
+ background-color: transparent !important;
466
+ border-color: transparent !important;
467
+ box-shadow: none !important;
468
+ color: inherit !important;
469
+ font-size: inherit !important;
470
+ font-weight: inherit !important;
471
+ }
472
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] header button:not([data-codexhost-settings-trigger] *),
473
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-pip-obstacle="app-shell-header"] button:not([data-codexhost-settings-trigger] *),
474
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-header-obstacle] button:not([data-codexhost-settings-trigger] *),
475
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-testid="app-shell-header-context-menu-surface"] button:not([data-codexhost-settings-trigger] *),
476
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-header-toolbar] button:not([data-codexhost-settings-trigger] *),
477
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] header [role="button"]:not([data-codexhost-settings-trigger] *) {
478
+ background: transparent !important;
479
+ background-color: transparent !important;
480
+ border-color: transparent !important;
481
+ box-shadow: none !important;
482
+ }
483
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > button[aria-haspopup="menu"] {
484
+ color: color-mix(in srgb, ${text} 62%, transparent) !important;
485
+ background: transparent !important;
486
+ background-color: transparent !important;
487
+ border-color: transparent !important;
488
+ box-shadow: none !important;
489
+ filter: none !important;
490
+ outline: none !important;
491
+ }
492
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] header button:not([data-codexhost-settings-trigger] *):hover,
493
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-pip-obstacle="app-shell-header"] button:not([data-codexhost-settings-trigger] *):hover,
494
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-header-obstacle] button:not([data-codexhost-settings-trigger] *):hover,
495
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-testid="app-shell-header-context-menu-surface"] button:not([data-codexhost-settings-trigger] *):hover,
496
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-header-toolbar] button:not([data-codexhost-settings-trigger] *):hover {
497
+ background: color-mix(in srgb, ${text} 12%, transparent) !important;
498
+ background-color: color-mix(in srgb, ${text} 12%, transparent) !important;
499
+ }
500
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > button[aria-haspopup="menu"]:hover,
501
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > button[aria-haspopup="menu"]:focus-visible {
502
+ color: ${text} !important;
503
+ background: transparent !important;
504
+ background-color: transparent !important;
505
+ border-color: transparent !important;
506
+ box-shadow: none !important;
507
+ filter: none !important;
508
+ outline: none !important;
509
+ }
510
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] header button:not([data-codexhost-settings-trigger] *):active,
511
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-pip-obstacle="app-shell-header"] button:not([data-codexhost-settings-trigger] *):active,
512
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-header-obstacle] button:not([data-codexhost-settings-trigger] *):active,
513
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-testid="app-shell-header-context-menu-surface"] button:not([data-codexhost-settings-trigger] *):active,
514
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-header-toolbar] button:not([data-codexhost-settings-trigger] *):active {
515
+ background: color-mix(in srgb, ${text} 18%, transparent) !important;
516
+ background-color: color-mix(in srgb, ${text} 18%, transparent) !important;
517
+ }
518
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > button[aria-haspopup="menu"]:active,
519
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-shell-page-header] > [data-app-shell-header-toolbar] > button[aria-haspopup="menu"][data-state="open"] {
520
+ color: ${text} !important;
521
+ background: transparent !important;
522
+ background-color: transparent !important;
523
+ border-color: transparent !important;
524
+ box-shadow: none !important;
525
+ filter: none !important;
526
+ outline: none !important;
527
+ }
528
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] :where(
529
+ [data-app-shell-tab-controller],
530
+ [data-app-shell-tab-controller] [data-tab-id],
531
+ .\@container\/app-shell-tab,
532
+ [data-tab-id].group\/tab,
533
+ .group\/tab[data-tab-id],
534
+ [data-tab-id],
535
+ [data-app-shell-tab-capture]
536
+ ) {
537
+ max-width: min(680px, 55vw) !important;
538
+ }
539
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-tab-id] :where([class*="max-w-"], [class*="min-w-0"]) {
540
+ max-width: none !important;
541
+ }
542
+ @media (min-width: 100rem) {
543
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] {
544
+ --thread-content-max-width: min(72rem, calc(100vw - 22rem));
545
+ }
546
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [class*="thread-content-max-width"] {
547
+ --thread-content-max-width: min(72rem, calc(100vw - 22rem)) !important;
548
+ }
549
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-local-conversation-final-assistant] {
550
+ max-width: min(72rem, calc(100vw - 22rem)) !important;
551
+ }
552
+ }
553
+ @container home-main-content (inline-size <= 52rem) {
554
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] div[role="main"]:has([data-composer-placement="home"]) [class*="_Hero_"] {
555
+ min-block-size: clamp(8.5rem, 28cqh, 12.5rem) !important;
556
+ flex-basis: auto !important;
557
+ padding-block: 0.75rem 1rem !important;
558
+ }
559
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] div[role="main"]:has([data-composer-placement="home"]) [data-feature="game-source"] {
560
+ padding-inline: 1rem;
561
+ font-size: clamp(1.35rem, 4cqw, 1.75rem) !important;
562
+ line-height: 1.22 !important;
563
+ }
564
+ }
565
+ @media (max-width: 68.75rem), (max-height: 47.5rem) {
566
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] div[role="main"]:has([data-composer-placement="home"]) [class*="_Hero_"] {
567
+ min-block-size: clamp(8.5rem, 28vh, 12.5rem) !important;
568
+ flex-basis: auto !important;
569
+ padding-block: 0.75rem 1rem !important;
570
+ }
571
+ html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] div[role="main"]:has([data-composer-placement="home"]) [data-feature="game-source"] {
572
+ padding-inline: 1rem;
573
+ font-size: clamp(1.35rem, 3.2vw, 1.75rem) !important;
574
+ line-height: 1.22 !important;
575
+ }
345
576
  }
346
577
  html[${RENDERER_SKIN_ATTRIBUTE}="${skin.id}"] [data-app-action-sidebar-thread-active="true"] {
347
578
  background: linear-gradient(90deg, color-mix(in srgb, ${accent} 22%, transparent), color-mix(in srgb, ${secondary} 16%, transparent)) !important;
@@ -385,6 +385,64 @@ describe("Renderer draft Agent controller", () => {
385
385
  });
386
386
  });
387
387
 
388
+ it("restores an unsubmitted new-Thread draft after visiting another conversation", async () => {
389
+ const composer = {};
390
+ const agents = controller();
391
+ const draftTarget = ["default", "client-new-thread:draft-a"] as const;
392
+ const conversationTarget = ["conversation", "existing-thread", "local"] as const;
393
+ const model = harnessModelRefSchema.parse({ id: "gpt-5.6-sol" });
394
+ const thinkingOptionId = harnessThinkingOptionIdSchema.parse("high");
395
+ const permissionModeId = harnessPermissionModeIdSchema.parse("full-access");
396
+
397
+ agents.mount(composer, draftTarget);
398
+ await agents.switchAgent(composer, "pi", {
399
+ applyAgent: () => true,
400
+ clearPrewarm: async () => undefined,
401
+ });
402
+ agents.setExternalModel(composer, "pi", model);
403
+ agents.setExternalThinkingOption(composer, "pi", thinkingOptionId);
404
+ agents.setExternalPermissionMode(composer, "pi", permissionModeId);
405
+ const draftState = agents.get(composer);
406
+
407
+ expect(agents.rebindTarget(composer, conversationTarget)).toMatchObject({
408
+ agent: "codex",
409
+ phase: "draft",
410
+ });
411
+ agents.restore(composer, "codex");
412
+
413
+ expect(agents.rebindTarget(composer, draftTarget)).toBe(draftState);
414
+ expect(agents.get(composer)).toMatchObject({
415
+ agent: "pi",
416
+ phase: "draft",
417
+ piModel: model,
418
+ piThinkingOptionId: thinkingOptionId,
419
+ permissionModeByAgent: { pi: permissionModeId },
420
+ });
421
+ });
422
+
423
+ it("does not revive a submitted draft after it becomes a conversation", async () => {
424
+ const composer = {};
425
+ const agents = controller();
426
+ const draftTarget = ["default", "client-new-thread:submitted"] as const;
427
+ const conversationTarget = ["conversation", "created-thread", "local"] as const;
428
+
429
+ agents.mount(composer, draftTarget);
430
+ await agents.switchAgent(composer, "pi", {
431
+ applyAgent: () => true,
432
+ clearPrewarm: async () => undefined,
433
+ });
434
+ agents.markSubmissionPending(composer);
435
+ agents.recordSubmission(composer);
436
+ expect(agents.transfer(composer, composer, conversationTarget)).toBe(true);
437
+ expect(agents.get(composer)).toMatchObject({ agent: "pi", phase: "locked" });
438
+
439
+ expect(agents.rebindTarget(composer, draftTarget)).toMatchObject({
440
+ agent: "pi",
441
+ phase: "draft",
442
+ });
443
+ expect(agents.get(composer).composerId).not.toBe("composer-1");
444
+ });
445
+
388
446
  it("restores a newly mounted Fork owner and ignores stale ownership generations", () => {
389
447
  const forkComposer = {};
390
448
  const replacement = {};
@@ -19,6 +19,7 @@ import {
19
19
  lockedPermissionMode,
20
20
  permissionModeSelectionLocked,
21
21
  lateConversationTargetResolution,
22
+ composerTargetResolution,
22
23
  harnessAvailabilityDuringInspect,
23
24
  passiveHarnessAvailabilityAgents,
24
25
  refreshConnectionHosts,
@@ -1240,6 +1241,17 @@ describe("Renderer Composer DOM behavior", () => {
1240
1241
  expect(lateConversationTargetResolution(null, conversationTarget, "draft")).toBe("inspect");
1241
1242
  });
1242
1243
 
1244
+ it("restores the exact draft target when navigation returns from a conversation", () => {
1245
+ const draftTarget = ["default", "client-new-thread:draft-a"];
1246
+ const conversationTarget = ["conversation", "opaque-1"];
1247
+
1248
+ expect(composerTargetResolution(conversationTarget, draftTarget, "locked")).toBe(
1249
+ "restore-draft",
1250
+ );
1251
+ expect(composerTargetResolution(draftTarget, conversationTarget, "draft")).toBe("inspect");
1252
+ expect(composerTargetResolution(draftTarget, draftTarget, "draft")).toBe("none");
1253
+ });
1254
+
1243
1255
  it("does not transfer an unsubmitted default draft when an existing conversation opens", () => {
1244
1256
  const defaultTarget = ["default"];
1245
1257
  const firstConversationTarget = ["conversation", "opaque-1"];
@@ -315,6 +315,7 @@ describe("Renderer fixed Model request client", () => {
315
315
  "messageThread",
316
316
  "openHarnessWebUi",
317
317
  "optimizeStorage",
318
+ "petsClient",
318
319
  "readCurrentVersion",
319
320
  "readUpdateStatus",
320
321
  "refreshCodexAccounts",
@@ -83,6 +83,7 @@ describe("Renderer settings localization", () => {
83
83
  "会话导入",
84
84
  "存储与验证",
85
85
  "皮肤",
86
+ "桌宠",
86
87
  "更新",
87
88
  "关于",
88
89
  ]);
@@ -28,6 +28,7 @@ describe("Renderer settings foundation", () => {
28
28
  "Session Import",
29
29
  "Storage & verification",
30
30
  "Skins",
31
+ "Pets",
31
32
  "Updates",
32
33
  "About",
33
34
  ]);
@@ -39,6 +40,7 @@ describe("Renderer settings foundation", () => {
39
40
  "session-import",
40
41
  "storage",
41
42
  "palette",
43
+ "pets",
42
44
  "updates",
43
45
  "about",
44
46
  ]);
@@ -76,6 +78,7 @@ describe("Renderer settings foundation", () => {
76
78
  "session-import",
77
79
  "storage",
78
80
  "skins",
81
+ "pets",
79
82
  "updates",
80
83
  "about",
81
84
  ]);
@@ -102,7 +102,8 @@ describe("renderer skin runtime", () => {
102
102
  expect(fixture.getStyle()?.textContent).toContain('.main-surface');
103
103
  expect(fixture.getStyle()?.textContent).toContain('[data-app-shell-main-surface="default"]');
104
104
  expect(fixture.getStyle()?.textContent).toContain("[data-response-annotation-conversation]");
105
- expect(fixture.getStyle()?.textContent).toContain("90%, transparent");
105
+ expect(fixture.getStyle()?.textContent).toContain("96%, transparent");
106
+ expect(fixture.getStyle()?.textContent).toContain("backdrop-filter: blur");
106
107
  expect(fixture.getStyle()?.textContent).not.toContain("background: transparent !important; border-color: transparent");
107
108
  expect(fixture.getStyle()?.textContent).toContain("--color-text-primary");
108
109
  expect(fixture.getStyle()?.textContent).toContain("--color-token-foreground");
@@ -117,7 +118,15 @@ describe("renderer skin runtime", () => {
117
118
  expect(fixture.getStyle()?.textContent).toContain("polaroid.webp");
118
119
  expect(fixture.getStyle()?.textContent).toContain("body::after");
119
120
  expect(fixture.getStyle()?.textContent).not.toContain("background-attachment: fixed");
120
- expect(fixture.getStyle()?.textContent).not.toContain("backdrop-filter: blur");
121
+ expect(fixture.getStyle()?.textContent).toContain("--codex-titlebar-tint: transparent");
122
+ expect(fixture.getStyle()?.textContent).toContain("[data-tab-id]");
123
+ expect(fixture.getStyle()?.textContent).toContain("[data-app-shell-header-obstacle] button");
124
+ expect(fixture.getStyle()?.textContent).toContain("[data-app-shell-main-content-top-fade]");
125
+ expect(fixture.getStyle()?.textContent).toContain("[data-app-shell-page-header] > [data-app-shell-header-toolbar] > div:first-child");
126
+ expect(fixture.getStyle()?.textContent).toContain('button[aria-haspopup="menu"]:hover');
127
+ expect(fixture.getStyle()?.textContent).toContain("@container home-main-content");
128
+ expect(fixture.getStyle()?.textContent).toContain('[data-composer-placement="home"]');
129
+ expect(fixture.getStyle()?.textContent).toContain("--thread-content-max-width: min(72rem");
121
130
  expect(fixture.getStyle()?.textContent).toContain("hero.webp");
122
131
  expect(storage.getItem(RENDERER_SKIN_STORAGE_KEY)).toBe("miku-488137");
123
132
  expect(readRendererSkin(storage)).toBe("miku-488137");
@@ -133,4 +142,21 @@ describe("renderer skin runtime", () => {
133
142
  storage.setItem(RENDERER_SKIN_STORAGE_KEY, "removed-theme");
134
143
  expect(readRendererSkin(storage)).toBe("native");
135
144
  });
145
+
146
+ it("applies shared transparent header chrome and readable user messages to every custom skin", () => {
147
+ const storage = new MemoryStorage();
148
+ const fixture = fakeDocument();
149
+
150
+ for (const skin of RENDERER_SKINS.slice(1)) {
151
+ applyRendererSkin(skin.id, fixture.document, storage);
152
+ const css = fixture.getStyle()?.textContent ?? "";
153
+ expect(fixture.attributes.get(RENDERER_SKIN_ATTRIBUTE)).toBe(skin.id);
154
+ expect(css).toContain("--color-text-user-message:");
155
+ expect(css).toContain('[data-user-message-bubble] :where(');
156
+ expect(css).toContain('[data-markdown-text-tone="user-message"]');
157
+ expect(css).toContain('[data-app-shell-header-toolbar] > div:first-child {');
158
+ expect(css).toContain('> button[aria-haspopup="menu"][data-state="open"]');
159
+ expect(css).toContain("background: transparent !important;");
160
+ }
161
+ });
136
162
  });