@design.estate/dees-catalog 9.7.0 → 9.8.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 +194 -45
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-appui/dees-appui/dees-appui.js +6 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.js +14 -4
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.d.ts +4 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +52 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +93 -20
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +7 -21
- package/dist_ts_web/elements/00group-harness/harness.helpers.d.ts +20 -0
- package/dist_ts_web/elements/00group-harness/harness.helpers.js +58 -1
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +6 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +8 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.js +10 -2
- package/package.json +2 -2
- package/readme.md +2 -2
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-appui/dees-appui/dees-appui.ts +4 -0
- package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.ts +12 -3
- package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +50 -0
- package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +95 -23
- package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +7 -20
- package/ts_web/elements/00group-harness/harness.helpers.ts +63 -0
- package/ts_web/elements/00group-harness/interfaces.ts +6 -0
- package/ts_web/elements/00group-harness/toolregistry.ts +22 -1
|
@@ -111,7 +111,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
111
111
|
private fullscreenPortal: HTMLElementTagNameMap['dees-harness-tool-fullscreen'] | null = null;
|
|
112
112
|
private disclosureFrame: number | null = null;
|
|
113
113
|
private returnScrollFrame: number | null = null;
|
|
114
|
-
private
|
|
114
|
+
private subtaskDetailElements: HTMLElement[] = [];
|
|
115
115
|
private subtaskResultResizeObserver: ResizeObserver | null = null;
|
|
116
116
|
private terminalTailFollowing = true;
|
|
117
117
|
private terminalScrollHydrated = false;
|
|
@@ -141,7 +141,11 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
141
141
|
.harness-card.subtaskPreviewOpen {
|
|
142
142
|
display: flex;
|
|
143
143
|
flex-direction: column;
|
|
144
|
-
|
|
144
|
+
/*
|
|
145
|
+
* A cap, not a fixed height: a short brief and result let the card size to their own
|
|
146
|
+
* text, and only content taller than the cap hands the overflow to the inner scrollers.
|
|
147
|
+
*/
|
|
148
|
+
max-height: var(--dees-harness-active-subtask-height, 320px);
|
|
145
149
|
box-sizing: border-box;
|
|
146
150
|
overflow: hidden;
|
|
147
151
|
}
|
|
@@ -169,7 +173,27 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
169
173
|
overflow: hidden;
|
|
170
174
|
}
|
|
171
175
|
|
|
172
|
-
|
|
176
|
+
/*
|
|
177
|
+
* The body takes its height from flex layout rather than a percentage: a capped card has no
|
|
178
|
+
* definite height for a percentage to resolve against, so a 100% height would collapse to
|
|
179
|
+
* auto and the inner scrollers would never engage. As a flex item it shrinks to the cap when
|
|
180
|
+
* the content overflows and sizes to its text when it does not.
|
|
181
|
+
*/
|
|
182
|
+
.harness-card.subtaskPreviewOpen .bodyClipFrame {
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-direction: column;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.harness-card.subtaskPreviewOpen .body {
|
|
188
|
+
flex: 1;
|
|
189
|
+
min-height: 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* A streaming child fills the cap so its transcript has a stable window to scroll in. */
|
|
193
|
+
.harness-card.subtaskPreviewOpen:has(.liveSubtaskBody) {
|
|
194
|
+
height: var(--dees-harness-active-subtask-height, 320px);
|
|
195
|
+
}
|
|
196
|
+
|
|
173
197
|
.harness-card.subtaskPreviewOpen .subtaskPreview {
|
|
174
198
|
min-height: 0;
|
|
175
199
|
height: 100%;
|
|
@@ -259,6 +283,15 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
259
283
|
font-size: 12px;
|
|
260
284
|
}
|
|
261
285
|
|
|
286
|
+
/* Stands in for the body while a finished card is collapsed. */
|
|
287
|
+
.resultSummary {
|
|
288
|
+
display: block;
|
|
289
|
+
margin-top: 2px;
|
|
290
|
+
color: var(--dees-color-text-secondary);
|
|
291
|
+
font-family: var(--dees-font-family-mono, monospace);
|
|
292
|
+
font-size: 11px;
|
|
293
|
+
}
|
|
294
|
+
|
|
262
295
|
.metaColumn {
|
|
263
296
|
display: flex;
|
|
264
297
|
align-items: center;
|
|
@@ -680,10 +713,27 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
680
713
|
&& (this.call.subtask.status.type === 'busy' || this.call.subtask.status.type === 'error');
|
|
681
714
|
}
|
|
682
715
|
if (this.call.status === 'error' || this.call.isError) return true;
|
|
716
|
+
// A card that opened while it was live closes again once the work has settled, so a finished
|
|
717
|
+
// run stops holding transcript height. 'stopped' is deliberately excluded: a killed command
|
|
718
|
+
// keeps its partial output on screen, like an error does.
|
|
719
|
+
if (this.call.status === 'completed' && this.resolvedCollapseWhenFinished) return false;
|
|
683
720
|
if (typeof descriptor.defaultExpanded === 'function') return descriptor.defaultExpanded(this.enrichedCall);
|
|
684
721
|
return descriptor.defaultExpanded ?? false;
|
|
685
722
|
}
|
|
686
723
|
|
|
724
|
+
private get resolvedCollapseWhenFinished(): boolean {
|
|
725
|
+
const collapseWhenFinished = this.descriptor.collapseWhenFinished;
|
|
726
|
+
if (typeof collapseWhenFinished === 'function') return collapseWhenFinished(this.enrichedCall);
|
|
727
|
+
return collapseWhenFinished ?? false;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/** Outcome line shown only while collapsed; the subtitle already carries the request. */
|
|
731
|
+
private get collapsedSummary(): string {
|
|
732
|
+
if (this.isOpen || !this.descriptor.summary) return '';
|
|
733
|
+
if (this.call.status !== 'completed' && this.call.status !== 'stopped') return '';
|
|
734
|
+
return this.descriptor.summary(this.enrichedCall).trim();
|
|
735
|
+
}
|
|
736
|
+
|
|
687
737
|
private get hasSubtaskPreview(): boolean {
|
|
688
738
|
return this.allowSubtaskStreams
|
|
689
739
|
&& this.descriptor.kind === 'subtask'
|
|
@@ -704,11 +754,19 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
704
754
|
const open = this.isOpen;
|
|
705
755
|
const renderBody = this.bodyMounted || open;
|
|
706
756
|
const label = typeof descriptor.label === 'function' ? descriptor.label(call) : descriptor.label;
|
|
707
|
-
const
|
|
757
|
+
const derivedSubtitle = descriptor.subtitle?.(call) ?? '';
|
|
758
|
+
// A producer-supplied title describes the call in its own words — "Read SKILL.md" rather than
|
|
759
|
+
// the sed invocation behind it — so it leads, and the derived line stays as the tooltip.
|
|
760
|
+
const producerTitle = typeof call.title === 'string' ? call.title.trim() : '';
|
|
761
|
+
const subtitle = producerTitle || derivedSubtitle;
|
|
762
|
+
const subtitleTooltip = producerTitle && derivedSubtitle && derivedSubtitle !== producerTitle
|
|
763
|
+
? `${producerTitle}\n${derivedSubtitle}`
|
|
764
|
+
: subtitle;
|
|
708
765
|
const effectiveStatus = harnessEffectiveToolStatus(call);
|
|
709
766
|
const statusTone = effectiveStatus === 'completed' ? 'success'
|
|
710
767
|
: effectiveStatus === 'stopped' ? 'warning' : effectiveStatus;
|
|
711
768
|
const duration = harnessFormatDuration(call.startedAt, call.finishedAt);
|
|
769
|
+
const collapsedSummary = this.collapsedSummary;
|
|
712
770
|
return html`
|
|
713
771
|
<div class="harness-card status-${effectiveStatus} ${descriptor.destructive ? 'destructive' : ''} ${this.hasSubtaskPreview ? 'subtaskPreviewCard' : ''} ${this.hasSubtaskPreview && open ? 'subtaskPreviewOpen' : ''} ${this.returnFlash ? 'returnFlash' : ''} ${this.outcomeScanTone ? `outcomeScan outcomeScan-${this.outcomeScanTone}` : ''}"
|
|
714
772
|
@mouseenter=${this.cancelTerminalTailReturn}
|
|
@@ -734,7 +792,10 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
734
792
|
${this.renderMcpBadges(call)}
|
|
735
793
|
</span>
|
|
736
794
|
${subtitle
|
|
737
|
-
? html`<dees-harness-overflow-text class="subtitle" title=${
|
|
795
|
+
? html`<dees-harness-overflow-text class="subtitle" title=${subtitleTooltip}>${subtitle}</dees-harness-overflow-text>`
|
|
796
|
+
: ''}
|
|
797
|
+
${collapsedSummary
|
|
798
|
+
? html`<dees-harness-overflow-text class="resultSummary" title=${collapsedSummary}>${collapsedSummary}</dees-harness-overflow-text>`
|
|
738
799
|
: ''}
|
|
739
800
|
</span>
|
|
740
801
|
<span class="metaColumn">
|
|
@@ -1047,30 +1108,39 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
1047
1108
|
};
|
|
1048
1109
|
|
|
1049
1110
|
private readonly syncSubtaskResultScroll = (): void => {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1111
|
+
// Both panels hide their scrollbar, so the mask is the only cue that there is more text.
|
|
1112
|
+
// Leaving the brief unwired made a long brief look truncated rather than scrollable.
|
|
1113
|
+
for (const panel of this.subtaskDetailElements) {
|
|
1114
|
+
panel.classList.toggle('fadeTop', panel.scrollTop > 1);
|
|
1115
|
+
panel.classList.toggle(
|
|
1116
|
+
'fadeBottom',
|
|
1117
|
+
panel.scrollHeight - panel.scrollTop - panel.clientHeight > 1,
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1057
1120
|
};
|
|
1058
1121
|
|
|
1059
1122
|
private connectSubtaskResultScroll(): void {
|
|
1060
|
-
const
|
|
1061
|
-
'.harness-subtask-detail-
|
|
1062
|
-
|
|
1063
|
-
|
|
1123
|
+
const nextPanels = [...(this.shadowRoot?.querySelectorAll<HTMLElement>(
|
|
1124
|
+
'.harness-subtask-detail-brief .harness-subtask-detail-text,'
|
|
1125
|
+
+ ' .harness-subtask-detail-result .harness-subtask-detail-text',
|
|
1126
|
+
) ?? [])];
|
|
1127
|
+
const unchanged = nextPanels.length === this.subtaskDetailElements.length
|
|
1128
|
+
&& nextPanels.every((panel, index) => panel === this.subtaskDetailElements[index]);
|
|
1129
|
+
if (unchanged) {
|
|
1064
1130
|
this.syncSubtaskResultScroll();
|
|
1065
1131
|
return;
|
|
1066
1132
|
}
|
|
1067
|
-
|
|
1133
|
+
for (const panel of this.subtaskDetailElements) {
|
|
1134
|
+
panel.removeEventListener('scroll', this.syncSubtaskResultScroll);
|
|
1135
|
+
}
|
|
1068
1136
|
this.subtaskResultResizeObserver?.disconnect();
|
|
1069
|
-
this.
|
|
1070
|
-
if (!
|
|
1071
|
-
nextResult.addEventListener('scroll', this.syncSubtaskResultScroll, { passive: true });
|
|
1137
|
+
this.subtaskDetailElements = nextPanels;
|
|
1138
|
+
if (!nextPanels.length) return;
|
|
1072
1139
|
this.subtaskResultResizeObserver ??= new ResizeObserver(this.syncSubtaskResultScroll);
|
|
1073
|
-
|
|
1140
|
+
for (const panel of nextPanels) {
|
|
1141
|
+
panel.addEventListener('scroll', this.syncSubtaskResultScroll, { passive: true });
|
|
1142
|
+
this.subtaskResultResizeObserver.observe(panel);
|
|
1143
|
+
}
|
|
1074
1144
|
this.syncSubtaskResultScroll();
|
|
1075
1145
|
}
|
|
1076
1146
|
|
|
@@ -1116,8 +1186,10 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
1116
1186
|
this.bodyResizeObserver?.disconnect();
|
|
1117
1187
|
this.bodyResizeObserver = null;
|
|
1118
1188
|
this.observedBodyInner = null;
|
|
1119
|
-
|
|
1120
|
-
|
|
1189
|
+
for (const panel of this.subtaskDetailElements) {
|
|
1190
|
+
panel.removeEventListener('scroll', this.syncSubtaskResultScroll);
|
|
1191
|
+
}
|
|
1192
|
+
this.subtaskDetailElements = [];
|
|
1121
1193
|
this.subtaskResultResizeObserver?.disconnect();
|
|
1122
1194
|
this.subtaskResultResizeObserver = null;
|
|
1123
1195
|
await super.disconnectedCallback();
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
harnessLanguageForPath,
|
|
7
7
|
harnessParseTodos,
|
|
8
8
|
harnessStringifyValue,
|
|
9
|
+
harnessStripAnsi,
|
|
9
10
|
harnessSummarizeValue,
|
|
11
|
+
harnessTerminalOutcome,
|
|
10
12
|
} from '../harness.helpers.js';
|
|
11
13
|
import '../dees-harness-todos/dees-harness-todos.js';
|
|
12
14
|
|
|
@@ -125,35 +127,20 @@ export const applyPatchFirstPath = (call: IHarnessToolCall): string => {
|
|
|
125
127
|
return match ? match[1].trim() : '';
|
|
126
128
|
};
|
|
127
129
|
|
|
128
|
-
// eslint-disable-next-line no-control-regex
|
|
129
|
-
const ansiEscapePattern = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07]*(?:\x07|\x1b\\)|\x1b[=>]/g;
|
|
130
|
-
|
|
131
|
-
const stripAnsi = (text: string): string => text.replace(ansiEscapePattern, '');
|
|
132
|
-
|
|
133
130
|
const renderTerminal = (call: IHarnessToolCall, helpers: IHarnessToolRenderHelpers): TemplateResult => {
|
|
134
131
|
const input = inputRecord(call);
|
|
135
132
|
const command = String(input.command ?? '');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
let stderr = '';
|
|
139
|
-
let nestedExitCode: number | undefined;
|
|
140
|
-
if (harnessIsRecord(output)) {
|
|
141
|
-
stdout = typeof output.stdout === 'string' ? output.stdout : harnessStringifyValue(output.stdout ?? '');
|
|
142
|
-
stderr = typeof output.stderr === 'string' ? output.stderr : '';
|
|
143
|
-
nestedExitCode = typeof output.exitCode === 'number' ? output.exitCode : undefined;
|
|
144
|
-
} else {
|
|
145
|
-
stdout = harnessStringifyValue(output);
|
|
146
|
-
}
|
|
147
|
-
const exitCode = typeof call.exitCode === 'number' ? call.exitCode : nestedExitCode;
|
|
133
|
+
// Shared with the collapsed-card summary so both read the same outcome.
|
|
134
|
+
const { stdout, stderr, exitCode } = harnessTerminalOutcome(call);
|
|
148
135
|
const running = call.status === 'pending' || call.status === 'running';
|
|
149
136
|
return html`
|
|
150
137
|
<div class="harness-terminal">
|
|
151
138
|
${command
|
|
152
139
|
? html`<div class="harness-terminal-command"><span class="prompt">$</span> ${command}</div>`
|
|
153
140
|
: ''}
|
|
154
|
-
${stdout ? html`<pre class="harness-terminal-output">${
|
|
155
|
-
${stderr ? html`<pre class="harness-terminal-output tone-error">${
|
|
156
|
-
${call.errorText ? html`<pre class="harness-terminal-output tone-error">${
|
|
141
|
+
${stdout ? html`<pre class="harness-terminal-output">${harnessStripAnsi(stdout)}</pre>` : ''}
|
|
142
|
+
${stderr ? html`<pre class="harness-terminal-output tone-error">${harnessStripAnsi(stderr)}</pre>` : ''}
|
|
143
|
+
${call.errorText ? html`<pre class="harness-terminal-output tone-error">${harnessStripAnsi(call.errorText)}</pre>` : ''}
|
|
157
144
|
${running && !stdout && !stderr
|
|
158
145
|
? html`<div class="harness-terminal-running"><span class="cursor"></span> running…</div>`
|
|
159
146
|
: ''}
|
|
@@ -313,3 +313,66 @@ export const harnessSummarizeValue = (value: unknown): string => {
|
|
|
313
313
|
export const harnessSummaryLine = (value: unknown): string => {
|
|
314
314
|
return harnessSummarizeValue(value).split('\n')[0] || '';
|
|
315
315
|
};
|
|
316
|
+
|
|
317
|
+
// eslint-disable-next-line no-control-regex
|
|
318
|
+
const ansiEscapePattern = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07]*(?:\x07|\x1b\\)|\x1b[=>]/g;
|
|
319
|
+
|
|
320
|
+
/** Terminal output carries raw escape sequences; they must never reach a header or a summary. */
|
|
321
|
+
export const harnessStripAnsi = (text: string): string => text.replace(ansiEscapePattern, '');
|
|
322
|
+
|
|
323
|
+
export interface IHarnessTerminalOutcome {
|
|
324
|
+
stdout: string;
|
|
325
|
+
stderr: string;
|
|
326
|
+
/** Top-level exitCode wins over a nested one, matching how producers report it. */
|
|
327
|
+
exitCode?: number;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Normalizes the three shapes producers use for shell output: a `{stdout, stderr, exitCode}`
|
|
332
|
+
* record, a combined string with a separate top-level `exitCode`, or an arbitrary value.
|
|
333
|
+
*/
|
|
334
|
+
export const harnessTerminalOutcome = (callArg: IHarnessToolCall): IHarnessTerminalOutcome => {
|
|
335
|
+
const output = callArg.output;
|
|
336
|
+
let stdout = '';
|
|
337
|
+
let stderr = '';
|
|
338
|
+
let nestedExitCode: number | undefined;
|
|
339
|
+
if (harnessIsRecord(output)) {
|
|
340
|
+
stdout = typeof output.stdout === 'string' ? output.stdout : harnessStringifyValue(output.stdout ?? '');
|
|
341
|
+
stderr = typeof output.stderr === 'string' ? output.stderr : '';
|
|
342
|
+
nestedExitCode = typeof output.exitCode === 'number' ? output.exitCode : undefined;
|
|
343
|
+
} else {
|
|
344
|
+
stdout = harnessStringifyValue(output);
|
|
345
|
+
}
|
|
346
|
+
const exitCode = typeof callArg.exitCode === 'number' ? callArg.exitCode : nestedExitCode;
|
|
347
|
+
return { stdout, stderr, ...(exitCode === undefined ? {} : { exitCode }) };
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const harnessLastMeaningfulLine = (text: string): string => {
|
|
351
|
+
const lines = harnessStripAnsi(text).split('\n');
|
|
352
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
353
|
+
const line = lines[index].trim();
|
|
354
|
+
if (line) return line;
|
|
355
|
+
}
|
|
356
|
+
return '';
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* One-line outcome for a finished shell call, for the collapsed card header. The command itself
|
|
361
|
+
* is already the card subtitle, so this describes the RESULT: exit code, how much came back, and
|
|
362
|
+
* the last meaningful line. Harness support for a richer summary is uneven — only OpenCode
|
|
363
|
+
* supplies a model-authored description, so this is derived from output that every harness has.
|
|
364
|
+
*/
|
|
365
|
+
export const harnessTerminalResultSummary = (callArg: IHarnessToolCall): string => {
|
|
366
|
+
const { stdout, stderr, exitCode } = harnessTerminalOutcome(callArg);
|
|
367
|
+
const parts: string[] = [];
|
|
368
|
+
if (exitCode !== undefined) parts.push(exitCode === 0 ? 'exit 0' : `exit ${exitCode}`);
|
|
369
|
+
const combined = `${stdout}${stdout && stderr ? '\n' : ''}${stderr}`;
|
|
370
|
+
const lineCount = combined ? harnessStripAnsi(combined).split('\n').filter((line) => line.trim()).length : 0;
|
|
371
|
+
if (lineCount > 0) parts.push(`${lineCount} ${lineCount === 1 ? 'line' : 'lines'}`);
|
|
372
|
+
else if (exitCode !== undefined) parts.push('no output');
|
|
373
|
+
const lastLine = harnessLastMeaningfulLine(stderr) || harnessLastMeaningfulLine(stdout);
|
|
374
|
+
const prefix = parts.join(' · ');
|
|
375
|
+
if (!lastLine) return prefix;
|
|
376
|
+
const trimmed = lastLine.length > 160 ? `${lastLine.slice(0, 160)}…` : lastLine;
|
|
377
|
+
return prefix ? `${prefix} · ${trimmed}` : trimmed;
|
|
378
|
+
};
|
|
@@ -230,6 +230,12 @@ export interface IHarnessToolCall {
|
|
|
230
230
|
subtask?: IHarnessSubtaskStream;
|
|
231
231
|
/** model the spawned session runs on, e.g. "anthropic/claude-opus-5" */
|
|
232
232
|
model?: string;
|
|
233
|
+
/**
|
|
234
|
+
* Producer-supplied one-line description of the call, e.g. OpenCode's tool title or a parsed
|
|
235
|
+
* shell action. Single line, already bounded by the producer. Preferred over any line the card
|
|
236
|
+
* derives for itself; absent whenever the producer offers nothing.
|
|
237
|
+
*/
|
|
238
|
+
title?: string;
|
|
233
239
|
/** epoch ms */
|
|
234
240
|
startedAt?: number;
|
|
235
241
|
finishedAt?: number;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { TemplateResult } from '@design.estate/dees-element';
|
|
2
2
|
import type { TDiffViewRequest } from '../00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js';
|
|
3
3
|
import type { IHarnessToolCall, THarnessContentBlock } from './interfaces.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
harnessIsRecord,
|
|
6
|
+
harnessSummaryLine,
|
|
7
|
+
harnessTerminalOutcome,
|
|
8
|
+
harnessTerminalResultSummary,
|
|
9
|
+
} from './harness.helpers.js';
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* Render helpers handed to tool body renderers. Implemented by
|
|
@@ -49,6 +54,14 @@ export interface IHarnessToolDescriptor {
|
|
|
49
54
|
*/
|
|
50
55
|
renderBody?: (call: IHarnessToolCall, helpers: IHarnessToolRenderHelpers) => TemplateResult;
|
|
51
56
|
defaultExpanded?: boolean | ((call: IHarnessToolCall) => boolean);
|
|
57
|
+
/**
|
|
58
|
+
* Collapses a card that opened while it was live once its work is done, so a finished run
|
|
59
|
+
* stops occupying the transcript. Evaluated only for a settled call, and always subordinate to
|
|
60
|
+
* an explicit user toggle.
|
|
61
|
+
*/
|
|
62
|
+
collapseWhenFinished?: boolean | ((call: IHarnessToolCall) => boolean);
|
|
63
|
+
/** One-line outcome shown in the header while the card is collapsed. */
|
|
64
|
+
summary?: (call: IHarnessToolCall) => string;
|
|
52
65
|
/** Transcript grid packing; omitted descriptors use the compact layout. */
|
|
53
66
|
layout?: THarnessToolLayout;
|
|
54
67
|
/** styles the card with a destructive accent regardless of status */
|
|
@@ -101,6 +114,14 @@ const defaultDescriptors: IHarnessToolDescriptor[] = [
|
|
|
101
114
|
},
|
|
102
115
|
defaultExpanded: true,
|
|
103
116
|
layout: 'full-row',
|
|
117
|
+
/*
|
|
118
|
+
* Only a command that can be PROVEN to have succeeded collapses. An absent exit code is not
|
|
119
|
+
* evidence of success: a producer that reports a failed command as completed and encodes the
|
|
120
|
+
* failure in its output would otherwise have that failure hidden behind a one-line summary.
|
|
121
|
+
* Such a card stays open, and the producer reporting an exit code is the real fix.
|
|
122
|
+
*/
|
|
123
|
+
collapseWhenFinished: (call) => harnessTerminalOutcome(call).exitCode === 0,
|
|
124
|
+
summary: harnessTerminalResultSummary,
|
|
104
125
|
},
|
|
105
126
|
{
|
|
106
127
|
kind: 'file-read',
|