@buildautomaton/cli 0.1.69 → 0.1.70

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/index.js CHANGED
@@ -29341,6 +29341,18 @@ function getAgentModelFromAgentConfig(config2) {
29341
29341
  return t !== "" ? t : null;
29342
29342
  }
29343
29343
 
29344
+ // ../types/src/overview/heatmap/constants.ts
29345
+ var HOUR_MS = 36e5;
29346
+ var DAY_MS = 24 * HOUR_MS;
29347
+
29348
+ // ../types/src/bridges/agents/types.ts
29349
+ var INSTALLABLE_BRIDGE_AGENTS = [
29350
+ { value: "claude-code", label: "Anthropic Claude Code", tokenLabel: "Anthropic API key", tokenEnvVar: "ANTHROPIC_API_KEY" },
29351
+ { value: "codex-acp", label: "Codex", tokenLabel: "OpenAI API key", tokenEnvVar: "OPENAI_API_KEY" },
29352
+ { value: "cursor-cli", label: "Cursor CLI agent", tokenLabel: "Cursor API key", tokenEnvVar: "CURSOR_API_KEY" },
29353
+ { value: "opencode", label: "OpenCode agent", tokenLabel: "Provider API key", tokenEnvVar: "OPENCODE_API_KEY" }
29354
+ ];
29355
+
29344
29356
  // src/agents/acp/safe-fs-path.ts
29345
29357
  import * as path2 from "node:path";
