@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.
- package/dist_bundle/bundle.js +3097 -1497
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +2 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.js +484 -8
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +10 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.d.ts +3 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.js +24 -4
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.d.ts +26 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +398 -94
- package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.d.ts +32 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.demo.d.ts +1 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.demo.js +31 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-overflow-text.js +297 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.js +1 -9
- package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.d.ts +10 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.js +181 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +17 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +476 -36
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +23 -20
- package/dist_ts_web/elements/00group-harness/harness.helpers.d.ts +5 -1
- package/dist_ts_web/elements/00group-harness/harness.helpers.js +66 -1
- package/dist_ts_web/elements/00group-harness/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-harness/index.js +2 -1
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +31 -2
- package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +4 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.js +8 -1
- package/package.json +2 -2
- package/readme.hints.md +16 -1
- package/readme.md +41 -9
- package/readme.playbook.md +8 -4
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.ts +490 -7
- package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +9 -0
- package/ts_web/elements/00group-harness/dees-harness-message/dees-harness-message.ts +24 -3
- package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +397 -84
- package/ts_web/elements/00group-harness/dees-harness-overflow-text.demo.ts +31 -0
- package/ts_web/elements/00group-harness/dees-harness-overflow-text.ts +236 -0
- package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.ts +0 -8
- package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.ts +193 -1
- package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +480 -34
- package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +24 -21
- package/ts_web/elements/00group-harness/harness.helpers.ts +68 -1
- package/ts_web/elements/00group-harness/index.ts +1 -0
- package/ts_web/elements/00group-harness/interfaces.ts +36 -2
- 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
|
-
|
|
444
|
-
height:
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
455
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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>` : ''}
|