@design.estate/dees-catalog 6.1.0 → 6.3.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 (50) hide show
  1. package/dist_bundle/bundle.js +3161 -1498
  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 +5 -1
  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 +33 -2
  6. package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.d.ts +6 -4
  7. package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.js +7 -1
  8. package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.js +67 -6
  9. package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.d.ts +3 -1
  10. package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.js +24 -4
  11. package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.d.ts +26 -0
  12. package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +398 -94
  13. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.d.ts +32 -0
  14. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.demo.d.ts +1 -0
  15. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.demo.js +31 -0
  16. package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.js +297 -0
  17. package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.js +1 -9
  18. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.d.ts +10 -1
  19. package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.js +181 -2
  20. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +17 -1
  21. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +476 -36
  22. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +23 -20
  23. package/dist_ts_web/elements/00group-harness/harness.helpers.d.ts +5 -1
  24. package/dist_ts_web/elements/00group-harness/harness.helpers.js +66 -1
  25. package/dist_ts_web/elements/00group-harness/index.d.ts +1 -0
  26. package/dist_ts_web/elements/00group-harness/index.js +2 -1
  27. package/dist_ts_web/elements/00group-harness/interfaces.d.ts +39 -2
  28. package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +4 -0
  29. package/dist_ts_web/elements/00group-harness/toolregistry.js +8 -1
  30. package/package.json +2 -2
  31. package/readme.hints.md +16 -1
  32. package/readme.md +48 -11
  33. package/readme.playbook.md +8 -4
  34. package/ts_web/00_commitinfo_data.ts +1 -1
  35. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.ts +490 -7
  36. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +22 -0
  37. package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.ts +6 -0
  38. package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.ts +64 -5
  39. package/ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.ts +24 -3
  40. package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +397 -84
  41. package/ts_web/elements/00group-harness/dees-harness-overflow-text.demo.ts +31 -0
  42. package/ts_web/elements/00group-harness/dees-harness-overflow-text.ts +236 -0
  43. package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.ts +0 -8
  44. package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.ts +193 -1
  45. package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +480 -34
  46. package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +24 -21
  47. package/ts_web/elements/00group-harness/harness.helpers.ts +68 -1
  48. package/ts_web/elements/00group-harness/index.ts +1 -0
  49. package/ts_web/elements/00group-harness/interfaces.ts +46 -2
  50. 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>
@@ -11,7 +11,9 @@ import {
11
11
  } from '@design.estate/dees-element';
12
12
  import { themeDefaultStyles } from '../../00theme.js';
13
13
  import type {
14
+ IHarnessAccountChangeDetail,
14
15
  IHarnessAttachment,
16
+ IHarnessComposerOption,
15
17
  IHarnessMessage,
16
18
  IHarnessPermissionRequest,
17
19
  IHarnessQuestionRequest,
@@ -148,6 +150,12 @@ export class DeesHarnessChat extends DeesElement {
148
150
  @property({ attribute: false })
149
151
  accessor attachments: IHarnessAttachment[] = [];
150
152
 
153
+ @property()
154
+ accessor account: string = '';
155
+
156
+ @property({ attribute: false })
157
+ accessor accountOptions: IHarnessComposerOption[] = [];
158
+
151
159
  @property()
152
160
  accessor model: string = '';
153
161
 
@@ -426,6 +434,7 @@ export class DeesHarnessChat extends DeesElement {
426
434
  /** Streams an incremental update into the message list. */
427
435
  public applyDelta(delta: THarnessStreamDelta): void {
428
436
  this.listElement?.applyDelta(delta);
437
+ if (!delta.parentMessageId && delta.type === 'tool-update') this.requestUpdate();
429
438
  }
430
439
 
431
440
  public async focusComposer(): Promise<void> {
@@ -481,10 +490,16 @@ export class DeesHarnessChat extends DeesElement {
481
490
  return [];
482
491
  }
483
492
 
493
+ /** Top-level tool messages still present in the host's retained transcript window. */
494
+ public get toolActivityMessages(): IHarnessMessage[] {
495
+ return this.messages.filter((message) => Boolean(message.toolCall));
496
+ }
497
+
484
498
  public get hasSessionSidebar(): boolean {
485
499
  if (!this.showSessionSidebar) return false;
486
500
  return (
487
501
  (this.showTodosPanel && this.effectiveTodos.length > 0)
502
+ || this.toolActivityMessages.length > 0
488
503
  || Object.values(this.sessionMetrics).some((value) => value !== undefined)
489
504
  || this.scratchpad !== undefined
490
505
  || this.sessionIntelligenceEnabled
@@ -610,6 +625,8 @@ export class DeesHarnessChat extends DeesElement {
610
625
  <dees-harness-session-sidebar
611
626
  .todos=${this.effectiveTodos}
612
627
  .showTasks=${this.showTodosPanel}
628
+ .toolMessages=${this.toolActivityMessages}
629
+ .toolRegistry=${this.toolRegistry}
613
630
  .metrics=${this.sessionMetrics}
614
631
  .scratchpad=${this.scratchpad}
615
632
  .scratchpadBusy=${this.scratchpadBusy}
@@ -632,6 +649,8 @@ export class DeesHarnessChat extends DeesElement {
632
649
  .disabled=${this.disabled}
633
650
  .busy=${this.busy || this.status.type === 'busy'}
634
651
  .queuedCount=${this.queuedCount}
652
+ .account=${this.account}
653
+ .accountOptions=${this.accountOptions}
635
654
  .model=${this.model}
636
655
  .modelOptions=${this.modelOptions}
637
656
  .reasoningEffort=${this.reasoningEffort}
@@ -644,6 +663,9 @@ export class DeesHarnessChat extends DeesElement {
644
663
  @harness-attachments-change=${(event: CustomEvent<{ attachments: IHarnessAttachment[] }>) => {
645
664
  this.attachments = event.detail?.attachments ?? [];
646
665
  }}
666
+ @harness-account-change=${(event: CustomEvent<IHarnessAccountChangeDetail>) => {
667
+ if (event.detail?.account) this.account = event.detail.account;
668
+ }}
647
669
  @harness-model-change=${(event: CustomEvent<{ model?: string; reasoningEffort?: string }>) => {
648
670
  if (event.detail?.model) this.model = event.detail.model;
649
671
  if (event.detail?.reasoningEffort) this.reasoningEffort = event.detail.reasoningEffort;
@@ -29,15 +29,21 @@ export const demoFunc = () => html`
29
29
  @harness-input=${logEvent}
30
30
  @harness-attachments-change=${logEvent}
31
31
  @harness-attachment-error=${logEvent}
32
+ @harness-account-change=${logEvent}
32
33
  @harness-model-change=${logEvent}
33
34
  @harness-mode-change=${logEvent}
34
35
  >
35
36
  <div class="label">default with mode switcher + model controls (events log to console)</div>
36
37
  <dees-harness-composer
38
+ account="account-1"
37
39
  model="gpt-5.5"
38
40
  reasoningEffort="high"
39
41
  mode="Planning"
40
42
  .modeOptions=${['Planning', 'Building']}
43
+ .accountOptions=${[
44
+ { label: 'Personal · alex@example.com', value: 'account-1' },
45
+ { label: 'Team · alex@example.com', value: 'account-2' },
46
+ ]}
41
47
  .modelOptions=${['gpt-5.5', 'gpt-5', 'o4-mini']}
42
48
  .effortOptions=${['xhigh', 'high', 'medium', 'low']}
43
49
  ></dees-harness-composer>