@ai-matrx/coding-sessions 0.1.85 → 0.1.86

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.
@@ -196,6 +196,8 @@ interface CodingSession {
196
196
  queued: string[];
197
197
  /** The AI Matrx agent loaded into every run of this session, when one was picked. */
198
198
  agent?: SessionAgent | null | undefined;
199
+ /** Codex must be told, in the next prompt, that an earlier agent no longer applies. */
200
+ agentRemovedDue?: boolean | undefined;
199
201
  }
200
202
  /**
201
203
  * One normalized transcript item — the same shape from Claude and Codex, from
@@ -309,6 +311,19 @@ interface SessionAgent {
309
311
  name: string;
310
312
  reason: string;
311
313
  }[];
314
+ /** The skills it carries (labels) and the tools it serves (names) — the agent's own set. */
315
+ skills?: string[] | undefined;
316
+ tools?: string[] | undefined;
317
+ /** The person's variable values, kept so "Update to vN" can reuse them. */
318
+ variables?: Record<string, JsonValue> | undefined;
319
+ /**
320
+ * The CLI must be TOLD in the next prompt (a delimited block): a Codex
321
+ * takeover or agent switch (Codex ignores developer_instructions on resume),
322
+ * or a Codex compaction that dropped the block. Cleared once delivered.
323
+ */
324
+ blockDue?: boolean | undefined;
325
+ /** When the block (or native instructions) last reached the CLI. */
326
+ deliveredAt?: string | null | undefined;
312
327
  }
313
328
  /** Whether this host can reach AI Matrx as a signed-in person. */
314
329
  interface AgentAccess {
@@ -339,12 +354,13 @@ interface AgentCatalogRpcResult {
339
354
  } | null;
340
355
  count?: number | null;
341
356
  }
