@tanstack/ai-devtools-core 0.5.17 → 0.5.18

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.
@@ -6491,6 +6491,9 @@ var AIProvider = (props) => {
6491
6491
  }
6492
6492
  const newIteration = {
6493
6493
  requestId,
6494
+ ...e.payload.runId ? {
6495
+ runId: e.payload.runId
6496
+ } : {},
6494
6497
  index: iteration,
6495
6498
  messageId,
6496
6499
  startedAt: e.payload.timestamp,
@@ -7330,6 +7333,241 @@ var formatDuration = (ms) => {
7330
7333
  if (ms < 1e3) return `${ms}ms`;
7331
7334
  return `${(ms / 1e3).toFixed(2)}s`;
7332
7335
  };
7336
+
7337
+ // src/store/subagent-steps.ts
7338
+ function isRecord(value) {
7339
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
7340
+ }
7341
+ function text(value) {
7342
+ return typeof value === "string" ? value : "";
7343
+ }
7344
+ function collectSubagents(messages) {
7345
+ const out = [];
7346
+ let turn = -1;
7347
+ const visit = (list, parentPath, parentTurn) => {
7348
+ for (const message of list) {
7349
+ if (!isRecord(message)) continue;
7350
+ if (parentTurn === void 0 && message.role === "user") turn += 1;
7351
+ if (!Array.isArray(message.parts)) continue;
7352
+ for (const part of message.parts) {
7353
+ if (!isRecord(part) || part.type !== "subagent") continue;
7354
+ const subagent = part.subagent;
7355
+ if (!isRecord(subagent) || typeof subagent.id !== "string") continue;
7356
+ const name = text(subagent.name) || "subagent";
7357
+ const path = parentPath ? `${parentPath} > ${name}` : name;
7358
+ const error = isRecord(subagent.error) ? text(subagent.error.message) : "";
7359
+ const childMessages = Array.isArray(subagent.messages) ? subagent.messages : [];
7360
+ const info = {
7361
+ id: subagent.id,
7362
+ name,
7363
+ path,
7364
+ ...typeof subagent.status === "string" ? { status: subagent.status } : {},
7365
+ ...error ? { error } : {},
7366
+ turn: parentTurn ?? Math.max(turn, 0),
7367
+ messages: childMessages
7368
+ };
7369
+ out.push(info);
7370
+ visit(childMessages, path, info.turn);
7371
+ }
7372
+ }
7373
+ };
7374
+ visit(messages, void 0, void 0);
7375
+ return out;
7376
+ }
7377
+ function subagentIdForRunId(runId, ids) {
7378
+ if (!runId) return void 0;
7379
+ if (ids.has(runId)) return runId;
7380
+ const last = runId.slice(runId.lastIndexOf(":") + 1);
7381
+ return last !== runId && ids.has(last) ? last : void 0;
7382
+ }
7383
+ function childRequestAgents(iterations, subagents) {
7384
+ const ids = new Set(subagents.map((agent) => agent.id));
7385
+ const pathById = new Map(subagents.map((agent) => [agent.id, agent.path]));
7386
+ const out = /* @__PURE__ */ new Map();
7387
+ for (const iteration of iterations) {
7388
+ const id = subagentIdForRunId(iteration.runId, ids);
7389
+ const path = id ? pathById.get(id) : void 0;
7390
+ if (path && iteration.requestId) out.set(iteration.requestId, path);
7391
+ }
7392
+ return out;
7393
+ }
7394
+ function collectServerSteps(root, conversations, subagents) {
7395
+ const ids = new Set(subagents.map((agent) => agent.id));
7396
+ const iterations = [...root?.iterations ?? []];
7397
+ const messages = [...root?.messages ?? []];
7398
+ const seenSteps = new Set(
7399
+ iterations.map((iteration) => `${iteration.requestId}:${iteration.index}`)
7400
+ );
7401
+ const seenMessages = new Set(messages.map((message) => message.id));
7402
+ for (const conversation of conversations) {
7403
+ if (conversation === root) continue;
7404
+ const child = conversation.iterations.filter(
7405
+ (iteration) => subagentIdForRunId(iteration.runId, ids)
7406
+ );
7407
+ if (child.length === 0) continue;
7408
+ for (const iteration of child) {
7409
+ const key = `${iteration.requestId}:${iteration.index}`;
7410
+ if (seenSteps.has(key)) continue;
7411
+ seenSteps.add(key);
7412
+ iterations.push(iteration);
7413
+ }
7414
+ for (const message of conversation.messages) {
7415
+ if (seenMessages.has(message.id)) continue;
7416
+ seenMessages.add(message.id);
7417
+ messages.push(message);
7418
+ }
7419
+ }
7420
+ return { iterations, messages };
7421
+ }
7422
+ function timestampOf(value) {
7423
+ if (value instanceof Date) return value.getTime();
7424
+ if (typeof value !== "string" && typeof value !== "number") return void 0;
7425
+ const time = new Date(value).getTime();
7426
+ return Number.isNaN(time) ? void 0 : time;
7427
+ }
7428
+ function snapshotTurns(messages) {
7429
+ const turns = [];
7430
+ for (const message of messages) {
7431
+ if (!isRecord(message) || message.role !== "user") continue;
7432
+ const timestamp = timestampOf(message.createdAt);
7433
+ if (timestamp === void 0 || typeof message.id !== "string") return [];
7434
+ const content = Array.isArray(message.parts) ? message.parts.map(
7435
+ (part) => isRecord(part) && part.type === "text" ? text(part.content) : ""
7436
+ ).join("") : text(message.content);
7437
+ turns.push({ id: message.id, role: "user", content, timestamp });
7438
+ }
7439
+ return turns;
7440
+ }
7441
+ function groupTimeline(iterations, messages, subagents, turns = []) {
7442
+ const ids = new Set(subagents.map((agent) => agent.id));
7443
+ const childRequests = childRequestAgents(iterations, subagents);
7444
+ const rootIterations = iterations.filter(
7445
+ (iteration) => !subagentIdForRunId(iteration.runId, ids)
7446
+ );
7447
+ const agents = subagents.map((agent) => {
7448
+ const server = iterations.filter(
7449
+ (iteration) => subagentIdForRunId(iteration.runId, ids) === agent.id
7450
+ );
7451
+ return server.length > 0 ? { agent, iterations: server, messages, source: "server" } : { agent, ...buildSubagentSteps(agent), source: "snapshot" };
7452
+ });
7453
+ const users = (turns.length > 0 ? [...turns] : messages.filter(
7454
+ (message) => message.role === "user" && !(message.requestId && childRequests.has(message.requestId))
7455
+ )).sort((a, b) => a.timestamp - b.timestamp);
7456
+ if (users.length === 0) {
7457
+ return rootIterations.length > 0 || agents.length > 0 ? [{ userMessage: null, iterations: rootIterations, agents }] : [];
7458
+ }
7459
+ const groups = [];
7460
+ const first = users[0];
7461
+ const early = first ? rootIterations.filter(
7462
+ (iteration) => iteration.startedAt < first.timestamp
7463
+ ) : [];
7464
+ if (early.length > 0) {
7465
+ groups.push({ userMessage: null, iterations: early, agents: [] });
7466
+ }
7467
+ users.forEach((user, turn) => {
7468
+ const next = users[turn + 1];
7469
+ const own = rootIterations.filter(
7470
+ (iteration) => iteration.startedAt >= user.timestamp && (!next || iteration.startedAt < next.timestamp)
7471
+ );
7472
+ const isLast = turn === users.length - 1;
7473
+ const ownAgents = agents.filter(
7474
+ (group) => isLast ? group.agent.turn >= turn : group.agent.turn === turn
7475
+ );
7476
+ if (own.length > 0 || ownAgents.length > 0) {
7477
+ groups.push({ userMessage: user, iterations: own, agents: ownAgents });
7478
+ }
7479
+ });
7480
+ return groups;
7481
+ }
7482
+ function resultText(value) {
7483
+ if (typeof value === "string") return value;
7484
+ if (value === void 0) return "";
7485
+ try {
7486
+ return JSON.stringify(value);
7487
+ } catch {
7488
+ return String(value);
7489
+ }
7490
+ }
7491
+ function buildSubagentSteps(agent) {
7492
+ const iterations = [];
7493
+ const messages = [];
7494
+ let current;
7495
+ const close = () => {
7496
+ if (!current) return;
7497
+ const index = iterations.length;
7498
+ const { message, results } = current;
7499
+ const hasContent = message.content || message.thinkingContent || (message.toolCalls?.length ?? 0) > 0 || results.length > 0;
7500
+ if (hasContent) {
7501
+ messages.push(message, ...results);
7502
+ iterations.push({
7503
+ requestId: `subagent:${agent.id}`,
7504
+ index,
7505
+ messageId: message.id,
7506
+ // ponytail: the snapshot has no timestamps, so steps have no duration.
7507
+ startedAt: 1,
7508
+ completedAt: 1,
7509
+ finishReason: (message.toolCalls?.length ?? 0) > 0 ? "tool_calls" : "stop",
7510
+ middlewareEvents: [],
7511
+ messageIds: [message.id, ...results.map((result) => result.id)]
7512
+ });
7513
+ }
7514
+ current = void 0;
7515
+ };
7516
+ const open = () => {
7517
+ const index = iterations.length;
7518
+ current = {
7519
+ message: {
7520
+ id: `${agent.id}:step-${index}`,
7521
+ role: "assistant",
7522
+ content: "",
7523
+ timestamp: 1,
7524
+ toolCalls: []
7525
+ },
7526
+ results: [],
7527
+ hasResult: false
7528
+ };
7529
+ return current;
7530
+ };
7531
+ for (const message of agent.messages) {
7532
+ if (!isRecord(message) || message.role !== "assistant") continue;
7533
+ if (!Array.isArray(message.parts)) continue;
7534
+ for (const part of message.parts) {
7535
+ if (!isRecord(part)) continue;
7536
+ const step = part.type === "tool-result" ? current ?? open() : current && !current.hasResult ? current : (close(), open());
7537
+ if (part.type === "thinking") {
7538
+ step.message.thinkingContent = (step.message.thinkingContent ?? "") + text(part.content);
7539
+ } else if (part.type === "text") {
7540
+ step.message.content += text(part.content);
7541
+ } else if (part.type === "tool-call") {
7542
+ const toolCall = {
7543
+ id: text(part.id),
7544
+ name: text(part.name) || "tool",
7545
+ arguments: text(part.arguments),
7546
+ state: text(part.state),
7547
+ ...part.output !== void 0 ? { result: part.output } : {}
7548
+ };
7549
+ step.message.toolCalls?.push(toolCall);
7550
+ } else if (part.type === "tool-result") {
7551
+ const toolCallId = text(part.toolCallId);
7552
+ const content = resultText(part.content ?? part.error);
7553
+ const call = step.message.toolCalls?.find((tc) => tc.id === toolCallId);
7554
+ if (call && call.result === void 0) call.result = content;
7555
+ step.results.push({
7556
+ id: `${agent.id}:result-${toolCallId || step.results.length}`,
7557
+ role: "tool",
7558
+ content,
7559
+ timestamp: 1
7560
+ });
7561
+ step.hasResult = true;
7562
+ }
7563
+ }
7564
+ }
7565
+ close();
7566
+ const last = iterations.at(-1);
7567
+ if (last && agent.status === "running") delete last.completedAt;
7568
+ if (last && agent.status === "error") last.finishReason = "error";
7569
+ return { iterations, messages };
7570
+ }
7333
7571
  var _tmpl$10 = ["<span", ">", "ms</span>"];
7334
7572
  var _tmpl$22 = ["<span", ">", "</span>"];
7335
7573
  var _tmpl$32 = ["<div", ' style="', '"><span class="', '">Middleware</span><span class="', '">', "</span><span", ">", "</span>", "", "", "</div>"];
@@ -7581,9 +7819,9 @@ var AssistantStep = (props) => {
7581
7819
  const contentLength = () => (msg().content || "").length;
7582
7820
  const isLong = () => contentLength() > 200;
7583
7821
  const preview = () => {
7584
- const text = msg().content || "";
7585
- if (text.length <= 200) return text;
7586
- return truncate(text, 200);
7822
+ const text2 = msg().content || "";
7823
+ if (text2.length <= 200) return text2;
7824
+ return truncate(text2, 200);
7587
7825
  };
7588
7826
  return [ssrElement("div", mergeProps(() => getHoverDataAttributes({
7589
7827
  messageIds: [msg().id]
@@ -7701,8 +7939,8 @@ var ToolResultStep = (props) => {
7701
7939
  const [isOpen, setIsOpen] = createSignal(false);
7702
7940
  const msg = () => props.step.message;
7703
7941
  const parsedContent = () => {
7704
- const text = msg().content || "";
7705
- return tryParseJson(text) || text;
7942
+ const text2 = msg().content || "";
7943
+ return tryParseJson(text2) || text2;
7706
7944
  };
7707
7945
  return [ssrElement("div", mergeProps(() => getHoverDataAttributes({
7708
7946
  messageIds: [msg().id]
@@ -8130,49 +8368,14 @@ var _tmpl$132 = ["<div", "><span", ">Tools</span><div", ">", "</div></div>"];
8130
8368
  var _tmpl$142 = ["<div", "><span", ">System Prompts (", ")</span>", "</div>"];
8131
8369
  var _tmpl$152 = ["<div", "><span", ">Model Options</span><div", ">", "</div></div>"];
8132
8370
  var _tmpl$162 = ['<div class="', '"><div', "><div>", "", "", "</div></div></div>"];
8133
- var _tmpl$172 = ['<div class="', '"><div', "><div", ">", "</div></div></div>"];
8371
+ var _tmpl$172 = ['<div class="', '"><div', "><div", ">", "", "</div></div></div>"];
8134
8372
  var _tmpl$182 = ['<span class="', '" title="', '">\u{1F3AF} ', "</span>"];
8135
8373
  var _tmpl$192 = ["<span", ">", "<span", ">", "</span></span>"];
8374
+ var _tmpl$202 = ['<div class="', '" style="', '" data-testid="ai-devtools-subagent-steps"', "><div", ' role="button" tabindex="0"', "><div", ">", "</div><div", "><span", ">", "</span><div", "><span", ">subagent</span>", "", "</div></div><div", '><span class="', '" title="', '">\u{1F504} ', '</span></div><span class="', '">\u25B6</span></div><div class="', '"><div', "><div", ">", "</div></div></div></div>"];
8136
8375
  var IterationTimeline = (props) => {
8137
8376
  const styles = useStyles();
8138
8377
  const s = () => styles().iterationTimeline;
8139
- const groups = createMemo(() => {
8140
- const userMessages = props.messages.filter((m) => m.role === "user");
8141
- const iters = props.iterations;
8142
- if (userMessages.length === 0) {
8143
- return iters.length > 0 ? [{
8144
- userMessage: null,
8145
- iterations: iters
8146
- }] : [];
8147
- }
8148
- const result = [];
8149
- const sortedUsers = [...userMessages].sort((a, b) => a.timestamp - b.timestamp);
8150
- for (const [u, currentUser] of sortedUsers.entries()) {
8151
- const nextUser = sortedUsers[u + 1];
8152
- const groupIters = iters.filter((it) => {
8153
- if (it.startedAt < currentUser.timestamp) return false;
8154
- if (nextUser && it.startedAt >= nextUser.timestamp) return false;
8155
- return true;
8156
- });
8157
- if (groupIters.length > 0) {
8158
- result.push({
8159
- userMessage: currentUser,
8160
- iterations: groupIters
8161
- });
8162
- }
8163
- }
8164
- const firstUser = sortedUsers[0];
8165
- if (firstUser) {
8166
- const earlyIters = iters.filter((it) => it.startedAt < firstUser.timestamp);
8167
- if (earlyIters.length > 0) {
8168
- result.unshift({
8169
- userMessage: null,
8170
- iterations: earlyIters
8171
- });
8172
- }
8173
- }
8174
- return result;
8175
- });
8378
+ const groups = createMemo(() => groupTimeline(props.iterations, props.messages, props.subagents ?? [], props.turns ?? []));
8176
8379
  return ssr(_tmpl$34, ssrAttribute("class", escape(s().container, true), false), escape(createComponent(Show, {
8177
8380
  get when() {
8178
8381
  return props.iterations.length > 0;
@@ -8304,8 +8507,8 @@ var UserMessageGroupCard = (props) => {
8304
8507
  const userContent = () => {
8305
8508
  const msg = userMsg();
8306
8509
  if (!msg) return "(no user message)";
8307
- const text = msg.content || "";
8308
- return text.length > 120 ? text.slice(0, 120) + "..." : text;
8510
+ const text2 = msg.content || "";
8511
+ return text2.length > 120 ? text2.slice(0, 120) + "..." : text2;
8309
8512
  };
8310
8513
  const isUserMessageHighlighted = () => {
8311
8514
  const messageId = userMessageId();
@@ -8485,8 +8688,89 @@ var UserMessageGroupCard = (props) => {
8485
8688
  return props.onHoverTarget;
8486
8689
  }
8487
8690
  })
8691
+ })), escape(createComponent(For, {
8692
+ get each() {
8693
+ return group().agents;
8694
+ },
8695
+ children: (agentGroup) => createComponent(SubagentStepsCard, {
8696
+ group: agentGroup,
8697
+ get hoverTarget() {
8698
+ return props.hoverTarget;
8699
+ },
8700
+ get onHoverTarget() {
8701
+ return props.onHoverTarget;
8702
+ }
8703
+ })
8488
8704
  })))], false);
8489
8705
  };
8706
+ var SubagentStepsCard = (props) => {
8707
+ const styles = useStyles();
8708
+ const s = () => styles().iterationTimeline;
8709
+ const [isOpen, setIsOpen] = createSignal(true);
8710
+ const group = createMemo(() => {
8711
+ if (props.group) return props.group;
8712
+ if (!props.agent) return void 0;
8713
+ return {
8714
+ agent: props.agent,
8715
+ ...buildSubagentSteps(props.agent),
8716
+ source: "snapshot"
8717
+ };
8718
+ });
8719
+ const agent = () => group()?.agent;
8720
+ const iterations = () => group()?.iterations ?? [];
8721
+ const accentClass = () => {
8722
+ const status = agent()?.status;
8723
+ if (status === "running") return s().cardActive;
8724
+ if (status === "error") return s().cardError;
8725
+ if (status === "finished") return s().cardCompleted;
8726
+ return "";
8727
+ };
8728
+ return createComponent(Show, {
8729
+ get when() {
8730
+ return agent();
8731
+ },
8732
+ children: (current) => ssr(_tmpl$202, `${escape(s().card, true)} ${escape(accentClass(), true)}`, ssrStyleProperty("margin-top:", "8px"), ssrAttribute("data-agent", escape(current().path, true), false) + ssrAttribute("data-source", escape(group()?.source, true), false), ssrAttribute("class", escape(s().cardHeader, true), false), ssrAttribute("aria-expanded", escape(isOpen(), true), false), ssrAttribute("class", escape(s().userBubble, true), false), escape(current().name.charAt(0).toUpperCase()), ssrAttribute("class", escape(s().cardHeaderContent, true), false), ssrAttribute("class", escape(s().cardHeaderLabel, true), false), escape(current().path), ssrAttribute("class", escape(s().cardSubtitle, true), false), ssrAttribute("class", escape(s().subtitleText, true), false), escape(createComponent(Show, {
8733
+ get when() {
8734
+ return current().status;
8735
+ },
8736
+ get children() {
8737
+ return ssr(_tmpl$35, ssrAttribute("class", escape(s().subtitleBadge, true), false), escape(current().status));
8738
+ }
8739
+ })), escape(createComponent(Show, {
8740
+ get when() {
8741
+ return current().error;
8742
+ },
8743
+ get children() {
8744
+ return ssr(_tmpl$35, ssrAttribute("class", escape(s().subtitleBadgeWarn, true), false), escape(current().error));
8745
+ }
8746
+ })), ssrAttribute("class", escape(s().cardHeaderBadges, true), false), `${escape(s().badge, true)} ${escape(s().badgeDuration, true)}`, `${escape(iterations().length, true)} ${iterations().length === 1 ? "iteration" : "iterations"}`, escape(iterations().length), `${escape(s().chevron, true)} ${isOpen() ? escape(s().chevronOpen, true) : ""}`, `${escape(s().cardBody, true)} ${isOpen() ? escape(s().cardBodyOpen, true) : ""}`, ssrAttribute("class", escape(s().cardBodyInner, true), false), ssrAttribute("class", escape(s().iterList, true), false), escape(createComponent(For, {
8747
+ get each() {
8748
+ return iterations();
8749
+ },
8750
+ children: (iteration, index) => createComponent(IterationCard, {
8751
+ iteration,
8752
+ get previousIteration() {
8753
+ return index() > 0 ? iterations()[index() - 1] : void 0;
8754
+ },
8755
+ get messages() {
8756
+ return group()?.messages ?? [];
8757
+ },
8758
+ get index() {
8759
+ return index();
8760
+ },
8761
+ get isLast() {
8762
+ return index() === iterations().length - 1;
8763
+ },
8764
+ get hoverTarget() {
8765
+ return props.hoverTarget;
8766
+ },
8767
+ get onHoverTarget() {
8768
+ return props.onHoverTarget;
8769
+ }
8770
+ })
8771
+ })))
8772
+ });
8773
+ };
8490
8774
  var _tmpl$36 = ["<form", ' data-testid="ai-devtools-fixture-save-popover"><label', "><span", ">Fixture name</span><input", ' data-testid="ai-devtools-fixture-name-input"', "></label><div", "><button", ' data-testid="ai-devtools-fixture-save-confirm" type="submit">Save</button><button class="', '" data-testid="ai-devtools-fixture-save-cancel" type="button">Cancel</button></div></form>'];
8491
8775
  var FixtureNamePopover = (props) => {
8492
8776
  const styles = useStyles();
@@ -8630,7 +8914,7 @@ var FieldInput = (props) => {
8630
8914
  function fieldsFromSchema(schema) {
8631
8915
  if (!isJsonSchemaObject(schema)) return [];
8632
8916
  const properties = schema.properties;
8633
- if (!isRecord(properties)) return [];
8917
+ if (!isRecord2(properties)) return [];
8634
8918
  const required = Array.isArray(schema.required) ? new Set(schema.required.filter((item) => typeof item === "string")) : /* @__PURE__ */ new Set();
8635
8919
  return Object.entries(properties).map(([name, property]) => {
8636
8920
  const propertySchema = isJsonSchemaObject(property) ? property : void 0;
@@ -8710,9 +8994,9 @@ function normalizeType(value) {
8710
8994
  return "string";
8711
8995
  }
8712
8996
  function isJsonSchemaObject(value) {
8713
- return isRecord(value);
8997
+ return isRecord2(value);
8714
8998
  }
8715
- function isRecord(value) {
8999
+ function isRecord2(value) {
8716
9000
  return typeof value === "object" && value !== null;
8717
9001
  }
8718
9002
 
@@ -8743,7 +9027,7 @@ function mergePreviewMessagesForUserView(conversationMessages, snapshotMessages)
8743
9027
  }
8744
9028
  function partsToMessageText(parts) {
8745
9029
  return parts.map((part) => {
8746
- if (!isRecord2(part)) return "";
9030
+ if (!isRecord3(part)) return "";
8747
9031
  if (part.type !== "text") return "";
8748
9032
  if (typeof part.text === "string") return part.text;
8749
9033
  if (typeof part.content === "string") return part.content;
@@ -8782,7 +9066,7 @@ function isRenderablePreviewMessage(message) {
8782
9066
  function isNonUserPreviewMessage(message) {
8783
9067
  return message.role !== void 0 && message.role !== "user";
8784
9068
  }
8785
- function isRecord2(value) {
9069
+ function isRecord3(value) {
8786
9070
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
8787
9071
  }
8788
9072
  function normalizeText(value) {
@@ -9133,7 +9417,7 @@ function runSortTime(run) {
9133
9417
  return run.startedAt ?? run.updatedAt ?? run.completedAt ?? 0;
9134
9418
  }
9135
9419
  function runFromUnknown(value) {
9136
- if (!isRecord3(value) || typeof value.id !== "string") {
9420
+ if (!isRecord4(value) || typeof value.id !== "string") {
9137
9421
  return void 0;
9138
9422
  }
9139
9423
  const progress = progressFromUnknown(value.progress);
@@ -9266,7 +9550,7 @@ function isGenerationRunView(value) {
9266
9550
  return value !== void 0;
9267
9551
  }
9268
9552
  function previewFromUnknown(value, fallbackResult) {
9269
- if (isRecord3(value) && typeof value.kind === "string") {
9553
+ if (isRecord4(value) && typeof value.kind === "string") {
9270
9554
  if (value.kind === "image") {
9271
9555
  return {
9272
9556
  kind: "image",
@@ -9317,7 +9601,7 @@ function previewFromUnknown(value, fallbackResult) {
9317
9601
  };
9318
9602
  }
9319
9603
  function progressFromUnknown(value) {
9320
- if (!isRecord3(value) || typeof value.value !== "number") {
9604
+ if (!isRecord4(value) || typeof value.value !== "number") {
9321
9605
  return void 0;
9322
9606
  }
9323
9607
  return {
@@ -9332,17 +9616,17 @@ function mediaItemsFromUnknown(value) {
9332
9616
  return value.filter(isMediaItem);
9333
9617
  }
9334
9618
  function isMediaItem(value) {
9335
- if (!isRecord3(value) || typeof value.src !== "string") {
9619
+ if (!isRecord4(value) || typeof value.src !== "string") {
9336
9620
  return false;
9337
9621
  }
9338
9622
  return true;
9339
9623
  }
9340
9624
  function isVideoJob(value) {
9341
- return isRecord3(value) && typeof value.jobId === "string";
9625
+ return isRecord4(value) && typeof value.jobId === "string";
9342
9626
  }
9343
9627
  function errorTextFromUnknown(value) {
9344
9628
  if (typeof value === "string") return value;
9345
- if (isRecord3(value) && typeof value.message === "string") {
9629
+ if (isRecord4(value) && typeof value.message === "string") {
9346
9630
  return value.message;
9347
9631
  }
9348
9632
  return void 0;
@@ -9354,7 +9638,7 @@ function numberFromUnknown(value) {
9354
9638
  return typeof value === "number" ? value : void 0;
9355
9639
  }
9356
9640
  function videoStatusLabel(value) {
9357
- if (!isRecord3(value)) return void 0;
9641
+ if (!isRecord4(value)) return void 0;
9358
9642
  const status = stringFromUnknown(value.status);
9359
9643
  const progress = numberFromUnknown(value.progress);
9360
9644
  if (!status && progress === void 0) return void 0;
@@ -9376,7 +9660,7 @@ function formatTime(timestamp) {
9376
9660
  if (timestamp === void 0) return "unknown";
9377
9661
  return new Date(timestamp).toLocaleTimeString();
9378
9662
  }
9379
- function isRecord3(value) {
9663
+ function isRecord4(value) {
9380
9664
  return typeof value === "object" && value !== null && !Array.isArray(value);
9381
9665
  }
9382
9666
  var _tmpl$40 = ["<div", ' data-testid="ai-devtools-memory-panel">', "</div>"];
@@ -9847,7 +10131,7 @@ var _tmpl$164 = ["<div", ">No messages.</div>"];
9847
10131
  var _tmpl$174 = ["<div", ">", "</div>"];
9848
10132
  var _tmpl$183 = ["<div", '><button type="button"', ' data-testid="ai-devtools-preview-save-tool-call">Save</button><button type="button"', ' data-testid="ai-devtools-preview-replay-tool-call">Replay</button></div>'];
9849
10133
  var _tmpl$193 = ['<div class="', '">', "</div>"];
9850
- var _tmpl$202 = ["<div", "><span", ">", "</span><div", ">", "</div></div>"];
10134
+ var _tmpl$203 = ["<div", "><span", ">", "</span><div", ">", "</div></div>"];
9851
10135
  var _tmpl$217 = ["<div", ">No messages yet. Start a conversation to see messages here.</div>"];
9852
10136
  var _tmpl$223 = ["<div", ' data-testid="ai-devtools-tools"><div', ">", "</div><div", ">", "</div></div>"];
9853
10137
  var _tmpl$232 = ["<div", ">No tools.</div>"];
@@ -9892,13 +10176,34 @@ var HookDetails = () => {
9892
10176
  setActiveTab("conversation");
9893
10177
  }
9894
10178
  });
10179
+ const subagents = createMemo(() => {
10180
+ const messages = hook()?.state.messages;
10181
+ return Array.isArray(messages) ? collectSubagents(messages) : [];
10182
+ });
10183
+ const serverSteps = createMemo(() => collectServerSteps(conversation(), Object.values(state.conversations), subagents()));
10184
+ const turns = createMemo(() => {
10185
+ const messages = hook()?.state.messages;
10186
+ return Array.isArray(messages) ? snapshotTurns(messages) : [];
10187
+ });
9895
10188
  const previewMessages = createMemo(() => {
9896
10189
  const activeHook = hook();
9897
10190
  if (!activeHook) return [];
9898
10191
  const snapshotMessages = messagesFromSnapshot(activeHook.state);
10192
+ if (snapshotMessages.length > 0 && subagents().length > 0) {
10193
+ return snapshotMessages;
10194
+ }
9899
10195
  const conversationMessages = conversation()?.messages ?? [];
9900
10196
  if (conversationMessages.length > 0) {
9901
- return mergePreviewMessagesForUserView(conversationMessages.map(messageFromConversation), snapshotMessages);
10197
+ const childAgents = childRequestAgents(conversation()?.iterations ?? [], subagents());
10198
+ const agentOf = (message) => message.requestId ? childAgents.get(message.requestId) : void 0;
10199
+ return mergePreviewMessagesForUserView(conversationMessages.filter((message) => !(message.role === "user" && agentOf(message))).map((message) => {
10200
+ const preview = messageFromConversation(message);
10201
+ const agent = agentOf(message);
10202
+ return agent ? {
10203
+ ...preview,
10204
+ agent
10205
+ } : preview;
10206
+ }), snapshotMessages);
9902
10207
  }
9903
10208
  if (snapshotMessages.length > 0) return snapshotMessages;
9904
10209
  return [];
@@ -10022,6 +10327,15 @@ var HookDetails = () => {
10022
10327
  get messages() {
10023
10328
  return previewMessages();
10024
10329
  },
10330
+ get subagents() {
10331
+ return subagents();
10332
+ },
10333
+ get steps() {
10334
+ return serverSteps();
10335
+ },
10336
+ get turns() {
10337
+ return turns();
10338
+ },
10025
10339
  get hoverTarget() {
10026
10340
  return hoverTarget();
10027
10341
  },
@@ -10299,7 +10613,7 @@ var ConversationPanel = (props) => {
10299
10613
  const styles = useStyles();
10300
10614
  return createComponent(Show, {
10301
10615
  get when() {
10302
- return props.conversation;
10616
+ return props.conversation || props.steps.iterations.length > 0;
10303
10617
  },
10304
10618
  get fallback() {
10305
10619
  return createComponent(Show, {
@@ -10319,6 +10633,9 @@ var ConversationPanel = (props) => {
10319
10633
  get messages() {
10320
10634
  return props.messages;
10321
10635
  },
10636
+ get subagents() {
10637
+ return props.subagents;
10638
+ },
10322
10639
  get hoverTarget() {
10323
10640
  return props.hoverTarget;
10324
10641
  },
@@ -10329,40 +10646,51 @@ var ConversationPanel = (props) => {
10329
10646
  }
10330
10647
  });
10331
10648
  },
10332
- children: (conversation) => createComponent(Show, {
10333
- get when() {
10334
- return conversation().iterations.length > 0;
10335
- },
10336
- get fallback() {
10337
- return createComponent(HookMessageTimeline, {
10338
- get messages() {
10339
- return props.messages;
10340
- },
10341
- get hoverTarget() {
10342
- return props.hoverTarget;
10343
- },
10344
- get onHoverTarget() {
10345
- return props.onHoverTarget;
10346
- }
10347
- });
10348
- },
10349
- get children() {
10350
- return createComponent(IterationTimeline, {
10351
- get iterations() {
10352
- return conversation().iterations;
10353
- },
10354
- get messages() {
10355
- return conversation().messages;
10356
- },
10357
- get hoverTarget() {
10358
- return props.hoverTarget;
10359
- },
10360
- get onHoverTarget() {
10361
- return props.onHoverTarget;
10362
- }
10363
- });
10364
- }
10365
- })
10649
+ get children() {
10650
+ return createComponent(Show, {
10651
+ get when() {
10652
+ return props.steps.iterations.length > 0;
10653
+ },
10654
+ get fallback() {
10655
+ return createComponent(HookMessageTimeline, {
10656
+ get messages() {
10657
+ return props.messages;
10658
+ },
10659
+ get subagents() {
10660
+ return props.subagents;
10661
+ },
10662
+ get hoverTarget() {
10663
+ return props.hoverTarget;
10664
+ },
10665
+ get onHoverTarget() {
10666
+ return props.onHoverTarget;
10667
+ }
10668
+ });
10669
+ },
10670
+ get children() {
10671
+ return createComponent(IterationTimeline, {
10672
+ get iterations() {
10673
+ return props.steps.iterations;
10674
+ },
10675
+ get messages() {
10676
+ return props.steps.messages;
10677
+ },
10678
+ get subagents() {
10679
+ return props.subagents;
10680
+ },
10681
+ get turns() {
10682
+ return props.turns;
10683
+ },
10684
+ get hoverTarget() {
10685
+ return props.hoverTarget;
10686
+ },
10687
+ get onHoverTarget() {
10688
+ return props.onHoverTarget;
10689
+ }
10690
+ });
10691
+ }
10692
+ });
10693
+ }
10366
10694
  });
10367
10695
  };
10368
10696
  var MessagesPreview = (props) => {
@@ -10414,7 +10742,7 @@ var MessagesPreview = (props) => {
10414
10742
  return props.messages;
10415
10743
  },
10416
10744
  children: (message) => {
10417
- const visibleParts = () => visiblePreviewPartsForMessage(message);
10745
+ const visibleParts = () => visiblePreviewPartsForMessage(message).filter((part) => !hiddenInUserView(message, part));
10418
10746
  return ssrElement("div", mergeProps(() => getHoverDataAttributes({
10419
10747
  messageIds: [message.id],
10420
10748
  partIds: visibleParts().map((part) => part.id)
@@ -10429,7 +10757,7 @@ var MessagesPreview = (props) => {
10429
10757
  get ["data-role"]() {
10430
10758
  return message.role;
10431
10759
  }
10432
- }), [ssr(_tmpl$174, ssrAttribute("class", escape(styles().hookDetails.messageRole, true), false), escape(message.role)), escape(createComponent(Show, {
10760
+ }), [ssr(_tmpl$174, ssrAttribute("class", escape(styles().hookDetails.messageRole, true), false), escape(roleLabel(message))), escape(createComponent(Show, {
10433
10761
  get when() {
10434
10762
  return message.content;
10435
10763
  },
@@ -10473,7 +10801,7 @@ var MessagesPreview = (props) => {
10473
10801
  get each() {
10474
10802
  return part.jsonItems;
10475
10803
  },
10476
- children: (item) => ssr(_tmpl$202, ssrAttribute("class", escape(styles().hookDetails.previewJsonItem, true), false), ssrAttribute("class", escape(styles().hookDetails.previewJsonItemLabel, true), false), escape(item.label), ssrAttribute("class", escape(styles().hookDetails.previewJsonPanel, true), false), escape(createComponent(JsonTree, {
10804
+ children: (item) => ssr(_tmpl$203, ssrAttribute("class", escape(styles().hookDetails.previewJsonItem, true), false), ssrAttribute("class", escape(styles().hookDetails.previewJsonItemLabel, true), false), escape(item.label), ssrAttribute("class", escape(styles().hookDetails.previewJsonPanel, true), false), escape(createComponent(JsonTree, {
10477
10805
  get value() {
10478
10806
  return item.value;
10479
10807
  },
@@ -10516,7 +10844,7 @@ var HookMessageTimeline = (props) => {
10516
10844
  get ["class"]() {
10517
10845
  return `${styles().hookDetails.timelineMessage} ${isMessageHighlighted(message.id, props.hoverTarget) ? styles().hookDetails.timelineMessageHighlighted : ""}`;
10518
10846
  }
10519
- }), [ssr(_tmpl$174, ssrAttribute("class", escape(styles().hookDetails.messageRole, true), false), escape(message.role)), escape(createComponent(Show, {
10847
+ }), [ssr(_tmpl$174, ssrAttribute("class", escape(styles().hookDetails.messageRole, true), false), escape(roleLabel(message))), escape(createComponent(Show, {
10520
10848
  get when() {
10521
10849
  return message.content;
10522
10850
  },
@@ -10525,49 +10853,74 @@ var HookMessageTimeline = (props) => {
10525
10853
  }
10526
10854
  })), escape(createComponent(For, {
10527
10855
  get each() {
10528
- return visiblePreviewPartsForMessage(message);
10856
+ return visiblePreviewPartsForMessage(message).filter((part) => part.subagentId || !part.subagentPath);
10529
10857
  },
10530
- children: (part) => ssrElement("div", mergeProps(() => getHoverDataAttributes({
10531
- messageIds: [message.id],
10532
- partIds: [part.id]
10533
- }), {
10534
- get ["class"]() {
10535
- return styles().hookDetails.previewPart;
10536
- },
10537
- "data-testid": "ai-devtools-timeline-part",
10538
- get ["data-part-id"]() {
10539
- return part.id;
10540
- },
10541
- get ["data-part-kind"]() {
10542
- return part.kind;
10543
- }
10544
- }), [ssr(_tmpl$07, ssrAttribute("class", escape(styles().hookDetails.previewPartLabel, true), false), escape(part.label)), escape(createComponent(Show, {
10858
+ children: (part) => createComponent(Show, {
10545
10859
  get when() {
10546
- return part.jsonItems?.length;
10860
+ return props.subagents.find((agent) => agent.id === part.subagentId);
10547
10861
  },
10548
10862
  get fallback() {
10549
- return ssr(_tmpl$07, ssrAttribute("class", escape(styles().hookDetails.previewPartContent, true), false), escape(part.content));
10550
- },
10551
- get children() {
10552
- return ssr(_tmpl$193, `${escape(styles().hookDetails.previewJsonItems, true)} ${part.kind === "structured-output" ? escape(styles().hookDetails.previewJsonItemsCompare, true) : ""}`, escape(createComponent(For, {
10553
- get each() {
10554
- return part.jsonItems;
10863
+ return ssrElement("div", mergeProps(() => getHoverDataAttributes({
10864
+ messageIds: [message.id],
10865
+ partIds: [part.id]
10866
+ }), {
10867
+ get ["class"]() {
10868
+ return styles().hookDetails.previewPart;
10555
10869
  },
10556
- children: (item) => ssr(_tmpl$202, ssrAttribute("class", escape(styles().hookDetails.previewJsonItem, true), false), ssrAttribute("class", escape(styles().hookDetails.previewJsonItemLabel, true), false), escape(item.label), ssrAttribute("class", escape(styles().hookDetails.previewJsonPanel, true), false), escape(createComponent(JsonTree, {
10557
- get value() {
10558
- return item.value;
10559
- },
10560
- defaultExpansionDepth: 1,
10561
- copyable: true
10562
- })))
10563
- })));
10564
- }
10565
- }))], false)
10870
+ "data-testid": "ai-devtools-timeline-part",
10871
+ get ["data-part-id"]() {
10872
+ return part.id;
10873
+ },
10874
+ get ["data-part-kind"]() {
10875
+ return part.kind;
10876
+ }
10877
+ }), [ssr(_tmpl$07, ssrAttribute("class", escape(styles().hookDetails.previewPartLabel, true), false), escape(part.label)), escape(createComponent(Show, {
10878
+ get when() {
10879
+ return part.jsonItems?.length;
10880
+ },
10881
+ get fallback() {
10882
+ return ssr(_tmpl$07, ssrAttribute("class", escape(styles().hookDetails.previewPartContent, true), false), escape(part.content));
10883
+ },
10884
+ get children() {
10885
+ return ssr(_tmpl$193, `${escape(styles().hookDetails.previewJsonItems, true)} ${part.kind === "structured-output" ? escape(styles().hookDetails.previewJsonItemsCompare, true) : ""}`, escape(createComponent(For, {
10886
+ get each() {
10887
+ return part.jsonItems;
10888
+ },
10889
+ children: (item) => ssr(_tmpl$203, ssrAttribute("class", escape(styles().hookDetails.previewJsonItem, true), false), ssrAttribute("class", escape(styles().hookDetails.previewJsonItemLabel, true), false), escape(item.label), ssrAttribute("class", escape(styles().hookDetails.previewJsonPanel, true), false), escape(createComponent(JsonTree, {
10890
+ get value() {
10891
+ return item.value;
10892
+ },
10893
+ defaultExpansionDepth: 1,
10894
+ copyable: true
10895
+ })))
10896
+ })));
10897
+ }
10898
+ }))], false);
10899
+ },
10900
+ children: (agent) => createComponent(SubagentStepsCard, {
10901
+ get agent() {
10902
+ return agent();
10903
+ },
10904
+ get hoverTarget() {
10905
+ return props.hoverTarget;
10906
+ },
10907
+ get onHoverTarget() {
10908
+ return props.onHoverTarget;
10909
+ }
10910
+ })
10911
+ })
10566
10912
  }))], false)
10567
10913
  })));
10568
10914
  }
10569
10915
  });
10570
10916
  };
10917
+ function roleLabel(message) {
10918
+ return message.agent ? `${message.role} (${message.agent})` : message.role;
10919
+ }
10920
+ function hiddenInUserView(message, part) {
10921
+ if (!message.agent && !part.subagentPath) return false;
10922
+ return part.kind === "thinking" || part.kind === "tool-call";
10923
+ }
10571
10924
  var ToolsView = (props) => {
10572
10925
  const {
10573
10926
  state,
@@ -10666,7 +11019,7 @@ function findConversationForHook(hook, conversations) {
10666
11019
  }
10667
11020
  const snapshotMessages = hook.state.messages;
10668
11021
  if (Array.isArray(snapshotMessages)) {
10669
- const messageIds = new Set(snapshotMessages.map((message) => isRecord4(message) && typeof message.id === "string" ? message.id : void 0).filter((id) => typeof id === "string"));
11022
+ const messageIds = new Set(snapshotMessages.map((message) => isRecord5(message) && typeof message.id === "string" ? message.id : void 0).filter((id) => typeof id === "string"));
10670
11023
  if (messageIds.size > 0) {
10671
11024
  addCandidate(Object.values(conversations).find((conversation) => conversation.messages.some((message) => messageIds.has(message.id))));
10672
11025
  }
@@ -10694,7 +11047,7 @@ function messageFromConversation(message) {
10694
11047
  };
10695
11048
  }
10696
11049
  function messageFromUnknown(value) {
10697
- if (!isRecord4(value)) return void 0;
11050
+ if (!isRecord5(value)) return void 0;
10698
11051
  const id = typeof value.id === "string" ? value.id : void 0;
10699
11052
  const role = typeof value.role === "string" ? value.role : void 0;
10700
11053
  if (!id || !role) return void 0;
@@ -10711,10 +11064,49 @@ function messageFromUnknown(value) {
10711
11064
  };
10712
11065
  }
10713
11066
  function partsFromMessageParts(parts, messageId) {
10714
- return parts.map((part, index) => previewPartFromRecord(part, index, messageId));
11067
+ return parts.flatMap((part, index) => previewPartsFromRecord(part, index, messageId));
10715
11068
  }
10716
11069
  function partsFromUnknown(parts, messageId) {
10717
- return parts.map((part, index) => isRecord4(part) ? previewPartFromRecord(part, index, messageId) : void 0).filter(isPreviewPart);
11070
+ return parts.flatMap((part, index) => isRecord5(part) ? previewPartsFromRecord(part, index, messageId) : []);
11071
+ }
11072
+ function previewPartsFromRecord(part, index, messageId, path) {
11073
+ if (part.type === "subagent" && isRecord5(part.subagent)) {
11074
+ return subagentPreviewParts(part.subagent, index, path);
11075
+ }
11076
+ const preview = previewPartFromRecord(part, index, messageId);
11077
+ if (!path) return [preview];
11078
+ const {
11079
+ fixture: _fixture,
11080
+ ...childPart
11081
+ } = preview;
11082
+ return [{
11083
+ ...childPart,
11084
+ id: `${path}:${messageId ?? index}:${preview.id}`,
11085
+ label: `${path} > ${preview.label}`,
11086
+ subagentPath: path
11087
+ }];
11088
+ }
11089
+ function subagentPreviewParts(subagent, index, parentPath) {
11090
+ const name = typeof subagent.name === "string" ? subagent.name : "subagent";
11091
+ const status = typeof subagent.status === "string" ? subagent.status : void 0;
11092
+ const id = typeof subagent.id === "string" ? subagent.id : `${index}`;
11093
+ const error = isRecord5(subagent.error) ? subagent.error.message : void 0;
11094
+ const path = parentPath ? `${parentPath} > ${name}` : name;
11095
+ const header = {
11096
+ id: `subagent:${id}`,
11097
+ label: status ? `subagent ${path} - ${status}` : `subagent ${path}`,
11098
+ ...typeof error === "string" ? {
11099
+ content: error
11100
+ } : {},
11101
+ kind: "subagent",
11102
+ subagentId: id
11103
+ };
11104
+ const messages = Array.isArray(subagent.messages) ? subagent.messages : [];
11105
+ return [header, ...messages.flatMap((message, messageIndex) => {
11106
+ if (!isRecord5(message) || !Array.isArray(message.parts)) return [];
11107
+ const childMessageId = typeof message.id === "string" ? message.id : `${id}:${messageIndex}`;
11108
+ return message.parts.flatMap((child, childIndex) => isRecord5(child) ? previewPartsFromRecord(child, childIndex, childMessageId, path) : []);
11109
+ })];
10718
11110
  }
10719
11111
  function previewPartFromRecord(part, index, messageId) {
10720
11112
  const type = typeof part.type === "string" ? part.type : "part";
@@ -10901,7 +11293,7 @@ function approvalStatusFromToolCall(tool) {
10901
11293
  return void 0;
10902
11294
  }
10903
11295
  function approvalStatusFromRecord(approval, state) {
10904
- if (isRecord4(approval)) {
11296
+ if (isRecord5(approval)) {
10905
11297
  if (approval.approved === true) return "approved";
10906
11298
  if (approval.approved === false) return "denied";
10907
11299
  if (approval.needsApproval === true) return "approval requested";
@@ -10958,10 +11350,7 @@ function formatUnknown(value) {
10958
11350
  function isPreviewMessage(value) {
10959
11351
  return Boolean(value);
10960
11352
  }
10961
- function isPreviewPart(value) {
10962
- return Boolean(value);
10963
- }
10964
- function isRecord4(value) {
11353
+ function isRecord5(value) {
10965
11354
  return typeof value === "object" && value !== null;
10966
11355
  }
10967
11356
  function isToolFixtureMessageRole(role) {