@charzhu/openjaw-agent 0.2.0 → 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 {
@@ -8978,6 +8991,19 @@ import { DatabaseSync } from "node:sqlite";
8978
8991
  import { join as join12 } from "node:path";
8979
8992
  import { homedir as homedir9 } from "node:os";
8980
8993
  import { mkdirSync as mkdirSync8, existsSync as existsSync10 } from "node:fs";
8994
+ function hasFts5() {
8995
+ return _hasFts5;
8996
+ }
8997
+ function detectFts5(database) {
8998
+ const probe = `__fts5_probe_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
8999
+ try {
9000
+ database.exec(`CREATE VIRTUAL TABLE temp.${probe} USING fts5(x)`);
9001
+ database.exec(`DROP TABLE temp.${probe}`);
9002
+ return true;
9003
+ } catch {
9004
+ return false;
9005
+ }
9006
+ }
8981
9007
  function getMemoryDb() {
8982
9008
  if (!db) {
8983
9009
  if (!existsSync10(DB_DIR)) {
@@ -8987,12 +9013,12 @@ function getMemoryDb() {
8987
9013
  db.exec("PRAGMA journal_mode=WAL");
8988
9014
  db.exec("PRAGMA busy_timeout=5000");
8989
9015
  ensureSchema(db);
8990
- logger_default.debug("Memory database opened", { path: DB_PATH });
9016
+ logger_default.debug("Memory database opened", { path: DB_PATH, hasFts5: _hasFts5 });
8991
9017
  }
8992
9018
  return db;
8993
9019
  }
8994
- function ensureSchema(db2) {
8995
- db2.exec(`
9020
+ function ensureSchema(database) {
9021
+ database.exec(`
8996
9022
  CREATE TABLE IF NOT EXISTS memories (
8997
9023
  id INTEGER PRIMARY KEY AUTOINCREMENT,
8998
9024
  content TEXT NOT NULL,
@@ -9002,36 +9028,49 @@ function ensureSchema(db2) {
9002
9028
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
9003
9029
  )
9004
9030
  `);
9005
- db2.exec(`
9006
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
9007
- content,
9008
- content='memories',
9009
- content_rowid='id',
9010
- tokenize='unicode61'
9011
- )
9012
- `);
9013
- db2.exec(`
9014
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
9015
- INSERT INTO memories_fts(rowid, content) VALUES (new.id, new.content);
9016
- END
9017
- `);
9018
- db2.exec(`
9019
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
9020
- INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.id, old.content);
9021
- END
9022
- `);
9023
- db2.exec(`
9024
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
9025
- INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.id, old.content);
9026
- INSERT INTO memories_fts(rowid, content) VALUES (new.id, new.content);
9027
- END
9028
- `);
9031
+ _hasFts5 = detectFts5(database);
9032
+ if (_hasFts5) {
9033
+ database.exec(`
9034
+ CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
9035
+ content,
9036
+ content='memories',
9037
+ content_rowid='id',
9038
+ tokenize='unicode61'
9039
+ )
9040
+ `);
9041
+ database.exec(`
9042
+ CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
9043
+ INSERT INTO memories_fts(rowid, content) VALUES (new.id, new.content);
9044
+ END
9045
+ `);
9046
+ database.exec(`
9047
+ CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
9048
+ INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.id, old.content);
9049
+ END
9050
+ `);
9051
+ database.exec(`
9052
+ CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
9053
+ INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.id, old.content);
9054
+ INSERT INTO memories_fts(rowid, content) VALUES (new.id, new.content);
9055
+ END
9056
+ `);
9057
+ try {
9058
+ database.exec(`INSERT INTO memories_fts(memories_fts) VALUES('rebuild')`);
9059
+ } catch (err) {
9060
+ logger_default.warn("FTS5 rebuild failed (non-fatal)", { error: String(err) });
9061
+ }
9062
+ } else {
9063
+ database.exec(`DROP TRIGGER IF EXISTS memories_ai`);
9064
+ database.exec(`DROP TRIGGER IF EXISTS memories_ad`);
9065
+ database.exec(`DROP TRIGGER IF EXISTS memories_au`);
9066
+ logger_default.warn("FTS5 not available in this SQLite build \u2014 memory search will use LIKE-based fallback. For best performance, upgrade to Node 23.5+ (bundled SQLite includes FTS5 by default).");
9067
+ }
9029
9068
  try {
9030
- db2.exec("ALTER TABLE memories ADD COLUMN hrr_vector BLOB");
9069
+ database.exec("ALTER TABLE memories ADD COLUMN hrr_vector BLOB");
9031
9070
  } catch {
9032
9071
  }
9033
9072
  }
9034
- var DB_DIR, DB_PATH, db;
9073
+ var DB_DIR, DB_PATH, db, _hasFts5;
9035
9074
  var init_db = __esm({
9036
9075
  "../openjaw-mcp/dist/memory/db.js"() {
9037
9076
  "use strict";
@@ -9039,6 +9078,9 @@ var init_db = __esm({
9039
9078
  DB_DIR = join12(homedir9(), ".openjaw");
9040
9079
  DB_PATH = join12(DB_DIR, "memory.db");
9041
9080
  db = null;
9081
+ _hasFts5 = false;
9082
+ __name(hasFts5, "hasFts5");
9083
+ __name(detectFts5, "detectFts5");
9042
9084
  __name(getMemoryDb, "getMemoryDb");
9043
9085
  __name(ensureSchema, "ensureSchema");
9044
9086
  }
@@ -9135,11 +9177,15 @@ var init_hrr = __esm({
9135
9177
  });
9136
9178
 
9137
9179
  // ../openjaw-mcp/dist/memory/retrieval.js
9138
- var STOPWORDS, DEFAULT_WEIGHTS, HybridRetriever;
9180
+ function escapeLike(token) {
9181
+ return `%${token.replace(/[\\%_]/g, "\\$&")}%`;
9182
+ }
9183
+ var STOPWORDS, MAX_LIKE_TERMS, DEFAULT_WEIGHTS_FTS, DEFAULT_WEIGHTS_NO_FTS, HybridRetriever;
9139
9184
  var init_retrieval = __esm({
9140
9185
  "../openjaw-mcp/dist/memory/retrieval.js"() {
9141
9186
  "use strict";
9142
9187
  init_hrr();
9188
+ init_db();
9143
9189
  init_logger();
9144
9190
  STOPWORDS = /* @__PURE__ */ new Set([
9145
9191
  "a",
@@ -9201,23 +9247,30 @@ var init_retrieval = __esm({
9201
9247
  "should",
9202
9248
  "could"
9203
9249
  ]);
9204
- DEFAULT_WEIGHTS = {
9250
+ MAX_LIKE_TERMS = 16;
9251
+ __name(escapeLike, "escapeLike");
9252
+ DEFAULT_WEIGHTS_FTS = {
9205
9253
  fts: 0.4,
9206
9254
  jaccard: 0.3,
9207
9255
  hrr: 0.3
9208
9256
  };
9257
+ DEFAULT_WEIGHTS_NO_FTS = {
9258
+ fts: 0,
9259
+ jaccard: 0.5,
9260
+ hrr: 0.5
9261
+ };
9209
9262
  HybridRetriever = class {
9210
9263
  static {
9211
9264
  __name(this, "HybridRetriever");
9212
9265
  }
9213
9266
  db;
9214
9267
  weights;
9215
- constructor(db2, weights = DEFAULT_WEIGHTS) {
9268
+ constructor(db2, weights) {
9216
9269
  this.db = db2;
9217
- this.weights = weights;
9270
+ this.weights = weights ?? (hasFts5() ? DEFAULT_WEIGHTS_FTS : DEFAULT_WEIGHTS_NO_FTS);
9218
9271
  }
9219
9272
  /**
9220
- * Hybrid search: FTS5 candidate retrieval → Jaccard + HRR re-ranking.
9273
+ * Hybrid search: FTS5 (or LIKE fallback) candidate retrieval → Jaccard + HRR re-ranking.
9221
9274
  */
9222
9275
  search(query, limit = 10) {
9223
9276
  const queryTokens = query.toLowerCase().match(/[\w]+/g);
@@ -9228,27 +9281,47 @@ var init_retrieval = __esm({
9228
9281
  return [];
9229
9282
  const queryTokenSet = new Set(filtered);
9230
9283
  const candidateLimit = Math.max(limit * 5, 50);
9231
- const ftsQuery = filtered.join(" OR ");
9232
9284
  let hasHrrColumn = true;
9233
9285
  try {
9234
9286
  this.db.prepare("SELECT hrr_vector FROM memories LIMIT 0").all();
9235
9287
  } catch {
9236
9288
  hasHrrColumn = false;
9237
9289
  }
9238
- const selectCols = hasHrrColumn ? "m.id, m.content, m.source, m.created_at, m.hrr_vector, fts.rank" : "m.id, m.content, m.source, m.created_at, fts.rank";
9239
- let candidates;
9240
- try {
9241
- candidates = this.db.prepare(`
9242
- SELECT ${selectCols}
9243
- FROM memories_fts fts
9244
- JOIN memories m ON m.id = fts.rowid
9245
- WHERE memories_fts MATCH ?
9246
- ORDER BY fts.rank
9247
- LIMIT ?
9248
- `).all(ftsQuery, candidateLimit);
9249
- } catch (err) {
9250
- logger_default.warn("FTS5 search failed in hybrid retriever", { query: ftsQuery, error: String(err) });
9251
- return [];
9290
+ let candidates = [];
9291
+ if (hasFts5()) {
9292
+ const selectCols = hasHrrColumn ? "m.id, m.content, m.source, m.created_at, m.hrr_vector, fts.rank" : "m.id, m.content, m.source, m.created_at, fts.rank";
9293
+ const ftsQuery = filtered.join(" OR ");
9294
+ try {
9295
+ candidates = this.db.prepare(`
9296
+ SELECT ${selectCols}
9297
+ FROM memories_fts fts
9298
+ JOIN memories m ON m.id = fts.rowid
9299
+ WHERE memories_fts MATCH ?
9300
+ ORDER BY fts.rank
9301
+ LIMIT ?
9302
+ `).all(ftsQuery, candidateLimit);
9303
+ } catch (err) {
9304
+ logger_default.warn("FTS5 search failed in hybrid retriever", { query: ftsQuery, error: String(err) });
9305
+ return [];
9306
+ }
9307
+ } else {
9308
+ const terms = Array.from(new Set(filtered)).slice(0, MAX_LIKE_TERMS);
9309
+ const selectCols = hasHrrColumn ? "m.id, m.content, m.source, m.created_at, m.hrr_vector, 0 AS rank" : "m.id, m.content, m.source, m.created_at, 0 AS rank";
9310
+ const wheres = terms.map(() => `m.content LIKE ? ESCAPE '\\'`).join(" OR ");
9311
+ const params = terms.map(escapeLike);
9312
+ params.push(candidateLimit);
9313
+ try {
9314
+ candidates = this.db.prepare(`
9315
+ SELECT ${selectCols}
9316
+ FROM memories m
9317
+ WHERE ${wheres}
9318
+ ORDER BY m.created_at DESC, m.id DESC
9319
+ LIMIT ?
9320
+ `).all(...params);
9321
+ } catch (err) {
9322
+ logger_default.warn("LIKE-fallback search failed in hybrid retriever", { error: String(err) });
9323
+ return [];
9324
+ }
9252
9325
  }
9253
9326
  if (candidates.length === 0)
9254
9327
  return [];
@@ -19380,6 +19453,9 @@ import { homedir as homedir11 } from "node:os";
19380
19453
  function setMemoryPrefetchQuery(query) {
19381
19454
  currentQuery = query;
19382
19455
  }
19456
+ function escapeLike2(token) {
19457
+ return `%${token.replace(/[\\%_]/g, "\\$&")}%`;
19458
+ }
19383
19459
  function getMemorySection() {
19384
19460
  if (!currentQuery) return null;
19385
19461
  try {
@@ -19396,15 +19472,34 @@ function getMemorySection() {
19396
19472
  if (!tokens || tokens.length === 0) return null;
19397
19473
  const filtered = tokens.filter((t) => !STOPWORDS2.has(t));
19398
19474
  if (filtered.length === 0) return null;
19399
- const safeQuery = filtered.join(" OR ");
19400
- const rows = db2.prepare(`
19401
- SELECT m.content, m.source, m.created_at
19402
- FROM memories_fts fts
19403
- JOIN memories m ON m.id = fts.rowid
19404
- WHERE memories_fts MATCH ?
19405
- ORDER BY fts.rank
19406
- LIMIT ?
19407
- `).all(safeQuery, MAX_PREFETCH_RESULTS);
19475
+ let rows = [];
19476
+ const ftsQuery = filtered.join(" OR ");
19477
+ try {
19478
+ rows = db2.prepare(`
19479
+ SELECT m.content, m.source, m.created_at
19480
+ FROM memories_fts fts
19481
+ JOIN memories m ON m.id = fts.rowid
19482
+ WHERE memories_fts MATCH ?
19483
+ ORDER BY fts.rank
19484
+ LIMIT ?
19485
+ `).all(ftsQuery, MAX_PREFETCH_RESULTS);
19486
+ } catch {
19487
+ const terms = Array.from(new Set(filtered)).slice(0, MAX_LIKE_TERMS2);
19488
+ const wheres = terms.map(() => `content LIKE ? ESCAPE '\\'`).join(" OR ");
19489
+ const params = terms.map(escapeLike2);
19490
+ params.push(MAX_PREFETCH_RESULTS);
19491
+ try {
19492
+ rows = db2.prepare(`
19493
+ SELECT content, source, created_at
19494
+ FROM memories
19495
+ WHERE ${wheres}
19496
+ ORDER BY created_at DESC, id DESC
19497
+ LIMIT ?
19498
+ `).all(...params);
19499
+ } catch {
19500
+ return null;
19501
+ }
19502
+ }
19408
19503
  if (rows.length === 0) return null;
19409
19504
  let result = "# Relevant Memories\n\nRecalled from your memory database based on your current query:\n\n";
19410
19505
  for (const row of rows) {
@@ -19421,12 +19516,13 @@ function getMemorySection() {
19421
19516
  return null;
19422
19517
  }
19423
19518
  }
19424
- var MAX_PREFETCH_RESULTS, MAX_PREFETCH_CHARS, STOPWORDS2, currentQuery;
19519
+ var MAX_PREFETCH_RESULTS, MAX_PREFETCH_CHARS, MAX_LIKE_TERMS2, STOPWORDS2, currentQuery;
19425
19520
  var init_memory2 = __esm({
19426
19521
  "src/prompts/memory.ts"() {
19427
19522
  "use strict";
19428
19523
  MAX_PREFETCH_RESULTS = 10;
19429
19524
  MAX_PREFETCH_CHARS = 4e3;
19525
+ MAX_LIKE_TERMS2 = 16;
19430
19526
  STOPWORDS2 = /* @__PURE__ */ new Set([
19431
19527
  "a",
19432
19528
  "an",
@@ -19489,6 +19585,7 @@ var init_memory2 = __esm({
19489
19585
  ]);
19490
19586
  currentQuery = null;
19491
19587
  __name(setMemoryPrefetchQuery, "setMemoryPrefetchQuery");
19588
+ __name(escapeLike2, "escapeLike");
19492
19589
  __name(getMemorySection, "getMemorySection");
19493
19590
  }
19494
19591
  });
@@ -47225,6 +47322,19 @@ function registerRpcHandlers(options) {
47225
47322
  let currentRun = null;
47226
47323
  const pendingResponders = /* @__PURE__ */ new Map();
47227
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");
47228
47338
  const onBridgeEvent = /* @__PURE__ */ __name((rawEvent) => {
47229
47339
  const source = inferBridgeSource(rawEvent);
47230
47340
  const user = bridgeUser(rawEvent, source);
@@ -47771,6 +47881,77 @@ ${helpMessage}` : field.label;
47771
47881
  }
47772
47882
  return { disconnected: true, slug };
47773
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
+ });
47774
47955
  bus.registerRpc("commands.catalog", () => {
47775
47956
  const skillCount = toolRegistry.listTools().filter((t) => /^skill[:_-]/i.test(t.name)).length;
47776
47957
  return buildCommandsCatalog({ skillCount });
@@ -48394,6 +48575,9 @@ ${helpMessage}` : field.label;
48394
48575
  bridgeEmitter?.off("bridgeEvent", onBridgeEvent);
48395
48576
  mcpManager.off("tools-changed", onToolsChanged);
48396
48577
  pendingResponders.clear();
48578
+ for (const id of Array.from(oauthFlows.keys())) {
48579
+ cleanupOAuthFlow(id, true);
48580
+ }
48397
48581
  };
48398
48582
  }
48399
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;
@@ -48402,6 +48586,7 @@ var init_rpcHandlers = __esm({
48402
48586
  "use strict";
48403
48587
  init_connect();
48404
48588
  init_config();
48589
+ init_copilot_auth();
48405
48590
  init_context_manager();
48406
48591
  init_eventBridge();
48407
48592
  init_fork();
@@ -56261,8 +56446,8 @@ var init_providers2 = __esm({
56261
56446
  });
56262
56447
 
56263
56448
  // src/components/modelPicker.tsx
56264
- import { useEffect as useEffect17, useMemo as useMemo12, useState as useState19 } from "react";
56265
- 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";
56266
56451
  function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t }) {
56267
56452
  const [providers, setProviders] = useState19([]);
56268
56453
  const [currentModel, setCurrentModel] = useState19("");
@@ -56275,10 +56460,19 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56275
56460
  const [keyInput, setKeyInput] = useState19("");
56276
56461
  const [keySaving, setKeySaving] = useState19(false);
56277
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);
56278
56471
  const { stdout } = useStdout();
56279
56472
  const width = Math.max(MIN_WIDTH2, Math.min(MAX_WIDTH2, (stdout?.columns ?? 80) - 6));
56280
56473
  useEffect17(() => {
56281
56474
  gw.request("model.options", sessionId ? { session_id: sessionId } : {}).then((raw) => {
56475
+ if (!mountedRef.current) return;
56282
56476
  const r = asRpcResult(raw);
56283
56477
  if (!r) {
56284
56478
  setErr("invalid response: model.options");
@@ -56299,6 +56493,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56299
56493
  setErr("");
56300
56494
  setLoading(false);
56301
56495
  }).catch((e) => {
56496
+ if (!mountedRef.current) return;
56302
56497
  setErr(rpcErrorMessage(e));
56303
56498
  setLoading(false);
56304
56499
  });
@@ -56306,7 +56501,121 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56306
56501
  const provider = providers[providerIdx];
56307
56502
  const models = provider?.models ?? [];
56308
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");
56309
56610
  const back = /* @__PURE__ */ __name(() => {
56611
+ if (stage === "oauth") {
56612
+ cancelActiveOAuth();
56613
+ setStage("provider");
56614
+ setOauthVerificationUri("");
56615
+ setOauthUserCode("");
56616
+ setOauthError("");
56617
+ return;
56618
+ }
56310
56619
  if (stage === "model" || stage === "key" || stage === "disconnect") {
56311
56620
  setStage("provider");
56312
56621
  setModelIdx(0);
@@ -56317,8 +56626,30 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56317
56626
  }
56318
56627
  onCancel();
56319
56628
  }, "back");
56320
- 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 });
56321
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
+ }
56322
56653
  if (stage === "key") {
56323
56654
  if (keySaving) {
56324
56655
  return;
@@ -56424,6 +56755,11 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56424
56755
  setStage("key");
56425
56756
  setKeyInput("");
56426
56757
  setKeyError("");
56758
+ return;
56759
+ }
56760
+ if (provider.auth_type === "oauth") {
56761
+ startOAuthFlow(provider.slug);
56762
+ return;
56427
56763
  }
56428
56764
  return;
56429
56765
  }
@@ -56470,6 +56806,41 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56470
56806
  /* @__PURE__ */ jsx25(OverlayHint, { t, children: "Esc/q cancel" })
56471
56807
  ] });
56472
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
+ }
56473
56844
  if (stage === "key" && provider) {
56474
56845
  const masked = keyInput ? "\u2022".repeat(Math.min(keyInput.length, 40)) : "";
56475
56846
  return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
@@ -57423,7 +57794,7 @@ var init_appOverlays = __esm({
57423
57794
 
57424
57795
  // src/components/branding.tsx
57425
57796
  import { useEffect as useEffect20, useState as useState24 } from "react";
57426
- 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";
57427
57798
  function useColumns() {
57428
57799
  const { stdout } = useStdout();
57429
57800
  const [cols, setCols] = useState24(stdout?.columns ?? 80);
@@ -58864,7 +59235,7 @@ var init_syntax = __esm({
58864
59235
  });
58865
59236
 
58866
59237
  // src/components/markdown.tsx
58867
- import { Fragment as Fragment6, memo, useMemo as useMemo14 } from "react";
59238
+ import { Fragment as Fragment7, memo, useMemo as useMemo14 } from "react";
58868
59239
  import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
58869
59240
  function ResolvedLink({ fallbackLabel, t, url }) {
58870
59241
  const fetched = useLinkTitle(url);
@@ -59396,7 +59767,7 @@ var init_markdown = __esm({
59396
59767
  const cellWidth = /* @__PURE__ */ __name((raw) => stringWidth(stripInlineMarkup(raw)), "cellWidth");
59397
59768
  const widths = rows[0].map((_, ci) => Math.max(...rows.map((r) => cellWidth(r[ci] ?? ""))));
59398
59769
  const sep2 = widths.map((w) => "\u2500".repeat(Math.max(1, w))).join(" ");
59399
- 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: [
59400
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: [
59401
59772
  /* @__PURE__ */ jsx34(MdInline, { t, text: row[ci] ?? "" }),
59402
59773
  " ".repeat(Math.max(0, w - cellWidth(row[ci] ?? ""))),
@@ -59437,7 +59808,7 @@ var init_markdown = __esm({
59437
59808
  });
59438
59809
 
59439
59810
  // src/components/streamingMarkdown.tsx
59440
- import { memo as memo2, useRef as useRef17 } from "react";
59811
+ import { memo as memo2, useRef as useRef18 } from "react";
59441
59812
  import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
59442
59813
  var fenceOpenAt, findStableBoundary, StreamingMd;
59443
59814
  var init_streamingMarkdown = __esm({
@@ -59500,7 +59871,7 @@ var init_streamingMarkdown = __esm({
59500
59871
  return -1;
59501
59872
  }, "findStableBoundary");
59502
59873
  StreamingMd = memo2(/* @__PURE__ */ __name(function StreamingMd2({ compact, t, text }) {
59503
- const stablePrefixRef = useRef17("");
59874
+ const stablePrefixRef = useRef18("");
59504
59875
  if (!text.startsWith(stablePrefixRef.current)) {
59505
59876
  stablePrefixRef.current = "";
59506
59877
  }
@@ -59527,7 +59898,7 @@ var init_streamingMarkdown = __esm({
59527
59898
  // src/components/thinking.tsx
59528
59899
  import { memo as memo3, useEffect as useEffect23, useMemo as useMemo15, useState as useState26 } from "react";
59529
59900
  import spinners from "unicode-animations";
59530
- 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";
59531
59902
  import { createElement } from "react";
59532
59903
  function TreeRow({
59533
59904
  branch,
@@ -59786,7 +60157,7 @@ function SubagentAccordion({
59786
60157
  {
59787
60158
  branch: index === item.tools.length - 1 ? "last" : "mid",
59788
60159
  color: t.color.text,
59789
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60160
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
59790
60161
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u25CF " }),
59791
60162
  line
59792
60163
  ] }),
@@ -60059,7 +60430,7 @@ var init_thinking = __esm({
60059
60430
  color: t.color.muted,
60060
60431
  dimColor: true,
60061
60432
  key: `tr-${i}`,
60062
- content: groups.length ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
60433
+ content: groups.length ? /* @__PURE__ */ jsxs23(Fragment8, { children: [
60063
60434
  /* @__PURE__ */ jsx36(Spinner3, { color: t.color.accent, variant: "think" }),
60064
60435
  " ",
60065
60436
  line
@@ -60076,7 +60447,7 @@ var init_thinking = __esm({
60076
60447
  key: tool.id,
60077
60448
  label,
60078
60449
  details: [],
60079
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60450
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60080
60451
  /* @__PURE__ */ jsx36(Spinner3, { color: t.color.accent, variant: "tool" }),
60081
60452
  " ",
60082
60453
  label,
@@ -60104,7 +60475,7 @@ var init_thinking = __esm({
60104
60475
  const inlineDelegateKey = hasSubagents && delegateGroups.length === 1 ? delegateGroups[0].key : null;
60105
60476
  const toolLabel = /* @__PURE__ */ __name((group) => {
60106
60477
  const { duration, label } = splitToolDuration(String(group.content));
60107
- return duration ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
60478
+ return duration ? /* @__PURE__ */ jsxs23(Fragment8, { children: [
60108
60479
  label,
60109
60480
  /* @__PURE__ */ jsx36(Text9, { color: t.color.statusFg, dim: true, children: duration })
60110
60481
  ] }) : group.content;
@@ -60216,7 +60587,7 @@ var init_thinking = __esm({
60216
60587
  {
60217
60588
  branch,
60218
60589
  color: group.color,
60219
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60590
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60220
60591
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u25CF " }),
60221
60592
  toolLabel(group)
60222
60593
  ] }),
@@ -60319,7 +60690,7 @@ var init_thinking = __esm({
60319
60690
  {
60320
60691
  branch: "last",
60321
60692
  color: t.color.statusFg,
60322
- content: /* @__PURE__ */ jsxs23(Fragment7, { children: [
60693
+ content: /* @__PURE__ */ jsxs23(Fragment8, { children: [
60323
60694
  /* @__PURE__ */ jsx36(Text9, { color: t.color.accent, children: "\u03A3 " }),
60324
60695
  totalTokensLabel
60325
60696
  ] }),
@@ -60622,7 +60993,7 @@ var init_queuedMessages = __esm({
60622
60993
  // src/components/streamingAssistant.tsx
60623
60994
  import { useStore as useStore8 } from "@nanostores/react";
60624
60995
  import { memo as memo6 } from "react";
60625
- 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";
60626
60997
  var groupedSegments, StreamingAssistant, LiveTodoPanel;
60627
60998
  var init_streamingAssistant = __esm({
60628
60999
  "src/components/streamingAssistant.tsx"() {
@@ -60650,7 +61021,7 @@ var init_streamingAssistant = __esm({
60650
61021
  if (!progress.showProgressArea && !showStreamingArea && !activeTools.length) {
60651
61022
  return null;
60652
61023
  }
60653
- return /* @__PURE__ */ jsxs27(Fragment8, { children: [
61024
+ return /* @__PURE__ */ jsxs27(Fragment9, { children: [
60654
61025
  groupedSegments(streamSegments).map((msg, i) => /* @__PURE__ */ jsx40(
60655
61026
  MessageLine,
60656
61027
  {
@@ -60719,8 +61090,8 @@ var init_streamingAssistant = __esm({
60719
61090
 
60720
61091
  // src/components/appLayout.tsx
60721
61092
  import { useStore as useStore9 } from "@nanostores/react";
60722
- import { Fragment as Fragment9, memo as memo7, useMemo as useMemo16, useRef as useRef18 } from "react";
60723
- 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";
60724
61095
  var PromptPrefix, TranscriptPane, ComposerPane, AgentsOverlayPane, StatusRulePane, AppLayout;
60725
61096
  var init_appLayout = __esm({
60726
61097
  "src/components/appLayout.tsx"() {
@@ -60776,7 +61147,7 @@ var init_appLayout = __esm({
60776
61147
  () => transcript.historyItems.findIndex((m) => m.role === "user"),
60777
61148
  [transcript.historyItems]
60778
61149
  );
60779
- return /* @__PURE__ */ jsxs28(Fragment10, { children: [
61150
+ return /* @__PURE__ */ jsxs28(Fragment11, { children: [
60780
61151
  /* @__PURE__ */ jsx41(
60781
61152
  ScrollBox_default,
60782
61153
  {
@@ -60850,7 +61221,7 @@ var init_appLayout = __esm({
60850
61221
  const promptBlank = " ".repeat(promptWidth);
60851
61222
  const inputColumns = stableComposerColumns(composer.cols, promptWidth);
60852
61223
  const inputHeight = inputVisualHeight(composer.input, inputColumns);
60853
- const inputMouseRef = useRef18(null);
61224
+ const inputMouseRef = useRef19(null);
60854
61225
  const captureInputDrag = /* @__PURE__ */ __name((e) => {
60855
61226
  if (e.button !== 0) {
60856
61227
  return;
@@ -60920,7 +61291,7 @@ var init_appLayout = __esm({
60920
61291
  }
60921
61292
  ),
60922
61293
  composer.input === "?" && !composer.inputBuf.length && /* @__PURE__ */ jsx41(HelpHint, { t: ui.theme }),
60923
- !isBlocked && /* @__PURE__ */ jsxs28(Fragment10, { children: [
61294
+ !isBlocked && /* @__PURE__ */ jsxs28(Fragment11, { children: [
60924
61295
  composer.inputBuf.map((line, i) => /* @__PURE__ */ jsxs28(Box_default, { children: [
60925
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 }) }),
60926
61297
  /* @__PURE__ */ jsx41(Text9, { color: ui.theme.color.composeText, children: line || " " })
@@ -61026,11 +61397,11 @@ var init_appLayout = __esm({
61026
61397
  }) {
61027
61398
  const overlay = useStore9($overlayState);
61028
61399
  const ui = useStore9($uiState);
61029
- const Shell = INLINE_MODE ? Fragment9 : AlternateScreen;
61400
+ const Shell = INLINE_MODE ? Fragment10 : AlternateScreen;
61030
61401
  const shellProps = INLINE_MODE ? {} : { mouseTracking };
61031
61402
  return /* @__PURE__ */ jsx41(Shell, { ...shellProps, children: /* @__PURE__ */ jsxs28(Box_default, { flexDirection: "column", flexGrow: 1, children: [
61032
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 }) }) }),
61033
- !overlay.agents && /* @__PURE__ */ jsxs28(Fragment10, { children: [
61404
+ !overlay.agents && /* @__PURE__ */ jsxs28(Fragment11, { children: [
61034
61405
  /* @__PURE__ */ jsx41(PerfPane, { id: "prompt", children: /* @__PURE__ */ jsx41(
61035
61406
  PromptZone,
61036
61407
  {