@springbrand/message-panel 0.1.3-alpha.0 → 0.1.3-alpha.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/message-panel",
3
- "version": "0.1.3-alpha.0",
3
+ "version": "0.1.3-alpha.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -74,6 +74,7 @@ export interface CamelChatMessagesProps {
74
74
  isServerStreaming?: boolean;
75
75
  /** Show the opt-in activity bar for recovering or server-streaming states. */
76
76
  showActivityStatus?: boolean;
77
+ activityStatusLabel?: string;
77
78
  hasPendingSteer?: boolean;
78
79
  startedWithPending?: boolean;
79
80
  error?: unknown;
@@ -403,16 +404,21 @@ function CamelToolRow({
403
404
  onApprove: (approvalId: string, decision: ToolApprovalDecision) => void;
404
405
  getToolApproval?: (part: unknown) => ToolApprovalView | undefined;
405
406
  }) {
406
- const [open, setOpen] = useState(false);
407
407
  const approval = getToolApproval?.(part) ?? toolApprovalOf(part);
408
408
  const pendingApproval = approval && approval.approved === undefined;
409
409
  const presentation = getCamelToolPresentation(part, isActive);
410
+ const autoOpen = presentation.running && (
411
+ presentation.isSubAgent ||
412
+ presentation.kind === "write" ||
413
+ presentation.kind === "edit"
414
+ );
415
+ const [open, setOpen] = useState(autoOpen);
410
416
  const hasDetails = children != null || presentation.hasDetails;
411
417
  const title = presentation.summary;
412
418
 
413
419
  useEffect(() => {
414
- if (presentation.isSubAgent && presentation.running) setOpen(true);
415
- }, [presentation.isSubAgent, presentation.running]);
420
+ if (autoOpen) setOpen(true);
421
+ }, [autoOpen]);
416
422
 
417
423
  return (
418
424
  <div data-part-type={normalizedPartType(part)}>
@@ -1189,6 +1195,11 @@ function AssistantTurn({
1189
1195
  const thinkingActivity = deriveThinkingActivity(
1190
1196
  turn.traceParts.at(-1)?.part ?? turn.finalParts.at(-1)?.part,
1191
1197
  );
1198
+ const hasRunningTool = turn.traceParts.some(
1199
+ ({ part }) =>
1200
+ toolNameOfPart(part) !== null &&
1201
+ getCamelToolPresentation(part, isActive).running,
1202
+ );
1192
1203
  const activeActivity = hasPendingSteer
1193
1204
  ? pendingSteerActivity
1194
1205
  : thinkingActivity;
@@ -1269,7 +1280,7 @@ function AssistantTurn({
1269
1280
  {turn.copyText && <ClipboardAction text={turn.copyText} />}
1270
1281
  </div>
1271
1282
  )}
1272
- {isActive && turn.finalParts.length === 0 && (
1283
+ {isActive && turn.finalParts.length === 0 && !hasRunningTool && (
1273
1284
  <ThinkingIndicator {...activeActivity} />
1274
1285
  )}
1275
1286
  </div>
@@ -1285,6 +1296,7 @@ export function CamelChatMessages({
1285
1296
  isRecovering = false,
1286
1297
  isServerStreaming = false,
1287
1298
  showActivityStatus = false,
1299
+ activityStatusLabel,
1288
1300
  hasPendingSteer = false,
1289
1301
  startedWithPending = false,
1290
1302
  error,
@@ -1345,7 +1357,7 @@ export function CamelChatMessages({
1345
1357
  role="status"
1346
1358
  >
1347
1359
  <span className="size-1.5 animate-pulse rounded-full bg-blue-500" />
1348
- {isRecovering ? "Recovering…" : "Running…"}
1360
+ {activityStatusLabel ?? (isRecovering ? "Recovering…" : "Running…")}
1349
1361
  </div>
1350
1362
  )}
1351
1363
  <div
@@ -169,11 +169,20 @@ function isTraceWork(
169
169
  if (isVisibleReasoning(part)) return true;
170
170
  const kind = resolvePartKind(part);
171
171
  if (kind === "plan") return false;
172
- const toolName = toolNameOfPart(part);
173
- if (toolName !== null && finalToolNames.includes(toolName)) return false;
172
+ if (isFinalOutputTool(part, finalToolNames)) return false;
174
173
  return TRACE_PART_KINDS.has(kind ?? "");
175
174
  }
176
175
 
176
+ function isFinalOutputTool(
177
+ part: CamelPart,
178
+ finalToolNames: readonly string[],
179
+ ): boolean {
180
+ const toolName = toolNameOfPart(part);
181
+ return toolName !== null &&
182
+ (finalToolNames.includes(toolName) ||
183
+ resolvePartKind(part) === "suggestions");
184
+ }
185
+
177
186
  function isTraceBoundary(
178
187
  part: CamelPart,
179
188
  finalToolNames: readonly string[],
@@ -226,8 +235,17 @@ export function projectCamelAssistantTurn(
226
235
  partIndex,
227
236
  part,
228
237
  }));
229
- let lastTraceBoundary = -1;
238
+ let finalOutputBoundary = parts.length;
230
239
  for (let index = parts.length - 1; index >= 0; index -= 1) {
240
+ if (isFinalOutputTool(parts[index].part, finalToolNames)) {
241
+ finalOutputBoundary = index;
242
+ break;
243
+ }
244
+ }
245
+ let lastTraceBoundary = -1;
246
+ // Provider cleanup steps after final output must not pull the answer back
247
+ // into the collapsible work trace.
248
+ for (let index = finalOutputBoundary - 1; index >= 0; index -= 1) {
231
249
  if (isTraceBoundary(parts[index].part, finalToolNames)) {
232
250
  lastTraceBoundary = index;
233
251
  break;
package/src/parts/plan.ts CHANGED
@@ -26,8 +26,9 @@ export function planStepsOfPart(part: unknown): PlanStep[] {
26
26
  const value = recordOf(step);
27
27
  const text = typeof value.text === "string" ? value.text.trim() : "";
28
28
  if (!text) return [];
29
- const status =
30
- value.status === "in_progress" || value.status === "done"
29
+ const status = value.status === "completed"
30
+ ? "done"
31
+ : value.status === "in_progress" || value.status === "done"
31
32
  ? value.status
32
33
  : "pending";
33
34
  return [{ text, status }];