@design.estate/dees-catalog 6.1.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 +3097 -1497
  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 +476 -36
  19. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +23 -20
  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 +31 -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 +480 -34
  41. package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +24 -21
  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 +36 -2
  45. package/ts_web/elements/00group-harness/toolregistry.ts +12 -0
@@ -79,11 +79,482 @@ const pendingPermission: IHarnessPermissionRequest = {
79
79
  metadata: { path: '/srv/app/src/jobs/sitemap.job.ts', bytes: 812 },
80
80
  };
81
81
 
82
+ interface ISubagentScenario {
83
+ description: string;
84
+ model: string;
85
+ toolName: string;
86
+ toolInput: Record<string, unknown>;
87
+ toolOutput: unknown;
88
+ resultChunks: string[];
89
+ startDelay: number;
90
+ toolDuration: number;
91
+ chunkDelay: number;
92
+ }
93
+
94
+ const firstSubagentWave: ISubagentScenario[] = [
95
+ {
96
+ description: 'API contract',
97
+ model: 'openai/gpt-5.5',
98
+ toolName: 'read_file',
99
+ toolInput: { path: '/srv/app/ts_interfaces/harness.ts', startLine: 1, endLine: 220 },
100
+ toolOutput: 'IHarnessSessionProjection and event contracts loaded.',
101
+ resultChunks: [
102
+ 'The projection boundary is already runtime-neutral. ',
103
+ 'Child ownership can stay on the session service. ',
104
+ 'Only bounded messages and status need to cross the API. ',
105
+ 'Contract review complete.',
106
+ ],
107
+ startDelay: 0,
108
+ toolDuration: 1_300,
109
+ chunkDelay: 620,
110
+ },
111
+ {
112
+ description: 'Browser lifecycle',
113
+ model: 'anthropic/claude-sonnet-4',
114
+ toolName: 'browser',
115
+ toolInput: { action: 'snapshot', url: 'http://localhost:3002/harness' },
116
+ toolOutput: 'Captured active, reconnecting, and completed child-card states.',
117
+ resultChunks: [
118
+ 'The nested transcript keeps its own scroll boundary. ',
119
+ 'Reconnect restores the content observer without duplicating it. ',
120
+ 'Completed cards return to chronology with DOM identity intact. ',
121
+ 'Browser lifecycle checks pass.',
122
+ ],
123
+ startDelay: 240,
124
+ toolDuration: 1_750,
125
+ chunkDelay: 700,
126
+ },
127
+ {
128
+ description: 'Regression coverage',
129
+ model: 'openai/gpt-5.5',
130
+ toolName: 'run_command',
131
+ toolInput: { command: 'pnpm exec tstest test/test.harness-subagent-streams.chromium.ts --web', cwd: '/srv/app' },
132
+ toolOutput: { exitCode: 0, stdout: '11/11 focused Chromium tests passed' },
133
+ resultChunks: [
134
+ 'Focused coverage exercises nested deltas and grid movement. ',
135
+ 'The projection-only drill-in path resolves correctly. ',
136
+ 'Native overscroll containment is present. ',
137
+ 'Regression coverage is green.',
138
+ ],
139
+ startDelay: 480,
140
+ toolDuration: 2_200,
141
+ chunkDelay: 760,
142
+ },
143
+ ];
144
+
145
+ const secondSubagentWave: ISubagentScenario[] = [
146
+ {
147
+ description: 'Authorization boundaries',
148
+ model: 'openai/gpt-5.5',
149
+ toolName: 'grep',
150
+ toolInput: { pattern: 'requireSessionAccess', path: '/srv/app/ts' },
151
+ toolOutput: '8 ownership checks found; all projection routes are guarded.',
152
+ resultChunks: ['Checked route guards. ', 'Verified parent and child ownership. ', 'No cross-tenant projection path found. ', 'Authorization review complete.'],
153
+ startDelay: 0,
154
+ toolDuration: 1_350,
155
+ chunkDelay: 620,
156
+ },
157
+ {
158
+ description: 'Storage adapters',
159
+ model: 'anthropic/claude-sonnet-4',
160
+ toolName: 'read_file',
161
+ toolInput: { path: '/srv/app/ts/classes.sessionstore.ts', startLine: 80, endLine: 260 },
162
+ toolOutput: 'Session lookup and bounded transcript paging reviewed.',
163
+ resultChunks: ['Read the paging path. ', 'Confirmed stable cursor ordering. ', 'Validated truncation metadata. ', 'Storage review complete.'],
164
+ startDelay: 140,
165
+ toolDuration: 1_700,
166
+ chunkDelay: 680,
167
+ },
168
+ {
169
+ description: 'RPC event mapping',
170
+ model: 'openai/gpt-5.5',
171
+ toolName: 'read_file',
172
+ toolInput: { path: '/srv/app/ts_interfaces/events.ts', startLine: 1, endLine: 180 },
173
+ toolOutput: 'Parent and nested stream event mapping is complete.',
174
+ resultChunks: ['Mapped child status events. ', 'Mapped tool and reasoning deltas. ', 'Confirmed snapshot recovery after gaps. ', 'RPC event mapping complete.'],
175
+ startDelay: 280,
176
+ toolDuration: 1_150,
177
+ chunkDelay: 740,
178
+ },
179
+ {
180
+ description: 'Mobile layout',
181
+ model: 'anthropic/claude-sonnet-4',
182
+ toolName: 'browser',
183
+ toolInput: { action: 'snapshot', url: 'http://localhost:3002/harness?viewport=phone' },
184
+ toolOutput: 'Single-column cards and session overlay captured at 390px.',
185
+ resultChunks: ['Reduced the transcript container. ', 'Confirmed one-column active cards. ', 'Opened the session overlay above the chat. ', 'Mobile layout is stable.'],
186
+ startDelay: 420,
187
+ toolDuration: 2_050,
188
+ chunkDelay: 610,
189
+ },
190
+ {
191
+ description: 'Reconnect behavior',
192
+ model: 'openai/gpt-5.5',
193
+ toolName: 'run_command',
194
+ toolInput: { command: 'pnpm test --filter reconnect', cwd: '/srv/app' },
195
+ toolOutput: { exitCode: 0, stdout: '6 reconnect scenarios passed' },
196
+ resultChunks: ['Interrupted the child feed. ', 'Replayed the authoritative snapshot. ', 'Resumed deltas without duplication. ', 'Reconnect behavior passes.'],
197
+ startDelay: 560,
198
+ toolDuration: 2_400,
199
+ chunkDelay: 720,
200
+ },
201
+ {
202
+ description: 'Permission flow',
203
+ model: 'anthropic/claude-sonnet-4',
204
+ toolName: 'grep',
205
+ toolInput: { pattern: 'harness-permission-response', path: '/srv/app/ts_web' },
206
+ toolOutput: 'Permission responses remain child-session scoped.',
207
+ resultChunks: ['Traced permission ownership. ', 'Confirmed answered cards persist. ', 'Verified nested requests never leak outward. ', 'Permission flow is correct.'],
208
+ startDelay: 700,
209
+ toolDuration: 1_500,
210
+ chunkDelay: 790,
211
+ },
212
+ {
213
+ description: 'Documentation audit',
214
+ model: 'openai/gpt-5.5',
215
+ toolName: 'read_file',
216
+ toolInput: { path: '/srv/app/readme.md', startLine: 1800, endLine: 1860 },
217
+ toolOutput: 'Subtask projection, drill-in, and truncation behavior documented.',
218
+ resultChunks: ['Compared docs with interfaces. ', 'Checked the projection example. ', 'Verified host responsibilities are explicit. ', 'Documentation audit complete.'],
219
+ startDelay: 840,
220
+ toolDuration: 1_900,
221
+ chunkDelay: 650,
222
+ },
223
+ {
224
+ description: 'Bundle impact',
225
+ model: 'anthropic/claude-sonnet-4',
226
+ toolName: 'run_command',
227
+ toolInput: { command: 'pnpm run build', cwd: '/srv/app' },
228
+ toolOutput: { exitCode: 0, stdout: 'TypeScript and browser bundle completed' },
229
+ resultChunks: ['Compiled the component graph. ', 'Checked the browser bundle. ', 'Found no runtime-specific dependency. ', 'Bundle verification passes.'],
230
+ startDelay: 980,
231
+ toolDuration: 2_600,
232
+ chunkDelay: 700,
233
+ },
234
+ {
235
+ description: 'Release checklist',
236
+ model: 'openai/gpt-5.5',
237
+ toolName: 'webfetch',
238
+ toolInput: { url: 'https://code.foss.global/design.estate/dees-catalog/raw/branch/main/changelog.md' },
239
+ toolOutput: 'Release notes include bounded live subagent streams.',
240
+ resultChunks: ['Reviewed the pending notes. ', 'Matched public API names. ', 'Confirmed verification results are recorded. ', 'Release checklist is ready.'],
241
+ startDelay: 1_120,
242
+ toolDuration: 2_150,
243
+ chunkDelay: 760,
244
+ },
245
+ ];
246
+
247
+ const createSubagentWave = (
248
+ runId: number,
249
+ waveNumber: number,
250
+ scenarios: ISubagentScenario[],
251
+ ): IHarnessMessage[] => {
252
+ const now = Date.now();
253
+ return scenarios.map((scenario, index) => {
254
+ const childSessionId = `demo-${runId}-wave-${waveNumber}-child-${index + 1}`;
255
+ const childToolId = `${childSessionId}-tool`;
256
+ return {
257
+ id: `demo-${runId}-wave-${waveNumber}-subagent-${index + 1}`,
258
+ role: 'tool' as const,
259
+ text: '',
260
+ createdAt: now + index,
261
+ toolCall: {
262
+ id: `demo-${runId}-wave-${waveNumber}-call-${index + 1}`,
263
+ name: 'task',
264
+ status: 'running' as const,
265
+ input: { description: scenario.description },
266
+ childSessionId,
267
+ model: scenario.model,
268
+ subtask: {
269
+ sessionId: childSessionId,
270
+ previewState: 'ready' as const,
271
+ status: { type: 'busy' as const, message: `Investigating ${scenario.description.toLowerCase()}…` },
272
+ messages: [
273
+ {
274
+ id: `${childSessionId}-plan`,
275
+ role: 'assistant' as const,
276
+ text: `I’ll inspect ${scenario.description.toLowerCase()} and report the concrete result.`,
277
+ createdAt: now,
278
+ markdown: false,
279
+ },
280
+ {
281
+ id: childToolId,
282
+ role: 'tool' as const,
283
+ text: '',
284
+ createdAt: now + 1,
285
+ toolCall: {
286
+ id: `${childToolId}-call`,
287
+ name: scenario.toolName,
288
+ status: 'running' as const,
289
+ input: scenario.toolInput,
290
+ startedAt: now,
291
+ },
292
+ },
293
+ {
294
+ id: `${childSessionId}-result`,
295
+ role: 'assistant' as const,
296
+ text: '',
297
+ createdAt: now + 2,
298
+ markdown: false,
299
+ streaming: true,
300
+ },
301
+ ],
302
+ },
303
+ startedAt: now,
304
+ },
305
+ };
306
+ });
307
+ };
308
+
82
309
  let stopStream: (() => void) | undefined;
