@charzhu/openjaw-agent 0.2.1 → 0.2.2

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/main.js CHANGED
@@ -3540,6 +3540,13 @@ var init_tool_exposure = __esm({
3540
3540
 
3541
3541
  // src/copilot-auth.ts
3542
3542
  import { setTimeout as sleep } from "node:timers/promises";
3543
+ function isOAuthAbortError(err) {
3544
+ if (!err || typeof err !== "object") return false;
3545
+ const anyErr = err;
3546
+ if (anyErr.name === "AbortError") return true;
3547
+ if (anyErr.code === "ABORT_ERR") return true;
3548
+ return false;
3549
+ }
3543
3550
  function oauthDomain(enterpriseUrl) {
3544
3551
  return enterpriseUrl ? normalizeCopilotEnterpriseDomain(enterpriseUrl) : "github.com";
3545
3552
  }
@@ -3578,11 +3585,14 @@ async function startCopilotDeviceFlow(clientId, enterpriseUrl) {
3578
3585
  enterpriseUrl: normalizedEnterpriseUrl
3579
3586
  };
3580
3587
  }
3581
- async function completeCopilotDeviceFlow(flow) {
3588
+ async function completeCopilotDeviceFlow(flow, signal) {
3582
3589
  const domain = oauthDomain(flow.enterpriseUrl);
3583
3590
  let intervalMs = flow.intervalSeconds * 1e3;
3584
3591
  while (true) {
3585
- await sleep(intervalMs + OAUTH_POLLING_SAFETY_MARGIN_MS);
3592
+ if (signal?.aborted) {
3593
+ throw signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
3594
+ }
3595
+ await sleep(intervalMs + OAUTH_POLLING_SAFETY_MARGIN_MS, void 0, { signal });
3586
3596
  const res = await fetch(`https://${domain}/login/oauth/access_token`, {
3587
3597
  method: "POST",
3588
3598
  headers: {
@@ -3594,7 +3604,8 @@ async function completeCopilotDeviceFlow(flow) {
3594
3604
  client_id: flow.clientId,
3595
3605
  device_code: flow.deviceCode,
3596
3606
  grant_type: "urn:ietf:params:oauth:grant-type:device_code"
3597
- })
3607
+ }),
3608
+ signal
3598
3609
  });
3599
3610
  if (!res.ok) {
3600
3611
  throw new Error(`GitHub device login failed: ${res.status}`);
@@ -3633,6 +3644,7 @@ var init_copilot_auth = __esm({
3633
3644
  init_provider_auth();
3634
3645
  init_copilot();
3635
3646
  OAUTH_POLLING_SAFETY_MARGIN_MS = 3e3;
3647
+ __name(isOAuthAbortError, "isOAuthAbortError");
3636
3648
  __name(oauthDomain, "oauthDomain");
3637
3649
  __name(startCopilotDeviceFlow, "startCopilotDeviceFlow");
3638
3650
  __name(completeCopilotDeviceFlow, "completeCopilotDeviceFlow");
@@ -3648,7 +3660,8 @@ __export(connect_exports, {
3648
3660
  disconnectContext: () => disconnectContext,
3649
3661
  handleConnectCommand: () => handleConnectCommand,
3650
3662
  listDisconnectChoices: () => listDisconnectChoices,
3651
- maestroConfig: () => maestroConfig
3663
+ maestroConfig: () => maestroConfig,
3664
+ resolveCopilotClientId: () => resolveCopilotClientId
3652
3665
  });
3653
3666
  function maestroConfig(flavor = "anthropic") {
3654
3667
  return {
@@ -47309,6 +47322,19 @@ function registerRpcHandlers(options) {
47309
47322
  let currentRun = null;
47310
47323
  const pendingResponders = /* @__PURE__ */ new Map();
47311
47324
  const promptCollector = createPromptCollector(bus, () => agentLoop.sessionId);
47325
+ const OAUTH_FLOW_TTL_MS = 15 * 60 * 1e3;
47326
+ const oauthFlows = /* @__PURE__ */ new Map();
47327
+ const cleanupOAuthFlow = /* @__PURE__ */ __name((flowId, abort) => {
47328
+ const entry = oauthFlows.get(flowId);
47329
+ if (!entry) {
47330
+ return;
47331
+ }
47332
+ clearTimeout(entry.timer);
47333
+ if (abort && !entry.controller.signal.aborted) {
47334
+ entry.controller.abort();
47335
+ }
47336
+ oauthFlows.delete(flowId);
47337
+ }, "cleanupOAuthFlow");
47312
47338
  const onBridgeEvent = /* @__PURE__ */ __name((rawEvent) => {
47313
47339
  const source = inferBridgeSource(rawEvent);
47314
47340
  const user = bridgeUser(rawEvent, source);
@@ -47855,6 +47881,77 @@ ${helpMessage}` : field.label;
47855
47881
  }
47856
47882
  return { disconnected: true, slug };
47857
47883
  });
47884
+ bus.registerRpc("model.oauth_start", async (params) => {
47885
+ const slug = String(params.slug ?? "").trim();
47886
+ if (!PROVIDERS2.includes(slug)) {
47887
+ throw new Error(`unknown provider: ${slug}`);
47888
+ }
47889
+ const auth = PROVIDER_AUTH[slug];
47890
+ if (auth.auth_type !== "oauth") {
47891
+ throw new Error(`${PROVIDER_LABELS[slug]} does not use OAuth (auth_type=${auth.auth_type})`);
47892
+ }
47893
+ const clientId = resolveCopilotClientId();
47894
+ if (!clientId) {
47895
+ throw new Error(
47896
+ "GitHub Copilot login needs a GitHub OAuth App client ID. Set GITHUB_COPILOT_CLIENT_ID or llm.copilot_oauth_client_id in ~/.openjaw-agent/config.yaml."
47897
+ );
47898
+ }
47899
+ const enterpriseUrl = (() => {
47900
+ const raw = params["enterprise_url"];
47901
+ if (typeof raw === "string" && raw.trim()) {
47902
+ return raw.trim();
47903
+ }
47904
+ return agentConfig.llm.copilot_enterprise_url;
47905
+ })();
47906
+ const flow = await startCopilotDeviceFlow(clientId, enterpriseUrl);
47907
+ const flowId = randomUUID13();
47908
+ const controller = new AbortController();
47909
+ const timer = setTimeout(() => {
47910
+ cleanupOAuthFlow(flowId, true);
47911
+ }, OAUTH_FLOW_TTL_MS);
47912
+ if (typeof timer.unref === "function") {
47913
+ timer.unref();
47914
+ }
47915
+ oauthFlows.set(flowId, { controller, flow, slug, timer });
47916
+ return {
47917
+ flow_id: flowId,
47918
+ interval_seconds: flow.intervalSeconds,
47919
+ user_code: flow.userCode,
47920
+ verification_uri: flow.verificationUri
47921
+ };
47922
+ });
47923
+ bus.registerRpc("model.oauth_complete", async (params) => {
47924
+ const flowId = String(params["flow_id"] ?? "").trim();
47925
+ const entry = oauthFlows.get(flowId);
47926
+ if (!entry) {
47927
+ throw new Error("OAuth flow not found or already completed/expired");
47928
+ }
47929
+ try {
47930
+ await completeCopilotDeviceFlow(entry.flow, entry.controller.signal);
47931
+ } catch (err) {
47932
+ cleanupOAuthFlow(flowId, false);
47933
+ if (isOAuthAbortError(err) || entry.controller.signal.aborted) {
47934
+ return { aborted: true, slug: entry.slug };
47935
+ }
47936
+ throw err;
47937
+ }
47938
+ cleanupOAuthFlow(flowId, false);
47939
+ reconnectProviderContext(entry.slug);
47940
+ const live = await fetchLiveModels(entry.slug, agentConfig, agentLoop.model);
47941
+ return {
47942
+ provider: buildProviderOption(entry.slug, agentLoop.providerName, agentLoop.model, live.models, live.error),
47943
+ slug: entry.slug
47944
+ };
47945
+ });
47946
+ bus.registerRpc("model.oauth_cancel", (params) => {
47947
+ const flowId = String(params["flow_id"] ?? "").trim();
47948
+ const entry = oauthFlows.get(flowId);
47949
+ if (!entry) {
47950
+ return { cancelled: false, reason: "flow not found" };
47951
+ }
47952
+ cleanupOAuthFlow(flowId, true);
47953
+ return { cancelled: true, slug: entry.slug };
47954
+ });
47858
47955
  bus.registerRpc("commands.catalog", () => {
47859
47956
  const skillCount = toolRegistry.listTools().filter((t) => /^skill[:_-]/i.test(t.name)).length;
47860
47957
  return buildCommandsCatalog({ skillCount });
@@ -48478,6 +48575,9 @@ ${helpMessage}` : field.label;
48478
48575
  bridgeEmitter?.off("bridgeEvent", onBridgeEvent);
48479
48576
  mcpManager.off("tools-changed", onToolsChanged);
48480
48577
  pendingResponders.clear();
48578
+ for (const id of Array.from(oauthFlows.keys())) {
48579
+ cleanupOAuthFlow(id, true);
48580
+ }
48481
48581
  };
48482
48582
  }
48483
48583
  var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot;
@@ -48486,6 +48586,7 @@ var init_rpcHandlers = __esm({
48486
48586
  "use strict";
48487
48587
  init_connect();
48488
48588
  init_config();
48589
+ init_copilot_auth();
48489
48590
  init_context_manager();
48490
48591
  init_eventBridge();
48491
48592
  init_fork();
@@ -56345,8 +56446,8 @@ var init_providers2 = __esm({
56345
56446
  });
56346
56447
 
56347
56448
  // src/components/modelPicker.tsx
56348
- import { useEffect as useEffect17, useMemo as useMemo12, useState as useState19 } from "react";
56349
- import { jsx as jsx25, jsxs as jsxs13 } from "react/jsx-runtime";
56449
+ import { useEffect as useEffect17, useMemo as useMemo12, useRef as useRef17, useState as useState19 } from "react";
56450
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs13 } from "react/jsx-runtime";
56350
56451
  function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t }) {
56351
56452
  const [providers, setProviders] = useState19([]);
56352
56453
  const [currentModel, setCurrentModel] = useState19("");
@@ -56359,10 +56460,19 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56359
56460
  const [keyInput, setKeyInput] = useState19("");
56360
56461
  const [keySaving, setKeySaving] = useState19(false);
56361
56462
  const [keyError, setKeyError] = useState19("");
56463
+ const [oauthStatus, setOauthStatus] = useState19("starting");
56464
+ const [oauthVerificationUri, setOauthVerificationUri] = useState19("");
56465
+ const [oauthUserCode, setOauthUserCode] = useState19("");
56466
+ const [oauthError, setOauthError] = useState19("");
56467
+ const oauthFlowIdRef = useRef17(null);
56468
+ const oauthProviderSlugRef = useRef17(null);
56469
+ const oauthAttemptRef = useRef17(0);
56470
+ const mountedRef = useRef17(true);
56362
56471
  const { stdout } = useStdout();
56363
56472
  const width = Math.max(MIN_WIDTH2, Math.min(MAX_WIDTH2, (stdout?.columns ?? 80) - 6));
56364
56473
  useEffect17(() => {
56365
56474
  gw.request("model.options", sessionId ? { session_id: sessionId } : {}).then((raw) => {
56475
+ if (!mountedRef.current) return;
56366
56476
  const r = asRpcResult(raw);
56367
56477
  if (!r) {
56368
56478
  setErr("invalid response: model.options");
@@ -56383,6 +56493,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56383
56493
  setErr("");
56384
56494
  setLoading(false);
56385
56495
  }).catch((e) => {
56496
+ if (!mountedRef.current) return;
56386
56497
  setErr(rpcErrorMessage(e));
56387
56498
  setLoading(false);
56388
56499
  });
@@ -56390,7 +56501,121 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56390
56501
  const provider = providers[providerIdx];
56391
56502
  const models = provider?.models ?? [];
56392
56503
  const names = useMemo12(() => providerDisplayNames(providers), [providers]);
56504
+ useEffect17(() => {
56505
+ mountedRef.current = true;
56506
+ return () => {
56507
+ mountedRef.current = false;
56508
+ const flowId = oauthFlowIdRef.current;
56509
+ if (flowId) {
56510
+ gw.request("model.oauth_cancel", { flow_id: flowId }).catch(() => {
56511
+ });
56512
+ oauthFlowIdRef.current = null;
56513
+ }
56514
+ };
56515
+ }, [gw]);
56516
+ const refreshProviders = /* @__PURE__ */ __name(() => gw.request("model.options", sessionId ? { session_id: sessionId } : {}).then((raw) => {
56517
+ if (!mountedRef.current) return;
56518
+ const r = asRpcResult(raw);
56519
+ if (!r) {
56520
+ return;
56521
+ }
56522
+ setProviders(r.providers ?? []);
56523
+ setCurrentModel(String(r.model ?? ""));
56524
+ }).catch(() => {
56525
+ }), "refreshProviders");
56526
+ const cancelActiveOAuth = /* @__PURE__ */ __name(() => {
56527
+ oauthAttemptRef.current += 1;
56528
+ const flowId = oauthFlowIdRef.current;
56529
+ if (!flowId) {
56530
+ return;
56531
+ }
56532
+ oauthFlowIdRef.current = null;
56533
+ gw.request("model.oauth_cancel", { flow_id: flowId }).catch(() => {
56534
+ });
56535
+ }, "cancelActiveOAuth");
56536
+ const startOAuthFlow = /* @__PURE__ */ __name((slug) => {
56537
+ cancelActiveOAuth();
56538
+ const attempt = ++oauthAttemptRef.current;
56539
+ oauthProviderSlugRef.current = slug;
56540
+ setStage("oauth");
56541
+ setOauthStatus("starting");
56542
+ setOauthVerificationUri("");
56543
+ setOauthUserCode("");
56544
+ setOauthError("");
56545
+ const isCurrent = /* @__PURE__ */ __name(() => mountedRef.current && oauthAttemptRef.current === attempt, "isCurrent");
56546
+ gw.request("model.oauth_start", {
56547
+ slug,
56548
+ ...sessionId ? { session_id: sessionId } : {}
56549
+ }).then((raw) => {
56550
+ const r = asRpcResult(raw);
56551
+ if (!isCurrent()) {
56552
+ if (r?.flow_id) {
56553
+ gw.request("model.oauth_cancel", { flow_id: r.flow_id }).catch(() => {
56554
+ });
56555
+ }
56556
+ return;
56557
+ }
56558
+ if (!r?.flow_id) {
56559
+ setOauthStatus("error");
56560
+ setOauthError("invalid response from model.oauth_start");
56561
+ return;
56562
+ }
56563
+ oauthFlowIdRef.current = r.flow_id;
56564
+ setOauthVerificationUri(r.verification_uri);
56565
+ setOauthUserCode(r.user_code);
56566
+ setOauthStatus("waiting");
56567
+ gw.request("model.oauth_complete", {
56568
+ flow_id: r.flow_id,
56569
+ ...sessionId ? { session_id: sessionId } : {}
56570
+ }).then((rawComplete) => {
56571
+ if (!isCurrent()) return;
56572
+ if (oauthFlowIdRef.current !== r.flow_id) return;
56573
+ oauthFlowIdRef.current = null;
56574
+ const rc = asRpcResult(rawComplete);
56575
+ if (!rc) {
56576
+ setOauthStatus("error");
56577
+ setOauthError("invalid response from model.oauth_complete");
56578
+ return;
56579
+ }
56580
+ if (rc.aborted) {
56581
+ setStage("provider");
56582
+ return;
56583
+ }
56584
+ if (rc.provider) {
56585
+ setProviders(
56586
+ (prev) => prev.map((p) => p.slug === rc.provider.slug ? rc.provider : p)
56587
+ );
56588
+ } else {
56589
+ refreshProviders();
56590
+ }
56591
+ if (mode === "connect") {
56592
+ onCancel();
56593
+ return;
56594
+ }
56595
+ setStage("model");
56596
+ setModelIdx(0);
56597
+ }).catch((e) => {
56598
+ if (!isCurrent()) return;
56599
+ if (oauthFlowIdRef.current !== r.flow_id) return;
56600
+ oauthFlowIdRef.current = null;
56601
+ setOauthStatus("error");
56602
+ setOauthError(rpcErrorMessage(e));
56603
+ });
56604
+ }).catch((e) => {
56605
+ if (!isCurrent()) return;
56606
+ setOauthStatus("error");
56607
+ setOauthError(rpcErrorMessage(e));
56608
+ });
56609
+ }, "startOAuthFlow");
56393
56610
  const back = /* @__PURE__ */ __name(() => {
56611
+ if (stage === "oauth") {
56612
+ cancelActiveOAuth();
56613
+ setStage("provider");
56614
+ setOauthVerificationUri("");
56615
+ setOauthUserCode("");
56616
+ setOauthError("");
56617
+ return;
56618
+ }
56394
56619
  if (stage === "model" || stage === "key" || stage === "disconnect") {
56395
56620
  setStage("provider");
56396
56621
  setModelIdx(0);
@@ -56401,8 +56626,30 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56401
56626
  }
56402
56627
  onCancel();
56403
56628
  }, "back");
56404
- useOverlayKeys({ onBack: back, onClose: onCancel });
56629
+ const close = /* @__PURE__ */ __name(() => {
56630
+ if (stage === "oauth") {
56631
+ cancelActiveOAuth();
56632
+ }
56633
+ onCancel();
56634
+ }, "close");
56635
+ useOverlayKeys({ onBack: back, onClose: close });
56405
56636
  use_input_default((ch, key) => {
56637
+ if (stage === "oauth") {
56638
+ if (key.escape) {
56639
+ back();
56640
+ return;
56641
+ }
56642
+ if (key.return && oauthStatus === "error") {
56643
+ const slug = oauthProviderSlugRef.current;
56644
+ if (slug) {
56645
+ startOAuthFlow(slug);
56646
+ } else {
56647
+ back();
56648
+ }
56649
+ return;
56650
+ }
56651
+ return;
56652
+ }
56406
56653
  if (stage === "key") {
56407
56654
  if (keySaving) {
56408
56655
  return;
@@ -56508,6 +56755,11 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56508
56755
  setStage("key");
56509
56756
  setKeyInput("");
56510
56757
  setKeyError("");
56758
+ return;
56759
+ }
56760
+ if (provider.auth_type === "oauth") {
56761
+ startOAuthFlow(provider.slug);
56762
+ return;
56511
56763
  }
56512
56764
  return;
56513
56765
  }
@@ -56554,6 +56806,41 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56554
56806
  /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Esc/q cancel" })
56555
56807
  ] });
56556
56808
  }
56809
+ if (stage === "oauth") {
56810
+ const slug = oauthProviderSlugRef.current;
56811
+ const providerName = providers.find((p) => p.slug === slug)?.name ?? "GitHub Copilot";
56812
+ return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
56813
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
56814
+ "Sign in to ",
56815
+ providerName
56816
+ ] }),
56817
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56818
+ oauthStatus === "starting" ? /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "requesting device code\u2026" }) : oauthStatus === "error" ? /* @__PURE__ */ jsxs13(Fragment5, { children: [
56819
+ /* @__PURE__ */ jsxs13(Text9, { color: t.color.label, wrap: "truncate-end", children: [
56820
+ "error: ",
56821
+ oauthError || "failed to start GitHub sign-in"
56822
+ ] }),
56823
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56824
+ /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Enter retry \xB7 Esc back" })
56825
+ ] }) : /* @__PURE__ */ jsxs13(Fragment5, { children: [
56826
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "1. Open this URL in your browser:" }),
56827
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
56828
+ " ",
56829
+ oauthVerificationUri
56830
+ ] }),
56831
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56832
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "2. Enter this code on the GitHub page:" }),
56833
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
56834
+ " ",
56835
+ oauthUserCode
56836
+ ] }),
56837
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56838
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "waiting for authorization\u2026" }),
56839
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56840
+ /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Esc cancel" })
56841
+ ] })
56842
+ ] });
56843
+ }
56557
56844
  if (stage === "key" && provider) {
56558
56845
  const masked = keyInput ? "\u2022".repeat(Math.min(keyInput.length, 40)) : "";
56559
56846
  return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
@@ -57507,7 +57794,7 @@ var init_appOverlays = __esm({
57507
57794
 
57508
57795
  // src/components/branding.tsx
57509
57796
  import { useEffect as useEffect20, useState as useState24 } from "react";
57510
- import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
57797
+ import { Fragment as Fragment6, jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
57511
57798
  function useColumns() {
57512
57799
  const { stdout } = useStdout();
57513
57800
  const [cols, setCols] = useState24(stdout?.columns ?? 80);
@@ -58948,7 +59235,7 @@ var init_syntax = __esm({
58948
59235
  });
58949
59236
 
58950
59237
  // src/components/markdown.tsx
58951
- import { Fragment as Fragment6, memo, useMemo as useMemo14 } from "react";
59238
+ import { Fragment as Fragment7, memo, useMemo as useMemo14 } from "react";
58952
59239
  import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
58953
59240
  function ResolvedLink({ fallbackLabel, t, url }) {
58954
59241
  const fetched = useLinkTitle(url);
@@ -59480,7 +59767,7 @@ var init_markdown = __esm({
59480
59767
  const cellWidth = /* @__PURE__ */ __name((raw) => stringWidth(stripInlineMarkup(raw)), "cellWidth");
59481
59768
  const widths = rows[0].map((_, ci) => Math.max(...rows.map((r) => cellWidth(r[ci] ?? ""))));
59482
59769
  const sep2 = widths.map((w) => "\u2500".repeat(Math.max(1, w))).join(" ");
59483
- return /* @__PURE__ */ jsx34(Box_default, { flexDirection: "column", paddingLeft: 2, children: rows.map((row, ri) => /* @__PURE__ */ jsxs21(Fragment6, { children: [
59770
+ return /* @__PURE__ */ jsx34(Box_default, { flexDirection: "column", paddingLeft: 2, children: rows.map((row, ri) => /* @__PURE__ */ jsxs21(Fragment7, { children: [
59484
59771
  /* @__PURE__ */ jsx34(Box_default, { children: widths.map((w, ci) => /* @__PURE__ */ jsxs21(Text9, { bold: ri === 0, color: ri === 0 ? t.color.accent : void 0, children: [
59485
59772
  /* @__PURE__ */ jsx34(MdInline, { t, text: row[ci] ?? "" }),
59486
59773
  " ".repeat(Math.max(0, w - cellWidth(row[ci] ?? ""))),
@@ -59521,7 +59808,7 @@ var init_markdown = __esm({
59521
59808
  });
59522
59809
 
59523
59810
  // src/components/streamingMarkdown.tsx
59524
- import { memo as memo2, useRef as useRef17 } from "react";
59811
+ import { memo as memo2, useRef as useRef18 } from "react";
59525
59812
  import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
59526
59813
  var fenceOpenAt, findStableBoundary, StreamingMd;
59527
59814
  var init_streamingMarkdown = __esm({
@@ -59584,7 +59871,7 @@ var init_streamingMarkdown = __esm({
59584
59871
  return -1;
59585
59872
  }, "findStableBoundary");
59586
59873
  StreamingMd = memo2(/* @__PURE__ */ __name(function StreamingMd2({ compact, t, text }) {
59587
- const stablePrefixRef = useRef17("");
59874
+ const stablePrefixRef = useRef18("");
59588
59875
  if (!text.startsWith(stablePrefixRef.current)) {
59589
59876
  stablePrefixRef.current = "";
59590
59877
  }
@@ -59611,7 +59898,7 @@ var init_streamingMarkdown = __esm({
59611
59898
  // src/components/thinking.tsx
59612
59899
  import { memo as memo3, useEffect as useEffect23, useMemo as useMemo15, useState as useState26 } from "react";
59613
59900
  import spinners from "unicode-animations";
59614
- import { Fragment as Fragment7, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
59901
+ import { Fragment as Fragment8, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
59615
59902
  import { createElement } from "react";
59616
59903
  function TreeRow({
59617
59904
  branch,
@@ -59870,7 +60157,7 @@ function SubagentAccordion({
59870
60157
  {
59871
60158
  branch: index === item.tools.length - 1 ? "last" : "mid",
59872
60159
  color: t.color.text,
59873
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60160
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
59874
60161
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u25CF " }),
59875
60162
  line
59876
60163
  ] }),
@@ -60143,7 +60430,7 @@ var init_thinking = __esm({
60143
60430
  color: t.color.muted,
60144
60431
  dimColor: true,
60145
60432
  key: `tr-${i}`,
60146
- content: groups.length ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
60433
+ content: groups.length ? /* @__PURE__ */ jsxs23(Fragment8, { children: [
60147
60434
  /* @__PURE__ */ jsx36(Spinner3, { color: t.color.accent, variant: "think" }),
60148
60435
  " ",
60149
60436
  line
@@ -60160,7 +60447,7 @@ var init_thinking = __esm({
60160
60447
  key: tool.id,
60161
60448
  label,
60162
60449
  details: [],
60163
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60450
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60164
60451
  /* @__PURE__ */ jsx36(Spinner3, { color: t.color.accent, variant: "tool" }),
60165
60452
  " ",
60166
60453
  label,
@@ -60188,7 +60475,7 @@ var init_thinking = __esm({
60188
60475
  const inlineDelegateKey = hasSubagents && delegateGroups.length === 1 ? delegateGroups[0].key : null;
60189
60476
  const toolLabel = /* @__PURE__ */ __name((group) => {
60190
60477
  const { duration, label } = splitToolDuration(String(group.content));
60191
- return duration ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
60478
+ return duration ? /* @__PURE__ */ jsxs23(Fragment8, { children: [
60192
60479
  label,
60193
60480
  /* @__PURE__ */ jsx36(Text9, { color: t.color.statusFg, dim: true, children: duration })
60194
60481
  ] }) : group.content;
@@ -60300,7 +60587,7 @@ var init_thinking = __esm({
60300
60587
  {
60301
60588
  branch,
60302
60589
  color: group.color,
60303
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60590
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60304
60591
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u25CF " }),
60305
60592
  toolLabel(group)
60306
60593
  ] }),
@@ -60403,7 +60690,7 @@ var init_thinking = __esm({
60403
60690
  {
60404
60691
  branch: "last",
60405
60692
  color: t.color.statusFg,
60406
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60693
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60407
60694
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u03A3 " }),
60408
60695
  totalTokensLabel
60409
60696
  ] }),
@@ -60706,7 +60993,7 @@ var init_queuedMessages = __esm({
60706
60993
  // src/components/streamingAssistant.tsx
60707
60994
  import { useStore as useStore8 } from "@nanostores/react";
60708
60995
  import { memo as memo6 } from "react";
60709
- import { Fragment as Fragment8, jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
60996
+ import { Fragment as Fragment9, jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
60710
60997
  var groupedSegments, StreamingAssistant, LiveTodoPanel;
60711
60998
  var init_streamingAssistant = __esm({
60712
60999
  "src/components/streamingAssistant.tsx"() {
@@ -60734,7 +61021,7 @@ var init_streamingAssistant = __esm({
60734
61021
  if (!progress.showProgressArea && !showStreamingArea && !activeTools.length) {
60735
61022
  return null;
60736
61023
  }
60737
- return /* @__PURE__ */ jsxs27(Fragment8, { children: [
61024
+ return /* @__PURE__ */ jsxs27(Fragment9, { children: [
60738
61025
  groupedSegments(streamSegments).map((msg, i) => /* @__PURE__ */ jsx40(
60739
61026
  MessageLine,
60740
61027
  {
@@ -60803,8 +61090,8 @@ var init_streamingAssistant = __esm({
60803
61090
 
60804
61091
  // src/components/appLayout.tsx
60805
61092
  import { useStore as useStore9 } from "@nanostores/react";
60806
- import { Fragment as Fragment9, memo as memo7, useMemo as useMemo16, useRef as useRef18 } from "react";
60807
- import { Fragment as Fragment10, jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
61093
+ import { Fragment as Fragment10, memo as memo7, useMemo as useMemo16, useRef as useRef19 } from "react";
61094
+ import { Fragment as Fragment11, jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
60808
61095
  var PromptPrefix, TranscriptPane, ComposerPane, AgentsOverlayPane, StatusRulePane, AppLayout;
60809
61096
  var init_appLayout = __esm({
60810
61097
  "src/components/appLayout.tsx"() {
@@ -60860,7 +61147,7 @@ var init_appLayout = __esm({
60860
61147
  () => transcript.historyItems.findIndex((m) => m.role === "user"),
60861
61148
  [transcript.historyItems]
60862
61149
  );
60863
- return /* @__PURE__ */ jsxs28(Fragment10, { children: [
61150
+ return /* @__PURE__ */ jsxs28(Fragment11, { children: [
60864
61151
  /* @__PURE__ */ jsx41(
60865
61152
  ScrollBox_default,
60866
61153
  {
@@ -60934,7 +61221,7 @@ var init_appLayout = __esm({
60934
61221
  const promptBlank = " ".repeat(promptWidth);
60935
61222
  const inputColumns = stableComposerColumns(composer.cols, promptWidth);
60936
61223
  const inputHeight = inputVisualHeight(composer.input, inputColumns);
60937
- const inputMouseRef = useRef18(null);
61224
+ const inputMouseRef = useRef19(null);
60938
61225
  const captureInputDrag = /* @__PURE__ */ __name((e) => {
60939
61226
  if (e.button !== 0) {
60940
61227
  return;
@@ -61004,7 +61291,7 @@ var init_appLayout = __esm({
61004
61291
  }
61005
61292
  ),
61006
61293
  composer.input === "?" && !composer.inputBuf.length && /* @__PURE__ */ jsx41(HelpHint, { t: ui.theme }),
61007
- !isBlocked && /* @__PURE__ */ jsxs28(Fragment10, { children: [
61294
+ !isBlocked && /* @__PURE__ */ jsxs28(Fragment11, { children: [
61008
61295
  composer.inputBuf.map((line, i) => /* @__PURE__ */ jsxs28(Box_default, { children: [
61009
61296
  /* @__PURE__ */ jsx41(Box_default, { width: promptWidth, children: i === 0 ? /* @__PURE__ */ jsx41(PromptPrefix, { color: ui.theme.color.muted, promptText, width: promptWidth }) : /* @__PURE__ */ jsx41(Text9, { color: ui.theme.color.muted, children: promptBlank }) }),
61010
61297
  /* @__PURE__ */ jsx41(Text9, { color: ui.theme.color.composeText, children: line || " " })
@@ -61110,11 +61397,11 @@ var init_appLayout = __esm({
61110
61397
  }) {
61111
61398
  const overlay = useStore9($overlayState);
61112
61399
  const ui = useStore9($uiState);
61113
- const Shell = INLINE_MODE ? Fragment9 : AlternateScreen;
61400
+ const Shell = INLINE_MODE ? Fragment10 : AlternateScreen;
61114
61401
  const shellProps = INLINE_MODE ? {} : { mouseTracking };
61115
61402
  return /* @__PURE__ */ jsx41(Shell, { ...shellProps, children: /* @__PURE__ */ jsxs28(Box_default, { flexDirection: "column", flexGrow: 1, children: [
61116
61403
  /* @__PURE__ */ jsx41(Box_default, { flexDirection: "row", flexGrow: 1, children: overlay.agents ? /* @__PURE__ */ jsx41(PerfPane, { id: "agents", children: /* @__PURE__ */ jsx41(AgentsOverlayPane, {}) }) : /* @__PURE__ */ jsx41(PerfPane, { id: "transcript", children: /* @__PURE__ */ jsx41(TranscriptPane, { actions, composer, progress, transcript }) }) }),
61117
- !overlay.agents && /* @__PURE__ */ jsxs28(Fragment10, { children: [
61404
+ !overlay.agents && /* @__PURE__ */ jsxs28(Fragment11, { children: [
61118
61405
  /* @__PURE__ */ jsx41(PerfPane, { id: "prompt", children: /* @__PURE__ */ jsx41(
61119
61406
  PromptZone,
61120
61407
  {