342
- /** What the New session form needs about one agent. */
357
+ /** What a picker needs about one agent — the LIGHT read (no skills, no tools). */
343
358
  interface AgentDetails {
344
359
  agentId: string;
345
360
  name: string;
346
361
  /** Variable declarations as authored (camelCase). */
347
362
  variables: Record<string, JsonValue>[];
363
+ /** Newest first. */
348
364
  versions: {
349
365
  id: string;
350
366
  versionNumber: number;
@@ -352,12 +368,15 @@ interface AgentDetails {
352
368
  }[];
353
369
  /** The version "Latest" resolves to right now. */
354
370
  latestVersionNumber: number | null;
371
+ latestVersionId: string | null;
355
372
  }
356
373
  interface TakeoverInput {
357
374
  provider: CodingProvider;
358
375
  providerSessionId: string;
359
376
  cwd: string;
360
377
  title?: string | undefined;
378
+ /** Continue the chat WITH an AI Matrx agent ("Continue with agent"). */
379
+ agent?: AgentSelection | null | undefined;
361
380
  }
362
381
  interface SendResult {
363
382
  session: CodingSession;
@@ -434,6 +453,12 @@ interface CodingSessionsApi {
434
453
  agentCatalogRpc(request: AgentCatalogRpcRequest): Promise<AgentCatalogRpcResult>;
435
454
  /** An agent's variables and saved versions, for the New session form. */
436
455
  agentDetails(agentId: string): Promise<AgentDetails>;
456
+ /**
457
+ * Load a different agent into a session from its next message on (or
458
+ * remove it, with `null`). Claude reads it every turn; Codex is told in the
459
+ * next prompt.
460
+ */
461
+ setAgent(sessionId: string, agent: AgentSelection | null): Promise<CodingSession>;
437
462
  }
438
463
  type RawTranscript = {
439
464
  available: true;
@@ -588,7 +613,7 @@ declare class CodingSessionsClient {
588
613
  transcript(sessionId: string): SessionTranscript;
589
614
  readPreview(provider: CodingProvider, providerSessionId: string, maxBytes?: number): Promise<PreviewState>;
590
615
  start(input: StartInput): Promise<SendResult>;
591
- takeover(session: DiscoveredSession): Promise<CodingSession>;
616
+ takeover(session: DiscoveredSession, agent?: AgentSelection | null): Promise<CodingSession>;
592
617
  send(sessionId: string, prompt: string): Promise<SendResult>;
593
618
  stop(runId: string): Promise<RunRecord | null>;
594
619
  }
@@ -196,6 +196,8 @@ interface CodingSession {
196
196
  queued: string[];
197
197
  /** The AI Matrx agent loaded into every run of this session, when one was picked. */
198
198
  agent?: SessionAgent | null | undefined;
199
+ /** Codex must be told, in the next prompt, that an earlier agent no longer applies. */
200
+ agentRemovedDue?: boolean | undefined;
199
201
  }
200
202
  /**
201
203
  * One normalized transcript item — the same shape from Claude and Codex, from
@@ -309,6 +311,19 @@ interface SessionAgent {
309
311
  name: string;
310
312
  reason: string;
311
313
  }[];
314
+ /** The skills it carries (labels) and the tools it serves (names) — the agent's own set. */
315
+ skills?: string[] | undefined;
316
+ tools?: string[] | undefined;
317
+ /** The person's variable values, kept so "Update to vN" can reuse them. */
318
+ variables?: Record<string, JsonValue> | undefined;
319
+ /**
320
+ * The CLI must be TOLD in the next prompt (a delimited block): a Codex
321
+ * takeover or agent switch (Codex ignores developer_instructions on resume),
322
+ * or a Codex compaction that dropped the block. Cleared once delivered.
323
+ */
324
+ blockDue?: boolean | undefined;
325
+ /** When the block (or native instructions) last reached the CLI. */
326
+ deliveredAt?: string | null | undefined;
312
327
  }
313
328
  /** Whether this host can reach AI Matrx as a signed-in person. */
314
329
  interface AgentAccess {
@@ -339,12 +354,13 @@ interface AgentCatalogRpcResult {
339
354
  } | null;
340
355
  count?: number | null;
341
356
  }
342
- /** What the New session form needs about one agent. */
357
+ /** What a picker needs about one agent — the LIGHT read (no skills, no tools). */
343
358
  interface AgentDetails {
344
359
  agentId: string;
345
360
  name: string;
346
361
  /** Variable declarations as authored (camelCase). */
347
362
  variables: Record<string, JsonValue>[];
363
+ /** Newest first. */
348
364
  versions: {
349
365
  id: string;
350
366
  versionNumber: number;
@@ -352,12 +368,15 @@ interface AgentDetails {
352
368
  }[];
353
369
  /** The version "Latest" resolves to right now. */
354
370
  latestVersionNumber: number | null;
371
+ latestVersionId: string | null;
355
372
  }
356
373
  interface TakeoverInput {
357
374
  provider: CodingProvider;
358
375
  providerSessionId: string;
359
376
  cwd: string;
360
377
  title?: string | undefined;
378
+ /** Continue the chat WITH an AI Matrx agent ("Continue with agent"). */
379
+ agent?: AgentSelection | null | undefined;
361
380
  }
362
381
  interface SendResult {
363
382
  session: CodingSession;
@@ -434,6 +453,12 @@ interface CodingSessionsApi {
434
453
  agentCatalogRpc(request: AgentCatalogRpcRequest): Promise<AgentCatalogRpcResult>;
435
454
  /** An agent's variables and saved versions, for the New session form. */
436
455
  agentDetails(agentId: string): Promise<AgentDetails>;
456
+ /**
457
+ * Load a different agent into a session from its next message on (or
458
+ * remove it, with `null`). Claude reads it every turn; Codex is told in the
459
+ * next prompt.
460
+ */
461
+ setAgent(sessionId: string, agent: AgentSelection | null): Promise<CodingSession>;
437
462
  }
438
463
  type RawTranscript = {
439
464
  available: true;
@@ -588,7 +613,7 @@ declare class CodingSessionsClient {
588
613
  transcript(sessionId: string): SessionTranscript;
589
614
  readPreview(provider: CodingProvider, providerSessionId: string, maxBytes?: number): Promise<PreviewState>;
590
615
  start(input: StartInput): Promise<SendResult>;
591
- takeover(session: DiscoveredSession): Promise<CodingSession>;
616
+ takeover(session: DiscoveredSession, agent?: AgentSelection | null): Promise<CodingSession>;
592
617
  send(sessionId: string, prompt: string): Promise<SendResult>;
593
618
  stop(runId: string): Promise<RunRecord | null>;
594
619
  }
@@ -17,15 +17,15 @@ import {
17
17
  import { createExternalThreadSource as createExternalThreadSource2 } from "@ai-matrx/chat/external-store";
18
18
  import {
19
19
  Button as Button3,
20
- Dialog,
21
- DropdownMenu as DropdownMenu2,
22
- DropdownMenuContent as DropdownMenuContent2,
23
- DropdownMenuTrigger as DropdownMenuTrigger2,
24
- DialogContent,
20
+ Dialog as Dialog2,
21
+ DropdownMenu as DropdownMenu3,
22
+ DropdownMenuContent as DropdownMenuContent3,
23
+ DropdownMenuTrigger as DropdownMenuTrigger3,
24
+ DialogContent as DialogContent2,
25
25
  DialogDescription,
26
- DialogFooter,
27
- DialogHeader,
28
- DialogTitle,
26
+ DialogFooter as DialogFooter2,
27
+ DialogHeader as DialogHeader2,
28
+ DialogTitle as DialogTitle2,
29
29
  Input as Input2,
30
30
  Textarea as Textarea2,
31
31
  cn as cn5
@@ -909,6 +909,17 @@ import { AgentCatalogProvider, AgentListDropdown } from "@ai-matrx/agents/catalo
909
909
  import { humanizeContextKey } from "@ai-matrx/agents/context";
910
910
  import {
911
911
  Button,
912
+ Dialog,
913
+ DialogContent,
914
+ DialogFooter,
915
+ DialogHeader,
916
+ DialogTitle,
917
+ DropdownMenu,
918
+ DropdownMenuContent,
919
+ DropdownMenuItem as DropdownMenuItem2,
920
+ DropdownMenuLabel,
921
+ DropdownMenuSeparator as DropdownMenuSeparator2,
922
+ DropdownMenuTrigger,
912
923
  Input,
913
924
  Select,
914
925
  SelectContent,
@@ -1093,30 +1104,167 @@ var PART_LABEL = {
1093
1104
  skills: "Skills",
1094
1105
  tools: "Tools"
1095
1106
  };
1096
- function AgentChip({ agent }) {
1107
+ function agentChipText(agent) {
1097
1108
  const parts = agent.loaded.map(
1098
1109
  (part) => part.part === "instructions" ? `${PART_LABEL.instructions} ${part.loaded === 1 ? "\u2713" : "\u2014"}` : `${PART_LABEL[part.part]} ${part.loaded}/${part.total}`
1099
1110
  );
1100
- const missing = agent.unavailable.slice(0, 6).map((u) => `${u.name}: ${u.reason}`);
1101
- const more = agent.unavailable.length > 6 ? [`+${agent.unavailable.length - 6} more`] : [];
1102
- const complete = agent.loaded.every((part) => part.loaded === part.total);
1103
- return /* @__PURE__ */ jsxs5(
1104
- "span",
1105
- {
1106
- className: cn3("cs-agent-chip", !complete && "cs-agent-chip--partial"),
1107
- title: missing.length ? [...missing, ...more].join("\n") : "Everything loaded",
1108
- "data-testid": "cs-agent-chip",
1109
- children: [
1110
- /* @__PURE__ */ jsxs5("span", { className: "font-medium text-foreground", children: [
1111
- agent.name,
1112
- agent.versionNumber !== null ? ` v${agent.versionNumber}` : ""
1113
- ] }),
1114
- " ",
1115
- "\xB7 ",
1116
- parts.join(" \xB7 ")
1117
- ]
1111
+ return `${agent.name}${agent.versionNumber !== null ? ` v${agent.versionNumber}` : ""} \xB7 ${parts.join(" \xB7 ")}`;
1112
+ }
1113
+ function AgentMenu(props) {
1114
+ const { agent } = props;
1115
+ const show = useToastShow();
1116
+ const [open, setOpen] = useState4(false);
1117
+ const [choosing, setChoosing] = useState4(false);
1118
+ const [latest, setLatest] = useState4(null);
1119
+ const [access, setAccess] = useState4(null);
1120
+ useEffect2(() => {
1121
+ if (!open) return;
1122
+ let alive = true;
1123
+ void props.transport.call("agentAccess").then((next) => alive && setAccess(next)).catch(() => void 0);
1124
+ if (agent) {
1125
+ props.transport.call("agentDetails", agent.agentId).then((d) => {
1126
+ if (alive && d.latestVersionId && d.latestVersionNumber !== null) setLatest({ id: d.latestVersionId, number: d.latestVersionNumber });
1127
+ }).catch(() => void 0);
1118
1128
  }
1119
- );
1129
+ return () => {
1130
+ alive = false;
1131
+ };
1132
+ }, [open, agent, props.transport]);
1133
+ const apply = async (selection, done) => {
1134
+ try {
1135
+ await props.transport.call("setAgent", props.sessionId, selection);
1136
+ show(done);
1137
+ props.onChange();
1138
+ } catch (error) {
1139
+ show(`That didn't change: ${error instanceof Error ? error.message : String(error)}`);
1140
+ }
1141
+ };
1142
+ const behind = agent && latest && agent.versionNumber !== null && latest.number > agent.versionNumber ? latest : null;
1143
+ const complete = agent ? agent.loaded.every((part) => part.loaded === part.total) : true;
1144
+ const signedIn = access?.signedIn ?? false;
1145
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
1146
+ /* @__PURE__ */ jsxs5(DropdownMenu, { open, onOpenChange: setOpen, children: [
1147
+ /* @__PURE__ */ jsx5(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
1148
+ "button",
1149
+ {
1150
+ type: "button",
1151
+ className: cn3("cs-agent-chip", agent ? "cs-agent-chip--set" : "cs-agent-chip--none", !complete && "cs-agent-chip--partial"),
1152
+ "data-testid": "cs-agent-chip",
1153
+ "aria-label": agent ? `AI Matrx agent: ${agent.name}` : "Add an AI Matrx agent",
1154
+ children: [
1155
+ agent ? agentChipText(agent) : "No agent",
1156
+ behind ? /* @__PURE__ */ jsx5("span", { className: "cs-agent-chip-dot", "aria-label": "Update available" }) : null,
1157
+ /* @__PURE__ */ jsx5(ChevronDownIcon, { size: 12, className: "ml-1 inline shrink-0" })
1158
+ ]
1159
+ }
1160
+ ) }),
1161
+ /* @__PURE__ */ jsxs5(DropdownMenuContent, { align: "end", className: "w-[320px]", children: [
1162
+ agent ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1163
+ /* @__PURE__ */ jsxs5(DropdownMenuLabel, { className: "truncate", children: [
1164
+ agent.name,
1165
+ agent.versionNumber !== null ? ` \xB7 v${agent.versionNumber}` : ""
1166
+ ] }),
1167
+ /* @__PURE__ */ jsxs5("div", { className: "cs-agent-parts", children: [
1168
+ agent.blockDue ? /* @__PURE__ */ jsx5("p", { className: "text-[12px] text-muted-foreground", children: "Sent with your next message" }) : null,
1169
+ /* @__PURE__ */ jsx5(AgentPartList, { title: "Skills", items: agent.skills ?? [] }),
1170
+ /* @__PURE__ */ jsx5(AgentPartList, { title: "Tools", items: agent.tools ?? [] }),
1171
+ agent.unavailable.length > 0 ? /* @__PURE__ */ jsx5(
1172
+ AgentPartList,
1173
+ {
1174
+ title: "Not loaded",
1175
+ items: agent.unavailable.map((u) => `${u.name} \u2014 ${u.reason}`)
1176
+ }
1177
+ ) : null
1178
+ ] }),
1179
+ /* @__PURE__ */ jsx5(DropdownMenuSeparator2, {})
1180
+ ] }) : null,
1181
+ signedIn ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1182
+ behind && agent ? /* @__PURE__ */ jsxs5(
1183
+ DropdownMenuItem2,
1184
+ {
1185
+ onSelect: () => void apply(
1186
+ { agentId: agent.agentId, versionId: behind.id, variables: agent.variables ?? {} },
1187
+ `The next message uses ${agent.name} v${behind.number}.`
1188
+ ),
1189
+ children: [
1190
+ "Update to v",
1191
+ behind.number
1192
+ ]
1193
+ }
1194
+ ) : null,
1195
+ /* @__PURE__ */ jsx5(DropdownMenuItem2, { onSelect: () => setChoosing(true), children: agent ? "Change agent\u2026" : "Choose agent\u2026" }),
1196
+ agent ? /* @__PURE__ */ jsx5(DropdownMenuItem2, { onSelect: () => void apply(null, "The next message runs without an agent."), children: "Remove agent" }) : null
1197
+ ] }) : access ? /* @__PURE__ */ jsx5("p", { className: "px-2 py-1.5 text-[12px] text-muted-foreground", children: SIGNED_OUT_AGENTS }) : null
1198
+ ] })
1199
+ ] }),
1200
+ /* @__PURE__ */ jsx5(
1201
+ ChooseAgentDialog,
1202
+ {
1203
+ transport: props.transport,
1204
+ open: choosing,
1205
+ onOpenChange: setChoosing,
1206
+ title: agent ? "Change agent" : "Choose an agent",
1207
+ action: "Use this agent",
1208
+ onConfirm: (selection) => apply(selection, "The next message uses the new agent.")
1209
+ }
1210
+ )
1211
+ ] });
1212
+ }
1213
+ function AgentPartList({ title, items }) {
1214
+ return /* @__PURE__ */ jsxs5("div", { children: [
1215
+ /* @__PURE__ */ jsxs5("p", { className: "text-[11.5px] font-medium text-muted-foreground", children: [
1216
+ title,
1217
+ " \xB7 ",
1218
+ items.length
1219
+ ] }),
1220
+ items.length > 0 ? /* @__PURE__ */ jsx5("ul", { className: "cs-agent-part-list", children: items.map((item) => /* @__PURE__ */ jsx5("li", { className: "truncate", title: item, children: item }, item)) }) : null
1221
+ ] });
1222
+ }
1223
+ function ChooseAgentDialog(props) {
1224
+ const [value, setValue] = useState4(null);
1225
+ const [details, setDetails] = useState4(null);
1226
+ const [missing, setMissing] = useState4(null);
1227
+ const [busy, setBusy] = useState4(false);
1228
+ useEffect2(() => {
1229
+ if (props.open) {
1230
+ setValue(null);
1231
+ setMissing(null);
1232
+ }
1233
+ }, [props.open]);
1234
+ const confirm = async () => {
1235
+ if (!value) return;
1236
+ const gap = missingRequiredVariable(details, value);
1237
+ setMissing(gap);
1238
+ if (gap) return;
1239
+ setBusy(true);
1240
+ try {
1241
+ await props.onConfirm(value);
1242
+ props.onOpenChange(false);
1243
+ } finally {
1244
+ setBusy(false);
1245
+ }
1246
+ };
1247
+ return /* @__PURE__ */ jsx5(Dialog, { open: props.open, onOpenChange: props.onOpenChange, children: /* @__PURE__ */ jsxs5(DialogContent, { className: "max-w-[480px]", children: [
1248
+ /* @__PURE__ */ jsx5(DialogHeader, { children: /* @__PURE__ */ jsx5(DialogTitle, { children: props.title }) }),
1249
+ /* @__PURE__ */ jsx5(
1250
+ AgentField,
1251
+ {
1252
+ transport: props.transport,
1253
+ open: props.open,
1254
+ value,
1255
+ onChange: (next) => {
1256
+ setValue(next);
1257
+ setMissing(null);
1258
+ },
1259
+ missing,
1260
+ onDetails: setDetails
1261
+ }
1262
+ ),
1263
+ /* @__PURE__ */ jsxs5(DialogFooter, { children: [
1264
+ /* @__PURE__ */ jsx5(Button, { type: "button", variant: "ghost", onClick: () => props.onOpenChange(false), children: "Cancel" }),
1265
+ /* @__PURE__ */ jsx5(Button, { type: "button", disabled: !value || busy, onClick: () => void confirm(), children: props.action })
1266
+ ] })
1267
+ ] }) });
1120
1268
  }