29346
29358
  function resolveSafePathUnderCwd(cwd, filePath) {
@@ -30574,7 +30586,7 @@ function installBridgeProcessResilience() {
30574
30586
  }
30575
30587
 
30576
30588
  // src/cli-version.ts
30577
- var CLI_VERSION = "0.1.69".length > 0 ? "0.1.69" : "0.0.0-dev";
30589
+ var CLI_VERSION = "0.1.70".length > 0 ? "0.1.70" : "0.0.0-dev";
30578
30590
 
30579
30591
  // src/connection/heartbeat/constants.ts
30580
30592
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -48443,6 +48455,7 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
48443
48455
  "code_nav",
48444
48456
  "skill_layout_request",
48445
48457
  "install_skills",
48458
+ "install_agent",
48446
48459
  "refresh_local_skills",
48447
48460
  "bridge_git_context_request",
48448
48461
  "list_repo_branches_request"
@@ -48518,6 +48531,100 @@ var handleBridgeHeartbeatAck = (msg, deps) => {
48518
48531
  deps.onBridgeHeartbeatAck?.(Math.trunc(raw));
48519
48532
  };
48520
48533
 
48534
+ // src/routing/dispatch/connection.ts
48535
+ function dispatchBridgeConnectionMessage(msg, deps) {
48536
+ switch (msg.type) {
48537
+ case "auth_token":
48538
+ handleAuthToken(msg, deps);
48539
+ break;
48540
+ case "bridge_identified":
48541
+ handleBridgeIdentified(msg, deps);
48542
+ break;
48543
+ case "ha":
48544
+ handleBridgeHeartbeatAck(msg, deps);
48545
+ break;
48546
+ }
48547
+ }
48548
+
48549
+ // src/routing/handlers/preview-environment-control.ts
48550
+ var handlePreviewEnvironmentControl = (msg, deps) => {
48551
+ let wire;
48552
+ try {
48553
+ wire = deps.e2ee ? deps.e2ee.decryptMessage(msg) : msg;
48554
+ } catch (e) {
48555
+ deps.log(`[E2EE] Could not decrypt preview environment command: ${e instanceof Error ? e.message : String(e)}`);
48556
+ return;
48557
+ }
48558
+ const environmentId = typeof wire.environmentId === "string" ? wire.environmentId : "";
48559
+ const action = wire.action === "start" || wire.action === "stop" ? wire.action : null;
48560
+ if (!environmentId || !action) return;
48561
+ deps.previewEnvironmentManager?.handleControl(environmentId, action);
48562
+ };
48563
+
48564
+ // src/routing/handlers/preview-environments-config.ts
48565
+ var VALID_PORT = (p) => typeof p === "number" && Number.isInteger(p) && p > 0 && p < 65536;
48566
+ var handlePreviewEnvironmentsConfig = (msg, deps) => {
48567
+ const previewEnvironments = msg.previewEnvironments;
48568
+ const proxyPorts = Array.isArray(msg.proxyPorts) ? msg.proxyPorts.filter(VALID_PORT) : void 0;
48569
+ setImmediate(() => {
48570
+ deps.previewEnvironmentManager?.applyConfig(previewEnvironments ?? []);
48571
+ const environmentIds = parsePreviewEnvironmentDefs(previewEnvironments ?? []).map((d) => d.environmentId);
48572
+ void deps.previewWorktreeManager?.syncConfiguredEnvironments(environmentIds);
48573
+ if (proxyPorts !== void 0) {
48574
+ deps.updateFirehoseProxyPorts?.(proxyPorts);
48575
+ }
48576
+ });
48577
+ };
48578
+
48579
+ // src/routing/handlers/send-deploy-session-to-preview-result.ts
48580
+ function sendDeploySessionToPreviewResult(ws, id, payload) {
48581
+ if (!ws) return;
48582
+ sendWsMessage(ws, { type: "deploy_session_to_preview_result", id, ...payload });
48583
+ }
48584
+
48585
+ // src/routing/handlers/deploy-session-to-preview.ts
48586
+ var handleDeploySessionToPreviewMessage = (msg, deps) => {
48587
+ if (typeof msg.id !== "string") return;
48588
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
48589
+ const environmentId = typeof msg.environmentId === "string" ? msg.environmentId : "";
48590
+ if (!sessionId || !environmentId) return;
48591
+ void (async () => {
48592
+ const ws = deps.getWs();
48593
+ const reply = (payload) => sendDeploySessionToPreviewResult(ws, msg.id, payload);
48594
+ try {
48595
+ if (!deps.previewWorktreeManager) {
48596
+ reply({ ok: false, error: "Preview worktree manager unavailable" });
48597
+ return;
48598
+ }
48599
+ const result = await deploySessionToPreviewEnvironment({
48600
+ sessionWorktreeManager: deps.sessionWorktreeManager,
48601
+ previewWorktreeManager: deps.previewWorktreeManager,
48602
+ sessionId,
48603
+ environmentId,
48604
+ log: deps.log
48605
+ });
48606
+ reply(result);
48607
+ } catch (e) {
48608
+ reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
48609
+ }
48610
+ })();
48611
+ };
48612
+
48613
+ // src/routing/dispatch/preview.ts
48614
+ function dispatchBridgePreviewMessage(msg, deps) {
48615
+ switch (msg.type) {
48616
+ case "preview_environments_config":
48617
+ handlePreviewEnvironmentsConfig(msg, deps);
48618
+ break;
48619
+ case "preview_environment_control":
48620
+ handlePreviewEnvironmentControl(msg, deps);
48621
+ break;
48622
+ case "deploy_session_to_preview":
48623
+ handleDeploySessionToPreviewMessage(msg, deps);
48624
+ break;
48625
+ }
48626
+ }
48627
+
48521
48628
  // src/agents/acp/from-bridge/handle-bridge-agent-config.ts
48522
48629
  function handleBridgeAgentConfig(msg, { acpManager }) {
48523
48630
  if (!Array.isArray(msg.agents) || msg.agents.length === 0) return;
@@ -49156,193 +49263,331 @@ var handleSessionRequestResponseMessage = (msg, deps) => {
49156
49263
  handleBridgeSessionRequestResponse(msg, deps);
49157
49264
  };
49158
49265
 
49159
- // src/skills/handle-skill-call.ts
49160
- function handleSkillCall(msg, socket, log2) {
49161
- callSkill(msg.skillId, msg.operationId, msg.params ?? {}).then((result) => {
49162
- sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
49163
- }).catch((err) => {
49164
- sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
49165
- log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
49266
+ // src/routing/handlers/git/handle-session-git-commit.ts
49267
+ async function handleSessionGitCommitAction(deps, sessionId, msg, reply) {
49268
+ const branch = typeof msg.branch === "string" ? msg.branch : "";
49269
+ const message = typeof msg.message === "string" ? msg.message : "";
49270
+ const pushAfterCommit = msg.pushAfterCommit === true;
49271
+ if (!branch.trim() || !message.trim()) {
49272
+ reply({ ok: false, error: "branch and message are required for commit" });
49273
+ return;
49274
+ }
49275
+ const commitRes = await deps.sessionWorktreeManager.commitSession({
49276
+ sessionId,
49277
+ branch: branch.trim(),
49278
+ message: message.trim(),
49279
+ push: pushAfterCommit
49280
+ });
49281
+ if (!commitRes.ok) {
49282
+ reply({ ok: false, error: commitRes.error ?? "Commit failed" });
49283
+ return;
49284
+ }
49285
+ const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
49286
+ reply({
49287
+ ok: true,
49288
+ hasUncommittedChanges: st.hasUncommittedChanges,
49289
+ hasUnpushedCommits: st.hasUnpushedCommits,
49290
+ uncommittedFileCount: st.uncommittedFileCount
49166
49291
  });
49167
49292
  }
49168
49293
 
49169
- // src/routing/handlers/skill-call.ts
49170
- var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
49171
- const skillId = typeof msg.skillId === "string" ? msg.skillId : "";
49172
- const operationId = typeof msg.operationId === "string" ? msg.operationId : "";
49173
- if (!skillId || !operationId) return;
49174
- const socket = getWs();
49175
- if (!socket) return;
49176
- handleSkillCall(
49177
- { id: msg.id, skillId, operationId, params: msg.params },
49178
- socket,
49179
- log2
49180
- );
49181
- };
49182
-
49183
- // src/files/browser/send-file-browser-message.ts
49184
- function sendFileBrowserMessage(socket, e2ee, payload) {
49185
- sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
49294
+ // src/routing/handlers/git/session-git-changes-params.ts
49295
+ function readString(value) {
49296
+ return typeof value === "string" ? value.trim() : "";
49297
+ }
49298
+ function readChangesObject(msg) {
49299
+ const changes = msg.changes;
49300
+ if (!changes || typeof changes !== "object" || Array.isArray(changes)) return null;
49301
+ return changes;
49302
+ }
49303
+ function parseSessionGitChangesParams(msg) {
49304
+ const nested = readChangesObject(msg);
49305
+ const file2 = nested?.file && typeof nested.file === "object" && !Array.isArray(nested.file) ? nested.file : null;
49306
+ const repoRelPath = readString(nested?.repoRelPath) || readString(msg.changesRepoRelPath);
49307
+ const viewRaw = nested?.view ?? msg.changesView;
49308
+ const view = viewRaw === "commit" ? "commit" : "working";
49309
+ const commitSha = readString(nested?.commitSha) || readString(msg.changesCommitSha);
49310
+ const recentCommitsLimit = nested?.recentCommitsLimit ?? msg.changesRecentCommitsLimit;
49311
+ return {
49312
+ repoRelPath,
49313
+ view,
49314
+ commitSha,
49315
+ recentCommitsLimit: typeof recentCommitsLimit === "number" || typeof recentCommitsLimit === "string" ? recentCommitsLimit : void 0,
49316
+ fileWorkspaceRelPath: readString(file2?.workspaceRelPath),
49317
+ fileChange: readString(file2?.change),
49318
+ fileMovedFromWorkspaceRelPath: readString(file2?.movedFromWorkspaceRelPath)
49319
+ };
49186
49320
  }
49187
49321
 
49188
- // src/files/browser/handle-file-browser-list.ts
49189
- async function handleFileBrowserList(socket, e2ee, id, reqPath, sessionParentPath, gitScope) {
49190
- const result = await executeFileBrowserList({ reqPath, sessionParentPath, gitScope });
49191
- if ("error" in result) {
49192
- sendWsMessage(socket, { type: "file_browser_response", id, error: result.error });
49322
+ // src/routing/handlers/git/handle-session-git-get-change-file-patch.ts
49323
+ async function handleSessionGitGetChangeFilePatchAction(deps, msg, reply) {
49324
+ const changes = parseSessionGitChangesParams(msg);
49325
+ const { repoRelPath: repoRel, view, commitSha, fileWorkspaceRelPath, fileChange, fileMovedFromWorkspaceRelPath } = changes;
49326
+ const change = parseWorkingTreeChangeKind(fileChange);
49327
+ if (!repoRel || !fileWorkspaceRelPath || !change) {
49328
+ reply({
49329
+ ok: false,
49330
+ error: "changes.repoRelPath, changes.file.workspaceRelPath, and changes.file.change are required"
49331
+ });
49193
49332
  return;
49194
49333
  }
49195
- sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id, entries: result.entries });
49196
- }
49197
-
49198
- // src/files/browser/handle-file-browser-read.ts
49199
- async function handleFileBrowserRead(socket, e2ee, msg, reqPath, sessionParentPath, gitScope) {
49200
- const result = await executeFileBrowserRead({ msg, reqPath, sessionParentPath, gitScope });
49201
- if ("error" in result) {
49202
- sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
49334
+ if (view === "commit" && !commitSha) {
49335
+ reply({ ok: false, error: "changes.commitSha is required for commit file patch" });
49203
49336
  return;
49204
49337
  }
49205
- const payload = {
49206
- type: "file_browser_response",
49207
- id: msg.id,
49208
- content: result.content,
49209
- totalLines: result.totalLines,
49210
- size: result.size
49211
- };
49212
- if (result.lineOffset != null) payload.lineOffset = result.lineOffset;
49213
- if (result.mimeType != null) payload.mimeType = result.mimeType;
49214
- if (result.resolvedPath != null) payload.resolvedPath = result.resolvedPath;
49215
- sendFileBrowserMessage(socket, e2ee, payload);
49338
+ const patch = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeFilePatch(msg.sessionId, {
49339
+ repoRelPath: repoRel,
49340
+ workspaceRelPath: fileWorkspaceRelPath,
49341
+ change,
49342
+ movedFromWorkspaceRelPath: fileMovedFromWorkspaceRelPath || null,
49343
+ basis: view === "commit" ? { kind: "commit", sha: commitSha } : { kind: "working" }
49344
+ });
49345
+ reply(
49346
+ {
49347
+ ok: true,
49348
+ patchContent: patch.patchContent ?? null,
49349
+ totalLines: patch.totalLines
49350
+ },
49351
+ ["patchContent"]
49352
+ );
49216
49353
  }
49217
49354
 
49218
- // src/files/browser/index.ts
49219
- init_in_flight();
49220
- function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
49221
- beginFileBrowserRequest();
49222
- void (async () => {
49223
- try {
49224
- const reqPath = msg.path.replace(/^\/+/, "") || ".";
49225
- const sessionParentPath = sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : void 0;
49226
- const gitScope = resolveGitBranchScope(msg);
49227
- if (msg.source === "git_branch" && typeof msg.repoRelPath === "string" && typeof msg.branch === "string" && msg.branch.trim().length > 0 && !gitScope) {
49228
- sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: "Invalid repository path" });
49229
- return;
49230
- }
49231
- if (msg.op === "read") {
49232
- await handleFileBrowserRead(socket, e2ee, msg, reqPath, sessionParentPath, gitScope);
49233
- } else {
49234
- await handleFileBrowserList(socket, e2ee, msg.id, reqPath, sessionParentPath, gitScope);
49235
- }
49236
- } finally {
49237
- endFileBrowserRequest();
49238
- }
49239
- })();
49355
+ // src/routing/handlers/git/coalesce-list-changes.ts
49356
+ var listChangesInflight = /* @__PURE__ */ new Map();
49357
+ function normalizeListChangesRepoPathRelativeToWorkspaceRoot(repoRelPath) {
49358
+ if (!repoRelPath?.trim()) return "";
49359
+ return normalizeRepoPathRelativeToWorkspaceRoot(repoRelPath.trim());
49240
49360
  }
49241
-
49242
- // src/files/handle-file-browser-search.ts
49243
- init_yield_to_event_loop();
49244
- import path79 from "node:path";
49245
- var SEARCH_LIMIT = 100;
49246
- function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
49247
- void (async () => {
49248
- await yieldToEventLoop();
49249
- const q = typeof msg.q === "string" ? msg.q : "";
49250
- const sessionParentPath = path79.resolve(
49251
- sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
49252
- );
49253
- if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
49254
- triggerFileIndexBuild(sessionParentPath);
49255
- const payload2 = {
49256
- type: "file_browser_search_response",
49257
- id: msg.id,
49258
- paths: [],
49259
- indexReady: false
49260
- };
49261
- sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
49361
+ function listChangesInflightKey(sessionId, opts) {
49362
+ const basis = opts?.basis;
49363
+ return [
49364
+ sessionId,
49365
+ normalizeListChangesRepoPathRelativeToWorkspaceRoot(opts?.repoRelPath),
49366
+ basis?.kind === "commit" ? `commit:${basis.sha}` : "working",
49367
+ String(opts?.recentCommitsLimit ?? "")
49368
+ ].join("|");
49369
+ }
49370
+ function getSessionWorkingTreeChangeDetailsCoalesced(sessionWorktreeManager, sessionId, opts) {
49371
+ const key = listChangesInflightKey(sessionId, opts);
49372
+ const existing = listChangesInflight.get(key);
49373
+ if (existing) return existing;
49374
+ const promise2 = sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts).finally(() => {
49375
+ if (listChangesInflight.get(key) === promise2) listChangesInflight.delete(key);
49376
+ });
49377
+ listChangesInflight.set(key, promise2);
49378
+ return promise2;
49379
+ }
49380
+
49381
+ // src/routing/handlers/git/handle-session-git-list-changes.ts
49382
+ async function handleSessionGitListChangesAction(deps, msg, reply) {
49383
+ const changes = parseSessionGitChangesParams(msg);
49384
+ const { repoRelPath: repoRel, view, commitSha, recentCommitsLimit } = changes;
49385
+ if (view === "commit") {
49386
+ if (!repoRel || !commitSha) {
49387
+ reply({
49388
+ ok: false,
49389
+ error: "changes.repoRelPath and changes.commitSha are required for commit view"
49390
+ });
49262
49391
  return;
49263
49392
  }
49264
- const results = await searchBridgeFilePathsAsync(sessionParentPath, q, SEARCH_LIMIT);
49265
- const payload = {
49266
- type: "file_browser_search_response",
49267
- id: msg.id,
49268
- paths: results,
49269
- indexReady: true
49270
- };
49271
- sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
49272
- })();
49393
+ }
49394
+ const opts = repoRel && view === "commit" && commitSha ? {
49395
+ repoRelPath: repoRel,
49396
+ basis: { kind: "commit", sha: commitSha },
49397
+ recentCommitsLimit
49398
+ } : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
49399
+ const repos = await getSessionWorkingTreeChangeDetailsCoalesced(
49400
+ deps.sessionWorktreeManager,
49401
+ msg.sessionId,
49402
+ opts
49403
+ );
49404
+ reply({ ok: true, repos }, ["repos"]);
49273
49405
  }
49274
- function triggerFileIndexBuild(sessionParentPath = getBridgeRoot()) {
49275
- setImmediate(() => {
49276
- void ensureFileIndexAsync(sessionParentPath).catch((e) => {
49277
- console.error("[file-index] Background build failed:", e);
49278
- });
49406
+
49407
+ // src/routing/handlers/git/handle-session-git-push.ts
49408
+ async function handleSessionGitPushAction(deps, sessionId, reply) {
49409
+ const pushRes = await deps.sessionWorktreeManager.pushSessionUpstream(sessionId);
49410
+ if (!pushRes.ok) {
49411
+ reply({ ok: false, error: pushRes.error ?? "Push failed" });
49412
+ return;
49413
+ }
49414
+ const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
49415
+ reply({
49416
+ ok: true,
49417
+ hasUncommittedChanges: st.hasUncommittedChanges,
49418
+ hasUnpushedCommits: st.hasUnpushedCommits,
49419
+ uncommittedFileCount: st.uncommittedFileCount
49279
49420
  });
49280
49421
  }
49281
49422
 
49282
- // src/routing/handlers/file-browser-messages.ts
49283
- function handleFileBrowserRequestMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
49284
- if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
49285
- const socket = getWs();
49286
- if (!socket) return;
49287
- handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager);
49288
- }
49289
- function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
49290
- if (typeof msg.id !== "string") return;
49291
- const socket = getWs();
49292
- if (!socket) return;
49293
- handleFileBrowserSearch(
49294
- msg,
49295
- socket,
49296
- e2ee,
49297
- sessionWorktreeManager
49298
- );
49423
+ // src/routing/handlers/git/handle-session-git-status.ts
49424
+ async function handleSessionGitStatusAction(deps, sessionId, reply) {
49425
+ const r = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
49426
+ reply({
49427
+ ok: true,
49428
+ hasUncommittedChanges: r.hasUncommittedChanges,
49429
+ hasUnpushedCommits: r.hasUnpushedCommits,
49430
+ uncommittedFileCount: r.uncommittedFileCount
49431
+ });
49299
49432
  }
49300
49433
 
49301
- // src/code-nav/handlers/send-code-nav-response.ts
49302
- var ENCRYPTED_FIELDS = ["definition", "definitions", "references", "symbols", "confidentTarget"];
49303
- function sendCodeNavResponse(socket, e2ee, payload) {
49304
- const fieldsToEncrypt = ENCRYPTED_FIELDS.filter((field) => field in payload && payload[field] !== void 0);
49305
- sendWsMessage(
49306
- socket,
49307
- e2ee && fieldsToEncrypt.length > 0 ? e2ee.encryptFields(payload, [...fieldsToEncrypt]) : payload
49308
- );
49434
+ // src/routing/handlers/git/send-session-git-result.ts
49435
+ function sendSessionGitResult(ws, id, payload, e2ee, encryptedFields = []) {
49436
+ if (!ws) return;
49437
+ const message = { type: "session_git_result", id, ...payload };
49438
+ let wire = message;
49439
+ if (e2ee && encryptedFields.length > 0) {
49440
+ wire = e2ee.encryptFields(message, encryptedFields);
49441
+ }
49442
+ sendWsMessage(ws, wire);
49309
49443
  }
49310
49444
 
49311
- // src/code-nav/handlers/handle-code-nav-request.ts
49312
- function handleCodeNavRequest(msg, socket, e2ee, sessionWorktreeManager) {
49445
+ // src/routing/handlers/git/session-git-request.ts
49446
+ var handleSessionGitRequestMessage = (msg, deps) => {
49447
+ if (typeof msg.id !== "string") return;
49448
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49449
+ const action = msg.action;
49450
+ if (!sessionId || action !== "status" && action !== "push" && action !== "commit" && action !== "list_changes" && action !== "file_diff")
49451
+ return;
49313
49452
  void (async () => {
49314
- const body = await executeCodeNavRequest(msg, sessionWorktreeManager);
49315
- sendCodeNavResponse(socket, e2ee, {
49316
- type: "code_nav_response",
49317
- id: msg.id,
49318
- ...body
49453
+ const ws = deps.getWs();
49454
+ const reply = (payload, encryptedFields = []) => sendSessionGitResult(ws, msg.id, payload, deps.e2ee, encryptedFields);
49455
+ try {
49456
+ if (action === "status") {
49457
+ await handleSessionGitStatusAction(deps, sessionId, reply);
49458
+ return;
49459
+ }
49460
+ if (action === "list_changes") {
49461
+ await handleSessionGitListChangesAction(deps, { ...msg, sessionId }, reply);
49462
+ return;
49463
+ }
49464
+ if (action === "file_diff") {
49465
+ await handleSessionGitGetChangeFilePatchAction(deps, { ...msg, sessionId }, reply);
49466
+ return;
49467
+ }
49468
+ if (action === "push") {
49469
+ await handleSessionGitPushAction(deps, sessionId, reply);
49470
+ return;
49471
+ }
49472
+ await handleSessionGitCommitAction(deps, sessionId, msg, reply);
49473
+ } catch (e) {
49474
+ reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
49475
+ }
49476
+ })();
49477
+ };
49478
+
49479
+ // src/routing/handlers/rename-session-branch.ts
49480
+ var handleRenameSessionBranchMessage = (msg, deps) => {
49481
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49482
+ const newBranch = typeof msg.newBranch === "string" ? msg.newBranch : "";
49483
+ if (!sessionId || !newBranch) return;
49484
+ void deps.sessionWorktreeManager.renameSessionBranch(sessionId, newBranch);
49485
+ };
49486
+
49487
+ // src/routing/handlers/session-archived.ts
49488
+ var handleSessionArchivedMessage = (msg, deps) => {
49489
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49490
+ if (!sessionId) return;
49491
+ void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
49492
+ };
49493
+
49494
+ // src/routing/handlers/session-discarded.ts
49495
+ var handleSessionDiscardedMessage = (msg, deps) => {
49496
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49497
+ if (!sessionId) return;
49498
+ void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
49499
+ };
49500
+
49501
+ // src/routing/handlers/revert-turn-snapshot.ts
49502
+ import * as fs59 from "node:fs";
49503
+ var handleRevertTurnSnapshotMessage = (msg, deps) => {
49504
+ const id = typeof msg.id === "string" ? msg.id : "";
49505
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49506
+ const turnId = typeof msg.turnId === "string" ? msg.turnId : "";
49507
+ if (!id || !sessionId || !turnId) return;
49508
+ const { getWs, log: log2, sessionWorktreeManager } = deps;
49509
+ void (async () => {
49510
+ const s = getWs();
49511
+ if (!s) return;
49512
+ const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
49513
+ const file2 = snapshotFilePath(agentBase, turnId);
49514
+ try {
49515
+ await fs59.promises.access(file2, fs59.constants.F_OK);
49516
+ } catch {
49517
+ sendWsMessage(s, {
49518
+ type: "revert_turn_snapshot_result",
49519
+ id,
49520
+ ok: false,
49521
+ error: "No snapshot found for this turn (git state may be unavailable)."
49522
+ });
49523
+ return;
49524
+ }
49525
+ const res = await applyPreTurnSnapshot(file2, log2);
49526
+ sendWsMessage(s, {
49527
+ type: "revert_turn_snapshot_result",
49528
+ id,
49529
+ ok: res.ok,
49530
+ ...res.error ? { error: res.error } : {}
49319
49531
  });
49320
49532
  })();
49533
+ };
49534
+
49535
+ // src/routing/dispatch/session.ts
49536
+ function dispatchBridgeSessionMessage(msg, deps) {
49537
+ switch (msg.type) {
49538
+ case "agent_config":
49539
+ handleAgentConfigMessage(msg, deps);
49540
+ break;
49541
+ case "prompt_queue_state":
49542
+ handlePromptQueueStateMessage(msg, deps);
49543
+ break;
49544
+ case "prompt":
49545
+ handlePromptMessage(msg, deps);
49546
+ break;
49547
+ case "session_git_request":
49548
+ handleSessionGitRequestMessage(msg, deps);
49549
+ break;
49550
+ case "rename_session_branch":
49551
+ handleRenameSessionBranchMessage(msg, deps);
49552
+ break;
49553
+ case "session_archived":
49554
+ handleSessionArchivedMessage(msg, deps);
49555
+ break;
49556
+ case "session_discarded":
49557
+ handleSessionDiscardedMessage(msg, deps);
49558
+ break;
49559
+ case "revert_turn_snapshot":
49560
+ handleRevertTurnSnapshotMessage(msg, deps);
49561
+ break;
49562
+ case "cursor_request_response":
49563
+ handleSessionRequestResponseMessage(msg, deps);
49564
+ break;
49565
+ }
49321
49566
  }
49322
49567
 
49323
- // src/code-nav/handlers/trigger-symbol-index-build.ts
49324
- init_normalize_resolved_path();
49568
+ // src/skills/handle-skill-call.ts
49569
+ function handleSkillCall(msg, socket, log2) {
49570
+ callSkill(msg.skillId, msg.operationId, msg.params ?? {}).then((result) => {
49571
+ sendWsMessage(socket, { type: "skill_result", id: msg.id, result });
49572
+ }).catch((err) => {
49573
+ sendWsMessage(socket, { type: "skill_result", id: msg.id, error: String(err) });
49574
+ log2(`[Bridge service] Skill invocation failed (${msg.skillId}/${msg.operationId}): ${err}`);
49575
+ });
49576
+ }
49325
49577
 
49326
- // src/routing/handlers/code-nav-messages.ts
49327
- function handleFileBrowserCodeNavMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
49328
- if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
49578
+ // src/routing/handlers/skill-call.ts
49579
+ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
49580
+ const skillId = typeof msg.skillId === "string" ? msg.skillId : "";
49581
+ const operationId = typeof msg.operationId === "string" ? msg.operationId : "";
49582
+ if (!skillId || !operationId) return;
49329
49583
  const socket = getWs();
49330
49584
  if (!socket) return;
49331
- handleCodeNavRequest(
49332
- {
49333
- id: msg.id,
49334
- type: "code_nav",
49335
- op: msg.op === "references" ? "references" : msg.op === "symbols" ? "symbols" : msg.op === "definitions" ? "definitions" : "definition",
49336
- path: msg.path,
49337
- line: typeof msg.line === "number" ? msg.line : 1,
49338
- column: typeof msg.column === "number" ? msg.column : 0,
49339
- sessionId: msg.sessionId
49340
- },
49585
+ handleSkillCall(
49586
+ { id: msg.id, skillId, operationId, params: msg.params },
49341
49587
  socket,
49342
- e2ee,
49343
- sessionWorktreeManager
49588
+ log2
49344
49589
  );
49345
- }
49590
+ };
49346
49591
 
49347
49592
  // src/routing/handlers/skill-layout-request.ts
49348
49593
  function handleSkillLayoutRequest(msg, deps) {
@@ -49365,413 +49610,409 @@ function isValidRemoteSkillInstallItem(item) {
49365
49610
 
49366
49611
  // src/skills/install/install-remote-skills-async.ts
49367
49612
  init_yield_to_event_loop();
49368
- import fs59 from "node:fs";
49369
- import path80 from "node:path";
49613
+ import fs60 from "node:fs";
49614
+ import path79 from "node:path";
49370
49615
 
49371
49616
  // src/skills/install/constants.ts
49372
49617
  var INSTALL_SKILLS_YIELD_EVERY = 16;
49373
49618
 
49374
49619
  // src/skills/install/install-remote-skills-async.ts
49375
49620
  async function writeInstallFileAsync(skillDir, f, filesWritten) {
49376
- if (typeof f.path !== "string" || !f.text && !f.base64) return;
49377
- if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
49378
- await yieldToEventLoop();
49379
- }
49380
- const dest = path80.join(skillDir, f.path);
49381
- await fs59.promises.mkdir(path80.dirname(dest), { recursive: true });
49382
- if (f.text !== void 0) {
49383
- await fs59.promises.writeFile(dest, f.text, "utf8");
49384
- } else if (f.base64) {
49385
- await fs59.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
49386
- }
49387
- }
49388
- async function installRemoteSkillsAsync(cwd, targetDir, items) {
49389
- const installed2 = [];
49390
- if (!Array.isArray(items)) {
49391
- return { success: false, error: "Invalid items" };
49392
- }
49393
- const filesWritten = { count: 0 };
49394
- try {
49395
- for (const item of items) {
49396
- if (!isValidRemoteSkillInstallItem(item)) continue;
49397
- const skillDir = path80.join(cwd, targetDir, item.skillName);
49398
- for (const f of item.files) {
49399
- await writeInstallFileAsync(skillDir, f, filesWritten);
49400
- }
49401
- installed2.push({
49402
- sourceId: item.sourceId,
49403
- skillName: item.skillName,
49404
- versionHash: item.versionHash
49405
- });
49406
- }
49407
- return { success: true, installed: installed2 };
49408
- } catch (e) {
49409
- return { success: false, error: e instanceof Error ? e.message : String(e) };
49410
- }
49411
- }
49412
-
49413
- // src/routing/handlers/install-skills.ts
49414
- var handleInstallSkillsMessage = (msg, deps) => {
49415
- const id = typeof msg.id === "string" ? msg.id : "";
49416
- const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
49417
- const rawItems = msg.items;
49418
- const cwd = getBridgeRoot();
49419
- void (async () => {
49420
- const socket = deps.getWs();
49421
- const result = await installRemoteSkillsAsync(cwd, targetDir, rawItems);
49422
- if (!result.success) {
49423
- const err = result.error ?? "Invalid items";
49424
- deps.log(`[Bridge service] Install skills failed: ${err}`);
49425
- if (socket) {
49426
- sendWsMessage(socket, { type: "install_skills_result", id, success: false, error: err });
49427
- }
49428
- return;
49429
- }
49430
- if (socket) {
49431
- sendWsMessage(socket, { type: "install_skills_result", id, success: true, installed: result.installed });
49432
- }
49433
- })();
49434
- };
49435
-
49436
- // src/routing/handlers/refresh-local-skills.ts
49437
- var handleRefreshLocalSkills = (_msg, deps) => {
49438
- setImmediate(() => {
49439
- deps.sendLocalSkillsReport?.();
49440
- });
49441
- };
49442
-
49443
- // src/routing/handlers/git/handle-session-git-commit.ts
49444
- async function handleSessionGitCommitAction(deps, sessionId, msg, reply) {
49445
- const branch = typeof msg.branch === "string" ? msg.branch : "";
49446
- const message = typeof msg.message === "string" ? msg.message : "";
49447
- const pushAfterCommit = msg.pushAfterCommit === true;
49448
- if (!branch.trim() || !message.trim()) {
49449
- reply({ ok: false, error: "branch and message are required for commit" });
49450
- return;
49451
- }
49452
- const commitRes = await deps.sessionWorktreeManager.commitSession({
49453
- sessionId,
49454
- branch: branch.trim(),
49455
- message: message.trim(),
49456
- push: pushAfterCommit
49457
- });
49458
- if (!commitRes.ok) {
49459
- reply({ ok: false, error: commitRes.error ?? "Commit failed" });
49460
- return;
49461
- }
49462
- const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
49463
- reply({
49464
- ok: true,
49465
- hasUncommittedChanges: st.hasUncommittedChanges,
49466
- hasUnpushedCommits: st.hasUnpushedCommits,
49467
- uncommittedFileCount: st.uncommittedFileCount
49468
- });
49469
- }
49470
-
49471
- // src/routing/handlers/git/session-git-changes-params.ts
49472
- function readString(value) {
49473
- return typeof value === "string" ? value.trim() : "";
49474
- }
49475
- function readChangesObject(msg) {
49476
- const changes = msg.changes;
49477
- if (!changes || typeof changes !== "object" || Array.isArray(changes)) return null;
49478
- return changes;
49479
- }
49480
- function parseSessionGitChangesParams(msg) {
49481
- const nested = readChangesObject(msg);
49482
- const file2 = nested?.file && typeof nested.file === "object" && !Array.isArray(nested.file) ? nested.file : null;
49483
- const repoRelPath = readString(nested?.repoRelPath) || readString(msg.changesRepoRelPath);
49484
- const viewRaw = nested?.view ?? msg.changesView;
49485
- const view = viewRaw === "commit" ? "commit" : "working";
49486
- const commitSha = readString(nested?.commitSha) || readString(msg.changesCommitSha);
49487
- const recentCommitsLimit = nested?.recentCommitsLimit ?? msg.changesRecentCommitsLimit;
49488
- return {
49489
- repoRelPath,
49490
- view,
49491
- commitSha,
49492
- recentCommitsLimit: typeof recentCommitsLimit === "number" || typeof recentCommitsLimit === "string" ? recentCommitsLimit : void 0,
49493
- fileWorkspaceRelPath: readString(file2?.workspaceRelPath),
49494
- fileChange: readString(file2?.change),
49495
- fileMovedFromWorkspaceRelPath: readString(file2?.movedFromWorkspaceRelPath)
49496
- };
49497
- }
49498
-
49499
- // src/routing/handlers/git/handle-session-git-get-change-file-patch.ts
49500
- async function handleSessionGitGetChangeFilePatchAction(deps, msg, reply) {
49501
- const changes = parseSessionGitChangesParams(msg);
49502
- const { repoRelPath: repoRel, view, commitSha, fileWorkspaceRelPath, fileChange, fileMovedFromWorkspaceRelPath } = changes;
49503
- const change = parseWorkingTreeChangeKind(fileChange);
49504
- if (!repoRel || !fileWorkspaceRelPath || !change) {
49505
- reply({
49506
- ok: false,
49507
- error: "changes.repoRelPath, changes.file.workspaceRelPath, and changes.file.change are required"
49508
- });
49509
- return;
49510
- }
49511
- if (view === "commit" && !commitSha) {
49512
- reply({ ok: false, error: "changes.commitSha is required for commit file patch" });
49513
- return;
49514
- }
49515
- const patch = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeFilePatch(msg.sessionId, {
49516
- repoRelPath: repoRel,
49517
- workspaceRelPath: fileWorkspaceRelPath,
49518
- change,
49519
- movedFromWorkspaceRelPath: fileMovedFromWorkspaceRelPath || null,
49520
- basis: view === "commit" ? { kind: "commit", sha: commitSha } : { kind: "working" }
49521
- });
49522
- reply(
49523
- {
49524
- ok: true,
49525
- patchContent: patch.patchContent ?? null,
49526
- totalLines: patch.totalLines
49527
- },
49528
- ["patchContent"]
49529
- );
49530
- }
49531
-
49532
- // src/routing/handlers/git/coalesce-list-changes.ts
49533
- var listChangesInflight = /* @__PURE__ */ new Map();
49534
- function normalizeListChangesRepoPathRelativeToWorkspaceRoot(repoRelPath) {
49535
- if (!repoRelPath?.trim()) return "";
49536
- return normalizeRepoPathRelativeToWorkspaceRoot(repoRelPath.trim());
49537
- }
49538
- function listChangesInflightKey(sessionId, opts) {
49539
- const basis = opts?.basis;
49540
- return [
49541
- sessionId,
49542
- normalizeListChangesRepoPathRelativeToWorkspaceRoot(opts?.repoRelPath),
49543
- basis?.kind === "commit" ? `commit:${basis.sha}` : "working",
49544
- String(opts?.recentCommitsLimit ?? "")
49545
- ].join("|");
49621
+ if (typeof f.path !== "string" || !f.text && !f.base64) return;
49622
+ if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
49623
+ await yieldToEventLoop();
49624
+ }
49625
+ const dest = path79.join(skillDir, f.path);
49626
+ await fs60.promises.mkdir(path79.dirname(dest), { recursive: true });
49627
+ if (f.text !== void 0) {
49628
+ await fs60.promises.writeFile(dest, f.text, "utf8");
49629
+ } else if (f.base64) {
49630
+ await fs60.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
49631
+ }
49546
49632
  }
49547
- function getSessionWorkingTreeChangeDetailsCoalesced(sessionWorktreeManager, sessionId, opts) {
49548
- const key = listChangesInflightKey(sessionId, opts);
49549
- const existing = listChangesInflight.get(key);
49550
- if (existing) return existing;
49551
- const promise2 = sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts).finally(() => {
49552
- if (listChangesInflight.get(key) === promise2) listChangesInflight.delete(key);
49553
- });
49554
- listChangesInflight.set(key, promise2);
49555
- return promise2;
49633
+ async function installRemoteSkillsAsync(cwd, targetDir, items) {
49634
+ const installed2 = [];
49635
+ if (!Array.isArray(items)) {
49636
+ return { success: false, error: "Invalid items" };
49637
+ }
49638
+ const filesWritten = { count: 0 };
49639
+ try {
49640
+ for (const item of items) {
49641
+ if (!isValidRemoteSkillInstallItem(item)) continue;
49642
+ const skillDir = path79.join(cwd, targetDir, item.skillName);
49643
+ for (const f of item.files) {
49644
+ await writeInstallFileAsync(skillDir, f, filesWritten);
49645
+ }
49646
+ installed2.push({
49647
+ sourceId: item.sourceId,
49648
+ skillName: item.skillName,
49649
+ versionHash: item.versionHash
49650
+ });
49651
+ }
49652
+ return { success: true, installed: installed2 };
49653
+ } catch (e) {
49654
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
49655
+ }
49556
49656
  }
49557
49657
 
49558
- // src/routing/handlers/git/handle-session-git-list-changes.ts
49559
- async function handleSessionGitListChangesAction(deps, msg, reply) {
49560
- const changes = parseSessionGitChangesParams(msg);
49561
- const { repoRelPath: repoRel, view, commitSha, recentCommitsLimit } = changes;
49562
- if (view === "commit") {
49563
- if (!repoRel || !commitSha) {
49564
- reply({
49565
- ok: false,
49566
- error: "changes.repoRelPath and changes.commitSha are required for commit view"
49567
- });
49658
+ // src/routing/handlers/install-skills.ts
49659
+ var handleInstallSkillsMessage = (msg, deps) => {
49660
+ const id = typeof msg.id === "string" ? msg.id : "";
49661
+ const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
49662
+ const rawItems = msg.items;
49663
+ const cwd = getBridgeRoot();
49664
+ void (async () => {
49665
+ const socket = deps.getWs();
49666
+ const result = await installRemoteSkillsAsync(cwd, targetDir, rawItems);
49667
+ if (!result.success) {
49668
+ const err = result.error ?? "Invalid items";
49669
+ deps.log(`[Bridge service] Install skills failed: ${err}`);
49670
+ if (socket) {
49671
+ sendWsMessage(socket, { type: "install_skills_result", id, success: false, error: err });
49672
+ }
49568
49673
  return;
49569
49674
  }
49570
- }
49571
- const opts = repoRel && view === "commit" && commitSha ? {
49572
- repoRelPath: repoRel,
49573
- basis: { kind: "commit", sha: commitSha },
49574
- recentCommitsLimit
49575
- } : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
49576
- const repos = await getSessionWorkingTreeChangeDetailsCoalesced(
49577
- deps.sessionWorktreeManager,
49578
- msg.sessionId,
49579
- opts
49580
- );
49581
- reply({ ok: true, repos }, ["repos"]);
49675
+ if (socket) {
49676
+ sendWsMessage(socket, { type: "install_skills_result", id, success: true, installed: result.installed });
49677
+ }
49678
+ })();
49679
+ };
49680
+
49681
+ // src/agents/install/commands/run-npm-global-install.ts
49682
+ import { execFile as execFile9 } from "node:child_process";
49683
+ import { promisify as promisify10 } from "node:util";
49684
+ var execFileAsync8 = promisify10(execFile9);
49685
+ async function runNpmGlobalInstall(packageName, env, timeoutMs = 3e5) {
49686
+ await execFileAsync8("npm", ["install", "-g", packageName], { timeout: timeoutMs, env });
49582
49687
  }
49583
49688
 
49584
- // src/routing/handlers/git/handle-session-git-push.ts
49585
- async function handleSessionGitPushAction(deps, sessionId, reply) {
49586
- const pushRes = await deps.sessionWorktreeManager.pushSessionUpstream(sessionId);
49587
- if (!pushRes.ok) {
49588
- reply({ ok: false, error: pushRes.error ?? "Push failed" });
49589
- return;
49689
+ // src/agents/install/commands/claude-code.ts
49690
+ var claudeCodeInstallCommand = {
49691
+ agentType: "claude-code",
49692
+ detectCommand: "claude",
49693
+ async install(ctx) {
49694
+ ctx.onProgress?.("Installing Anthropic Claude Code");
49695
+ await runNpmGlobalInstall("@anthropic-ai/claude-code", {
49696
+ ...ctx.env,
49697
+ ANTHROPIC_API_KEY: ctx.authToken
49698
+ });
49590
49699
  }
49591
- const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
49592
- reply({
49593
- ok: true,
49594
- hasUncommittedChanges: st.hasUncommittedChanges,
49595
- hasUnpushedCommits: st.hasUnpushedCommits,
49596
- uncommittedFileCount: st.uncommittedFileCount
49597
- });
49598
- }
49700
+ };
49599
49701
 
49600
- // src/routing/handlers/git/handle-session-git-status.ts
49601
- async function handleSessionGitStatusAction(deps, sessionId, reply) {
49602
- const r = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
49603
- reply({
49604
- ok: true,
49605
- hasUncommittedChanges: r.hasUncommittedChanges,
49606
- hasUnpushedCommits: r.hasUnpushedCommits,
49607
- uncommittedFileCount: r.uncommittedFileCount
49608
- });
49609
- }
49702
+ // src/agents/install/commands/codex-acp.ts
49703
+ var codexAcpInstallCommand = {
49704
+ agentType: "codex-acp",
49705
+ detectCommand: "codex",
49706
+ async install(ctx) {
49707
+ ctx.onProgress?.("Installing Codex");
49708
+ await runNpmGlobalInstall("@openai/codex", {
49709
+ ...ctx.env,
49710
+ OPENAI_API_KEY: ctx.authToken
49711
+ });
49712
+ }
49713
+ };
49610
49714
 
49611
- // src/routing/handlers/git/send-session-git-result.ts
49612
- function sendSessionGitResult(ws, id, payload, e2ee, encryptedFields = []) {
49613
- if (!ws) return;
49614
- const message = { type: "session_git_result", id, ...payload };
49615
- let wire = message;
49616
- if (e2ee && encryptedFields.length > 0) {
49617
- wire = e2ee.encryptFields(message, encryptedFields);
49715
+ // src/agents/install/commands/cursor-cli.ts
49716
+ import { execFile as execFile10 } from "node:child_process";
49717
+ import { promisify as promisify11 } from "node:util";
49718
+ var execFileAsync9 = promisify11(execFile10);
49719
+ var cursorCliInstallCommand = {
49720
+ agentType: "cursor-cli",
49721
+ detectCommand: "agent",
49722
+ async install(ctx) {
49723
+ ctx.onProgress?.("Installing Cursor CLI");
49724
+ await execFileAsync9("bash", ["-lc", "curl -fsSL https://cursor.com/install | bash"], {
49725
+ timeout: 3e5,
49726
+ env: { ...ctx.env, CURSOR_API_KEY: ctx.authToken }
49727
+ });
49618
49728
  }
49619
- sendWsMessage(ws, wire);
49729
+ };
49730
+
49731
+ // src/agents/install/commands/opencode.ts
49732
+ var opencodeInstallCommand = {
49733
+ agentType: "opencode",
49734
+ detectCommand: "opencode",
49735
+ async install(ctx) {
49736
+ ctx.onProgress?.("Installing OpenCode");
49737
+ await runNpmGlobalInstall("opencode-ai", {
49738
+ ...ctx.env,
49739
+ OPENCODE_API_KEY: ctx.authToken
49740
+ });
49741
+ }
49742
+ };
49743
+
49744
+ // src/agents/install/commands/index.ts
49745
+ var COMMANDS = [
49746
+ claudeCodeInstallCommand,
49747
+ codexAcpInstallCommand,
49748
+ cursorCliInstallCommand,
49749
+ opencodeInstallCommand
49750
+ ];
49751
+ var byType = new Map(COMMANDS.map((c) => [c.agentType, c]));
49752
+ function getAgentInstallCommand(agentType) {
49753
+ return byType.get(agentType);
49754
+ }
49755
+
49756
+ // src/agents/install/install-local-agent.ts
49757
+ async function installLocalAgentOnBridge(params) {
49758
+ const spec = INSTALLABLE_BRIDGE_AGENTS.find((a) => a.value === params.agentType);
49759
+ if (!spec) return { success: false, error: `Unsupported agent type: ${params.agentType}` };
49760
+ const command = getAgentInstallCommand(params.agentType);
49761
+ if (!command) return { success: false, error: `No install command for ${params.agentType}` };
49762
+ params.onProgress?.(`Configuring ${spec.label} credentials`);
49763
+ try {
49764
+ await command.install({
49765
+ authToken: params.authToken,
49766
+ onProgress: params.onProgress,
49767
+ env: { ...process.env, [spec.tokenEnvVar]: params.authToken }
49768
+ });
49769
+ } catch (e) {
49770
+ const msg = e instanceof Error ? e.message : String(e);
49771
+ return { success: false, error: msg };
49772
+ }
49773
+ const found = await isCommandOnPath(command.detectCommand);
49774
+ if (!found) {
49775
+ return { success: false, error: `${command.detectCommand} not found on PATH after install` };
49776
+ }
49777
+ return { success: true };
49620
49778
  }
49621
49779
 
49622
- // src/routing/handlers/git/session-git-request.ts
49623
- var handleSessionGitRequestMessage = (msg, deps) => {
49624
- if (typeof msg.id !== "string") return;
49625
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49626
- const action = msg.action;
49627
- if (!sessionId || action !== "status" && action !== "push" && action !== "commit" && action !== "list_changes" && action !== "file_diff")
49628
- return;
49780
+ // src/routing/handlers/install-agent.ts
49781
+ var handleInstallAgentMessage = (msg, deps) => {
49782
+ const processId = typeof msg.processId === "string" ? msg.processId : "";
49783
+ const agentId = typeof msg.agentId === "string" ? msg.agentId : "";
49784
+ const tokenId = typeof msg.tokenId === "string" ? msg.tokenId : "";
49785
+ const agentType = typeof msg.agentType === "string" ? msg.agentType : "";
49786
+ const authToken = typeof msg.authToken === "string" ? msg.authToken : "";
49787
+ if (!processId || !agentId || !tokenId || !agentType || !authToken) return;
49629
49788
  void (async () => {
49630
- const ws = deps.getWs();
49631
- const reply = (payload, encryptedFields = []) => sendSessionGitResult(ws, msg.id, payload, deps.e2ee, encryptedFields);
49632
- try {
49633
- if (action === "status") {
49634
- await handleSessionGitStatusAction(deps, sessionId, reply);
49635
- return;
49636
- }
49637
- if (action === "list_changes") {
49638
- await handleSessionGitListChangesAction(deps, { ...msg, sessionId }, reply);
49639
- return;
49640
- }
49641
- if (action === "file_diff") {
49642
- await handleSessionGitGetChangeFilePatchAction(deps, { ...msg, sessionId }, reply);
49643
- return;
49644
- }
49645
- if (action === "push") {
49646
- await handleSessionGitPushAction(deps, sessionId, reply);
49647
- return;
49789
+ const socket = deps.getWs();
49790
+ const report = (step, message) => {
49791
+ if (socket) {
49792
+ sendWsMessage(socket, {
49793
+ type: "install_agent_progress",
49794
+ processId,
49795
+ agentId,
49796
+ tokenId,
49797
+ agentType,
49798
+ step,
49799
+ message
49800
+ });
49648
49801
  }
49649
- await handleSessionGitCommitAction(deps, sessionId, msg, reply);
49650
- } catch (e) {
49651
- reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
49802
+ };
49803
+ const result = await installLocalAgentOnBridge({
49804
+ agentType,
49805
+ authToken,
49806
+ onProgress: (message) => report("agent_install_package", message)
49807
+ });
49808
+ if (socket) {
49809
+ sendWsMessage(socket, {
49810
+ type: "install_agent_result",
49811
+ processId,
49812
+ agentId,
49813
+ tokenId,
49814
+ agentType,
49815
+ success: result.success,
49816
+ error: result.error
49817
+ });
49818
+ }
49819
+ if (!result.success) {
49820
+ deps.log(`[Bridge service] Install agent failed: ${result.error ?? "unknown"}`);
49652
49821
  }
49653
49822
  })();
49654
49823
  };
49655
49824
 
49656
- // src/routing/handlers/rename-session-branch.ts
49657
- var handleRenameSessionBranchMessage = (msg, deps) => {
49658
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49659
- const newBranch = typeof msg.newBranch === "string" ? msg.newBranch : "";
49660
- if (!sessionId || !newBranch) return;
49661
- void deps.sessionWorktreeManager.renameSessionBranch(sessionId, newBranch);
49825
+ // src/routing/handlers/refresh-local-skills.ts
49826
+ var handleRefreshLocalSkills = (_msg, deps) => {
49827
+ setImmediate(() => {
49828
+ deps.sendLocalSkillsReport?.();
49829
+ });
49662
49830
  };
49663
49831
 
49664
- // src/routing/handlers/session-archived.ts
49665
- var handleSessionArchivedMessage = (msg, deps) => {
49666
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49667
- if (!sessionId) return;
49668
- void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
49669
- };
49832
+ // src/routing/dispatch/skills.ts
49833
+ function dispatchBridgeSkillsMessage(msg, deps) {
49834
+ switch (msg.type) {
49835
+ case "skill_call":
49836
+ handleSkillCallMessage(msg, deps);
49837
+ break;
49838
+ case "skill_layout_request":
49839
+ handleSkillLayoutRequest(msg, deps);
49840
+ break;
49841
+ case "install_skills":
49842
+ handleInstallSkillsMessage(msg, deps);
49843
+ break;
49844
+ case "install_agent":
49845
+ handleInstallAgentMessage(msg, deps);
49846
+ break;
49847
+ case "refresh_local_skills":
49848
+ handleRefreshLocalSkills(msg, deps);
49849
+ break;
49850
+ }
49851
+ }
49670
49852
 
49671
- // src/routing/handlers/session-discarded.ts
49672
- var handleSessionDiscardedMessage = (msg, deps) => {
49673
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49674
- if (!sessionId) return;
49675
- void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
49676
- };
49853
+ // src/files/browser/send-file-browser-message.ts
49854
+ function sendFileBrowserMessage(socket, e2ee, payload) {
49855
+ sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
49856
+ }
49677
49857
 
49678
- // src/routing/handlers/revert-turn-snapshot.ts
49679
- import * as fs60 from "node:fs";
49680
- var handleRevertTurnSnapshotMessage = (msg, deps) => {
49681
- const id = typeof msg.id === "string" ? msg.id : "";
49682
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49683
- const turnId = typeof msg.turnId === "string" ? msg.turnId : "";
49684
- if (!id || !sessionId || !turnId) return;
49685
- const { getWs, log: log2, sessionWorktreeManager } = deps;
49858
+ // src/files/browser/handle-file-browser-list.ts
49859
+ async function handleFileBrowserList(socket, e2ee, id, reqPath, sessionParentPath, gitScope) {
49860
+ const result = await executeFileBrowserList({ reqPath, sessionParentPath, gitScope });
49861
+ if ("error" in result) {
49862
+ sendWsMessage(socket, { type: "file_browser_response", id, error: result.error });
49863
+ return;
49864
+ }
49865
+ sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id, entries: result.entries });
49866
+ }
49867
+
49868
+ // src/files/browser/handle-file-browser-read.ts
49869
+ async function handleFileBrowserRead(socket, e2ee, msg, reqPath, sessionParentPath, gitScope) {
49870
+ const result = await executeFileBrowserRead({ msg, reqPath, sessionParentPath, gitScope });
49871
+ if ("error" in result) {
49872
+ sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
49873
+ return;
49874
+ }
49875
+ const payload = {
49876
+ type: "file_browser_response",
49877
+ id: msg.id,
49878
+ content: result.content,
49879
+ totalLines: result.totalLines,
49880
+ size: result.size
49881
+ };
49882
+ if (result.lineOffset != null) payload.lineOffset = result.lineOffset;
49883
+ if (result.mimeType != null) payload.mimeType = result.mimeType;
49884
+ if (result.resolvedPath != null) payload.resolvedPath = result.resolvedPath;
49885
+ sendFileBrowserMessage(socket, e2ee, payload);
49886
+ }
49887
+
49888
+ // src/files/browser/index.ts
49889
+ init_in_flight();
49890
+ function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
49891
+ beginFileBrowserRequest();
49686
49892
  void (async () => {
49687
- const s = getWs();
49688
- if (!s) return;
49689
- const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
49690
- const file2 = snapshotFilePath(agentBase, turnId);
49691
49893
  try {
49692
- await fs60.promises.access(file2, fs60.constants.F_OK);
49693
- } catch {
49694
- sendWsMessage(s, {
49695
- type: "revert_turn_snapshot_result",
49696
- id,
49697
- ok: false,
49698
- error: "No snapshot found for this turn (git state may be unavailable)."
49699
- });
49700
- return;
49894
+ const reqPath = msg.path.replace(/^\/+/, "") || ".";
49895
+ const sessionParentPath = sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : void 0;
49896
+ const gitScope = resolveGitBranchScope(msg);
49897
+ if (msg.source === "git_branch" && typeof msg.repoRelPath === "string" && typeof msg.branch === "string" && msg.branch.trim().length > 0 && !gitScope) {
49898
+ sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: "Invalid repository path" });
49899
+ return;
49900
+ }
49901
+ if (msg.op === "read") {
49902
+ await handleFileBrowserRead(socket, e2ee, msg, reqPath, sessionParentPath, gitScope);
49903
+ } else {
49904
+ await handleFileBrowserList(socket, e2ee, msg.id, reqPath, sessionParentPath, gitScope);
49905
+ }
49906
+ } finally {
49907
+ endFileBrowserRequest();
49701
49908
  }
49702
- const res = await applyPreTurnSnapshot(file2, log2);
49703
- sendWsMessage(s, {
49704
- type: "revert_turn_snapshot_result",
49705
- id,
49706
- ok: res.ok,
49707
- ...res.error ? { error: res.error } : {}
49708
- });
49709
49909
  })();
49710
- };
49711
-
49712
- // src/routing/handlers/preview-environment-control.ts
49713
- var handlePreviewEnvironmentControl = (msg, deps) => {
49714
- let wire;
49715
- try {
49716
- wire = deps.e2ee ? deps.e2ee.decryptMessage(msg) : msg;
49717
- } catch (e) {
49718
- deps.log(`[E2EE] Could not decrypt preview environment command: ${e instanceof Error ? e.message : String(e)}`);
49719
- return;
49720
- }
49721
- const environmentId = typeof wire.environmentId === "string" ? wire.environmentId : "";
49722
- const action = wire.action === "start" || wire.action === "stop" ? wire.action : null;
49723
- if (!environmentId || !action) return;
49724
- deps.previewEnvironmentManager?.handleControl(environmentId, action);
49725
- };
49910
+ }
49726
49911
 
49727
- // src/routing/handlers/preview-environments-config.ts
49728
- var VALID_PORT = (p) => typeof p === "number" && Number.isInteger(p) && p > 0 && p < 65536;
49729
- var handlePreviewEnvironmentsConfig = (msg, deps) => {
49730
- const previewEnvironments = msg.previewEnvironments;
49731
- const proxyPorts = Array.isArray(msg.proxyPorts) ? msg.proxyPorts.filter(VALID_PORT) : void 0;
49732
- setImmediate(() => {
49733
- deps.previewEnvironmentManager?.applyConfig(previewEnvironments ?? []);
49734
- const environmentIds = parsePreviewEnvironmentDefs(previewEnvironments ?? []).map((d) => d.environmentId);
49735
- void deps.previewWorktreeManager?.syncConfiguredEnvironments(environmentIds);
49736
- if (proxyPorts !== void 0) {
49737
- deps.updateFirehoseProxyPorts?.(proxyPorts);
49912
+ // src/files/handle-file-browser-search.ts
49913
+ init_yield_to_event_loop();
49914
+ import path80 from "node:path";
49915
+ var SEARCH_LIMIT = 100;
49916
+ function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
49917
+ void (async () => {
49918
+ await yieldToEventLoop();
49919
+ const q = typeof msg.q === "string" ? msg.q : "";
49920
+ const sessionParentPath = path80.resolve(
49921
+ sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
49922
+ );
49923
+ if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
49924
+ triggerFileIndexBuild(sessionParentPath);
49925
+ const payload2 = {
49926
+ type: "file_browser_search_response",
49927
+ id: msg.id,
49928
+ paths: [],
49929
+ indexReady: false
49930
+ };
49931
+ sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
49932
+ return;
49738
49933
  }
49934
+ const results = await searchBridgeFilePathsAsync(sessionParentPath, q, SEARCH_LIMIT);
49935
+ const payload = {
49936
+ type: "file_browser_search_response",
49937
+ id: msg.id,
49938
+ paths: results,
49939
+ indexReady: true
49940
+ };
49941
+ sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
49942
+ })();
49943
+ }
49944
+ function triggerFileIndexBuild(sessionParentPath = getBridgeRoot()) {
49945
+ setImmediate(() => {
49946
+ void ensureFileIndexAsync(sessionParentPath).catch((e) => {
49947
+ console.error("[file-index] Background build failed:", e);
49948
+ });
49739
49949
  });
49740
- };
49741
-
49742
- // src/routing/handlers/send-deploy-session-to-preview-result.ts
49743
- function sendDeploySessionToPreviewResult(ws, id, payload) {
49744
- if (!ws) return;
49745
- sendWsMessage(ws, { type: "deploy_session_to_preview_result", id, ...payload });
49746
49950
  }
49747
49951
 
49748
- // src/routing/handlers/deploy-session-to-preview.ts
49749
- var handleDeploySessionToPreviewMessage = (msg, deps) => {
49952
+ // src/routing/handlers/file-browser-messages.ts
49953
+ function handleFileBrowserRequestMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
49954
+ if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
49955
+ const socket = getWs();
49956
+ if (!socket) return;
49957
+ handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager);
49958
+ }
49959
+ function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
49750
49960
  if (typeof msg.id !== "string") return;
49751
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
49752
- const environmentId = typeof msg.environmentId === "string" ? msg.environmentId : "";
49753
- if (!sessionId || !environmentId) return;
49961
+ const socket = getWs();
49962
+ if (!socket) return;
49963
+ handleFileBrowserSearch(
49964
+ msg,
49965
+ socket,
49966
+ e2ee,
49967
+ sessionWorktreeManager
49968
+ );
49969
+ }
49970
+
49971
+ // src/code-nav/handlers/send-code-nav-response.ts
49972
+ var ENCRYPTED_FIELDS = ["definition", "definitions", "references", "symbols", "confidentTarget"];
49973
+ function sendCodeNavResponse(socket, e2ee, payload) {
49974
+ const fieldsToEncrypt = ENCRYPTED_FIELDS.filter((field) => field in payload && payload[field] !== void 0);
49975
+ sendWsMessage(
49976
+ socket,
49977
+ e2ee && fieldsToEncrypt.length > 0 ? e2ee.encryptFields(payload, [...fieldsToEncrypt]) : payload
49978
+ );
49979
+ }
49980
+
49981
+ // src/code-nav/handlers/handle-code-nav-request.ts
49982
+ function handleCodeNavRequest(msg, socket, e2ee, sessionWorktreeManager) {
49754
49983
  void (async () => {
49755
- const ws = deps.getWs();
49756
- const reply = (payload) => sendDeploySessionToPreviewResult(ws, msg.id, payload);
49757
- try {
49758
- if (!deps.previewWorktreeManager) {
49759
- reply({ ok: false, error: "Preview worktree manager unavailable" });
49760
- return;
49761
- }
49762
- const result = await deploySessionToPreviewEnvironment({
49763
- sessionWorktreeManager: deps.sessionWorktreeManager,
49764
- previewWorktreeManager: deps.previewWorktreeManager,
49765
- sessionId,
49766
- environmentId,
49767
- log: deps.log
49768
- });
49769
- reply(result);
49770
- } catch (e) {
49771
- reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
49772
- }
49984
+ const body = await executeCodeNavRequest(msg, sessionWorktreeManager);
49985
+ sendCodeNavResponse(socket, e2ee, {
49986
+ type: "code_nav_response",
49987
+ id: msg.id,
49988
+ ...body
49989
+ });
49773
49990
  })();
49774
- };
49991
+ }
49992
+
49993
+ // src/code-nav/handlers/trigger-symbol-index-build.ts
49994
+ init_normalize_resolved_path();
49995
+
49996
+ // src/routing/handlers/code-nav-messages.ts
49997
+ function handleFileBrowserCodeNavMessage(msg, { getWs, e2ee, sessionWorktreeManager }) {
49998
+ if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
49999
+ const socket = getWs();
50000
+ if (!socket) return;
50001
+ handleCodeNavRequest(
50002
+ {
50003
+ id: msg.id,
50004
+ type: "code_nav",
50005
+ op: msg.op === "references" ? "references" : msg.op === "symbols" ? "symbols" : msg.op === "definitions" ? "definitions" : "definition",
50006
+ path: msg.path,
50007
+ line: typeof msg.line === "number" ? msg.line : 1,
50008
+ column: typeof msg.column === "number" ? msg.column : 0,
50009
+ sessionId: msg.sessionId
50010
+ },
50011
+ socket,
50012
+ e2ee,
50013
+ sessionWorktreeManager
50014
+ );
50015
+ }
49775
50016
 
49776
50017
  // src/git/bridge-git-context.ts
49777
50018
  import * as path81 from "node:path";
@@ -49918,80 +50159,64 @@ function handleListRepoBranchesRequestMessage(msg, getWs) {
49918
50159
  })();
49919
50160
  }
49920
50161
 
49921
- // src/routing/dispatch-bridge-message.ts
50162
+ // src/routing/dispatch/file-browser.ts
50163
+ function dispatchBridgeFileBrowserMessage(msg, deps) {
50164
+ switch (msg.type) {
50165
+ case "file_browser_request":
50166
+ handleFileBrowserRequestMessage(msg, deps);
50167
+ break;
50168
+ case "file_browser_search":
50169
+ handleFileBrowserSearchMessage(msg, deps);
50170
+ break;
50171
+ case "code_nav":
50172
+ handleFileBrowserCodeNavMessage(msg, deps);
50173
+ break;
50174
+ case "bridge_git_context_request":
50175
+ handleBridgeGitContextRequestMessage(msg, deps.getWs);
50176
+ break;
50177
+ case "list_repo_branches_request":
50178
+ handleListRepoBranchesRequestMessage(msg, deps.getWs);
50179
+ break;
50180
+ }
50181
+ }
50182
+
50183
+ // src/routing/dispatch/index.ts
49922
50184
  function dispatchBridgeMessage(msg, deps) {
49923
50185
  switch (msg.type) {
49924
50186
  case "auth_token":
49925
- handleAuthToken(msg, deps);
49926
- break;
49927
50187
  case "bridge_identified":
49928
- handleBridgeIdentified(msg, deps);
49929
- break;
49930
50188
  case "ha":
49931
- handleBridgeHeartbeatAck(msg, deps);
50189
+ dispatchBridgeConnectionMessage(msg, deps);
49932
50190
  break;
49933
50191
  case "preview_environments_config":
49934
- handlePreviewEnvironmentsConfig(msg, deps);
49935
- break;
49936
50192
  case "preview_environment_control":
49937
- handlePreviewEnvironmentControl(msg, deps);
49938
- break;
49939
50193
  case "deploy_session_to_preview":
49940
- handleDeploySessionToPreviewMessage(msg, deps);
50194
+ dispatchBridgePreviewMessage(msg, deps);
49941
50195
  break;
49942
50196
  case "agent_config":
49943
- handleAgentConfigMessage(msg, deps);
49944
- break;
49945
50197
  case "prompt_queue_state":
49946
- handlePromptQueueStateMessage(msg, deps);
49947
- break;
49948
50198
  case "prompt":
49949
- handlePromptMessage(msg, deps);
49950
- break;
49951
50199
  case "session_git_request":
49952
- handleSessionGitRequestMessage(msg, deps);
49953
- break;
49954
50200
  case "rename_session_branch":
49955
- handleRenameSessionBranchMessage(msg, deps);
49956
- break;
49957
50201
  case "session_archived":
49958
- handleSessionArchivedMessage(msg, deps);
49959
- break;
49960
50202
  case "session_discarded":
49961
- handleSessionDiscardedMessage(msg, deps);
49962
- break;
49963
50203
  case "revert_turn_snapshot":
49964
- handleRevertTurnSnapshotMessage(msg, deps);
49965
- break;
49966
50204
  case "cursor_request_response":
49967
- handleSessionRequestResponseMessage(msg, deps);
50205
+ dispatchBridgeSessionMessage(msg, deps);
49968
50206
  break;
49969
50207
  case "skill_call":
49970
- handleSkillCallMessage(msg, deps);
49971
- break;
49972
- case "file_browser_request":
49973
- handleFileBrowserRequestMessage(msg, deps);
49974
- break;
49975
- case "file_browser_search":
49976
- handleFileBrowserSearchMessage(msg, deps);
49977
- break;
49978
- case "code_nav":
49979
- handleFileBrowserCodeNavMessage(msg, deps);
49980
- break;
49981
50208
  case "skill_layout_request":
49982
- handleSkillLayoutRequest(msg, deps);
49983
- break;
49984
50209
  case "install_skills":
49985
- handleInstallSkillsMessage(msg, deps);
49986
- break;
50210
+ case "install_agent":
49987
50211
  case "refresh_local_skills":
49988
- handleRefreshLocalSkills(msg, deps);
50212
+ dispatchBridgeSkillsMessage(msg, deps);
49989
50213
  break;
50214
+ case "file_browser_request":
50215
+ case "file_browser_search":
50216
+ case "code_nav":
49990
50217
  case "bridge_git_context_request":
49991
- handleBridgeGitContextRequestMessage(msg, deps.getWs);
49992
- break;
49993
50218
  case "list_repo_branches_request":
49994
- handleListRepoBranchesRequestMessage(msg, deps.getWs);
50219
+ dispatchBridgeFileBrowserMessage(msg, deps);
49995
50220
  break;
49996
50221
  }
49997
50222
  }