@design.estate/dees-catalog 4.11.1 → 5.0.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 (34) hide show
  1. package/dist_bundle/bundle.js +2345 -1251
  2. package/dist_ts_web/00_commitinfo_data.js +2 -2
  3. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +17 -3
  4. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.js +291 -78
  5. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +328 -35
  6. package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.js +44 -3
  7. package/dist_ts_web/elements/00group-harness/dees-harness-reasoning/dees-harness-reasoning.js +5 -7
  8. package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.js +28 -2
  9. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.d.ts +49 -0
  10. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.demo.d.ts +1 -0
  11. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.demo.js +56 -0
  12. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.js +649 -0
  13. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/index.d.ts +1 -0
  14. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/index.js +2 -0
  15. package/dist_ts_web/elements/00group-harness/harness.markdown.js +3 -1
  16. package/dist_ts_web/elements/00group-harness/harness.styles.js +29 -1
  17. package/dist_ts_web/elements/00group-harness/index.d.ts +1 -0
  18. package/dist_ts_web/elements/00group-harness/index.js +2 -1
  19. package/dist_ts_web/elements/00group-harness/interfaces.d.ts +41 -0
  20. package/package.json +1 -1
  21. package/readme.md +11 -5
  22. package/ts_web/00_commitinfo_data.ts +1 -1
  23. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.ts +278 -79
  24. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +283 -28
  25. package/ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.ts +43 -2
  26. package/ts_web/elements/00group-harness/dees-harness-reasoning/dees-harness-reasoning.ts +4 -6
  27. package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.ts +27 -1
  28. package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.demo.ts +67 -0
  29. package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.ts +557 -0
  30. package/ts_web/elements/00group-harness/dees-harness-session-sidebar/index.ts +1 -0
  31. package/ts_web/elements/00group-harness/harness.markdown.ts +2 -0
  32. package/ts_web/elements/00group-harness/harness.styles.ts +28 -0
  33. package/ts_web/elements/00group-harness/index.ts +1 -0
  34. package/ts_web/elements/00group-harness/interfaces.ts +48 -0
