@charzhu/openjaw-agent 0.2.1 → 0.2.3

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 {
@@ -19276,6 +19289,16 @@ var init_registry = __esm({
19276
19289
  this.registerTool(tool);
19277
19290
  }
19278
19291
  }
19292
+ /**
19293
+ * Remove a tool by name. Returns true if a tool was removed.
19294
+ *
19295
+ * Used to drop stale MCP tools when a server reconnects, is denied,
19296
+ * or is removed so the LLM does not try to call a tool whose backing
19297
+ * connection no longer exists.
19298
+ */
19299
+ unregisterTool(name) {
19300
+ return this.tools.delete(name);
19301
+ }
19279
19302
  getTool(name) {
19280
19303
  return this.tools.get(name);
19281
19304
  }
@@ -47309,6 +47332,19 @@ function registerRpcHandlers(options) {
47309
47332
  let currentRun = null;
47310
47333
  const pendingResponders = /* @__PURE__ */ new Map();
47311
47334
  const promptCollector = createPromptCollector(bus, () => agentLoop.sessionId);
47335
+ const OAUTH_FLOW_TTL_MS = 15 * 60 * 1e3;
47336
+ const oauthFlows = /* @__PURE__ */ new Map();
47337
+ const cleanupOAuthFlow = /* @__PURE__ */ __name((flowId, abort) => {
47338
+ const entry = oauthFlows.get(flowId);
47339
+ if (!entry) {
47340
+ return;
47341
+ }
47342
+ clearTimeout(entry.timer);
47343
+ if (abort && !entry.controller.signal.aborted) {
47344
+ entry.controller.abort();
47345
+ }
47346
+ oauthFlows.delete(flowId);
47347
+ }, "cleanupOAuthFlow");
47312
47348
  const onBridgeEvent = /* @__PURE__ */ __name((rawEvent) => {
47313
47349
  const source = inferBridgeSource(rawEvent);
47314
47350
  const user = bridgeUser(rawEvent, source);
@@ -47855,6 +47891,77 @@ ${helpMessage}` : field.label;
47855
47891
  }
47856
47892
  return { disconnected: true, slug };
47857
47893
  });
47894
+ bus.registerRpc("model.oauth_start", async (params) => {
47895
+ const slug = String(params.slug ?? "").trim();
47896
+ if (!PROVIDERS2.includes(slug)) {
47897
+ throw new Error(`unknown provider: ${slug}`);
47898
+ }
47899
+ const auth = PROVIDER_AUTH[slug];
47900
+ if (auth.auth_type !== "oauth") {
47901
+ throw new Error(`${PROVIDER_LABELS[slug]} does not use OAuth (auth_type=${auth.auth_type})`);
47902
+ }
47903
+ const clientId = resolveCopilotClientId();
47904
+ if (!clientId) {
47905
+ throw new Error(
47906
+ "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."
47907
+ );
47908
+ }
47909
+ const enterpriseUrl = (() => {
47910
+ const raw = params["enterprise_url"];
47911
+ if (typeof raw === "string" && raw.trim()) {
47912
+ return raw.trim();
47913
+ }
47914
+ return agentConfig.llm.copilot_enterprise_url;
47915
+ })();
47916
+ const flow = await startCopilotDeviceFlow(clientId, enterpriseUrl);
47917
+ const flowId = randomUUID13();
47918
+ const controller = new AbortController();
47919
+ const timer = setTimeout(() => {
47920
+ cleanupOAuthFlow(flowId, true);
47921
+ }, OAUTH_FLOW_TTL_MS);
47922
+ if (typeof timer.unref === "function") {
47923
+ timer.unref();
47924
+ }
47925
+ oauthFlows.set(flowId, { controller, flow, slug, timer });
47926
+ return {
47927
+ flow_id: flowId,
47928
+ interval_seconds: flow.intervalSeconds,
47929
+ user_code: flow.userCode,
47930
+ verification_uri: flow.verificationUri
47931
+ };
47932
+ });
47933
+ bus.registerRpc("model.oauth_complete", async (params) => {
47934
+ const flowId = String(params["flow_id"] ?? "").trim();
47935
+ const entry = oauthFlows.get(flowId);
47936
+ if (!entry) {
47937
+ throw new Error("OAuth flow not found or already completed/expired");
47938
+ }
47939
+ try {
47940
+ await completeCopilotDeviceFlow(entry.flow, entry.controller.signal);
47941
+ } catch (err) {
47942
+ cleanupOAuthFlow(flowId, false);
47943
+ if (isOAuthAbortError(err) || entry.controller.signal.aborted) {
47944
+ return { aborted: true, slug: entry.slug };
47945
+ }
47946
+ throw err;
47947
+ }
47948
+ cleanupOAuthFlow(flowId, false);
47949
+ reconnectProviderContext(entry.slug);
47950
+ const live = await fetchLiveModels(entry.slug, agentConfig, agentLoop.model);
47951
+ return {
47952
+ provider: buildProviderOption(entry.slug, agentLoop.providerName, agentLoop.model, live.models, live.error),
47953
+ slug: entry.slug
47954
+ };
47955
+ });
47956
+ bus.registerRpc("model.oauth_cancel", (params) => {
47957
+ const flowId = String(params["flow_id"] ?? "").trim();
47958
+ const entry = oauthFlows.get(flowId);
47959
+ if (!entry) {
47960
+ return { cancelled: false, reason: "flow not found" };
47961
+ }
47962
+ cleanupOAuthFlow(flowId, true);
47963
+ return { cancelled: true, slug: entry.slug };
47964
+ });
47858
47965
  bus.registerRpc("commands.catalog", () => {
47859
47966
  const skillCount = toolRegistry.listTools().filter((t) => /^skill[:_-]/i.test(t.name)).length;
47860
47967
  return buildCommandsCatalog({ skillCount });
@@ -48461,12 +48568,14 @@ ${helpMessage}` : field.label;
48461
48568
  bus.registerRpc("delegation.status", () => ({ delegated: [], paused: false }));
48462
48569
  bus.registerRpc("delegation.pause", (params) => ({ paused: Boolean(params.paused) }));
48463
48570
  bus.registerRpc("subagent.interrupt", () => ({ ok: true }));
48571
+ syncMcpTools(mcpManager, toolRegistry);
48464
48572
  bus.emitEvent({
48465
48573
  payload: sessionInfoSnapshot(agentLoop, toolRegistry),
48466
48574
  session_id: agentLoop.sessionId,
48467
48575
  type: "session.info"
48468
48576
  });
48469
48577
  const onToolsChanged = /* @__PURE__ */ __name(() => {
48578
+ syncMcpTools(mcpManager, toolRegistry);
48470
48579
  bus.emitEvent({
48471
48580
  payload: sessionInfoSnapshot(agentLoop, toolRegistry),
48472
48581
  session_id: agentLoop.sessionId,
@@ -48478,14 +48587,18 @@ ${helpMessage}` : field.label;
48478
48587
  bridgeEmitter?.off("bridgeEvent", onBridgeEvent);
48479
48588
  mcpManager.off("tools-changed", onToolsChanged);
48480
48589
  pendingResponders.clear();
48590
+ for (const id of Array.from(oauthFlows.keys())) {
48591
+ cleanupOAuthFlow(id, true);
48592
+ }
48481
48593
  };
48482
48594
  }
48483
- var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot;
48595
+ var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot, MCP_TOOL_PREFIX, MAX_TOTAL_TOOLS, syncMcpTools;
48484
48596
  var init_rpcHandlers = __esm({
48485
48597
  "src/rpcHandlers.ts"() {
48486
48598
  "use strict";
48487
48599
  init_connect();
48488
48600
  init_config();
48601
+ init_copilot_auth();
48489
48602
  init_context_manager();
48490
48603
  init_eventBridge();
48491
48604
  init_fork();
@@ -48708,6 +48821,23 @@ ${raw}`;
48708
48821
  tools: { [agentLoop.providerName]: toolRegistry.listTools().map((t) => t.name) },
48709
48822
  usage: usageSnapshot(agentLoop)
48710
48823
  }), "sessionInfoSnapshot");
48824
+ MCP_TOOL_PREFIX = "mcp__";
48825
+ MAX_TOTAL_TOOLS = 200;
48826
+ syncMcpTools = /* @__PURE__ */ __name((mcpManager, toolRegistry) => {
48827
+ let removed = 0;
48828
+ for (const tool of toolRegistry.listTools()) {
48829
+ if (tool.name.startsWith(MCP_TOOL_PREFIX)) {
48830
+ if (toolRegistry.unregisterTool(tool.name)) removed++;
48831
+ }
48832
+ }
48833
+ const nonMcpCount = toolRegistry.listTools().length;
48834
+ const budget = Math.max(0, MAX_TOTAL_TOOLS - nonMcpCount);
48835
+ const mcpTools = mcpManager.getTools(budget);
48836
+ for (const tool of mcpTools) {
48837
+ toolRegistry.registerTool(tool);
48838
+ }
48839
+ return { added: mcpTools.length, removed };
48840
+ }, "syncMcpTools");
48711
48841
  __name(registerRpcHandlers, "registerRpcHandlers");
48712
48842
  }
48713
48843
  });
@@ -56345,8 +56475,8 @@ var init_providers2 = __esm({
56345
56475
  });
56346
56476
 
56347
56477
  // 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";
56478
+ import { useEffect as useEffect17, useMemo as useMemo12, useRef as useRef17, useState as useState19 } from "react";
56479
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs13 } from "react/jsx-runtime";
56350
56480
  function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t }) {
56351
56481
  const [providers, setProviders] = useState19([]);
56352
56482
  const [currentModel, setCurrentModel] = useState19("");
@@ -56359,10 +56489,19 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56359
56489
  const [keyInput, setKeyInput] = useState19("");
56360
56490
  const [keySaving, setKeySaving] = useState19(false);
56361
56491
  const [keyError, setKeyError] = useState19("");
56492
+ const [oauthStatus, setOauthStatus] = useState19("starting");
56493
+ const [oauthVerificationUri, setOauthVerificationUri] = useState19("");
56494
+ const [oauthUserCode, setOauthUserCode] = useState19("");
56495
+ const [oauthError, setOauthError] = useState19("");
56496
+ const oauthFlowIdRef = useRef17(null);
56497
+ const oauthProviderSlugRef = useRef17(null);
56498
+ const oauthAttemptRef = useRef17(0);
56499
+ const mountedRef = useRef17(true);
56362
56500
  const { stdout } = useStdout();
56363
56501
  const width = Math.max(MIN_WIDTH2, Math.min(MAX_WIDTH2, (stdout?.columns ?? 80) - 6));
56364
56502
  useEffect17(() => {
56365
56503
  gw.request("model.options", sessionId ? { session_id: sessionId } : {}).then((raw) => {
56504
+ if (!mountedRef.current) return;
56366
56505
  const r = asRpcResult(raw);
56367
56506
  if (!r) {
56368
56507
  setErr("invalid response: model.options");
@@ -56383,6 +56522,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56383
56522
  setErr("");
56384
56523
  setLoading(false);
56385
56524
  }).catch((e) => {
56525
+ if (!mountedRef.current) return;
56386
56526
  setErr(rpcErrorMessage(e));
56387
56527
  setLoading(false);
56388
56528
  });
@@ -56390,7 +56530,121 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56390
56530
  const provider = providers[providerIdx];
56391
56531
  const models = provider?.models ?? [];
56392
56532
  const names = useMemo12(() => providerDisplayNames(providers), [providers]);
56533
+ useEffect17(() => {
56534
+ mountedRef.current = true;
56535
+ return () => {
56536
+ mountedRef.current = false;
56537
+ const flowId = oauthFlowIdRef.current;
56538
+ if (flowId) {
56539
+ gw.request("model.oauth_cancel", { flow_id: flowId }).catch(() => {
56540
+ });
56541
+ oauthFlowIdRef.current = null;
56542
+ }
56543
+ };
56544
+ }, [gw]);
56545
+ const refreshProviders = /* @__PURE__ */ __name(() => gw.request("model.options", sessionId ? { session_id: sessionId } : {}).then((raw) => {
56546
+ if (!mountedRef.current) return;
56547
+ const r = asRpcResult(raw);
56548
+ if (!r) {
56549
+ return;
56550
+ }
56551
+ setProviders(r.providers ?? []);
56552
+ setCurrentModel(String(r.model ?? ""));
56553
+ }).catch(() => {
56554
+ }), "refreshProviders");
56555
+ const cancelActiveOAuth = /* @__PURE__ */ __name(() => {
56556
+ oauthAttemptRef.current += 1;
56557
+ const flowId = oauthFlowIdRef.current;
56558
+ if (!flowId) {
56559
+ return;
56560
+ }
56561
+ oauthFlowIdRef.current = null;
56562
+ gw.request("model.oauth_cancel", { flow_id: flowId }).catch(() => {
56563
+ });
56564
+ }, "cancelActiveOAuth");
56565
+ const startOAuthFlow = /* @__PURE__ */ __name((slug) => {
56566
+ cancelActiveOAuth();
56567
+ const attempt = ++oauthAttemptRef.current;
56568
+ oauthProviderSlugRef.current = slug;
56569
+ setStage("oauth");
56570
+ setOauthStatus("starting");
56571
+ setOauthVerificationUri("");
56572
+ setOauthUserCode("");
56573
+ setOauthError("");
56574
+ const isCurrent = /* @__PURE__ */ __name(() => mountedRef.current && oauthAttemptRef.current === attempt, "isCurrent");
56575
+ gw.request("model.oauth_start", {
56576
+ slug,
56577
+ ...sessionId ? { session_id: sessionId } : {}
56578
+ }).then((raw) => {
56579
+ const r = asRpcResult(raw);
56580
+ if (!isCurrent()) {
56581
+ if (r?.flow_id) {
56582
+ gw.request("model.oauth_cancel", { flow_id: r.flow_id }).catch(() => {
56583
+ });
56584
+ }
56585
+ return;
56586
+ }
56587
+ if (!r?.flow_id) {
56588
+ setOauthStatus("error");
56589
+ setOauthError("invalid response from model.oauth_start");
56590
+ return;
56591
+ }
56592
+ oauthFlowIdRef.current = r.flow_id;
56593
+ setOauthVerificationUri(r.verification_uri);
56594
+ setOauthUserCode(r.user_code);
56595
+ setOauthStatus("waiting");
56596
+ gw.request("model.oauth_complete", {
56597
+ flow_id: r.flow_id,
56598
+ ...sessionId ? { session_id: sessionId } : {}
56599
+ }).then((rawComplete) => {
56600
+ if (!isCurrent()) return;
56601
+ if (oauthFlowIdRef.current !== r.flow_id) return;
56602
+ oauthFlowIdRef.current = null;
56603
+ const rc = asRpcResult(rawComplete);
56604
+ if (!rc) {
56605
+ setOauthStatus("error");
56606
+ setOauthError("invalid response from model.oauth_complete");
56607
+ return;
56608
+ }
56609
+ if (rc.aborted) {
56610
+ setStage("provider");
56611
+ return;
56612
+ }
56613
+ if (rc.provider) {
56614
+ setProviders(
56615
+ (prev) => prev.map((p) => p.slug === rc.provider.slug ? rc.provider : p)
56616
+ );
56617
+ } else {
56618
+ refreshProviders();
56619
+ }
56620
+ if (mode === "connect") {
56621
+ onCancel();
56622
+ return;
56623
+ }
56624
+ setStage("model");
56625
+ setModelIdx(0);
56626
+ }).catch((e) => {
56627
+ if (!isCurrent()) return;
56628
+ if (oauthFlowIdRef.current !== r.flow_id) return;
56629
+ oauthFlowIdRef.current = null;
56630
+ setOauthStatus("error");
56631
+ setOauthError(rpcErrorMessage(e));
56632
+ });
56633
+ }).catch((e) => {
56634
+ if (!isCurrent()) return;
56635
+ setOauthStatus("error");
56636
+ setOauthError(rpcErrorMessage(e));
56637
+ });
56638
+ }, "startOAuthFlow");
56393
56639
  const back = /* @__PURE__ */ __name(() => {
56640
+ if (stage === "oauth") {
56641
+ cancelActiveOAuth();
56642
+ setStage("provider");
56643
+ setOauthVerificationUri("");
56644
+ setOauthUserCode("");
56645
+ setOauthError("");
56646
+ return;
56647
+ }
56394
56648
  if (stage === "model" || stage === "key" || stage === "disconnect") {
56395
56649
  setStage("provider");
56396
56650
  setModelIdx(0);
@@ -56401,8 +56655,30 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56401
56655
  }
56402
56656
  onCancel();
56403
56657
  }, "back");
56404
- useOverlayKeys({ onBack: back, onClose: onCancel });
56658
+ const close = /* @__PURE__ */ __name(() => {
56659
+ if (stage === "oauth") {
56660
+ cancelActiveOAuth();
56661
+ }
56662
+ onCancel();
56663
+ }, "close");
56664
+ useOverlayKeys({ onBack: back, onClose: close });
56405
56665
  use_input_default((ch, key) => {
56666
+ if (stage === "oauth") {
56667
+ if (key.escape) {
56668
+ back();
56669
+ return;
56670
+ }
56671
+ if (key.return && oauthStatus === "error") {
56672
+ const slug = oauthProviderSlugRef.current;
56673
+ if (slug) {
56674
+ startOAuthFlow(slug);
56675
+ } else {
56676
+ back();
56677
+ }
56678
+ return;
56679
+ }
56680
+ return;
56681
+ }
56406
56682
  if (stage === "key") {
56407
56683
  if (keySaving) {
56408
56684
  return;
@@ -56508,6 +56784,11 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56508
56784
  setStage("key");
56509
56785
  setKeyInput("");
56510
56786
  setKeyError("");
56787
+ return;
56788
+ }
56789
+ if (provider.auth_type === "oauth") {
56790
+ startOAuthFlow(provider.slug);
56791
+ return;
56511
56792
  }
56512
56793
  return;
56513
56794
  }
@@ -56554,6 +56835,41 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56554
56835
  /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Esc/q cancel" })
56555
56836
  ] });
56556
56837
  }
56838
+ if (stage === "oauth") {
56839
+ const slug = oauthProviderSlugRef.current;
56840
+ const providerName = providers.find((p) => p.slug === slug)?.name ?? "GitHub Copilot";
56841
+ return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
56842
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
56843
+ "Sign in to ",
56844
+ providerName
56845
+ ] }),
56846
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56847
+ oauthStatus === "starting" ? /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "requesting device code\u2026" }) : oauthStatus === "error" ? /* @__PURE__ */ jsxs13(Fragment5, { children: [
56848
+ /* @__PURE__ */ jsxs13(Text9, { color: t.color.label, wrap: "truncate-end", children: [
56849
+ "error: ",
56850
+ oauthError || "failed to start GitHub sign-in"
56851
+ ] }),
56852
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56853
+ /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Enter retry \xB7 Esc back" })
56854
+ ] }) : /* @__PURE__ */ jsxs13(Fragment5, { children: [
56855
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "1. Open this URL in your browser:" }),
56856
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
56857
+ " ",
56858
+ oauthVerificationUri
56859
+ ] }),
56860
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56861
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "2. Enter this code on the GitHub page:" }),
56862
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
56863
+ " ",
56864
+ oauthUserCode
56865
+ ] }),
56866
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56867
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "waiting for authorization\u2026" }),
56868
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
56869
+ /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Esc cancel" })
56870
+ ] })
56871
+ ] });
56872
+ }
56557
56873
  if (stage === "key" && provider) {
56558
56874
  const masked = keyInput ? "\u2022".repeat(Math.min(keyInput.length, 40)) : "";
56559
56875
  return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
@@ -57507,7 +57823,7 @@ var init_appOverlays = __esm({
57507
57823
 
57508
57824
  // src/components/branding.tsx
57509
57825
  import { useEffect as useEffect20, useState as useState24 } from "react";
57510
- import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
57826
+ import { Fragment as Fragment6, jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
57511
57827
  function useColumns() {
57512
57828
  const { stdout } = useStdout();
57513
57829
  const [cols, setCols] = useState24(stdout?.columns ?? 80);
@@ -58948,7 +59264,7 @@ var init_syntax = __esm({
58948
59264
  });
58949
59265
 
58950
59266
  // src/components/markdown.tsx
58951
- import { Fragment as Fragment6, memo, useMemo as useMemo14 } from "react";
59267
+ import { Fragment as Fragment7, memo, useMemo as useMemo14 } from "react";
58952
59268
  import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
58953
59269
  function ResolvedLink({ fallbackLabel, t, url }) {
58954
59270
  const fetched = useLinkTitle(url);
@@ -59480,7 +59796,7 @@ var init_markdown = __esm({
59480
59796
  const cellWidth = /* @__PURE__ */ __name((raw) => stringWidth(stripInlineMarkup(raw)), "cellWidth");
59481
59797
  const widths = rows[0].map((_, ci) => Math.max(...rows.map((r) => cellWidth(r[ci] ?? ""))));
59482
59798
  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: [
59799
+ return /* @__PURE__ */ jsx34(Box_default, { flexDirection: "column", paddingLeft: 2, children: rows.map((row, ri) => /* @__PURE__ */ jsxs21(Fragment7, { children: [
59484
59800
  /* @__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
59801
  /* @__PURE__ */ jsx34(MdInline, { t, text: row[ci] ?? "" }),
59486
59802
  " ".repeat(Math.max(0, w - cellWidth(row[ci] ?? ""))),
@@ -59521,7 +59837,7 @@ var init_markdown = __esm({
59521
59837
  });
59522
59838
 
59523
59839
  // src/components/streamingMarkdown.tsx
59524
- import { memo as memo2, useRef as useRef17 } from "react";
59840
+ import { memo as memo2, useRef as useRef18 } from "react";
59525
59841
  import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
59526
59842
  var fenceOpenAt, findStableBoundary, StreamingMd;
59527
59843
  var init_streamingMarkdown = __esm({
@@ -59584,7 +59900,7 @@ var init_streamingMarkdown = __esm({
59584
59900
  return -1;
59585
59901
  }, "findStableBoundary");
59586
59902
  StreamingMd = memo2(/* @__PURE__ */ __name(function StreamingMd2({ compact, t, text }) {
59587
- const stablePrefixRef = useRef17("");
59903
+ const stablePrefixRef = useRef18("");
59588
59904
  if (!text.startsWith(stablePrefixRef.current)) {
59589
59905
  stablePrefixRef.current = "";
59590
59906
  }
@@ -59611,7 +59927,7 @@ var init_streamingMarkdown = __esm({
59611
59927
  // src/components/thinking.tsx
59612
59928
  import { memo as memo3, useEffect as useEffect23, useMemo as useMemo15, useState as useState26 } from "react";
59613
59929
  import spinners from "unicode-animations";
59614
- import { Fragment as Fragment7, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
59930
+ import { Fragment as Fragment8, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
59615
59931
  import { createElement } from "react";
59616
59932
  function TreeRow({
59617
59933
  branch,
@@ -59870,7 +60186,7 @@ function SubagentAccordion({
59870
60186
  {
59871
60187
  branch: index === item.tools.length - 1 ? "last" : "mid",
59872
60188
  color: t.color.text,
59873
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60189
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
59874
60190
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u25CF " }),
59875
60191
  line
59876
60192
  ] }),
@@ -60143,7 +60459,7 @@ var init_thinking = __esm({
60143
60459
  color: t.color.muted,
60144
60460
  dimColor: true,
60145
60461
  key: `tr-${i}`,
60146
- content: groups.length ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
60462
+ content: groups.length ? /* @__PURE__ */ jsxs23(Fragment8, { children: [
60147
60463
  /* @__PURE__ */ jsx36(Spinner3, { color: t.color.accent, variant: "think" }),
60148
60464
  " ",
60149
60465
  line
@@ -60160,7 +60476,7 @@ var init_thinking = __esm({
60160
60476
  key: tool.id,
60161
60477
  label,
60162
60478
  details: [],
60163
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60479
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60164
60480
  /* @__PURE__ */ jsx36(Spinner3, { color: t.color.accent, variant: "tool" }),
60165
60481
  " ",
60166
60482
  label,
@@ -60188,7 +60504,7 @@ var init_thinking = __esm({
60188
60504
  const inlineDelegateKey = hasSubagents && delegateGroups.length === 1 ? delegateGroups[0].key : null;
60189
60505
  const toolLabel = /* @__PURE__ */ __name((group) => {
60190
60506
  const { duration, label } = splitToolDuration(String(group.content));
60191
- return duration ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
60507
+ return duration ? /* @__PURE__ */ jsxs23(Fragment8, { children: [
60192
60508
  label,
60193
60509
  /* @__PURE__ */ jsx36(Text9, { color: t.color.statusFg, dim: true, children: duration })
60194
60510
  ] }) : group.content;
@@ -60300,7 +60616,7 @@ var init_thinking = __esm({
60300
60616
  {
60301
60617
  branch,
60302
60618
  color: group.color,
60303
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60619
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60304
60620
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u25CF " }),
60305
60621
  toolLabel(group)
60306
60622
  ] }),
@@ -60403,7 +60719,7 @@ var init_thinking = __esm({
60403
60719
  {
60404
60720
  branch: "last",
60405
60721
  color: t.color.statusFg,
60406
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60722
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60407
60723
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u03A3 " }),
60408
60724
  totalTokensLabel
60409
60725
  ] }),
@@ -60706,7 +61022,7 @@ var init_queuedMessages = __esm({
60706
61022
  // src/components/streamingAssistant.tsx
60707
61023
  import { useStore as useStore8 } from "@nanostores/react";
60708
61024
  import { memo as memo6 } from "react";
60709
- import { Fragment as Fragment8, jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
61025
+ import { Fragment as Fragment9, jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
60710
61026
  var groupedSegments, StreamingAssistant, LiveTodoPanel;
60711
61027
  var init_streamingAssistant = __esm({
60712
61028
  "src/components/streamingAssistant.tsx"() {
@@ -60734,7 +61050,7 @@ var init_streamingAssistant = __esm({
60734
61050
  if (!progress.showProgressArea && !showStreamingArea && !activeTools.length) {
60735
61051
  return null;
60736
61052
  }
60737
- return /* @__PURE__ */ jsxs27(Fragment8, { children: [
61053
+ return /* @__PURE__ */ jsxs27(Fragment9, { children: [
60738
61054
  groupedSegments(streamSegments).map((msg, i) => /* @__PURE__ */ jsx40(
60739
61055
  MessageLine,
60740
61056
  {
@@ -60803,8 +61119,8 @@ var init_streamingAssistant = __esm({
60803
61119
 
60804
61120
  // src/components/appLayout.tsx
60805
61121
  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";
61122
+ import { Fragment as Fragment10, memo as memo7, useMemo as useMemo16, useRef as useRef19 } from "react";
61123
+ import { Fragment as Fragment11, jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
60808
61124
  var PromptPrefix, TranscriptPane, ComposerPane, AgentsOverlayPane, StatusRulePane, AppLayout;
60809
61125
  var init_appLayout = __esm({
60810
61126
  "src/components/appLayout.tsx"() {
@@ -60860,7 +61176,7 @@ var init_appLayout = __esm({
60860
61176
  () => transcript.historyItems.findIndex((m) => m.role === "user"),
60861
61177
  [transcript.historyItems]
60862
61178
  );
60863
- return /* @__PURE__ */ jsxs28(Fragment10, { children: [
61179
+ return /* @__PURE__ */ jsxs28(Fragment11, { children: [
60864
61180
  /* @__PURE__ */ jsx41(
60865
61181
  ScrollBox_default,
60866
61182
  {
@@ -60934,7 +61250,7 @@ var init_appLayout = __esm({
60934
61250
  const promptBlank = " ".repeat(promptWidth);
60935
61251
  const inputColumns = stableComposerColumns(composer.cols, promptWidth);
60936
61252
  const inputHeight = inputVisualHeight(composer.input, inputColumns);
60937
- const inputMouseRef = useRef18(null);
61253
+ const inputMouseRef = useRef19(null);
60938
61254
  const captureInputDrag = /* @__PURE__ */ __name((e) => {
60939
61255
  if (e.button !== 0) {
60940
61256
  return;
@@ -61004,7 +61320,7 @@ var init_appLayout = __esm({
61004
61320
  }
61005
61321
  ),
61006
61322
  composer.input === "?" && !composer.inputBuf.length && /* @__PURE__ */ jsx41(HelpHint, { t: ui.theme }),
61007
- !isBlocked && /* @__PURE__ */ jsxs28(Fragment10, { children: [
61323
+ !isBlocked && /* @__PURE__ */ jsxs28(Fragment11, { children: [
61008
61324
  composer.inputBuf.map((line, i) => /* @__PURE__ */ jsxs28(Box_default, { children: [
61009
61325
  /* @__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
61326
  /* @__PURE__ */ jsx41(Text9, { color: ui.theme.color.composeText, children: line || " " })
@@ -61110,11 +61426,11 @@ var init_appLayout = __esm({
61110
61426
  }) {
61111
61427
  const overlay = useStore9($overlayState);
61112
61428
  const ui = useStore9($uiState);
61113
- const Shell = INLINE_MODE ? Fragment9 : AlternateScreen;
61429
+ const Shell = INLINE_MODE ? Fragment10 : AlternateScreen;
61114
61430
  const shellProps = INLINE_MODE ? {} : { mouseTracking };
61115
61431
  return /* @__PURE__ */ jsx41(Shell, { ...shellProps, children: /* @__PURE__ */ jsxs28(Box_default, { flexDirection: "column", flexGrow: 1, children: [
61116
61432
  /* @__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: [
61433
+ !overlay.agents && /* @__PURE__ */ jsxs28(Fragment11, { children: [
61118
61434
  /* @__PURE__ */ jsx41(PerfPane, { id: "prompt", children: /* @__PURE__ */ jsx41(
61119
61435
  PromptZone,
61120
61436
  {