@shipfox/client-logs 27.0.1 → 29.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type$colon$emit.log +0 -1
- package/CHANGELOG.md +10 -0
- package/dist/components/agent-session-rows.js +273 -232
- package/dist/components/agent-session-rows.js.map +1 -1
- package/dist/components/log-view.d.ts.map +1 -1
- package/dist/components/log-view.js +4 -1
- package/dist/components/log-view.js.map +1 -1
- package/dist/core/log-tree.d.ts.map +1 -1
- package/dist/core/log-tree.js +109 -123
- package/dist/core/log-tree.js.map +1 -1
- package/dist/hooks/api/step-logs.d.ts.map +1 -1
- package/dist/hooks/api/step-logs.js +51 -38
- package/dist/hooks/api/step-logs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/agent-session-rows.tsx +259 -177
- package/src/components/log-view.tsx +7 -5
- package/src/core/log-tree.ts +117 -104
- package/src/hooks/api/step-logs.ts +73 -47
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-logs",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "29.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@swc/helpers": "^0.5.17",
|
|
21
21
|
"@shipfox/api-logs-dto": "17.0.0",
|
|
22
22
|
"@shipfox/client-api": "6.0.1",
|
|
23
|
-
"@shipfox/react-ui": "2.3.
|
|
23
|
+
"@shipfox/react-ui": "2.3.3"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {Icon} from '@shipfox/react-ui/icon';
|
|
3
|
+
import {Icon, type IconName} from '@shipfox/react-ui/icon';
|
|
4
4
|
import {
|
|
5
5
|
LogContent,
|
|
6
6
|
LogDisclosure,
|
|
@@ -67,198 +67,280 @@ function AgentSessionRowView({
|
|
|
67
67
|
|
|
68
68
|
switch (row.kind) {
|
|
69
69
|
case 'message':
|
|
70
|
-
return
|
|
71
|
-
<LogRow
|
|
72
|
-
lineNumber={null}
|
|
73
|
-
timestamp={new Date(row.timestamp)}
|
|
74
|
-
indent={indent}
|
|
75
|
-
tone={row.terminalFailure ? 'error' : 'default'}
|
|
76
|
-
data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}
|
|
77
|
-
>
|
|
78
|
-
<LogContent className="text-foreground-contrast-primary">
|
|
79
|
-
<span className="flex min-w-0 items-start gap-inline">
|
|
80
|
-
<MessageIcon role={row.role} terminalFailure={row.terminalFailure} />
|
|
81
|
-
<span className="flex min-w-0 flex-1 flex-col gap-tight">
|
|
82
|
-
<span className="flex min-w-0 items-center gap-inline">
|
|
83
|
-
<MessageRoleLabel label={row.label} terminalFailure={row.terminalFailure} />
|
|
84
|
-
<RowMetadata meta={row.meta} className="ml-auto flex-none" />
|
|
85
|
-
</span>
|
|
86
|
-
<span className="block min-w-0">
|
|
87
|
-
<PreviewText text={row.text} />
|
|
88
|
-
</span>
|
|
89
|
-
</span>
|
|
90
|
-
</span>
|
|
91
|
-
</LogContent>
|
|
92
|
-
</LogRow>
|
|
93
|
-
);
|
|
70
|
+
return <SessionMessageRow row={row} indent={indent} />;
|
|
94
71
|
case 'thinking':
|
|
72
|
+
return <SessionThinkingRow row={row} indent={indent} disclosure={disclosureProps} />;
|
|
73
|
+
case 'tool-call':
|
|
95
74
|
return (
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
thinking
|
|
103
|
-
</LogDisclosureTrigger>
|
|
104
|
-
<LogDisclosureContent className="text-foreground-contrast-secondary">
|
|
105
|
-
<LogContent className="text-foreground-contrast-secondary">
|
|
106
|
-
<PreviewText text={row.text} />
|
|
107
|
-
</LogContent>
|
|
108
|
-
</LogDisclosureContent>
|
|
109
|
-
</LogDisclosure>
|
|
110
|
-
);
|
|
111
|
-
case 'tool-call': {
|
|
112
|
-
const awaitingResult = row.id != null && !resolvedToolCallIds.has(row.id);
|
|
113
|
-
return (
|
|
114
|
-
<LogDisclosure indent={indent} {...disclosureProps}>
|
|
115
|
-
<LogDisclosureTrigger
|
|
116
|
-
timestamp={new Date(row.timestamp)}
|
|
117
|
-
summary={compactPreview(row.summary ?? row.input)}
|
|
118
|
-
trailing={
|
|
119
|
-
awaitingResult ? (
|
|
120
|
-
<span className="inline-flex items-center gap-tight">
|
|
121
|
-
<Icon
|
|
122
|
-
name="loader4Line"
|
|
123
|
-
className="size-12 motion-safe:animate-spin"
|
|
124
|
-
aria-hidden="true"
|
|
125
|
-
/>
|
|
126
|
-
awaiting result
|
|
127
|
-
</span>
|
|
128
|
-
) : null
|
|
129
|
-
}
|
|
130
|
-
>
|
|
131
|
-
<span className="inline-flex min-w-0 items-center gap-inline">
|
|
132
|
-
<Icon name="terminalBoxLine" className="size-14 flex-none" aria-hidden="true" />
|
|
133
|
-
<span className="truncate">tool {row.name}</span>
|
|
134
|
-
</span>
|
|
135
|
-
</LogDisclosureTrigger>
|
|
136
|
-
<LogDisclosureContent>
|
|
137
|
-
{row.summary != null ? (
|
|
138
|
-
<>
|
|
139
|
-
<LogContent>
|
|
140
|
-
<PreviewText text={row.summary} />
|
|
141
|
-
</LogContent>
|
|
142
|
-
<LogContent variant="code">
|
|
143
|
-
<PreviewText text={row.input} />
|
|
144
|
-
</LogContent>
|
|
145
|
-
</>
|
|
146
|
-
) : (
|
|
147
|
-
<LogContent variant="code">
|
|
148
|
-
<PreviewText text={row.input} />
|
|
149
|
-
</LogContent>
|
|
150
|
-
)}
|
|
151
|
-
</LogDisclosureContent>
|
|
152
|
-
</LogDisclosure>
|
|
75
|
+
<SessionToolCallRow
|
|
76
|
+
row={row}
|
|
77
|
+
indent={indent}
|
|
78
|
+
disclosure={disclosureProps}
|
|
79
|
+
resolvedToolCallIds={resolvedToolCallIds}
|
|
80
|
+
/>
|
|
153
81
|
);
|
|
154
|
-
|
|
155
|
-
case 'tool-result': {
|
|
156
|
-
const toolName =
|
|
157
|
-
row.toolName === 'tool'
|
|
158
|
-
? ((row.toolCallId != null ? toolCallNames.get(row.toolCallId) : undefined) ??
|
|
159
|
-
'(unmatched)')
|
|
160
|
-
: row.toolName;
|
|
82
|
+
case 'tool-result':
|
|
161
83
|
return (
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
timestamp={new Date(row.timestamp)}
|
|
165
|
-
summary={compactPreview(row.output)}
|
|
166
|
-
trailing={
|
|
167
|
-
<span
|
|
168
|
-
className={cn(
|
|
169
|
-
'inline-flex items-center gap-tight',
|
|
170
|
-
row.isError ? 'text-tag-error-icon' : 'text-foreground-contrast-secondary',
|
|
171
|
-
)}
|
|
172
|
-
>
|
|
173
|
-
<Icon
|
|
174
|
-
name={row.isError ? 'closeCircleLine' : 'checkLine'}
|
|
175
|
-
className="size-12"
|
|
176
|
-
aria-hidden="true"
|
|
177
|
-
/>
|
|
178
|
-
{row.isError ? 'error' : 'ok'}
|
|
179
|
-
</span>
|
|
180
|
-
}
|
|
181
|
-
>
|
|
182
|
-
<span className="inline-flex min-w-0 items-center gap-inline">
|
|
183
|
-
<Icon name="terminalWindowLine" className="size-14 flex-none" aria-hidden="true" />
|
|
184
|
-
<span className="truncate">result {toolName}</span>
|
|
185
|
-
</span>
|
|
186
|
-
</LogDisclosureTrigger>
|
|
187
|
-
<LogDisclosureContent>
|
|
188
|
-
<LogContent variant="code" className="text-foreground-contrast-primary">
|
|
189
|
-
<PreviewText text={row.output} />
|
|
190
|
-
</LogContent>
|
|
191
|
-
</LogDisclosureContent>
|
|
192
|
-
</LogDisclosure>
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
case 'lifecycle':
|
|
196
|
-
return (
|
|
197
|
-
<LogRow
|
|
198
|
-
lineNumber={null}
|
|
199
|
-
timestamp={new Date(row.timestamp)}
|
|
84
|
+
<SessionToolResultRow
|
|
85
|
+
row={row}
|
|
200
86
|
indent={indent}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
<LogContent className="text-foreground-contrast-secondary">
|
|
205
|
-
<span className="inline-flex w-full items-center gap-inline">
|
|
206
|
-
<Icon name="informationLine" className="size-14 flex-none" aria-hidden="true" />
|
|
207
|
-
<span className="min-w-0">
|
|
208
|
-
<span className="font-medium">{row.label}</span>
|
|
209
|
-
{row.detail != null ? (
|
|
210
|
-
<>
|
|
211
|
-
{' · '}
|
|
212
|
-
<span className="text-foreground-contrast-secondary">{row.detail}</span>
|
|
213
|
-
</>
|
|
214
|
-
) : null}
|
|
215
|
-
</span>
|
|
216
|
-
<span
|
|
217
|
-
aria-hidden="true"
|
|
218
|
-
className="h-px flex-1 border-t border-dashed border-current opacity-30"
|
|
219
|
-
/>
|
|
220
|
-
<RowMetadata meta={row.meta} />
|
|
221
|
-
</span>
|
|
222
|
-
</LogContent>
|
|
223
|
-
</LogRow>
|
|
87
|
+
disclosure={disclosureProps}
|
|
88
|
+
toolCallNames={toolCallNames}
|
|
89
|
+
/>
|
|
224
90
|
);
|
|
91
|
+
case 'lifecycle':
|
|
92
|
+
return <SessionLifecycleRow row={row} indent={indent} />;
|
|
225
93
|
case 'raw':
|
|
226
|
-
return
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
94
|
+
return <SessionRawRow row={row} indent={indent} disclosure={disclosureProps} />;
|
|
95
|
+
default:
|
|
96
|
+
return assertNever(row);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type DisclosureState = {open: boolean; onOpenChange: (open: boolean) => void};
|
|
101
|
+
|
|
102
|
+
function SessionMessageRow({
|
|
103
|
+
row,
|
|
104
|
+
indent,
|
|
105
|
+
}: {
|
|
106
|
+
row: Extract<SessionViewRow, {kind: 'message'}>;
|
|
107
|
+
indent: number;
|
|
108
|
+
}) {
|
|
109
|
+
return (
|
|
110
|
+
<LogRow
|
|
111
|
+
lineNumber={null}
|
|
112
|
+
timestamp={new Date(row.timestamp)}
|
|
113
|
+
indent={indent}
|
|
114
|
+
tone={row.terminalFailure ? 'error' : 'default'}
|
|
115
|
+
data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}
|
|
116
|
+
>
|
|
117
|
+
<LogContent className="text-foreground-contrast-primary">
|
|
118
|
+
<span className="flex min-w-0 items-start gap-inline">
|
|
119
|
+
<MessageIcon role={row.role} terminalFailure={row.terminalFailure} />
|
|
120
|
+
<span className="flex min-w-0 flex-1 flex-col gap-tight">
|
|
121
|
+
<span className="flex min-w-0 items-center gap-inline">
|
|
122
|
+
<MessageRoleLabel label={row.label} terminalFailure={row.terminalFailure} />
|
|
123
|
+
<RowMetadata meta={row.meta} className="ml-auto flex-none" />
|
|
124
|
+
</span>
|
|
125
|
+
<span className="block min-w-0">
|
|
126
|
+
<PreviewText text={row.text} />
|
|
127
|
+
</span>
|
|
128
|
+
</span>
|
|
129
|
+
</span>
|
|
130
|
+
</LogContent>
|
|
131
|
+
</LogRow>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function SessionThinkingRow({
|
|
136
|
+
row,
|
|
137
|
+
indent,
|
|
138
|
+
disclosure,
|
|
139
|
+
}: {
|
|
140
|
+
row: Extract<SessionViewRow, {kind: 'thinking'}>;
|
|
141
|
+
indent: number;
|
|
142
|
+
disclosure: DisclosureState;
|
|
143
|
+
}) {
|
|
144
|
+
return (
|
|
145
|
+
<LogDisclosure indent={indent} {...disclosure}>
|
|
146
|
+
<LogDisclosureTrigger
|
|
147
|
+
summary={wordSummary(row.text)}
|
|
148
|
+
timestamp={new Date(row.timestamp)}
|
|
149
|
+
className="text-foreground-contrast-secondary"
|
|
150
|
+
>
|
|
151
|
+
thinking
|
|
152
|
+
</LogDisclosureTrigger>
|
|
153
|
+
<LogDisclosureContent className="text-foreground-contrast-secondary">
|
|
154
|
+
<LogContent className="text-foreground-contrast-secondary">
|
|
155
|
+
<PreviewText text={row.text} />
|
|
156
|
+
</LogContent>
|
|
157
|
+
</LogDisclosureContent>
|
|
158
|
+
</LogDisclosure>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function SessionToolCallRow({
|
|
163
|
+
row,
|
|
164
|
+
indent,
|
|
165
|
+
disclosure,
|
|
166
|
+
resolvedToolCallIds,
|
|
167
|
+
}: {
|
|
168
|
+
row: Extract<SessionViewRow, {kind: 'tool-call'}>;
|
|
169
|
+
indent: number;
|
|
170
|
+
disclosure: DisclosureState;
|
|
171
|
+
resolvedToolCallIds: ReadonlySet<string>;
|
|
172
|
+
}) {
|
|
173
|
+
const awaitingResult = row.id != null && !resolvedToolCallIds.has(row.id);
|
|
174
|
+
return (
|
|
175
|
+
<LogDisclosure indent={indent} {...disclosure}>
|
|
176
|
+
<LogDisclosureTrigger
|
|
177
|
+
timestamp={new Date(row.timestamp)}
|
|
178
|
+
summary={compactPreview(row.summary ?? row.input)}
|
|
179
|
+
trailing={
|
|
180
|
+
awaitingResult ? (
|
|
181
|
+
<span className="inline-flex items-center gap-tight">
|
|
234
182
|
<Icon
|
|
235
|
-
name="
|
|
236
|
-
className="size-
|
|
183
|
+
name="loader4Line"
|
|
184
|
+
className="size-12 motion-safe:animate-spin"
|
|
237
185
|
aria-hidden="true"
|
|
238
186
|
/>
|
|
239
|
-
|
|
187
|
+
awaiting result
|
|
240
188
|
</span>
|
|
241
|
-
|
|
242
|
-
|
|
189
|
+
) : null
|
|
190
|
+
}
|
|
191
|
+
>
|
|
192
|
+
<span className="inline-flex min-w-0 items-center gap-inline">
|
|
193
|
+
<Icon name="terminalBoxLine" className="size-14 flex-none" aria-hidden="true" />
|
|
194
|
+
<span className="truncate">tool {row.name}</span>
|
|
195
|
+
</span>
|
|
196
|
+
</LogDisclosureTrigger>
|
|
197
|
+
<LogDisclosureContent>
|
|
198
|
+
{row.summary != null ? (
|
|
199
|
+
<>
|
|
200
|
+
<LogContent>
|
|
201
|
+
<PreviewText text={row.summary} />
|
|
202
|
+
</LogContent>
|
|
243
203
|
<LogContent variant="code">
|
|
244
|
-
<PreviewText text={row.
|
|
204
|
+
<PreviewText text={row.input} />
|
|
245
205
|
</LogContent>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
206
|
+
</>
|
|
207
|
+
) : (
|
|
208
|
+
<LogContent variant="code">
|
|
209
|
+
<PreviewText text={row.input} />
|
|
210
|
+
</LogContent>
|
|
211
|
+
)}
|
|
212
|
+
</LogDisclosureContent>
|
|
213
|
+
</LogDisclosure>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function SessionToolResultRow({
|
|
218
|
+
row,
|
|
219
|
+
indent,
|
|
220
|
+
disclosure,
|
|
221
|
+
toolCallNames,
|
|
222
|
+
}: {
|
|
223
|
+
row: Extract<SessionViewRow, {kind: 'tool-result'}>;
|
|
224
|
+
indent: number;
|
|
225
|
+
disclosure: DisclosureState;
|
|
226
|
+
toolCallNames: ReadonlyMap<string, string>;
|
|
227
|
+
}) {
|
|
228
|
+
const toolName =
|
|
229
|
+
row.toolName === 'tool'
|
|
230
|
+
? ((row.toolCallId != null ? toolCallNames.get(row.toolCallId) : undefined) ?? '(unmatched)')
|
|
231
|
+
: row.toolName;
|
|
232
|
+
return (
|
|
233
|
+
<LogDisclosure indent={indent} {...disclosure}>
|
|
234
|
+
<LogDisclosureTrigger
|
|
235
|
+
timestamp={new Date(row.timestamp)}
|
|
236
|
+
summary={compactPreview(row.output)}
|
|
237
|
+
trailing={
|
|
238
|
+
<span
|
|
239
|
+
className={cn(
|
|
240
|
+
'inline-flex items-center gap-tight',
|
|
241
|
+
row.isError ? 'text-tag-error-icon' : 'text-foreground-contrast-secondary',
|
|
242
|
+
)}
|
|
243
|
+
>
|
|
244
|
+
<Icon
|
|
245
|
+
name={row.isError ? 'closeCircleLine' : 'checkLine'}
|
|
246
|
+
className="size-12"
|
|
247
|
+
aria-hidden="true"
|
|
248
|
+
/>
|
|
249
|
+
{row.isError ? 'error' : 'ok'}
|
|
250
|
+
</span>
|
|
251
|
+
}
|
|
252
|
+
>
|
|
253
|
+
<span className="inline-flex min-w-0 items-center gap-inline">
|
|
254
|
+
<Icon name="terminalWindowLine" className="size-14 flex-none" aria-hidden="true" />
|
|
255
|
+
<span className="truncate">result {toolName}</span>
|
|
256
|
+
</span>
|
|
257
|
+
</LogDisclosureTrigger>
|
|
258
|
+
<LogDisclosureContent>
|
|
259
|
+
<LogContent variant="code" className="text-foreground-contrast-primary">
|
|
260
|
+
<PreviewText text={row.output} />
|
|
261
|
+
</LogContent>
|
|
262
|
+
</LogDisclosureContent>
|
|
263
|
+
</LogDisclosure>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function SessionLifecycleRow({
|
|
268
|
+
row,
|
|
269
|
+
indent,
|
|
270
|
+
}: {
|
|
271
|
+
row: Extract<SessionViewRow, {kind: 'lifecycle'}>;
|
|
272
|
+
indent: number;
|
|
273
|
+
}) {
|
|
274
|
+
return (
|
|
275
|
+
<LogRow
|
|
276
|
+
lineNumber={null}
|
|
277
|
+
timestamp={new Date(row.timestamp)}
|
|
278
|
+
indent={indent}
|
|
279
|
+
tone={row.tone}
|
|
280
|
+
data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}
|
|
281
|
+
>
|
|
282
|
+
<LogContent className="text-foreground-contrast-secondary">
|
|
283
|
+
<span className="inline-flex w-full items-center gap-inline">
|
|
284
|
+
<Icon name="informationLine" className="size-14 flex-none" aria-hidden="true" />
|
|
285
|
+
<span className="min-w-0">
|
|
286
|
+
<span className="font-medium">{row.label}</span>
|
|
287
|
+
{row.detail != null ? (
|
|
288
|
+
<>
|
|
289
|
+
{' · '}
|
|
290
|
+
<span className="text-foreground-contrast-secondary">{row.detail}</span>
|
|
291
|
+
</>
|
|
292
|
+
) : null}
|
|
293
|
+
</span>
|
|
294
|
+
<span
|
|
295
|
+
aria-hidden="true"
|
|
296
|
+
className="h-px flex-1 border-t border-dashed border-current opacity-30"
|
|
297
|
+
/>
|
|
298
|
+
<RowMetadata meta={row.meta} />
|
|
299
|
+
</span>
|
|
300
|
+
</LogContent>
|
|
301
|
+
</LogRow>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function SessionRawRow({
|
|
306
|
+
row,
|
|
307
|
+
indent,
|
|
308
|
+
disclosure,
|
|
309
|
+
}: {
|
|
310
|
+
row: Extract<SessionViewRow, {kind: 'raw'}>;
|
|
311
|
+
indent: number;
|
|
312
|
+
disclosure: DisclosureState;
|
|
313
|
+
}) {
|
|
314
|
+
return (
|
|
315
|
+
<LogDisclosure indent={indent} {...disclosure}>
|
|
316
|
+
<LogDisclosureTrigger
|
|
317
|
+
timestamp={new Date(row.timestamp)}
|
|
318
|
+
summary={compactPreview(row.raw)}
|
|
319
|
+
className="text-foreground-contrast-primary"
|
|
320
|
+
>
|
|
321
|
+
<span className="inline-flex min-w-0 items-center gap-inline">
|
|
322
|
+
<Icon
|
|
323
|
+
name="errorWarningLine"
|
|
324
|
+
className="size-14 flex-none text-tag-warning-icon"
|
|
325
|
+
aria-hidden="true"
|
|
326
|
+
/>
|
|
327
|
+
<span className="truncate">{row.label}</span>
|
|
328
|
+
</span>
|
|
329
|
+
</LogDisclosureTrigger>
|
|
330
|
+
<LogDisclosureContent>
|
|
331
|
+
<LogContent variant="code">
|
|
332
|
+
<PreviewText text={row.raw} />
|
|
333
|
+
</LogContent>
|
|
334
|
+
</LogDisclosureContent>
|
|
335
|
+
</LogDisclosure>
|
|
336
|
+
);
|
|
252
337
|
}
|
|
253
338
|
|
|
254
339
|
function MessageIcon({role, terminalFailure}: {role: string; terminalFailure: boolean}) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
: role === 'assistant'
|
|
260
|
-
? 'robot2Line'
|
|
261
|
-
: 'message2Line';
|
|
340
|
+
let name: IconName = 'message2Line';
|
|
341
|
+
if (terminalFailure) name = 'closeCircleLine';
|
|
342
|
+
else if (role === 'user') name = 'userLine';
|
|
343
|
+
else if (role === 'assistant') name = 'robot2Line';
|
|
262
344
|
|
|
263
345
|
return (
|
|
264
346
|
<Icon
|
|
@@ -70,11 +70,13 @@ export function LogView({
|
|
|
70
70
|
const resolvedToolCalls = useMemo(() => collectResolvedToolCalls(tree.nodes), [tree.nodes]);
|
|
71
71
|
const noOutputState = normalizedSearch ? null : getNoOutputState(tree, emptyState);
|
|
72
72
|
const anchorRecordCount = records.length;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
let searchStatus: string | null = null;
|
|
74
|
+
if (normalizedSearch) {
|
|
75
|
+
searchStatus =
|
|
76
|
+
visibleNodes.length === 0
|
|
77
|
+
? `No log lines match “${deferredSearch.trim()}”.`
|
|
78
|
+
: `Log search updated for “${deferredSearch.trim()}”.`;
|
|
79
|
+
}
|
|
78
80
|
|
|
79
81
|
useEffect(() => {
|
|
80
82
|
if (!anchorToFailure) return;
|