@@ -0,0 +1,557 @@
1
+ import { demoFunc } from './dees-harness-session-sidebar.demo.js';
2
+ import {
3
+ DeesElement,
4
+ html,
5
+ css,
6
+ customElement,
7
+ type TemplateResult,
8
+ property,
9
+ state,
10
+ cssManager,
11
+ directives,
12
+ } from '@design.estate/dees-element';
13
+ import { themeDefaultStyles } from '../../00theme.js';
14
+ import type {
15
+ IHarnessIntelligenceExchange,
16
+ IHarnessScratchpad,
17
+ IHarnessScratchpadSaveDetail,
18
+ IHarnessSessionIntelligenceAskDetail,
19
+ IHarnessSessionMetrics,
20
+ IHarnessTodoItem,
21
+ } from '../interfaces.js';
22
+ import '../dees-harness-todos/dees-harness-todos.js';
23
+ import '../../00group-button/dees-button/dees-button.js';
24
+ import '../../00group-utility/dees-icon/dees-icon.js';
25
+
26
+ const { repeat } = directives;
27
+
28
+ const formatTokenCount = (valueArg: number): string => valueArg.toLocaleString('en-US');
29
+
30
+ declare global {
31
+ interface HTMLElementTagNameMap {
32
+ 'dees-harness-session-sidebar': DeesHarnessSessionSidebar;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Generic session context sidebar. It renders host-owned state and emits
38
+ * mutations, but never loads, persists, or executes session data itself.
39
+ */
40
+ @customElement('dees-harness-session-sidebar')
41
+ export class DeesHarnessSessionSidebar extends DeesElement {
42
+ public static demo = demoFunc;
43
+ public static demoGroups = ['Agent Chat'];
44
+
45
+ @property({ attribute: false })
46
+ accessor todos: IHarnessTodoItem[] = [];
47
+
48
+ @property({ type: Boolean })
49
+ accessor showTasks: boolean = true;
50
+
51
+ @property({ attribute: false })
52
+ accessor metrics: IHarnessSessionMetrics = {};
53
+
54
+ @property({ attribute: false })
55
+ accessor scratchpad: IHarnessScratchpad | undefined = undefined;
56
+
57
+ @property({ type: Boolean })
58
+ accessor scratchpadBusy: boolean = false;
59
+
60
+ @property()
61
+ accessor scratchpadError: string = '';
62
+
63
+ @property({ type: Boolean })
64
+ accessor intelligenceEnabled: boolean = false;
65
+
66
+ @property({ attribute: false })
67
+ accessor intelligenceExchanges: IHarnessIntelligenceExchange[] = [];
68
+
69
+ @property({ type: Boolean })
70
+ accessor intelligenceBusy: boolean = false;
71
+
72
+ @property()
73
+ accessor intelligenceError: string = '';
74
+
75
+ @property({ type: Boolean })
76
+ accessor disabled: boolean = false;
77
+
78
+ @state()
79
+ private accessor scratchpadDraft: string = '';
80
+
81
+ @state()
82
+ private accessor scratchpadDraftRevision: number = 0;
83
+
84
+ @state()
85
+ private accessor scratchpadDirty: boolean = false;
86
+
87
+ @state()
88
+ private accessor scratchpadStale: boolean = false;
89
+
90
+ @state()
91
+ private accessor intelligenceQuestion: string = '';
92
+
93
+ public static styles = [
94
+ themeDefaultStyles,
95
+ cssManager.defaultStyles,
96
+ css`
97
+ :host {
98
+ display: block;
99
+ min-width: 0;
100
+ height: 100%;
101
+ font-family: var(--dees-font-family);
102
+ color: var(--dees-color-text-primary);
103
+ }
104
+
105
+ .sidebar {
106
+ display: flex;
107
+ flex-direction: column;
108
+ min-height: 100%;
109
+ }
110
+
111
+ .section {
112
+ padding: var(--dees-spacing-sm) var(--dees-spacing-md);
113
+ border-bottom: 1px solid var(--dees-color-border-subtle);
114
+ }
115
+
116
+ .section:last-child {
117
+ border-bottom: 0;
118
+ }
119
+
120
+ .sectionHeading {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: var(--dees-spacing-xs);
124
+ margin-bottom: var(--dees-spacing-sm);
125
+ color: var(--dees-color-text-primary);
126
+ font-size: var(--dees-font-control-size-sm, 12px);
127
+ font-weight: 650;
128
+ }
129
+
130
+ .sectionHeading dees-icon {
131
+ color: var(--dees-color-text-secondary);
132
+ }
133
+
134
+ .metricList {
135
+ display: grid;
136
+ gap: 6px;
137
+ }
138
+
139
+ .metricRow {
140
+ display: flex;
141
+ align-items: baseline;
142
+ justify-content: space-between;
143
+ gap: var(--dees-spacing-md);
144
+ color: var(--dees-color-text-secondary);
145
+ font-size: var(--dees-font-caption1-size, 11px);
146
+ }
147
+
148
+ .metricValue {
149
+ color: var(--dees-color-text-primary);
150
+ font-family: var(--dees-font-family-mono);
151
+ font-variant-numeric: tabular-nums;
152
+ text-align: right;
153
+ }
154
+
155
+ .contextProgress {
156
+ height: 4px;
157
+ margin-top: 2px;
158
+ overflow: hidden;
159
+ border-radius: var(--dees-radius-full);
160
+ background: var(--dees-color-fill-secondary);
161
+ }
162
+
163
+ .contextProgressFill {
164
+ height: 100%;
165
+ border-radius: inherit;
166
+ background: var(--dees-color-accent-primary);
167
+ transition: width var(--dees-transition-default) var(--dees-ease-standard);
168
+ }
169
+
170
+ textarea {
171
+ box-sizing: border-box;
172
+ width: 100%;
173
+ min-height: 108px;
174
+ resize: vertical;
175
+ padding: var(--dees-spacing-sm);
176
+ border: 1px solid var(--dees-color-border-default);
177
+ border-radius: var(--dees-radius-md);
178
+ background: var(--dees-color-bg-primary);
179
+ color: var(--dees-color-text-primary);
180
+ font: var(--dees-font-caption1-size, 11px) / 1.5 var(--dees-font-family-mono);
181
+ outline: none;
182
+ }
183
+
184
+ textarea:focus-visible {
185
+ border-color: var(--dees-color-accent-primary);
186
+ box-shadow: 0 0 0 1px var(--dees-color-accent-primary);
187
+ }
188
+
189
+ textarea:disabled {
190
+ opacity: 0.62;
191
+ cursor: not-allowed;
192
+ }
193
+
194
+ .editorMeta,
195
+ .exchangeMeta,
196
+ .hint {
197
+ color: var(--dees-color-text-muted);
198
+ font-size: var(--dees-font-caption2-size, 10px);
199
+ line-height: 1.4;
200
+ }
201
+
202
+ .editorMeta {
203
+ margin-top: 5px;
204
+ }
205
+
206
+ .actions {
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: flex-end;
210
+ gap: var(--dees-spacing-xs);
211
+ margin-top: var(--dees-spacing-sm);
212
+ }
213
+
214
+ .conflict,
215
+ .error {
216
+ margin-top: var(--dees-spacing-sm);
217
+ padding: var(--dees-spacing-xs) var(--dees-spacing-sm);
218
+ border-radius: var(--dees-radius-md);
219
+ font-size: var(--dees-font-caption1-size, 11px);
220
+ line-height: 1.4;
221
+ }
222
+
223
+ .conflict {
224
+ color: var(--dees-color-text-warning);
225
+ background: var(--dees-color-badge-warning-bg);
226
+ }
227
+
228
+ .error {
229
+ color: var(--dees-color-text-error);
230
+ background: var(--dees-color-badge-error-bg);
231
+ }
232
+
233
+ .exchangeList {
234
+ display: grid;
235
+ gap: var(--dees-spacing-sm);
236
+ margin-bottom: var(--dees-spacing-sm);
237
+ }
238
+
239
+ .exchange {
240
+ display: grid;
241
+ gap: 4px;
242
+ padding-left: var(--dees-spacing-sm);
243
+ border-left: 1px solid var(--dees-color-border-default);
244
+ }
245
+
246
+ .question,
247
+ .answer {
248
+ overflow-wrap: anywhere;
249
+ font-size: var(--dees-font-caption1-size, 11px);
250
+ line-height: 1.45;
251
+ white-space: pre-wrap;
252
+ }
253
+
254
+ .question {
255
+ color: var(--dees-color-text-secondary);
256
+ }
257
+
258
+ .answer {
259
+ color: var(--dees-color-text-primary);
260
+ }
261
+
262
+ .exchange.running .answer {
263
+ color: var(--dees-color-text-muted);
264
+ }
265
+
266
+ .exchange.error .answer {
267
+ color: var(--dees-color-text-error);
268
+ }
269
+
270
+ .empty {
271
+ color: var(--dees-color-text-muted);
272
+ font-size: var(--dees-font-caption1-size, 11px);
273
+ }
274
+
275
+ @media (prefers-reduced-motion: reduce) {
276
+ .contextProgressFill {
277
+ transition: none;
278
+ }
279
+ }
280
+ `,
281
+ ];
282
+
283
+ protected willUpdate(changedPropertiesArg: Map<PropertyKey, unknown>): void {
284
+ if (!changedPropertiesArg.has('scratchpad')) return;
285
+ const next = this.scratchpad;
286
+ if (!next) {
287
+ this.scratchpadDraft = '';
288
+ this.scratchpadDraftRevision = 0;
289
+ this.scratchpadDirty = false;
290
+ this.scratchpadStale = false;
291
+ return;
292
+ }
293
+ const previous = changedPropertiesArg.get('scratchpad') as IHarnessScratchpad | undefined;
294
+ if (!previous || previous.id !== next.id) {
295
+ this.acceptScratchpad(next);
296
+ return;
297
+ }
298
+ if (!this.scratchpadDirty || next.text === this.scratchpadDraft) {
299
+ this.acceptScratchpad(next);
300
+ return;
301
+ }
302
+ if (next.revision > this.scratchpadDraftRevision) this.scratchpadStale = true;
303
+ }
304
+
305
+ public get contextPercentage(): number | undefined {
306
+ const current = this.metrics.currentContextTokens;
307
+ const maximum = this.metrics.maxContextTokens;
308
+ if (current === undefined || maximum === undefined || maximum <= 0) return undefined;
309
+ return Math.min(100, Math.max(0, (current / maximum) * 100));
310
+ }
311
+
312
+ public render(): TemplateResult {
313
+ return html`
314
+ <div class="sidebar">
315
+ ${this.showTasks ? this.renderTasks() : ''}
316
+ ${this.hasMetrics ? this.renderMetrics() : ''}
317
+ ${this.scratchpad ? this.renderScratchpad() : ''}
318
+ ${this.intelligenceEnabled ? this.renderIntelligence() : ''}
319
+ </div>
320
+ `;
321
+ }
322
+
323
+ private get hasMetrics(): boolean {
324
+ return Object.values(this.metrics).some((value) => value !== undefined);
325
+ }
326
+
327
+ private renderTasks(): TemplateResult {
328
+ return html`
329
+ <section class="section tasksSection">
330
+ <dees-harness-todos heading="Tasks" .todos=${this.todos}></dees-harness-todos>
331
+ </section>
332
+ `;
333
+ }
334
+
335
+ private renderMetrics(): TemplateResult {
336
+ const metrics = this.metrics;
337
+ const percentage = this.contextPercentage;
338
+ const rows: Array<[string, number | undefined]> = [
339
+ ['Lifetime used', metrics.lifetimeUsedTokens],
340
+ ['Compactions', metrics.compactionCount],
341
+ ['Before latest compaction', metrics.tokensBeforeLatestCompaction],
342
+ ['Context window', metrics.maxContextTokens],
343
+ ['Current context', metrics.currentContextTokens],
344
+ ];
345
+ return html`
346
+ <section class="section metricsSection">
347
+ <div class="sectionHeading">
348
+ <dees-icon icon="lucide:Gauge" iconSize="13"></dees-icon>
349
+ <span>Tokens</span>
350
+ </div>
351
+ <div class="metricList">
352
+ ${rows.map(([label, value]) => value === undefined ? '' : html`
353
+ <div class="metricRow">
354
+ <span>${label}</span>
355
+ <span class="metricValue">
356
+ ${formatTokenCount(value)}${label === 'Current context' && percentage !== undefined
357
+ ? ` · ${Math.round(percentage)}%`
358
+ : ''}
359
+ </span>
360
+ </div>
361
+ `)}
362
+ ${percentage === undefined ? '' : html`
363
+ <div
364
+ class="contextProgress"
365
+ role="progressbar"
366
+ aria-label="Current context window usage"
367
+ aria-valuemin="0"
368
+ aria-valuemax="100"
369
+ aria-valuenow=${String(Math.round(percentage))}
370
+ >
371
+ <div class="contextProgressFill" style=${`width: ${percentage}%`}></div>
372
+ </div>
373
+ `}
374
+ </div>
375
+ </section>
376
+ `;
377
+ }
378
+
379
+ private renderScratchpad(): TemplateResult {
380
+ const scratchpad = this.scratchpad!;
381
+ const updatedBy = scratchpad.updatedBy === 'intelligence' ? 'Session Intelligence' : 'you';
382
+ return html`
383
+ <section class="section scratchpadSection">
384
+ <div class="sectionHeading">
385
+ <dees-icon icon="lucide:NotebookPen" iconSize="13"></dees-icon>
386
+ <span>Session Scratchpad</span>
387
+ </div>
388
+ <textarea
389
+ aria-label="Session scratchpad"
390
+ maxlength="32768"
391
+ .value=${this.scratchpadDraft}
392
+ ?disabled=${this.disabled || this.scratchpadBusy}
393
+ @input=${(event: InputEvent) => {
394
+ this.scratchpadDraft = (event.currentTarget as HTMLTextAreaElement).value;
395
+ this.scratchpadDirty = this.scratchpadDraft !== scratchpad.text;
396
+ }}
397
+ @keydown=${(event: KeyboardEvent) => {
398
+ if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
399
+ event.preventDefault();
400
+ this.saveScratchpad();
401
+ }
402
+ }}
403
+ ></textarea>
404
+ <div class="editorMeta">
405
+ Revision ${scratchpad.revision}${scratchpad.updatedAt
406
+ ? ` · updated by ${updatedBy} ${new Date(scratchpad.updatedAt).toLocaleString()}`
407
+ : ''}
408
+ </div>
409
+ ${this.scratchpadStale ? html`
410
+ <div class="conflict" role="status">
411
+ A newer revision arrived. Keep this draft on the latest revision or discard it.
412
+ <div class="actions">
413
+ <dees-button
414
+ size="sm"
415
+ type="ghost"
416
+ text="Discard draft"
417
+ @clicked=${this.discardScratchpadDraft}
418
+ ></dees-button>
419
+ <dees-button
420
+ size="sm"
421
+ type="outline"
422
+ text="Keep draft on latest"
423
+ @clicked=${this.rebaseScratchpadDraft}
424
+ ></dees-button>
425
+ </div>
426
+ </div>
427
+ ` : ''}
428
+ ${this.scratchpadError ? html`<div class="error" role="alert">${this.scratchpadError}</div>` : ''}
429
+ <div class="actions">
430
+ <dees-button
431
+ size="sm"
432
+ type="accent"
433
+ text="Save"
434
+ .status=${this.scratchpadBusy ? 'pending' : 'normal'}
435
+ ?disabled=${this.disabled || this.scratchpadBusy || !this.scratchpadDirty || this.scratchpadStale}
436
+ @clicked=${this.saveScratchpad}
437
+ ></dees-button>
438
+ </div>
439
+ </section>
440
+ `;
441
+ }
442
+
443
+ private renderIntelligence(): TemplateResult {
444
+ return html`
445
+ <section class="section intelligenceSection">
446
+ <div class="sectionHeading">
447
+ <dees-icon icon="lucide:Sparkles" iconSize="13"></dees-icon>
448
+ <span>Session Intelligence</span>
449
+ </div>
450
+ ${this.intelligenceExchanges.length ? html`
451
+ <div class="exchangeList" role="log" aria-live="polite">
452
+ ${repeat(
453
+ this.intelligenceExchanges,
454
+ (exchange) => exchange.id,
455
+ (exchange) => this.renderExchange(exchange),
456
+ )}
457
+ </div>
458
+ ` : html`<div class="empty">Ask about this session without interrupting its main work.</div>`}
459
+ <textarea
460
+ class="intelligenceInput"
461
+ aria-label="Ask Session Intelligence"
462
+ placeholder="Ask a parallel question…"
463
+ maxlength="8192"
464
+ .value=${this.intelligenceQuestion}
465
+ ?disabled=${this.disabled || this.intelligenceBusy}
466
+ @input=${(event: InputEvent) => {
467
+ this.intelligenceQuestion = (event.currentTarget as HTMLTextAreaElement).value;
468
+ }}
469
+ @keydown=${(event: KeyboardEvent) => {
470
+ if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
471
+ event.preventDefault();
472
+ this.askIntelligence();
473
+ }
474
+ }}
475
+ ></textarea>
476
+ <div class="hint">Runs separately from the main session. Ctrl/⌘ + Enter to ask.</div>
477
+ ${this.intelligenceError ? html`<div class="error" role="alert">${this.intelligenceError}</div>` : ''}
478
+ <div class="actions">
479
+ <dees-button
480
+ size="sm"
481
+ type="accent"
482
+ text="Ask"
483
+ .status=${this.intelligenceBusy ? 'pending' : 'normal'}
484
+ ?disabled=${this.disabled || this.intelligenceBusy || !this.intelligenceQuestion.trim()}
485
+ @clicked=${this.askIntelligence}
486
+ ></dees-button>
487
+ </div>
488
+ </section>
489
+ `;
490
+ }
491
+
492
+ private renderExchange(exchangeArg: IHarnessIntelligenceExchange): TemplateResult {
493
+ const answer = exchangeArg.status === 'running'
494
+ ? 'Thinking in parallel…'
495
+ : exchangeArg.status === 'error'
496
+ ? exchangeArg.error || 'Session Intelligence failed.'
497
+ : exchangeArg.answer || '';
498
+ return html`
499
+ <article class="exchange ${exchangeArg.status}">
500
+ <div class="question">${exchangeArg.question}</div>
501
+ <div class="answer">${answer}</div>
502
+ ${exchangeArg.model
503
+ ? html`<div class="exchangeMeta">${exchangeArg.model}</div>`
504
+ : ''}
505
+ </article>
506
+ `;
507
+ }
508
+
509
+ private acceptScratchpad(scratchpadArg: IHarnessScratchpad): void {
510
+ this.scratchpadDraft = scratchpadArg.text;
511
+ this.scratchpadDraftRevision = scratchpadArg.revision;
512
+ this.scratchpadDirty = false;
513
+ this.scratchpadStale = false;
514
+ }
515
+
516
+ private readonly discardScratchpadDraft = (): void => {
517
+ if (this.scratchpad) this.acceptScratchpad(this.scratchpad);
518
+ };
519
+
520
+ private readonly rebaseScratchpadDraft = (): void => {
521
+ if (!this.scratchpad) return;
522
+ this.scratchpadDraftRevision = this.scratchpad.revision;
523
+ this.scratchpadStale = false;
524
+ this.scratchpadDirty = this.scratchpadDraft !== this.scratchpad.text;
525
+ };
526
+
527
+ private readonly saveScratchpad = (): void => {
528
+ if (
529
+ !this.scratchpad
530
+ || !this.scratchpadDirty
531
+ || this.scratchpadStale
532
+ || this.disabled
533
+ || this.scratchpadBusy
534
+ ) return;
535
+ const detail: IHarnessScratchpadSaveDetail = {
536
+ text: this.scratchpadDraft,
537
+ expectedRevision: this.scratchpadDraftRevision,
538
+ };
539
+ this.dispatchEvent(new CustomEvent('harness-scratchpad-save', {
540
+ detail,
541
+ bubbles: true,
542
+ composed: true,
543
+ }));
544
+ };
545
+
546
+ private readonly askIntelligence = (): void => {
547
+ const question = this.intelligenceQuestion.trim();
548
+ if (!question || this.disabled || this.intelligenceBusy) return;
549
+ const detail: IHarnessSessionIntelligenceAskDetail = { question };
550
+ this.dispatchEvent(new CustomEvent('harness-session-intelligence-ask', {
551
+ detail,
552
+ bubbles: true,
553
+ composed: true,
554
+ }));
555
+ this.intelligenceQuestion = '';
556
+ };
557
+ }
@@ -0,0 +1 @@
1
+ export * from './dees-harness-session-sidebar.js';
@@ -127,12 +127,14 @@ export const markdownStyles: CSSResult = css`
127
127
  .markdown-body table {
128
128
  margin: var(--dees-spacing-sm) 0;
129
129
  border-collapse: collapse;
130
+ color: var(--dees-color-text-primary);
130
131
  font-size: 0.95em;
131
132
  }
132
133
  .markdown-body th,
133
134
  .markdown-body td {
134
135
  padding: 4px 10px;
135
136
  border: 1px solid var(--dees-color-border-default);
137
+ color: var(--dees-color-text-primary);
136
138
  text-align: left;
137
139
  }
138
140
  .markdown-body th {
@@ -17,6 +17,17 @@ export const harnessCardStyles: CSSResult = css`
17
17
  .harness-card.status-running,
18
18
  .harness-card.status-pending {
19
19
  border-left-color: var(--dees-color-accent-primary);
20
+ background:
21
+ linear-gradient(
22
+ 105deg,
23
+ transparent 40%,
24
+ color-mix(in srgb, var(--dees-color-accent-primary) 6%, transparent) 45%,
25
+ color-mix(in srgb, var(--dees-color-accent-primary) 20%, transparent) 50%,
26
+ color-mix(in srgb, var(--dees-color-accent-primary) 6%, transparent) 55%,
27
+ transparent 60%
28
+ ) 100% 0 / 250% 100% no-repeat,
29
+ var(--dees-color-bg-secondary);
30
+ animation: harness-card-running-scan 2s linear infinite;
20
31
  }
21
32
  .harness-card.status-completed {
22
33
  border-left-color: var(--dees-color-accent-success);
@@ -27,6 +38,23 @@ export const harnessCardStyles: CSSResult = css`
27
38
  .harness-card.destructive {
28
39
  border-left-color: var(--dees-color-accent-error);
29
40
  }
41
+
42
+ @keyframes harness-card-running-scan {
43
+ from { background-position: 100% 0; }
44
+ to { background-position: 0 0; }
45
+ }
46
+
47
+ @media (prefers-reduced-motion: reduce) {
48
+ .harness-card.status-running,
49
+ .harness-card.status-pending {
50
+ animation: none;
51
+ background: color-mix(
52
+ in srgb,
53
+ var(--dees-color-accent-primary) 8%,
54
+ var(--dees-color-bg-secondary)
55
+ );
56
+ }
57
+ }
30
58
  `;
31
59
 
32
60
  export const harnessPillStyles: CSSResult = css`
@@ -17,6 +17,7 @@ export * from './dees-harness-question-card/index.js';
17
17
  export * from './dees-harness-message-list/index.js';
18
18
  export * from './dees-harness-reasoning/index.js';
19
19
  export * from './dees-harness-session-list/index.js';
20
+ export * from './dees-harness-session-sidebar/index.js';
20
21
  export * from './dees-terminal-view/index.js';
21
22
  export * from './dees-harness-status/index.js';
22
23
  export * from './dees-harness-todos/index.js';
@@ -43,6 +43,54 @@ export interface IHarnessUsage {
43
43
  cacheWriteTokens?: number;
44
44
  }
45
45
 
46
+ /** Session-level token facts. Unknown metrics stay undefined. */
47
+ export interface IHarnessSessionMetrics {
48
+ /** Cumulative token usage across the complete session. */
49
+ lifetimeUsedTokens?: number;
50
+ /** Number of completed context compactions. */
51
+ compactionCount?: number;
52
+ /** Context-token total immediately before the newest compaction. */
53
+ tokensBeforeLatestCompaction?: number;
54
+ /** Context window of the model that produced currentContextTokens. */
55
+ maxContextTokens?: number;
56
+ /** Current active context-token total. */
57
+ currentContextTokens?: number;
58
+ }
59
+
60
+ export type THarnessScratchpadUpdater = 'user' | 'intelligence';
61
+
62
+ export interface IHarnessScratchpad {
63
+ /** Stable session-scoped identity; a changed id resets local editor state. */
64
+ id: string;
65
+ text: string;
66
+ /** Monotonic persistence revision used for optimistic concurrency. */
67
+ revision: number;
68
+ updatedAt?: number;
69
+ updatedBy?: THarnessScratchpadUpdater;
70
+ }
71
+
72
+ export interface IHarnessScratchpadSaveDetail {
73
+ text: string;
74
+ expectedRevision: number;
75
+ }
76
+
77
+ export type THarnessIntelligenceStatus = 'running' | 'completed' | 'error';
78
+
79
+ export interface IHarnessIntelligenceExchange {
80
+ id: string;
81
+ question: string;
82
+ status: THarnessIntelligenceStatus;
83
+ answer?: string;
84
+ error?: string;
85
+ model?: string;
86
+ createdAt: number;
87
+ completedAt?: number;
88
+ }
89
+
90
+ export interface IHarnessSessionIntelligenceAskDetail {
91
+ question: string;
92
+ }
93
+
46
94
  export interface IHarnessMessage {
47
95
  /** stable id — used as the keyed-repeat key; never reuse across messages */
48
96
  id: string;