1121
1269
 
1122
1270
  // src/react/sidebar.tsx
@@ -1126,14 +1274,14 @@ import {
1126
1274
  ContextMenu,
1127
1275
  ContextMenuContent,
1128
1276
  ContextMenuTrigger,
1129
- DropdownMenu,
1130
- DropdownMenuContent,
1131
- DropdownMenuItem as DropdownMenuItem2,
1132
- DropdownMenuLabel,
1277
+ DropdownMenu as DropdownMenu2,
1278
+ DropdownMenuContent as DropdownMenuContent2,
1279
+ DropdownMenuItem as DropdownMenuItem3,
1280
+ DropdownMenuLabel as DropdownMenuLabel2,
1133
1281
  DropdownMenuRadioGroup,
1134
1282
  DropdownMenuRadioItem,
1135
- DropdownMenuSeparator as DropdownMenuSeparator2,
1136
- DropdownMenuTrigger,
1283
+ DropdownMenuSeparator as DropdownMenuSeparator3,
1284
+ DropdownMenuTrigger as DropdownMenuTrigger2,
1137
1285
  cn as cn4
1138
1286
  } from "@ai-matrx/design-system";
1139
1287
  import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -1240,9 +1388,9 @@ function Row(props) {
1240
1388
  ] }),
1241
1389
  renaming ? null : /* @__PURE__ */ jsxs6("span", { className: "cs-row__actions", children: [
1242
1390
  props.takeover,
1243
- /* @__PURE__ */ jsxs6(DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [
1244
- /* @__PURE__ */ jsx6(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx6("button", { type: "button", className: "cs-icon-button", "aria-label": `More for ${entry.title}`, title: "More", children: /* @__PURE__ */ jsx6(MoreIcon, { size: 15 }) }) }),
1245
- /* @__PURE__ */ jsx6(DropdownMenuContent, { align: "start", className: "w-60", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx6(ChatMenuItems, { entry, variant: "dropdown", onRename: () => setRenaming(true) }) })
1391
+ /* @__PURE__ */ jsxs6(DropdownMenu2, { open: menuOpen, onOpenChange: setMenuOpen, children: [
1392
+ /* @__PURE__ */ jsx6(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6("button", { type: "button", className: "cs-icon-button", "aria-label": `More for ${entry.title}`, title: "More", children: /* @__PURE__ */ jsx6(MoreIcon, { size: 15 }) }) }),
1393
+ /* @__PURE__ */ jsx6(DropdownMenuContent2, { align: "start", className: "w-60", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx6(ChatMenuItems, { entry, variant: "dropdown", onRename: () => setRenaming(true) }) })
1246
1394
  ] })
1247
1395
  ] })
