@design.estate/dees-catalog 6.0.0 → 6.2.0

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 (45) hide show
  1. package/dist_bundle/bundle.js +3105 -1500
  2. package/dist_ts_web/00_commitinfo_data.js +1 -1
  3. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +2 -0
  4. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.js +484 -8
  5. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +10 -1
  6. package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.d.ts +3 -1
  7. package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.js +24 -4
  8. package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.d.ts +26 -0
  9. package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +398 -94
  10. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.d.ts +32 -0
  11. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.demo.d.ts +1 -0
  12. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.demo.js +31 -0
  13. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.js +297 -0
  14. package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.js +1 -9
  15. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.d.ts +10 -1
  16. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.js +181 -2
  17. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +17 -1
  18. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +480 -36
  19. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +28 -24
  20. package/dist_ts_web/elements/00group-harness/harness.helpers.d.ts +5 -1
  21. package/dist_ts_web/elements/00group-harness/harness.helpers.js +66 -1
  22. package/dist_ts_web/elements/00group-harness/index.d.ts +1 -0
  23. package/dist_ts_web/elements/00group-harness/index.js +2 -1
  24. package/dist_ts_web/elements/00group-harness/interfaces.d.ts +33 -2
  25. package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +4 -0
  26. package/dist_ts_web/elements/00group-harness/toolregistry.js +8 -1
  27. package/package.json +2 -2
  28. package/readme.hints.md +16 -1
  29. package/readme.md +41 -9
  30. package/readme.playbook.md +8 -4
  31. package/ts_web/00_commitinfo_data.ts +1 -1
  32. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.ts +490 -7
  33. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +9 -0
  34. package/ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.ts +24 -3
  35. package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +397 -84
  36. package/ts_web/elements/00group-harness/dees-harness-overflow-text.demo.ts +31 -0
  37. package/ts_web/elements/00group-harness/dees-harness-overflow-text.ts +236 -0
  38. package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.ts +0 -8
  39. package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.ts +193 -1
  40. package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +484 -34
  41. package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +29 -25
  42. package/ts_web/elements/00group-harness/harness.helpers.ts +68 -1
  43. package/ts_web/elements/00group-harness/index.ts +1 -0
  44. package/ts_web/elements/00group-harness/interfaces.ts +38 -2
  45. package/ts_web/elements/00group-harness/toolregistry.ts +12 -0
@@ -10,7 +10,11 @@ import {
10
10
  cssManager,
11
11
  } from '@design.estate/dees-element';
12
12
  import { themeDefaultStyles } from '../../00theme.js';