83
310
 
84
311
  const chatOf = (event: MouseEvent): DeesHarnessChat | null =>
85
312
  (event.target as HTMLElement).closest('.chatDemo')?.querySelector('dees-harness-chat') ?? null;
86
313
 
314
+ const simulateSubagents = (event: MouseEvent): void => {
315
+ const chat = chatOf(event);
316
+ if (!chat) return;
317
+ stopStream?.();
318
+ const runId = Date.now();
319
+ let stopped = false;
320
+ let currentWaveLabel = '';
321
+ const pendingDelays = new Map<ReturnType<typeof setTimeout>, (continueRun: boolean) => void>();
322
+ const activeSubagents = new Map<string, IHarnessMessage>();
323
+ const activeMainMessages = new Set<string>();
324
+ const activeMainTools = new Set<string>();
325
+
326
+ const wait = async (milliseconds: number): Promise<boolean> => new Promise((resolve) => {
327
+ if (stopped) {
328
+ resolve(false);
329
+ return;
330
+ }
331
+ const timeout = setTimeout(() => {
332
+ pendingDelays.delete(timeout);
333
+ resolve(!stopped);
334
+ }, milliseconds);
335
+ pendingDelays.set(timeout, resolve);
336
+ });
337
+ const pushMessage = async (message: IHarnessMessage): Promise<boolean> => {
338
+ chat.messages = [...chat.messages, message];
339
+ await chat.updateComplete;
340
+ return !stopped;
341
+ };
342
+ const updateWaveStatus = (): void => {
343
+ if (stopped || !activeSubagents.size) return;
344
+ chat.status = {
345
+ type: 'busy',
346
+ message: `${currentWaveLabel}: ${activeSubagents.size} subagent${activeSubagents.size === 1 ? '' : 's'} still working…`,
347
+ };
348
+ };
349
+ const finishSubagent = (task: IHarnessMessage, status: 'completed' | 'error'): void => {
350
+ if (!activeSubagents.has(task.id)) return;
351
+ const call = task.toolCall;
352
+ const subtask = call?.subtask;
353
+ if (!call || subtask?.previewState !== 'ready') return;
354
+ const now = Date.now();
355
+ chat.applyDelta({
356
+ type: 'tool-update',
357
+ messageId: task.id,
358
+ tool: {
359
+ id: call.id,
360
+ status,
361
+ finishedAt: now,
362
+ ...(status === 'error' ? { errorText: 'Simulation stopped.' } : {}),
363
+ subtask: {
364
+ ...subtask,
365
+ status: status === 'completed' ? { type: 'idle' } : { type: 'error', message: 'Stopped' },
366
+ messages: subtask.messages.map((message) => {
367
+ if (message.toolCall?.status === 'pending' || message.toolCall?.status === 'running') {
368
+ return {
369
+ ...message,
370
+ toolCall: {
371
+ ...message.toolCall,
372
+ status: status === 'completed' ? 'completed' as const : 'error' as const,
373
+ finishedAt: now,
374
+ ...(status === 'error' ? { errorText: 'Simulation stopped.' } : {}),
375
+ },
376
+ };
377
+ }
378
+ if (!message.streaming) return message;
379
+ return {
380
+ ...message,
381
+ streaming: false,
382
+ ...(status === 'error' ? { error: 'Simulation stopped.' } : {}),
383
+ };
384
+ }),
385
+ },
386
+ },
387
+ });
388
+ activeSubagents.delete(task.id);
389
+ updateWaveStatus();
390
+ };
391
+ const runSubagent = async (task: IHarnessMessage, scenario: ISubagentScenario): Promise<void> => {
392
+ const subtask = task.toolCall?.subtask;
393
+ if (subtask?.previewState !== 'ready') return;
394
+ const childTool = subtask.messages.find((message) => message.toolCall);
395
+ const childResult = subtask.messages.find((message) => message.streaming);
396
+ if (!childTool?.toolCall || !childResult) return;
397
+ if (!await wait(scenario.startDelay)) return;
398
+ if (!await wait(scenario.toolDuration)) return;
399
+ chat.applyDelta({
400
+ type: 'tool-update',
401
+ parentMessageId: task.id,
402
+ messageId: childTool.id,
403
+ tool: {
404
+ id: childTool.toolCall.id,
405
+ status: 'completed',
406
+ output: scenario.toolOutput,
407
+ finishedAt: Date.now(),
408
+ },
409
+ });
410
+ for (const chunk of scenario.resultChunks) {
411
+ if (!await wait(scenario.chunkDelay)) return;
412
+ chat.applyDelta({
413
+ type: 'text',
414
+ parentMessageId: task.id,
415
+ messageId: childResult.id,
416
+ delta: chunk,
417
+ });
418
+ }
419
+ chat.applyDelta({
420
+ type: 'message-end',
421
+ parentMessageId: task.id,
422
+ messageId: childResult.id,
423
+ });
424
+ finishSubagent(task, 'completed');
425
+ };
426
+ const runWave = async (
427
+ waveNumber: number,
428
+ label: string,
429
+ scenarios: ISubagentScenario[],
430
+ ): Promise<boolean> => {
431
+ currentWaveLabel = label;
432
+ const tasks = createSubagentWave(runId, waveNumber, scenarios);
433
+ for (const task of tasks) activeSubagents.set(task.id, task);
434
+ chat.messages = [...chat.messages, ...tasks];
435
+ updateWaveStatus();
436
+ await chat.updateComplete;
437
+ await Promise.all(tasks.map((task, index) => runSubagent(task, scenarios[index])));
438
+ return !stopped;
439
+ };
440
+ const streamMainMessage = async (text: string): Promise<boolean> => {
441
+ const messageId = `demo-${runId}-main-${Date.now()}`;
442
+ const message: IHarnessMessage = {
443
+ id: messageId,
444
+ role: 'assistant',
445
+ text: '',
446
+ createdAt: Date.now(),
447
+ markdown: true,
448
+ streaming: true,
449
+ model: 'gpt-5.5',
450
+ };
451
+ activeMainMessages.add(messageId);
452
+ if (!await pushMessage(message)) return false;
453
+ for (let offset = 0; offset < text.length; offset += 18) {
454
+ if (!await wait(70)) return false;
455
+ chat.applyDelta({ type: 'text', messageId, delta: text.slice(offset, offset + 18) });
456
+ }
457
+ chat.applyDelta({ type: 'message-end', messageId });
458
+ activeMainMessages.delete(messageId);
459
+ return true;
460
+ };
461
+
462
+ const stop = (): void => {
463
+ if (stopped) return;
464
+ stopped = true;
465
+ for (const [timeout, resolve] of pendingDelays) {
466
+ clearTimeout(timeout);
467
+ resolve(false);
468
+ }
469
+ pendingDelays.clear();
470
+ for (const task of [...activeSubagents.values()]) finishSubagent(task, 'error');
471
+ for (const messageId of activeMainMessages) {
472
+ chat.applyDelta({ type: 'message-end', messageId, error: 'Simulation stopped.' });
473
+ }
474
+ activeMainMessages.clear();
475
+ for (const messageId of activeMainTools) {
476
+ const tool = chat.messages.find((message) => message.id === messageId)?.toolCall;
477
+ if (!tool) continue;
478
+ chat.applyDelta({
479
+ type: 'tool-update',
480
+ messageId,
481
+ tool: { id: tool.id, status: 'error', errorText: 'Simulation stopped.', finishedAt: Date.now() },
482
+ });
483
+ }
484
+ activeMainTools.clear();
485
+ chat.status = { type: 'idle' };
486
+ if (stopStream === stop) stopStream = undefined;
487
+ };
488
+ stopStream = stop;
489
+
490
+ const run = async (): Promise<void> => {
491
+ chat.messages = [...harnessDemoConversation().slice(-2)];
492
+ chat.permissions = [];
493
+ chat.questions = [];
494
+ if (!await runWave(1, 'First wave', firstSubagentWave)) return;
495
+
496
+ chat.status = { type: 'busy', message: 'Main thread is reconciling the first wave…' };
497
+ if (!await wait(520)) return;
498
+ if (!await streamMainMessage(
499
+ 'The first three reviews agree on the bounded projection. I’m consolidating their findings before widening the verification pass.',
500
+ )) return;
501
+
502
+ const mainToolId = `demo-${runId}-main-checklist`;
503
+ activeMainTools.add(mainToolId);
504
+ if (!await pushMessage({
505
+ id: mainToolId,
506
+ role: 'tool',
507
+ text: '',
508
+ createdAt: Date.now(),
509
+ toolCall: {
510
+ id: `${mainToolId}-call`,
511
+ name: 'todowrite',
512
+ status: 'running',
513
+ input: {
514
+ todos: [
515
+ { content: 'Reconcile initial contract findings', status: 'completed' },
516
+ { content: 'Run nine parallel integration reviews', status: 'in_progress' },
517
+ { content: 'Summarize release readiness', status: 'pending' },
518
+ ],
519
+ },
520
+ startedAt: Date.now(),
521
+ },
522
+ })) return;
523
+ if (!await wait(1_100)) return;
524
+ chat.applyDelta({
525
+ type: 'tool-update',
526
+ messageId: mainToolId,
527
+ tool: { id: `${mainToolId}-call`, status: 'completed', finishedAt: Date.now() },
528
+ });
529
+ activeMainTools.delete(mainToolId);
530
+ if (!await streamMainMessage(
531
+ 'The core path is sound. I’m launching nine focused subagents across authorization, storage, RPC, responsive UI, reconnects, permissions, docs, bundling, and release readiness.',
532
+ )) return;
533
+ if (!await wait(600)) return;
534
+
535
+ if (!await runWave(2, 'Integration wave', secondSubagentWave)) return;
536
+ chat.status = { type: 'busy', message: 'Main thread is assembling the final result…' };
537
+ if (!await wait(600)) return;
538
+ if (!await streamMainMessage([
539
+ 'All **nine integration reviews** have completed at their own pace.',
540
+ '',
541
+ '- Authorization and permission boundaries remain session-scoped',
542
+ '- Storage paging and reconnect recovery preserve bounded projections',
543
+ '- Desktop and mobile layouts retain stable card ownership',
544
+ '- Documentation, bundle, and release checks are ready',
545
+ '',
546
+ 'The staged subagent run is complete.',
547
+ ].join('\n'))) return;
548
+ chat.status = { type: 'idle' };
549
+ if (stopStream === stop) stopStream = undefined;
550
+ };
551
+
552
+ void run().catch((error) => {
553
+ console.error('Harness subagent demo simulation failed.', error);
554
+ stop();
555
+ });
556
+ };
557
+
87
558
  /**
88
559
  * Streams a realistic turn through planning, inspection, a failing test,
89
560
  * the file edit, verification, and the final assistant summary.
@@ -440,19 +911,30 @@ export const demoFunc = () => html`
440
911
  .chatDemo {
441
912
  display: grid;
442
913
  grid-template-rows: auto minmax(0, 1fr);
443
- gap: 12px;
444
- height: 640px;
445
- max-width: 860px;
446
- margin: 40px auto;
914
+ min-height: 0;
915
+ height: 100%;
916
+ width: 100%;
917
+ box-sizing: border-box;
918
+ overflow: hidden;
919
+ border: 1px solid var(--dees-color-border-subtle, rgb(128 128 128 / 28%));
920
+ border-radius: var(--dees-radius-xl, 14px);
921
+ background: var(--dees-color-bg-primary, inherit);
447
922
  }
448
923
  .chatDemo .controls {
924
+ position: relative;
925
+ z-index: 1;
449
926
  display: flex;
927
+ align-items: center;
450
928
  gap: 8px;
451
929
  flex-wrap: wrap;
930
+ padding: var(--dees-spacing-sm, 8px) var(--dees-spacing-md, 12px);
931
+ border-bottom: 1px solid var(--dees-color-border-subtle, rgb(128 128 128 / 28%));
932
+ background: var(--dees-color-bg-secondary, rgb(128 128 128 / 12%));
933
+ box-shadow: var(--dees-shadow-sm, 0 1px 3px rgb(0 0 0 / 16%));
452
934
  }
453
935
  .chatDemo dees-harness-chat {
454
- border: 1px solid var(--dees-color-border-subtle);
455
- border-radius: var(--dees-radius-xl);
936
+ min-width: 0;
937
+ min-height: 0;
456
938
  overflow: hidden;
457
939
  }
458
940
  </style>
@@ -491,8 +973,9 @@ export const demoFunc = () => html`
491
973
  console.log('harness-abort');
492
974
  }}
493
975
  >
494
- <div class="controls">
976
+ <div class="controls" role="toolbar" aria-label="Demo controls">
495
977
  <dees-button type="accent" @clicked=${simulate}>Simulate streaming</dees-button>
978
+ <dees-button type="accent" @clicked=${simulateSubagents}>Simulate subagents</dees-button>
496
979
  <dees-button @clicked=${askPermission}>Ask permission</dees-button>
497
980
  <dees-button @clicked=${askQuestion}>Ask a question</dees-button>
498
981
  <dees-button @clicked=${toggleError}>Toggle error status</dees-button>
@@ -426,6 +426,7 @@ export class DeesHarnessChat extends DeesElement {
426
426
  /** Streams an incremental update into the message list. */