1248
1396
  ]
@@ -1480,16 +1628,16 @@ function Sidebar(props) {
1480
1628
  SectionLabel,
1481
1629
  {
1482
1630
  action: /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-0.5", children: [
1483
- /* @__PURE__ */ jsxs6(DropdownMenu, { children: [
1484
- /* @__PURE__ */ jsx6(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx6("button", { type: "button", className: "cs-icon-button cs-icon-button--text", "aria-label": "How to list your other chats", children: groupMode === "date" ? "By date" : "By folder" }) }),
1485
- /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "end", className: "w-52", children: [
1486
- /* @__PURE__ */ jsx6(DropdownMenuLabel, { children: "Group your other chats" }),
1631
+ /* @__PURE__ */ jsxs6(DropdownMenu2, { children: [
1632
+ /* @__PURE__ */ jsx6(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6("button", { type: "button", className: "cs-icon-button cs-icon-button--text", "aria-label": "How to list your other chats", children: groupMode === "date" ? "By date" : "By folder" }) }),
1633
+ /* @__PURE__ */ jsxs6(DropdownMenuContent2, { align: "end", className: "w-52", children: [
1634
+ /* @__PURE__ */ jsx6(DropdownMenuLabel2, { children: "Group your other chats" }),
1487
1635
  /* @__PURE__ */ jsxs6(DropdownMenuRadioGroup, { value: groupMode, onValueChange: (v) => setMode(v), children: [
1488
1636
  /* @__PURE__ */ jsx6(DropdownMenuRadioItem, { value: "date", children: "By date" }),
1489
1637
  /* @__PURE__ */ jsx6(DropdownMenuRadioItem, { value: "folder", children: "By folder" })
1490
1638
  ] }),
1491
- /* @__PURE__ */ jsx6(DropdownMenuSeparator2, {}),
1492
- /* @__PURE__ */ jsx6(DropdownMenuItem2, { onSelect: () => setShowHidden((v) => !v), children: showHidden ? "Leave hidden chats out" : `Show hidden chats${hiddenCount ? ` (${hiddenCount})` : ""}` })
1639
+ /* @__PURE__ */ jsx6(DropdownMenuSeparator3, {}),
1640
+ /* @__PURE__ */ jsx6(DropdownMenuItem3, { onSelect: () => setShowHidden((v) => !v), children: showHidden ? "Leave hidden chats out" : `Show hidden chats${hiddenCount ? ` (${hiddenCount})` : ""}` })
1493
1641
  ] })
1494
1642
  ] }),
1495
1643
  /* @__PURE__ */ jsx6(
@@ -2138,12 +2286,13 @@ var CodingSessionsClient = class {
2138
2286
  start(input) {
2139
2287
  return this.transport.call("start", input);
2140
2288
  }
2141
- takeover(session) {
2289
+ takeover(session, agent) {
2142
2290
  return this.transport.call("takeover", {
2143
2291
  provider: session.provider,
2144
2292
  providerSessionId: session.providerSessionId,
2145
2293
  cwd: session.cwd ?? "",
2146
- title: session.title
2294
+ title: session.title,
2295
+ ...agent ? { agent } : {}
2147
2296
  });
2148
2297
  }
2149
2298
  send(sessionId, prompt) {
@@ -2370,9 +2519,9 @@ function useSessionUsage(sessionId, lastRunKey) {
2370
2519
  return usage;
2371
2520
  }
2372
2521
  function HeaderMenu(props) {
2373
- return /* @__PURE__ */ jsxs7(DropdownMenu2, { children: [
2374
- /* @__PURE__ */ jsx7(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx7("button", { type: "button", className: "cs-icon-button", "aria-label": "More for this chat", title: "More", children: /* @__PURE__ */ jsx7(MoreIcon, { size: 16 }) }) }),
2375
- /* @__PURE__ */ jsx7(DropdownMenuContent2, { align: "end", className: "w-64", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx7(ChatMenuItems, { entry: props.entry, variant: "dropdown", onRename: props.onRename }) })
2522
+ return /* @__PURE__ */ jsxs7(DropdownMenu3, { children: [
2523
+ /* @__PURE__ */ jsx7(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx7("button", { type: "button", className: "cs-icon-button", "aria-label": "More for this chat", title: "More", children: /* @__PURE__ */ jsx7(MoreIcon, { size: 16 }) }) }),
2524
+ /* @__PURE__ */ jsx7(DropdownMenuContent3, { align: "end", className: "w-64", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx7(ChatMenuItems, { entry: props.entry, variant: "dropdown", onRename: props.onRename }) })
2376
2525
  ] });
2377
2526
  }
2378
2527
  function HeaderTitle(props) {
@@ -2502,7 +2651,7 @@ function SessionView({ session, changesOpen, setChangesOpen }) {
2502
2651
  ] }) : null
2503
2652
  ] })
2504
2653
  ] }),
2505
- session.agent ? /* @__PURE__ */ jsx7(AgentChip, { agent: session.agent }) : null,
2654
+ /* @__PURE__ */ jsx7(AgentMenu, { transport: client.transport, sessionId: session.id, agent: session.agent ?? null, onChange: () => void 0 }),
2506
2655
  /* @__PURE__ */ jsxs7(
2507
2656
  "button",
2508
2657
  {
@@ -2675,13 +2824,14 @@ function DiscoveredView(props) {
2675
2824
  if (elsewhere) setConfirming(true);
2676
2825
  else void takeover();
2677
2826
  };
2678
- const takeover = async () => {
2827
+ const [choosingAgent, setChoosingAgent] = useState6(false);
2828
+ const takeover = async (agent) => {
2679
2829
  if (!d) return;
2680
2830
  setConfirming(false);
2681
2831
  setBusy(true);
2682
2832
  setError(null);
2683
2833
  try {
2684
- const taken = await client.takeover(d);
2834
+ const taken = await client.takeover(d, agent);
2685
2835
  props.onTakenOver(taken.id);
2686
2836
  } catch (e) {
2687
2837
  setError(e instanceof Error ? e.message : String(e));
@@ -2737,6 +2887,18 @@ function DiscoveredView(props) {
2737
2887
  ]
2738
2888
  }
2739
2889
  ) : null,
2890
+ d?.cwd && !elsewhere ? /* @__PURE__ */ jsx7(Button3, { size: "sm", variant: "outline", onClick: () => setChoosingAgent(true), disabled: busy, children: "Continue with agent\u2026" }) : null,
2891
+ /* @__PURE__ */ jsx7(
2892
+ ChooseAgentDialog,
2893
+ {
2894
+ transport: client.transport,
2895
+ open: choosingAgent,
2896
+ onOpenChange: setChoosingAgent,
2897
+ title: "Continue with an agent",
2898
+ action: "Take over with this agent",
2899
+ onConfirm: (agent) => takeover(agent)
2900
+ }
2901
+ ),
2740
2902
  d ? /* @__PURE__ */ jsx7(HeaderMenu, { entry, onRename: () => setRenaming(true) }) : null
2741
2903
  ] }),
2742
2904
  elsewhere ? /* @__PURE__ */ jsxs7("div", { role: "status", className: "flex items-start gap-2 border-b border-warning/40 bg-warning/10 px-5 py-2.5 text-[13px]", children: [
@@ -2746,9 +2908,9 @@ function DiscoveredView(props) {
2746
2908
  " Taking it over now would put two agents on the same chat, and they could overwrite each other's work. Finish or stop it there first if you can. This preview follows it as it runs."
2747
2909
  ] })
2748
2910
  ] }) : null,
