skydive-cli 0.1.0-beta.291 → 0.1.0-beta.297

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/js/bin.mjs CHANGED
@@ -16,7 +16,7 @@ import zlib from "node:zlib";
16
16
  import os from "node:os";
17
17
 
18
18
  //#region package.json
19
- var version$1 = "0.1.0-beta.291";
19
+ var version$1 = "0.1.0-beta.297";
20
20
 
21
21
  //#endregion
22
22
  //#region src/types.ts
@@ -1023,7 +1023,12 @@ function createRestClient({ appUrl, sessionToken }) {
1023
1023
  }, sendResultSchema),
1024
1024
  activeRun: async ({ conversationId }) => {
1025
1025
  const { run } = await get(`/api/v1/chat/active-run?${new URLSearchParams({ conversationId }).toString()}`, activeRunResponseSchema);
1026
- return run;
1026
+ if (!run) return null;
1027
+ return {
1028
+ runId: run.runId,
1029
+ agentId: run.agentId ?? "",
1030
+ agentName: run.agentName ?? ""
1031
+ };
1027
1032
  },
1028
1033
  cancelRun: async ({ runId }) => {
1029
1034
  await post(`/api/v1/chat/runs/${encodeURIComponent(runId)}/cancel`, {}, z.object({ ok: z.boolean() }));
@@ -1187,7 +1192,11 @@ const uiMessagePartSchema = z.union([
1187
1192
  const uiMessageSchema = z.object({
1188
1193
  id: z.string(),
1189
1194
  role: z.string(),
1190
- parts: z.array(uiMessagePartSchema)
1195
+ parts: z.array(uiMessagePartSchema),
1196
+ metadata: z.object({ custom: z.object({
1197
+ agentId: z.string().nullish(),
1198
+ agentName: z.string().nullish()
1199
+ }).passthrough().optional() }).passthrough().optional()
1191
1200
  });
1192
1201
  const recapResponseSchema = z.object({ recap: z.object({ text: z.string() }).nullable() });
1193
1202
  const listMessagesResponseSchema = z.object({ messages: z.array(uiMessageSchema) });
@@ -1196,6 +1205,8 @@ const sendResultSchema = z.object({
1196
1205
  messageId: z.string().uuid().nullish(),
1197
1206
  conversationId: z.string().uuid(),
1198
1207
  isNewConversation: z.boolean(),
1208
+ agentId: z.string().nullish(),
1209
+ agentName: z.string().nullish(),
1199
1210
  steered: z.boolean().optional(),
1200
1211
  directive: z.object({ id: z.string() }).passthrough().optional()
1201
1212
  });
@@ -1210,7 +1221,11 @@ const finalizeResponseSchema = z.object({
1210
1221
  mediaType: z.string(),
1211
1222
  sizeBytes: z.number().nullable().optional()
1212
1223
  });
1213
- const activeRunResponseSchema = z.object({ run: z.object({ runId: z.string() }).nullable() });
1224
+ const activeRunResponseSchema = z.object({ run: z.object({
1225
+ runId: z.string(),
1226
+ agentId: z.string().nullish(),
1227
+ agentName: z.string().nullish()
1228
+ }).nullable() });
1214
1229
  const oauthConnectResponseSchema = z.object({ connectLink: z.string() }).passthrough();
1215
1230
  const externalOauthConnectResponseSchema = z.object({ authorizationUrl: z.string().optional() }).passthrough();
1216
1231
  const runStreamEventSchema = z.union([z.object({
@@ -2592,7 +2607,7 @@ const chatCommand = {
2592
2607
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
2593
2608
  process.exit(1);
2594
2609
  }
2595
- const { runChat } = await import("./boot-fBjqXxKo.mjs");
2610
+ const { runChat } = await import("./boot-DYzw1U0y.mjs");
2596
2611
  await runChat({
2597
2612
  appUrl,
2598
2613
  sessionToken: session.value.sessionToken,
@@ -3375,7 +3390,7 @@ const switchCommand = {
3375
3390
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
3376
3391
  process.exit(1);
3377
3392
  }
3378
- const { runWorkspacePicker } = await import("./boot-fBjqXxKo.mjs");
3393
+ const { runWorkspacePicker } = await import("./boot-DYzw1U0y.mjs");
3379
3394
  await runWorkspacePicker(session);
3380
3395
  return;
3381
3396
  }
@@ -3027,16 +3027,23 @@ function str(chunk, key) {
3027
3027
  const value = chunk[key];
3028
3028
  return typeof value === "string" ? value : null;
3029
3029
  }
3030
- function applyChunk(items, chunk) {
3030
+ /**
3031
+ * `agentName` is the display name of the agent producing this run's stream.
3032
+ * It's stamped onto each assistant-origin item so a multi-agent conversation
3033
+ * can attribute live turns; pass null in a solo thread to leave items
3034
+ * un-attributed (the chat screen only renders the name when the thread has
3035
+ * more than one agent).
3036
+ */
3037
+ function applyChunk(items, chunk, agentName = null) {
3031
3038
  const type = str(chunk, "type");
3032
3039
  if (!type) return [...items];
3033
3040
  switch (type) {
3034
- case "text-start": return appendAssistantTextStart(items, str(chunk, "id") ?? cryptoId());
3041
+ case "text-start": return appendAssistantTextStart(items, str(chunk, "id") ?? cryptoId(), agentName);
3035
3042
  case "text-delta": {
3036
3043
  const id = str(chunk, "id");
3037
3044
  const delta = str(chunk, "delta") ?? str(chunk, "text") ?? "";
3038
3045
  if (!id || !delta) return [...items];
3039
- return appendAssistantTextDelta(items, id, delta);
3046
+ return appendAssistantTextDelta(items, id, delta, agentName);
3040
3047
  }
3041
3048
  case "text-end": {
3042
3049
  const id = str(chunk, "id");
@@ -3049,14 +3056,15 @@ function applyChunk(items, chunk) {
3049
3056
  kind: "reasoning",
3050
3057
  id: bubbleId,
3051
3058
  text: "",
3052
- done: false
3059
+ done: false,
3060
+ agentName
3053
3061
  }];
3054
3062
  }
3055
3063
  case "reasoning-delta": {
3056
3064
  const id = str(chunk, "id");
3057
3065
  const delta = str(chunk, "delta") ?? str(chunk, "text") ?? "";
3058
3066
  if (!id || !delta) return [...items];
3059
- return appendReasoningDelta(items, id, delta);
3067
+ return appendReasoningDelta(items, id, delta, agentName);
3060
3068
  }
3061
3069
  case "reasoning-end": {
3062
3070
  const id = str(chunk, "id");
@@ -3079,7 +3087,8 @@ function applyChunk(items, chunk) {
3079
3087
  inputText: "",
3080
3088
  input: null,
3081
3089
  output: null,
3082
- state: "streaming"
3090
+ state: "streaming",
3091
+ agentName
3083
3092
  }];
3084
3093
  }
3085
3094
  case "tool-input-delta": {
@@ -3108,7 +3117,8 @@ function applyChunk(items, chunk) {
3108
3117
  inputText: "",
3109
3118
  input,
3110
3119
  output: null,
3111
- state: "input-ready"
3120
+ state: "input-ready",
3121
+ agentName
3112
3122
  }];
3113
3123
  }
3114
3124
  case "tool-output-available": {
@@ -3165,7 +3175,8 @@ function applyChunk(items, chunk) {
3165
3175
  id,
3166
3176
  card,
3167
3177
  status: "idle",
3168
- detail: null
3178
+ detail: null,
3179
+ agentName
3169
3180
  };
3170
3181
  if (items.some((m) => m.kind === "card" && m.id === id)) return items.map((m) => m.kind === "card" && m.id === id ? next : m);
3171
3182
  return [...items, next];
@@ -3206,16 +3217,17 @@ function openTextIndex(items, id, kind) {
3206
3217
  }
3207
3218
  return -1;
3208
3219
  }
3209
- function appendAssistantTextStart(items, id) {
3220
+ function appendAssistantTextStart(items, id, agentName) {
3210
3221
  const bubbleId = freshTextId(items, id, "assistant-text");
3211
3222
  return [...items, {
3212
3223
  kind: "assistant-text",
3213
3224
  id: bubbleId,
3214
3225
  text: "",
3215
- done: false
3226
+ done: false,
3227
+ agentName
3216
3228
  }];
3217
3229
  }
3218
- function appendAssistantTextDelta(items, id, delta) {
3230
+ function appendAssistantTextDelta(items, id, delta, agentName) {
3219
3231
  const idx = openTextIndex(items, id, "assistant-text");
3220
3232
  if (idx === -1) {
3221
3233
  const bubbleId = freshTextId(items, id, "assistant-text");
@@ -3223,7 +3235,8 @@ function appendAssistantTextDelta(items, id, delta) {
3223
3235
  kind: "assistant-text",
3224
3236
  id: bubbleId,
3225
3237
  text: delta,
3226
- done: false
3238
+ done: false,
3239
+ agentName
3227
3240
  }];
3228
3241
  }
3229
3242
  return items.map((m, i) => i === idx && m.kind === "assistant-text" ? {
@@ -3231,7 +3244,7 @@ function appendAssistantTextDelta(items, id, delta) {
3231
3244
  text: m.text + delta
3232
3245
  } : m);
3233
3246
  }
3234
- function appendReasoningDelta(items, id, delta) {
3247
+ function appendReasoningDelta(items, id, delta, agentName) {
3235
3248
  const idx = openTextIndex(items, id, "reasoning");
3236
3249
  if (idx === -1) {
3237
3250
  const bubbleId = freshTextId(items, id, "reasoning");
@@ -3239,7 +3252,8 @@ function appendReasoningDelta(items, id, delta) {
3239
3252
  kind: "reasoning",
3240
3253
  id: bubbleId,
3241
3254
  text: delta,
3242
- done: false
3255
+ done: false,
3256
+ agentName
3243
3257
  }];
3244
3258
  }
3245
3259
  return items.map((m, i) => i === idx && m.kind === "reasoning" ? {
@@ -3269,6 +3283,59 @@ function cryptoId() {
3269
3283
  return crypto.randomUUID();
3270
3284
  }
3271
3285
 
3286
+ //#endregion
3287
+ //#region src/chat/tui/chat/attribution.ts
3288
+ /**
3289
+ * The agent that authored an item, or null for items with no agent (the user's
3290
+ * own turns, errors, the recap).
3291
+ */
3292
+ function itemAgentName(m) {
3293
+ return "agentName" in m ? m.agentName ?? null : null;
3294
+ }
3295
+ /**
3296
+ * Items that end an agent's turn: the human speaking or acting. The next
3297
+ * assistant item after one of these starts a new turn and is re-headed with its
3298
+ * author, even when the same agent replies again — in a multi-agent thread the
3299
+ * reader can't be asked to scan back past their own message to work out who is
3300
+ * answering.
3301
+ */
3302
+ function isTurnSeparator(m) {
3303
+ return m.kind === "user" || m.kind === "pending-steer" || m.kind === "shell";
3304
+ }
3305
+ /**
3306
+ * Every agent that has spoken in the transcript, plus the agent on screen.
3307
+ * Attribution renders only when this holds more than one name, mirroring web's
3308
+ * group-chat gate so a solo thread carries no attribution chrome at all.
3309
+ */
3310
+ function conversationAgentNames(items, onScreenAgentName) {
3311
+ const names = new Set([onScreenAgentName]);
3312
+ for (const item of items) {
3313
+ const name = itemAgentName(item);
3314
+ if (name) names.add(name);
3315
+ }
3316
+ return names;
3317
+ }
3318
+ /**
3319
+ * The agent-name label to render above `items[i]`, or null for no label. Every
3320
+ * assistant turn is headed with its author's name. Within one turn the items
3321
+ * (text, reasoning, tool calls) share the single header, so a turn isn't
3322
+ * re-labeled on each tool row; `error` and `recap` items are transparent and
3323
+ * don't break that grouping.
3324
+ */
3325
+ function agentLabelFor(items, i) {
3326
+ const current = items[i];
3327
+ const name = current ? itemAgentName(current) : null;
3328
+ if (!name) return null;
3329
+ for (let j = i - 1; j >= 0; j--) {
3330
+ const prevItem = items[j];
3331
+ if (!prevItem) continue;
3332
+ if (isTurnSeparator(prevItem)) return name;
3333
+ const prev = itemAgentName(prevItem);
3334
+ if (prev !== null) return prev === name ? null : name;
3335
+ }
3336
+ return name;
3337
+ }
3338
+
3272
3339
  //#endregion
3273
3340
  //#region src/chat/tui/chat/history.ts
3274
3341
  function isDynamicTool(part) {
@@ -3297,6 +3364,7 @@ function uiMessagesToItems(messages) {
3297
3364
  continue;
3298
3365
  }
3299
3366
  if (m.role === "assistant") {
3367
+ const agentName = m.metadata?.custom?.agentName ?? null;
3300
3368
  let textBuf = "";
3301
3369
  let textIdx = 0;
3302
3370
  for (const part of m.parts) {
@@ -3309,7 +3377,8 @@ function uiMessagesToItems(messages) {
3309
3377
  kind: "assistant-text",
3310
3378
  id: `${m.id}#t${textIdx++}`,
3311
3379
  text: textBuf,
3312
- done: true
3380
+ done: true,
3381
+ agentName
3313
3382
  });
3314
3383
  textBuf = "";
3315
3384
  }
@@ -3318,7 +3387,8 @@ function uiMessagesToItems(messages) {
3318
3387
  kind: "reasoning",
3319
3388
  id: `${m.id}#r${textIdx++}`,
3320
3389
  text: "text" in part && typeof part.text === "string" ? part.text : "",
3321
- done: true
3390
+ done: true,
3391
+ agentName
3322
3392
  });
3323
3393
  continue;
3324
3394
  }
@@ -3332,7 +3402,8 @@ function uiMessagesToItems(messages) {
3332
3402
  id: toolCallId ?? `${m.id}#c${textIdx++}`,
3333
3403
  card,
3334
3404
  status: "idle",
3335
- detail: null
3405
+ detail: null,
3406
+ agentName
3336
3407
  });
3337
3408
  }
3338
3409
  continue;
@@ -3352,7 +3423,8 @@ function uiMessagesToItems(messages) {
3352
3423
  kind: "ok",
3353
3424
  value: part.output
3354
3425
  } : null,
3355
- state
3426
+ state,
3427
+ agentName
3356
3428
  });
3357
3429
  }
3358
3430
  }
@@ -3360,7 +3432,8 @@ function uiMessagesToItems(messages) {
3360
3432
  kind: "assistant-text",
3361
3433
  id: `${m.id}#t${textIdx++}`,
3362
3434
  text: textBuf,
3363
- done: true
3435
+ done: true,
3436
+ agentName
3364
3437
  });
3365
3438
  }
3366
3439
  }
@@ -5168,10 +5241,11 @@ function ChatScreen({ agent, conversation }) {
5168
5241
  cancelled = true;
5169
5242
  };
5170
5243
  }, [rest, initialConversationId]);
5171
- const attachToRun = useCallback((runId) => {
5244
+ const attachToRun = useCallback((runId, runAgentName) => {
5172
5245
  if (!rest) return;
5173
5246
  const abort = new AbortController();
5174
5247
  const responsePreview = createResponsePreview();
5248
+ const streamAgentName = runAgentName ?? agent.name;
5175
5249
  setRun({
5176
5250
  kind: "streaming",
5177
5251
  runId,
@@ -5183,7 +5257,7 @@ function ChatScreen({ agent, conversation }) {
5183
5257
  onEvent: (event) => {
5184
5258
  if (event.kind === "chunk") {
5185
5259
  responsePreview.addChunk(event.chunk);
5186
- setItems((prev) => applyChunk(prev, event.chunk));
5260
+ setItems((prev) => applyChunk(prev, event.chunk, streamAgentName));
5187
5261
  return;
5188
5262
  }
5189
5263
  const errorText = event.error;
@@ -5221,6 +5295,7 @@ function ChatScreen({ agent, conversation }) {
5221
5295
  agent.name,
5222
5296
  agentHost
5223
5297
  ]);
5298
+ const multiAgent = useMemo(() => conversationAgentNames(items, agent.name), [items, agent.name]).size > 1;
5224
5299
  useEffect(() => {
5225
5300
  if (!rest || !conversationId) return;
5226
5301
  const abort = new AbortController();
@@ -5244,7 +5319,7 @@ function ChatScreen({ agent, conversation }) {
5244
5319
  try {
5245
5320
  const active = await rest.activeRun({ conversationId });
5246
5321
  if (cancelled || !active) return;
5247
- attachToRun(active.runId);
5322
+ attachToRun(active.runId, active.agentName || null);
5248
5323
  } catch (_error) {}
5249
5324
  })();
5250
5325
  return () => {
@@ -5311,10 +5386,10 @@ function ChatScreen({ agent, conversation }) {
5311
5386
  text: trimmed
5312
5387
  } : m));
5313
5388
  const current = runRef.current;
5314
- if (!(current.kind === "streaming" && current.runId === result.runId)) attachToRun(result.runId);
5389
+ if (!(current.kind === "streaming" && current.runId === result.runId)) attachToRun(result.runId, result.agentName ?? null);
5315
5390
  return;
5316
5391
  }
5317
- attachToRun(result.runId);
5392
+ attachToRun(result.runId, result.agentName ?? null);
5318
5393
  } catch (err) {
5319
5394
  setItems((prev) => [...prev.filter((m) => m.id !== optimisticId), {
5320
5395
  kind: "error",
@@ -6045,7 +6120,22 @@ function ChatScreen({ agent, conversation }) {
6045
6120
  agent.name,
6046
6121
  " below."
6047
6122
  ]
6048
- }) : /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => /* @__PURE__ */ jsx(RenderItem, { item }, item.id)) }), run.kind !== "idle" ? /* @__PURE__ */ jsx(ActivityRow, { label: run.kind === "sending" ? "sending" : "working" }) : null]
6123
+ }) : /* @__PURE__ */ jsx(Fragment, { children: items.map((item, i) => {
6124
+ const label = multiAgent ? agentLabelFor(items, i) : null;
6125
+ return /* @__PURE__ */ jsxs("box", {
6126
+ style: { flexDirection: "column" },
6127
+ children: [label ? /* @__PURE__ */ jsx("box", {
6128
+ style: {
6129
+ paddingLeft: 3,
6130
+ paddingRight: 1
6131
+ },
6132
+ children: /* @__PURE__ */ jsx("text", {
6133
+ fg: theme.muted,
6134
+ children: /* @__PURE__ */ jsx("b", { children: label })
6135
+ })
6136
+ }) : null, /* @__PURE__ */ jsx(RenderItem, { item })]
6137
+ }, item.id);
6138
+ }) }), run.kind !== "idle" ? /* @__PURE__ */ jsx(ActivityRow, { label: run.kind === "sending" ? "sending" : "working" }) : null]
6049
6139
  })
6050
6140
  }),
6051
6141
  pendingVisible ? /* @__PURE__ */ jsxs("box", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.1.0-beta.291",
3
+ "version": "0.1.0-beta.297",
4
4
  "description": "Skydive CLI — manage AI agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",