13
- import type { IHarnessToolCall, THarnessContentBlock } from '../interfaces.js';
13
+ import type {
14
+ IHarnessToolCall,
15
+ THarnessContentBlock,
16
+ THarnessMessageStreamDelta,
17
+ } from '../interfaces.js';
14
18
  import {
15
19
  DeesHarnessToolRegistry,
16
20
  parseMcpName,
@@ -19,8 +23,16 @@ import {
19
23
  } from '../toolregistry.js';
20
24
  import { builtinBodyRenderers } from './renderers.js';
21
25
  import { harnessCardStyles, harnessPillStyles, harnessMonoStyles } from '../harness.styles.js';
22
- import { harnessFormatClockTime, harnessFormatDuration, harnessTruncate } from '../harness.helpers.js';
26
+ import {
27
+ harnessApplyMessageDelta,
28
+ harnessEffectiveToolStatus,
29
+ harnessFormatClockTime,
30
+ harnessFormatDuration,
31
+ harnessTruncate,
32
+ } from '../harness.helpers.js';
33
+ import { DeesHarnessMessageList } from '../dees-harness-message-list/dees-harness-message-list.js';
23
34
  import '../dees-harness-content-blocks/dees-harness-content-blocks.js';
35
+ import '../dees-harness-overflow-text.js';
24
36
  import '../../00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js';
25
37
  import './dees-harness-tool-fullscreen.js';
26
38
  import '../../00group-feedback/dees-badge/dees-badge.js';
@@ -49,12 +61,19 @@ export class DeesHarnessToolCard extends DeesElement {
49
61
  @property({ attribute: false })
50
62
  accessor toolRegistry: DeesHarnessToolRegistry = DeesHarnessToolRegistry.default;
51
63
 
64
+ /** Nested lists disable this to keep inline subtask streams one level deep. */
65
+ @property({ type: Boolean })
66
+ accessor allowSubtaskStreams: boolean = true;
67
+
52
68
  @state()
53
69
  accessor expanded: boolean | undefined = undefined;
54
70
 
55
71
  @state()
56
72
  accessor outputExpanded: boolean = false;
57
73
 
74
+ @state()
75
+ accessor bodyMounted: boolean = false;
76
+
58
77
  /** Body height cap; taller content scrolls in place and offers fullscreen. */
59
78
  @property({ type: Number })
60
79
  accessor bodyCapPx: number = 360;
@@ -75,6 +94,9 @@ export class DeesHarnessToolCard extends DeesElement {
75
94
  private bodyResizeObserver: ResizeObserver | null = null;
76
95
  private observedBodyInner: Element | null = null;
77
96
  private fullscreenPortal: HTMLElementTagNameMap['dees-harness-tool-fullscreen'] | null = null;
97
+ private disclosureFrame: number | null = null;
98
+ private subtaskResultElement: HTMLElement | null = null;
99
+ private subtaskResultResizeObserver: ResizeObserver | null = null;
78
100
 
79
101
  public static styles = [
80
102
  themeDefaultStyles,
@@ -88,6 +110,56 @@ export class DeesHarnessToolCard extends DeesElement {
88
110
  font-family: var(--dees-font-family);
89
111
  }
90
112
 
113
+ .harness-card.subtaskPreviewCard {
114
+ max-height: var(--dees-harness-active-subtask-height, 320px);
115
+ }
116
+
117
+ .harness-card.subtaskPreviewOpen {
118
+ display: flex;
119
+ flex-direction: column;
120
+ height: var(--dees-harness-active-subtask-height, 320px);
121
+ box-sizing: border-box;
122
+ overflow: hidden;
123
+ }
124
+
125
+ .harness-card.subtaskPreviewOpen .bodyDisclosure.open,
126
+ .harness-card.subtaskPreviewOpen .bodyDisclosureInner,
127
+ .harness-card.subtaskPreviewOpen .bodyClipFrame {
128
+ flex: 1;
129
+ min-height: 0;
130
+ }
131
+
132
+ .harness-card.subtaskPreviewOpen .bodyDisclosure.open {
133
+ display: flex;
134
+ flex-direction: column;
135
+ }
136
+
137
+ .harness-card.subtaskPreviewOpen .bodyDisclosureInner {
138
+ display: flex;
139
+ flex-direction: column;
140
+ overflow: hidden;
141
+ }
142
+
143
+ .harness-card.subtaskPreviewOpen .bodyClipFrame {
144
+ max-height: none !important;
145
+ overflow: hidden;
146
+ }
147
+
148
+ .harness-card.subtaskPreviewOpen .body,
149
+ .harness-card.subtaskPreviewOpen .subtaskPreview {
150
+ min-height: 0;
151
+ height: 100%;
152
+ }
153
+
154
+ .harness-card.subtaskPreviewOpen .body {
155
+ grid-template-rows: auto minmax(0, 1fr) auto;
156
+ box-sizing: border-box;
157
+ }
158
+
159
+ .harness-card.subtaskPreviewOpen .body.liveSubtaskBody {
160
+ grid-template-rows: minmax(0, 1fr) auto;
161
+ }
162
+
91
163
  .header {
92
164
  display: flex;
93
165
  align-items: center;
@@ -119,6 +191,37 @@ export class DeesHarnessToolCard extends DeesElement {
119
191
  color: var(--dees-color-accent-primary);
120
192
  }
121
193
 
194
+ .harness-card.subtaskPreviewOpen.status-running::before,
195
+ .harness-card.subtaskPreviewOpen.status-pending::before {
196
+ animation: harness-card-open-chat-scan 10s linear infinite;
197
+ }
198
+
199
+ @keyframes harness-card-open-chat-scan {
200
+ 0% {
201
+ background-position: 100% 0;
202
+ opacity: 0;
203
+ }
204
+ 1% {
205
+ opacity: 0.35;
206
+ }
207
+ 20% {
208
+ background-position: 0 0;
209
+ opacity: 0.35;
210
+ }
211
+ 24%, 100% {
212
+ background-position: 0 0;
213
+ opacity: 0;
214
+ }
215
+ }
216
+
217
+ @media (prefers-reduced-motion: reduce) {
218
+ .harness-card.subtaskPreviewOpen.status-running::before,
219
+ .harness-card.subtaskPreviewOpen.status-pending::before {
220
+ animation: none;
221
+ opacity: 0;
222
+ }
223
+ }
224
+
122
225
  .titleBlock {
123
226
  min-width: 0;
124
227
  flex: 1;
@@ -147,12 +250,10 @@ export class DeesHarnessToolCard extends DeesElement {
147
250
  }
148
251
 
149
252
  .subtitle {
253
+ display: block;
150
254
  margin-top: 1px;
151
255
  color: var(--dees-color-text-secondary);
152
256
  font-size: var(--dees-font-caption1-size, 11px);
153
- overflow: hidden;
154
- text-overflow: ellipsis;
155
- white-space: nowrap;
156
257
  }
157
258
 
158
259
  .metaColumn {
@@ -176,7 +277,7 @@ export class DeesHarnessToolCard extends DeesElement {
176
277
  inner overflow-x scrollers engage instead. */
177
278
  grid-template-columns: minmax(0, 1fr);
178
279
  gap: var(--dees-spacing-sm);
179
- padding: 0 var(--dees-spacing-md) var(--dees-spacing-md);
280
+ padding: var(--dees-spacing-sm) var(--dees-spacing-md) var(--dees-spacing-md);
180
281
  }
181
282
 
182
283
  .kvGrid {
@@ -211,6 +312,30 @@ export class DeesHarnessToolCard extends DeesElement {
211
312
  /* Three body modes: collapsed (no body), normal (content fits the
212
313
  cap), capped (content taller than the cap scrolls inside the frame
213
314
  and offers the fullscreen modal). */
315
+ .bodyDisclosure {
316
+ display: grid;
317
+ grid-template-rows: 0fr;
318
+ min-width: 0;
319
+ opacity: 0;
320
+ transform: translateY(-3px);
321
+ transition:
322
+ grid-template-rows var(--dees-transition-default) var(--dees-ease-standard),
323
+ opacity var(--dees-transition-fast) var(--dees-ease-standard),
324
+ transform var(--dees-transition-default) var(--dees-ease-standard);
325
+ }
326
+
327
+ .bodyDisclosure.open {
328
+ grid-template-rows: 1fr;
329
+ opacity: 1;
330
+ transform: translateY(0);
331
+ }
332
+
333
+ .bodyDisclosureInner {
334
+ min-width: 0;
335
+ min-height: 0;
336
+ overflow: hidden;
337
+ }
338
+
214
339
  .bodyClipFrame {
215
340
  position: relative;
216
341
  overflow-y: auto;
@@ -219,6 +344,129 @@ export class DeesHarnessToolCard extends DeesElement {
219
344
  scrollbar-color: var(--dees-color-scrollbar-thumb) transparent;
220
345
  }
221
346
 
347
+ .subtaskPreview {
348
+ display: flex;
349
+ flex-direction: column;
350
+ min-width: 0;
351
+ block-size: 260px;
352
+ margin-inline: calc(0px - var(--dees-spacing-md));
353
+ overflow: hidden;
354
+ }
355
+
356
+ .subtaskPreview dees-harness-message-list {
357
+ flex: 1;
358
+ min-height: 0;
359
+ }
360
+
361
+ .subtaskPreviewState {
362
+ flex: 1;
363
+ display: grid;
364
+ place-content: center;
365
+ gap: var(--dees-spacing-xs);
366
+ min-height: 0;
367
+ padding: var(--dees-spacing-md);
368
+ color: var(--dees-color-text-secondary);
369
+ font-size: var(--dees-font-control-size-sm, 12px);
370
+ text-align: center;
371
+ }
372
+
373
+ .subtaskNotice {
374
+ padding: var(--dees-spacing-xs) var(--dees-spacing-sm);
375
+ border-bottom: 1px solid var(--dees-color-border-subtle);
376
+ color: var(--dees-color-text-secondary);
377
+ font-size: var(--dees-font-caption1-size, 11px);
378
+ }
379
+
380
+ .harness-subtask-detail {
381
+ display: grid;
382
+ align-content: start;
383
+ align-self: start;
384
+ gap: 3px;
385
+ min-width: 0;
386
+ }
387
+
388
+ .body:not(.liveSubtaskBody) > .harness-subtask-detail-brief {
389
+ grid-row: 1;
390
+ max-height: 35%;
391
+ }
392
+
393
+ .body:not(.liveSubtaskBody) > .harness-subtask-detail-brief,
394
+ .body:not(.liveSubtaskBody) > .harness-subtask-detail-result {
395
+ grid-template-rows: auto minmax(0, 1fr);
396
+ min-height: 0;
397
+ overflow: hidden;
398
+ }
399
+
400
+ .body:not(.liveSubtaskBody) > .harness-subtask-detail-result {
401
+ grid-row: 2;
402
+ align-self: stretch;
403
+ }
404
+
405
+ .body:not(.liveSubtaskBody) > .harness-subtask-open {
406
+ grid-row: 3;
407
+ align-self: end;
408
+ }
409
+
410
+ .harness-subtask-detail + .harness-subtask-detail {
411
+ padding-top: var(--dees-spacing-sm);
412
+ border-top: 1px solid var(--dees-color-border-subtle);
413
+ }
414
+
415
+ .harness-subtask-detail-label {
416
+ color: var(--dees-color-text-muted);
417
+ font-size: var(--dees-font-caption2-size, 10px);
418
+ font-weight: 700;
419
+ letter-spacing: 0.04em;
420
+ text-transform: uppercase;
421
+ }
422
+
423
+ .harness-subtask-detail-text {
424
+ color: var(--dees-color-text-primary);
425
+ font-size: var(--dees-font-control-size-sm, 12px);
426
+ line-height: 1.5;
427
+ overflow-wrap: anywhere;
428
+ white-space: pre-wrap;
429
+ }
430
+
431
+ .harness-subtask-detail-brief .harness-subtask-detail-text,
432
+ .harness-subtask-detail-result .harness-subtask-detail-text {
433
+ min-height: 0;
434
+ overflow-y: auto;
435
+ scrollbar-width: none;
436
+ }
437
+
438
+ .harness-subtask-detail-brief .harness-subtask-detail-text::-webkit-scrollbar,
439
+ .harness-subtask-detail-result .harness-subtask-detail-text::-webkit-scrollbar {
440
+ display: none;
441
+ }
442
+
443
+ .harness-subtask-detail-text.fadeTop {
444
+ -webkit-mask-image: linear-gradient(to bottom, transparent 0, black 40px, black 100%);
445
+ mask-image: linear-gradient(to bottom, transparent 0, black 40px, black 100%);
446
+ }
447
+
448
+ .harness-subtask-detail-text.fadeBottom {
449
+ -webkit-mask-image: linear-gradient(to bottom, black 0, black calc(100% - 40px), transparent 100%);
450
+ mask-image: linear-gradient(to bottom, black 0, black calc(100% - 40px), transparent 100%);
451
+ }
452
+
453
+ .harness-subtask-detail-text.fadeTop.fadeBottom {
454
+ -webkit-mask-image: linear-gradient(
455
+ to bottom,
456
+ transparent 0,
457
+ black 40px,
458
+ black calc(100% - 40px),
459
+ transparent 100%
460
+ );
461
+ mask-image: linear-gradient(
462
+ to bottom,
463
+ transparent 0,
464
+ black 40px,
465
+ black calc(100% - 40px),
466
+ transparent 100%
467
+ );
468
+ }
469
+
222
470
  .maximizeBar {
223
471
  display: flex;
224
472
  align-items: center;
@@ -309,6 +557,10 @@ export class DeesHarnessToolCard extends DeesElement {
309
557
  font-size: var(--dees-font-caption1-size, 11px);
310
558
  }
311
559
 
560
+ .harness-terminal-exit.tone-success {
561
+ color: var(--dees-color-accent-success);
562
+ }
563
+
312
564
  .harness-subtask-open {
313
565
  justify-self: start;
314
566
  display: inline-flex;
@@ -374,6 +626,10 @@ export class DeesHarnessToolCard extends DeesElement {
374
626
  }
375
627
 
376
628
  @media (prefers-reduced-motion: reduce) {
629
+ .bodyDisclosure {
630
+ transition: none;
631
+ }
632
+
377
633
  .harness-card.returnFlash {
378
634
  animation: none;
379
635
  }
@@ -400,20 +656,41 @@ export class DeesHarnessToolCard extends DeesElement {
400
656
  public get isOpen(): boolean {
401
657
  if (this.expanded !== undefined) return this.expanded;
402
658
  const descriptor = this.descriptor;
659
+ if (descriptor.kind === 'subtask' && this.call.subtask) {
660
+ return this.allowSubtaskStreams
661
+ && (this.call.subtask.status.type === 'busy' || this.call.subtask.status.type === 'error');
662
+ }
403
663
  if (this.call.status === 'error' || this.call.isError) return true;
404
664
  if (typeof descriptor.defaultExpanded === 'function') return descriptor.defaultExpanded(this.enrichedCall);
405
665
  return descriptor.defaultExpanded ?? false;
406
666
  }
407
667
 
668
+ private get hasSubtaskPreview(): boolean {
669
+ return this.allowSubtaskStreams
670
+ && this.descriptor.kind === 'subtask'
671
+ && this.call.subtask !== undefined;
672
+ }
673
+
674
+ private get hasLiveSubtaskPreview(): boolean {
675
+ return this.hasSubtaskPreview && this.call.subtask?.status.type === 'busy';
676
+ }
677
+
678
+ private get activeSubtaskPreview(): boolean {
679
+ return this.hasSubtaskPreview && this.call.subtask?.status.type === 'busy';
680
+ }
681
+
408
682
  public render(): TemplateResult {
409
683
  const call = this.enrichedCall;
410
684
  const descriptor = this.descriptor;
685
+ const open = this.isOpen;
686
+ const renderBody = this.bodyMounted || open;
411
687
  const label = typeof descriptor.label === 'function' ? descriptor.label(call) : descriptor.label;
412
688
  const subtitle = descriptor.subtitle?.(call) ?? '';
413
- const statusTone = call.isError ? 'error' : call.status === 'completed' ? 'success' : call.status;
689
+ const effectiveStatus = harnessEffectiveToolStatus(call);
690
+ const statusTone = effectiveStatus === 'completed' ? 'success' : effectiveStatus;
414
691
  const duration = harnessFormatDuration(call.startedAt, call.finishedAt);
415
692
  return html`
416
- <div class="harness-card status-${call.isError ? 'error' : call.status} ${descriptor.destructive ? 'destructive' : ''} ${this.returnFlash ? 'returnFlash' : ''} ${this.outcomeScanTone ? `outcomeScan outcomeScan-${this.outcomeScanTone}` : ''}"
693
+ <div class="harness-card status-${effectiveStatus} ${descriptor.destructive ? 'destructive' : ''} ${this.hasSubtaskPreview ? 'subtaskPreviewCard' : ''} ${this.hasSubtaskPreview && open ? 'subtaskPreviewOpen' : ''} ${this.returnFlash ? 'returnFlash' : ''} ${this.outcomeScanTone ? `outcomeScan outcomeScan-${this.outcomeScanTone}` : ''}"
417
694
  @animationend=${(event: AnimationEvent) => {
418
695
  if (event.animationName === 'harness-card-return-flash') {
419
696
  this.endReturnFlash();
@@ -425,10 +702,8 @@ export class DeesHarnessToolCard extends DeesElement {
425
702
  <button
426
703
  class="header"
427
704
  type="button"
428
- aria-expanded=${this.isOpen ? 'true' : 'false'}
429
- @click=${() => {
430
- this.expanded = !this.isOpen;
431
- }}
705
+ aria-expanded=${open ? 'true' : 'false'}
706
+ @click=${this.toggleDisclosure}
432
707
  >
433
708
  <span class="toolIcon"><dees-icon .icon=${descriptor.icon} iconSize="15"></dees-icon></span>
434
709
  <span class="titleBlock">
@@ -436,7 +711,9 @@ export class DeesHarnessToolCard extends DeesElement {
436
711
  <span class="toolLabel">${label}</span>
437
712
  ${this.renderMcpBadges(call)}
438
713
  </span>
439
- ${subtitle ? html`<span class="subtitle" title=${subtitle}>${subtitle}</span>` : ''}
714
+ ${subtitle
715
+ ? html`<dees-harness-overflow-text class="subtitle" title=${subtitle}>${subtitle}</dees-harness-overflow-text>`
716
+ : ''}
440
717
  </span>
441
718
  <span class="metaColumn">
442
719
  ${call.startedAt
@@ -444,34 +721,69 @@ export class DeesHarnessToolCard extends DeesElement {
444
721
  : ''}
445
722
  ${duration ? html`<span class="duration">${duration}</span>` : ''}
446
723
  <span class="harness-pill tone-${statusTone}">
447
- ${call.isError ? 'error' : call.status}
724
+ ${effectiveStatus}
448
725
  </span>
449
726
  </span>
450
727
  </button>
451
- ${this.isOpen
728
+ ${renderBody
452
729
  ? html`
453
730
  <div
454
- class="bodyClipFrame"
455
- style=${this.uncapped ? '' : `max-height: ${this.bodyCapPx}px;`}
731
+ class=${`bodyDisclosure ${open ? 'open' : ''}`}
732
+ aria-hidden=${String(!open)}
456
733
  >
457
- <div class="body bodyInner">${this.renderBody(call)}</div>
734
+ <div class="bodyDisclosureInner" ?inert=${!open}>
735
+ <div
736
+ class="bodyClipFrame"
737
+ style=${this.uncapped || this.activeSubtaskPreview ? '' : `max-height: ${this.bodyCapPx}px;`}
738
+ >
739
+ <div class=${`body bodyInner ${this.hasLiveSubtaskPreview ? 'liveSubtaskBody' : ''}`}>
740
+ ${this.renderBody(call)}
741
+ </div>
742
+ </div>
743
+ ${this.bodyOverflowing && !this.uncapped && !this.hasSubtaskPreview
744
+ ? html`
745
+ <button class="maximizeBar" type="button" @click=${this.openFullscreen}>
746
+ <dees-icon icon="lucide:Maximize2" iconSize="12"></dees-icon>
747
+ Show everything
748
+ </button>
749
+ `
750
+ : ''}
751
+ </div>
458
752
  </div>
459
- ${this.bodyOverflowing && !this.uncapped
460
- ? html`
461
- <button class="maximizeBar" type="button" @click=${this.openFullscreen}>
462
- <dees-icon icon="lucide:Maximize2" iconSize="12"></dees-icon>
463
- Show everything
464
- </button>
465
- `
466
- : ''}
467
753
  `
468
754
  : ''}
469
755
  </div>
470
756
  `;
471
757
  }
472
758
 
759
+ private readonly toggleDisclosure = (): void => {
760
+ if (this.isOpen) {
761
+ if (this.disclosureFrame !== null) cancelAnimationFrame(this.disclosureFrame);
762
+ this.disclosureFrame = null;
763
+ this.expanded = false;
764
+ return;
765
+ }
766
+ if (this.bodyMounted) {
767
+ if (this.disclosureFrame !== null) cancelAnimationFrame(this.disclosureFrame);
768
+ this.disclosureFrame = null;
769
+ this.expanded = true;
770
+ return;
771
+ }
772
+ this.bodyMounted = true;
773
+ this.expanded = false;
774
+ void this.updateComplete.then(() => {
775
+ if (!this.isConnected || this.expanded !== false) return;
776
+ this.disclosureFrame = requestAnimationFrame(() => {
777
+ this.disclosureFrame = null;
778
+ if (!this.isConnected || this.expanded !== false) return;
779
+ this.shadowRoot?.querySelector<HTMLElement>('.bodyDisclosure')?.getBoundingClientRect();
780
+ this.expanded = true;
781
+ });
782
+ });
783
+ };
784
+
473
785
  private readonly openFullscreen = (): void => {
474
- if (this.fullscreenPortal) return;
786
+ if (this.fullscreenPortal || this.hasSubtaskPreview) return;
475
787
  // Appended to document.body: content-visibility containment in the
476
788
  // transcript would trap a position: fixed layer inside this card.
477
789
  const portal = document.createElement('dees-harness-tool-fullscreen');
@@ -538,10 +850,12 @@ export class DeesHarnessToolCard extends DeesElement {
538
850
  super.willUpdate(changedProperties);
539
851
  const previousCall = changedProperties.get('call') as IHarnessToolCall | undefined;
540
852
  if (!previousCall || previousCall.id !== this.call.id) return;
541
- const wasActive = previousCall.status === 'pending' || previousCall.status === 'running';
542
- const outcomeTone = this.call.status === 'error' || this.call.isError
853
+ const previousStatus = harnessEffectiveToolStatus(previousCall);
854
+ const currentStatus = harnessEffectiveToolStatus(this.call);
855
+ const wasActive = previousStatus === 'pending' || previousStatus === 'running';
856
+ const outcomeTone = currentStatus === 'error'
543
857
  ? 'error'
544
- : this.call.status === 'completed'
858
+ : currentStatus === 'completed'
545
859
  ? 'success'
546
860
  : null;
547
861
  if (wasActive && outcomeTone) {
@@ -553,10 +867,18 @@ export class DeesHarnessToolCard extends DeesElement {
553
867
 
554
868
  public updated(changedProperties: Map<PropertyKey, unknown>): void {
555
869
  super.updated(changedProperties);
870
+ if (!this.isConnected) return;
871
+ if (this.isOpen && !this.bodyMounted) this.bodyMounted = true;
556
872
  if (changedProperties.has('call') && this.fullscreenPortal) {
557
873
  this.fullscreenPortal.call = this.enrichedCall;
558
874
  }
559
- if (this.uncapped) return;
875
+ this.connectSubtaskResultScroll();
876
+ if (!this.isOpen || this.uncapped || this.hasSubtaskPreview) {
877
+ this.bodyResizeObserver?.disconnect();
878
+ this.observedBodyInner = null;
879
+ this.bodyOverflowing = false;
880
+ return;
881
+ }
560
882
  const bodyInner = this.shadowRoot?.querySelector('.bodyInner') ?? null;
561
883
  if (bodyInner === this.observedBodyInner) return;
562
884
  this.bodyResizeObserver?.disconnect();
@@ -574,7 +896,45 @@ export class DeesHarnessToolCard extends DeesElement {
574
896
  this.bodyResizeObserver.observe(bodyInner);
575
897
  }
576
898
 
899
+ private readonly syncSubtaskResultScroll = (): void => {
900
+ const result = this.subtaskResultElement;
901
+ if (!result) return;
902
+ result.classList.toggle('fadeTop', result.scrollTop > 1);
903
+ result.classList.toggle(
904
+ 'fadeBottom',
905
+ result.scrollHeight - result.scrollTop - result.clientHeight > 1,
906
+ );
907
+ };
908
+
909
+ private connectSubtaskResultScroll(): void {
910
+ const nextResult = this.shadowRoot?.querySelector<HTMLElement>(
911
+ '.harness-subtask-detail-result .harness-subtask-detail-text',
912
+ ) ?? null;
913
+ if (nextResult === this.subtaskResultElement) {
914
+ this.syncSubtaskResultScroll();
915
+ return;
916
+ }
917
+ this.subtaskResultElement?.removeEventListener('scroll', this.syncSubtaskResultScroll);
918
+ this.subtaskResultResizeObserver?.disconnect();
919
+ this.subtaskResultElement = nextResult;
920
+ if (!nextResult) return;
921
+ nextResult.addEventListener('scroll', this.syncSubtaskResultScroll, { passive: true });
922
+ this.subtaskResultResizeObserver ??= new ResizeObserver(this.syncSubtaskResultScroll);
923
+ this.subtaskResultResizeObserver.observe(nextResult);
924
+ this.syncSubtaskResultScroll();
925
+ }
926
+
927
+ public async connectedCallback(): Promise<void> {
928
+ const connected = super.connectedCallback();
929
+ if (this.hasUpdated) this.requestUpdate();
930
+ await connected;
931
+ }
932
+
577
933
  public async disconnectedCallback(): Promise<void> {
934
+ if (this.disclosureFrame !== null) {
935
+ cancelAnimationFrame(this.disclosureFrame);
936
+ this.disclosureFrame = null;
937
+ }
578
938
  if (this.flashFallbackTimeout) {
579
939
  clearTimeout(this.flashFallbackTimeout);
580
940
  this.flashFallbackTimeout = null;
@@ -588,6 +948,10 @@ export class DeesHarnessToolCard extends DeesElement {
588
948
  this.bodyResizeObserver?.disconnect();
589
949
  this.bodyResizeObserver = null;
590
950
  this.observedBodyInner = null;
951
+ this.subtaskResultElement?.removeEventListener('scroll', this.syncSubtaskResultScroll);
952
+ this.subtaskResultElement = null;
953
+ this.subtaskResultResizeObserver?.disconnect();
954
+ this.subtaskResultResizeObserver = null;
591
955
  await super.disconnectedCallback();
592
956
  }
593
957
 
@@ -608,9 +972,95 @@ export class DeesHarnessToolCard extends DeesElement {
608
972
  private renderBody(call: IHarnessToolCall): TemplateResult {
609
973
  const descriptor = this.descriptor;
610
974
  const helpers = this.createHelpers();
611
- if (descriptor.renderBody) return descriptor.renderBody(call, helpers);
612
- const builtin = builtinBodyRenderers[descriptor.kind] ?? builtinBodyRenderers.unknown;
613
- return builtin(call, helpers);
975
+ const base = descriptor.renderBody
976
+ ? descriptor.renderBody(call, helpers)
977
+ : (builtinBodyRenderers[descriptor.kind] ?? builtinBodyRenderers.unknown)(
978
+ descriptor.kind === 'subtask' && this.hasLiveSubtaskPreview
979
+ ? { ...call, output: undefined, errorText: undefined }
980
+ : call,
981
+ helpers,
982
+ );
983
+ const subtaskAction = descriptor.kind === 'subtask'
984
+ ? this.renderSubtaskAction(call)
985
+ : html``;
986
+ if (!this.hasLiveSubtaskPreview || !call.subtask) {
987
+ return descriptor.kind === 'subtask' ? html`${base}${subtaskAction}` : base;
988
+ }
989
+ const subtask = call.subtask;
990
+ return html`
991
+ <section class="subtaskPreview" role="region" aria-label="Live subagent transcript">
992
+ ${subtask.previewState === 'ready' && (subtask.notice || subtask.truncated)
993
+ ? html`<div class="subtaskNotice">${subtask.notice ?? 'Earlier child messages were omitted.'}</div>`
994
+ : ''}
995
+ ${subtask.previewState === 'ready'
996
+ ? html`
997
+ <dees-harness-message-list
998
+ .messages=${subtask.messages}
999
+ .permissions=${subtask.permissions ?? []}
1000
+ .questions=${subtask.questions ?? []}
1001
+ .status=${subtask.status}
1002
+ .toolRegistry=${this.toolRegistry}
1003
+ .allowSubtaskStreams=${false}
1004
+ .autoFollow=${this.isOpen}
1005
+ .hideScrollbar=${true}
1006
+ .fadeScrollEdges=${true}
1007
+ .showRoles=${true}
1008
+ .showTimestamps=${false}
1009
+ emptyTitle="Waiting for subagent output"
1010
+ emptyText="The child session is active."
1011
+ >
1012
+ <div slot="leading" class="subtaskThreadBrief">${base}</div>
1013
+ </dees-harness-message-list>
1014
+ `
1015
+ : html`
1016
+ <div class="subtaskPreviewState">
1017
+ ${base}
1018
+ <dees-harness-status .status=${subtask.status}></dees-harness-status>
1019
+ <span>${subtask.notice ?? (subtask.previewState === 'loading'
1020
+ ? 'Loading live subagent transcript…'
1021
+ : 'Live preview unavailable. Open the subagent chat to follow it.')}</span>
1022
+ </div>
1023
+ `}
1024
+ </section>
1025
+ ${subtaskAction}
1026
+ `;
1027
+ }
1028
+
1029
+ private renderSubtaskAction(callArg: IHarnessToolCall): TemplateResult {
1030
+ const childSessionId = callArg.subtask?.sessionId ?? callArg.childSessionId;
1031
+ if (!childSessionId) return html``;
1032
+ return html`
1033
+ <button
1034
+ class="harness-subtask-open"
1035
+ type="button"
1036
+ @click=${() => {
1037
+ this.dispatchEvent(new CustomEvent('harness-subtask-open', {
1038
+ detail: { sessionId: childSessionId, call: callArg },
1039
+ bubbles: true,
1040
+ composed: true,
1041
+ }));
1042
+ }}
1043
+ >
1044
+ ${(callArg.subtask
1045
+ ? callArg.subtask.status.type !== 'busy'
1046
+ : callArg.status === 'completed' || callArg.status === 'error')
1047
+ ? 'Open subagent chat'
1048
+ : 'Follow subagent live'}
1049
+ </button>
1050
+ `;
1051
+ }
1052
+
1053
+ public applySubtaskDelta(delta: THarnessMessageStreamDelta): boolean {
1054
+ if (!this.allowSubtaskStreams || this.call.subtask?.previewState !== 'ready') return false;
1055
+ if (!this.call.subtask.messages.some((message) => message.id === delta.messageId)) return false;
1056
+ const list = this.shadowRoot?.querySelector('.subtaskPreview > dees-harness-message-list');
1057
+ if (list instanceof DeesHarnessMessageList) {
1058
+ list.applyDelta(delta);
1059
+ return true;
1060
+ }
1061
+ if (!harnessApplyMessageDelta(this.call.subtask.messages, delta)) return false;
1062
+ this.requestUpdate();
1063
+ return true;
614
1064
  }
615
1065
 
616
1066
  private createHelpers(): IHarnessToolRenderHelpers {