2749
- /* @__PURE__ */ jsx7(Dialog, { open: confirming, onOpenChange: setConfirming, children: /* @__PURE__ */ jsxs7(DialogContent, { className: "max-w-[460px]", children: [
2750
- /* @__PURE__ */ jsxs7(DialogHeader, { children: [
2751
- /* @__PURE__ */ jsxs7(DialogTitle, { children: [
2911
+ /* @__PURE__ */ jsx7(Dialog2, { open: confirming, onOpenChange: setConfirming, children: /* @__PURE__ */ jsxs7(DialogContent2, { className: "max-w-[460px]", children: [
2912
+ /* @__PURE__ */ jsxs7(DialogHeader2, { children: [
2913
+ /* @__PURE__ */ jsxs7(DialogTitle2, { children: [
2752
2914
  "This chat is running right now in ",
2753
2915
  PROVIDER_LABEL[props.provider]
2754
2916
  ] }),
@@ -2757,7 +2919,7 @@ function DiscoveredView(props) {
2757
2919
  " If you take it over, two agents will work in the same conversation at once and may overwrite each other's changes."
2758
2920
  ] })
2759
2921
  ] }),
2760
- /* @__PURE__ */ jsxs7(DialogFooter, { children: [
2922
+ /* @__PURE__ */ jsxs7(DialogFooter2, { children: [
2761
2923
  /* @__PURE__ */ jsx7(Button3, { variant: "ghost", onClick: () => setConfirming(false), children: "Cancel" }),
2762
2924
  /* @__PURE__ */ jsx7(Button3, { variant: "destructive", onClick: () => void takeover(), children: "Take over anyway" })
2763
2925
  ] })
@@ -2937,9 +3099,9 @@ function NewSessionDialog(props) {
2937
3099
  if (field === "folder") delete next.createFolder;
2938
3100
  return next;
2939
3101
  });
2940
- return /* @__PURE__ */ jsx7(Dialog, { open: props.open, onOpenChange: props.onOpenChange, children: /* @__PURE__ */ jsxs7(DialogContent, { className: "max-w-[560px]", children: [
2941
- /* @__PURE__ */ jsxs7(DialogHeader, { children: [
2942
- /* @__PURE__ */ jsx7(DialogTitle, { children: "New coding session" }),
3102
+ return /* @__PURE__ */ jsx7(Dialog2, { open: props.open, onOpenChange: props.onOpenChange, children: /* @__PURE__ */ jsxs7(DialogContent2, { className: "max-w-[560px]", children: [
3103
+ /* @__PURE__ */ jsxs7(DialogHeader2, { children: [
3104
+ /* @__PURE__ */ jsx7(DialogTitle2, { children: "New coding session" }),
2943
3105
  /* @__PURE__ */ jsxs7(DialogDescription, { children: [
2944
3106
  "Runs on your own ",
2945
3107
  provider === "claude" ? "Claude" : "ChatGPT",
@@ -3083,7 +3245,7 @@ function NewSessionDialog(props) {
3083
3245
  ] }),
3084
3246
  problem ? /* @__PURE__ */ jsx7(FieldError, { id: "cs-err-start", children: problem }) : null
3085
3247
  ] }),
3086
- /* @__PURE__ */ jsxs7(DialogFooter, { className: "pt-4", children: [
3248
+ /* @__PURE__ */ jsxs7(DialogFooter2, { className: "pt-4", children: [
3087
3249
  /* @__PURE__ */ jsx7(Button3, { type: "button", variant: "ghost", onClick: () => props.onOpenChange(false), children: "Cancel" }),
3088
3250
  /* @__PURE__ */ jsxs7(Button3, { type: "submit", disabled: starting, className: "gap-1.5", children: [
3089
3251
  starting ? /* @__PURE__ */ jsx7(SpinnerIcon, { size: 14 }) : null,
@@ -3252,9 +3414,9 @@ var SHORTCUTS = [
3252
3414
  { keys: "\u2318/", what: "These shortcuts" }
3253
3415
  ];
3254
3416
  function ShortcutsDialog(props) {
3255
- return /* @__PURE__ */ jsx7(Dialog, { open: props.open, onOpenChange: props.onOpenChange, children: /* @__PURE__ */ jsxs7(DialogContent, { className: "max-w-[420px]", children: [
3256
- /* @__PURE__ */ jsxs7(DialogHeader, { children: [
3257
- /* @__PURE__ */ jsx7(DialogTitle, { children: "Keyboard shortcuts" }),
3417
+ return /* @__PURE__ */ jsx7(Dialog2, { open: props.open, onOpenChange: props.onOpenChange, children: /* @__PURE__ */ jsxs7(DialogContent2, { className: "max-w-[420px]", children: [
3418
+ /* @__PURE__ */ jsxs7(DialogHeader2, { children: [
3419
+ /* @__PURE__ */ jsx7(DialogTitle2, { children: "Keyboard shortcuts" }),
3258
3420
  /* @__PURE__ */ jsx7(DialogDescription, { children: "Ctrl works in place of \u2318 on Windows and Linux." })
3259
3421
  ] }),
3260
3422
  /* @__PURE__ */ jsx7("dl", { className: "divide-y divide-border text-[13px]", children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-4 py-2", children: [