427
427
  public applyDelta(delta: THarnessStreamDelta): void {
428
428
  this.listElement?.applyDelta(delta);
429
+ if (!delta.parentMessageId && delta.type === 'tool-update') this.requestUpdate();
429
430
  }
430
431
 
431
432
  public async focusComposer(): Promise<void> {
@@ -481,10 +482,16 @@ export class DeesHarnessChat extends DeesElement {
481
482
  return [];
482
483
  }
483
484
 
485
+ /** Top-level tool messages still present in the host's retained transcript window. */
486
+ public get toolActivityMessages(): IHarnessMessage[] {
487
+ return this.messages.filter((message) => Boolean(message.toolCall));
488
+ }
489
+
484
490
  public get hasSessionSidebar(): boolean {
485
491
  if (!this.showSessionSidebar) return false;
486
492
  return (
487
493
  (this.showTodosPanel && this.effectiveTodos.length > 0)
494
+ || this.toolActivityMessages.length > 0
488
495
  || Object.values(this.sessionMetrics).some((value) => value !== undefined)
489
496
  || this.scratchpad !== undefined
490
497
  || this.sessionIntelligenceEnabled
@@ -610,6 +617,8 @@ export class DeesHarnessChat extends DeesElement {
610
617
  <dees-harness-session-sidebar
611
618
  .todos=${this.effectiveTodos}
612
619
  .showTasks=${this.showTodosPanel}
620
+ .toolMessages=${this.toolActivityMessages}
621
+ .toolRegistry=${this.toolRegistry}
613
622
  .metrics=${this.sessionMetrics}
614
623
  .scratchpad=${this.scratchpad}
615
624
  .scratchpadBusy=${this.scratchpadBusy}
@@ -13,7 +13,12 @@ import {
13
13
  import { themeDefaultStyles } from '../../00theme.js';
14
14
  import { syntaxPaletteStyles, syntaxScopeStyles } from '../../00syntax.js';
15
15
  import type { HLJSApi } from 'highlight.js';
16
- import type { IHarnessAttachment, IHarnessMessage, IHarnessUsage } from '../interfaces.js';
16
+ import type {
17
+ IHarnessAttachment,
18
+ IHarnessMessage,
19
+ IHarnessUsage,
20
+ THarnessMessageStreamDelta,
21
+ } from '../interfaces.js';
17
22
  import { DeesHarnessToolRegistry } from '../toolregistry.js';
18
23
  import { HarnessMarkdown, markdownStyles } from '../harness.markdown.js';
19
24
  import { harnessFormatBytes, harnessFormatClockTime } from '../harness.helpers.js';
@@ -57,6 +62,9 @@ export class DeesHarnessMessage extends DeesElement {
57
62
  @property({ attribute: false })
58
63
  accessor toolRegistry: DeesHarnessToolRegistry = DeesHarnessToolRegistry.default;
59
64
 
65
+ @property({ type: Boolean })
66
+ accessor allowSubtaskStreams: boolean = true;
67
+
60
68
  /**
61
69
  * When true at first render, the entry animation is skipped permanently
62
70
  * for this element (initial-batch rendering). Captured once in
@@ -406,6 +414,11 @@ export class DeesHarnessMessage extends DeesElement {
406
414
  this.requestUpdate();
407
415
  }
408
416
 
417
+ public applySubtaskDelta(delta: THarnessMessageStreamDelta): boolean {
418
+ const card = this.shadowRoot?.querySelector('dees-harness-tool-card');
419
+ return card?.applySubtaskDelta(delta) ?? false;
420
+ }
421
+
409
422
  // ------------------------------------------------------------------ render
410
423
 
411
424
  public render(): TemplateResult {
@@ -421,7 +434,11 @@ export class DeesHarnessMessage extends DeesElement {
421
434
  && !message.attachments?.length
422
435
  ) {
423
436
  return html`
424
- <dees-harness-tool-card .call=${message.toolCall} .toolRegistry=${this.toolRegistry}></dees-harness-tool-card>
437
+ <dees-harness-tool-card
438
+ .call=${message.toolCall}
439
+ .toolRegistry=${this.toolRegistry}
440
+ .allowSubtaskStreams=${this.allowSubtaskStreams}
441
+ ></dees-harness-tool-card>
425
442
  `;
426
443
  }
427
444
  const roleClass = `role-${message.role}`;
@@ -456,7 +473,11 @@ export class DeesHarnessMessage extends DeesElement {
456
473
  : ''}
457
474
  ${message.attachments?.length ? this.renderAttachments(message.attachments) : ''}
458
475
  ${message.toolCall
459
- ? html`<dees-harness-tool-card .call=${message.toolCall} .toolRegistry=${this.toolRegistry}></dees-harness-tool-card>`
476
+ ? html`<dees-harness-tool-card
477
+ .call=${message.toolCall}
478
+ .toolRegistry=${this.toolRegistry}
479
+ .allowSubtaskStreams=${this.allowSubtaskStreams}
480
+ ></dees-harness-tool-card>`
460
481
  : ''}
461
482
  ${this.renderText()}
462
483
  ${message.error ? html`<div class="errorText">${message.error}</div>` : ''}