@trydecember/cli 0.4.4 → 0.4.6

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/december.js +1969 -289
  3. package/package.json +2 -2
package/dist/december.js CHANGED
@@ -65,8 +65,8 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@trydecember/cli",
68
- version: "0.4.4",
69
- description: "a coding agent for terminal and cloud.",
68
+ version: "0.4.6",
69
+ description: "a (so much) better coding agent for terminal and cloud.",
70
70
  homepage: "https://trydecember.com",
71
71
  repository: {
72
72
  type: "git",
@@ -497,6 +497,7 @@ function supportsModelThinking(model) {
497
497
  function createProvider(config, streamImpl) {
498
498
  return {
499
499
  id: config.id,
500
+ api: config.api,
500
501
  stream: streamImpl
501
502
  };
502
503
  }
@@ -66427,13 +66428,22 @@ function resolveAnthropicModel(model) {
66427
66428
  function anthropicProvider(baseURL, apiKey, customClientOrDefaultHeaders, customClient) {
66428
66429
  const isCustomClient = customClientOrDefaultHeaders instanceof Anthropic || Boolean(customClientOrDefaultHeaders && typeof customClientOrDefaultHeaders.messages?.create === "function");
66429
66430
  const defaultHeaders = isCustomClient ? undefined : customClientOrDefaultHeaders;
66430
- const isOAuth = Boolean(defaultHeaders && (defaultHeaders["anthropic-beta"]?.includes("oauth") || defaultHeaders["anthropic-beta"]?.includes("claude-code"))) || Boolean(apiKey && !apiKey.startsWith("sk-ant-api"));
66431
+ let resolvedBaseURL = baseURL;
66432
+ let resolvedApiKey = apiKey;
66433
+ if (baseURL && !baseURL.startsWith("http") && !baseURL.startsWith("/") && apiKey === undefined) {
66434
+ resolvedApiKey = baseURL;
66435
+ resolvedBaseURL = undefined;
66436
+ }
66437
+ const isExplicitOAuth = Boolean(defaultHeaders && (defaultHeaders["anthropic-beta"]?.includes("oauth") || defaultHeaders["anthropic-beta"]?.includes("claude-code")));
66438
+ const isTokenOAuth = Boolean(resolvedApiKey && (resolvedApiKey.startsWith("sk-ant-oat") || resolvedApiKey.startsWith("Bearer ")));
66439
+ const isEnvOAuth = Boolean(!resolvedApiKey && !process.env.ANTHROPIC_API_KEY && (process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN));
66440
+ const isOAuth = isExplicitOAuth || isTokenOAuth || isEnvOAuth;
66431
66441
  const client = (isCustomClient ? customClientOrDefaultHeaders : customClient) || new Anthropic({
66432
- baseURL,
66433
- apiKey: isOAuth ? undefined : apiKey || process.env.ANTHROPIC_API_KEY,
66434
- authToken: isOAuth ? apiKey || process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN : undefined,
66442
+ baseURL: resolvedBaseURL,
66443
+ apiKey: isOAuth ? undefined : resolvedApiKey || process.env.ANTHROPIC_API_KEY,
66444
+ authToken: isOAuth ? resolvedApiKey || process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN : undefined,
66435
66445
  defaultHeaders: {
66436
- ...isOAuth ? { "anthropic-beta": "oauth-2024-11-18" } : {},
66446
+ ...isOAuth ? { "anthropic-beta": "claude-code-20250219,oauth-2024-06-20" } : {},
66437
66447
  ...defaultHeaders || {}
66438
66448
  }
66439
66449
  });
@@ -84297,7 +84307,7 @@ var require_websocket = __commonJS(function(exports, module) {
84297
84307
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
84298
84308
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
84299
84309
 
84300
- class WebSocket extends EventEmitter {
84310
+ class WebSocket2 extends EventEmitter {
84301
84311
  constructor(address, protocols, options) {
84302
84312
  super();
84303
84313
  this._binaryType = BINARY_TYPES[0];
@@ -84310,7 +84320,7 @@ var require_websocket = __commonJS(function(exports, module) {
84310
84320
  this._extensions = {};
84311
84321
  this._paused = false;
84312
84322
  this._protocol = "";
84313
- this._readyState = WebSocket.CONNECTING;
84323
+ this._readyState = WebSocket2.CONNECTING;
84314
84324
  this._receiver = null;
84315
84325
  this._sender = null;
84316
84326
  this._socket = null;
@@ -84412,12 +84422,12 @@ var require_websocket = __commonJS(function(exports, module) {
84412
84422
  socket.on("data", socketOnData);
84413
84423
  socket.on("end", socketOnEnd);
84414
84424
  socket.on("error", socketOnError);
84415
- this._readyState = WebSocket.OPEN;
84425
+ this._readyState = WebSocket2.OPEN;
84416
84426
  this.emit("open");
84417
84427
  }
84418
84428
  emitClose() {
84419
84429
  if (!this._socket) {
84420
- this._readyState = WebSocket.CLOSED;
84430
+ this._readyState = WebSocket2.CLOSED;
84421
84431
  this.emit("close", this._closeCode, this._closeMessage);
84422
84432
  return;
84423
84433
  }
@@ -84425,24 +84435,24 @@ var require_websocket = __commonJS(function(exports, module) {
84425
84435
  this._extensions[PerMessageDeflate.extensionName].cleanup();
84426
84436
  }
84427
84437
  this._receiver.removeAllListeners();
84428
- this._readyState = WebSocket.CLOSED;
84438
+ this._readyState = WebSocket2.CLOSED;
84429
84439
  this.emit("close", this._closeCode, this._closeMessage);
84430
84440
  }
84431
84441
  close(code, data) {
84432
- if (this.readyState === WebSocket.CLOSED)
84442
+ if (this.readyState === WebSocket2.CLOSED)
84433
84443
  return;
84434
- if (this.readyState === WebSocket.CONNECTING) {
84444
+ if (this.readyState === WebSocket2.CONNECTING) {
84435
84445
  const msg = "WebSocket was closed before the connection was established";
84436
84446
  abortHandshake(this, this._req, msg);
84437
84447
  return;
84438
84448
  }
84439
- if (this.readyState === WebSocket.CLOSING) {
84449
+ if (this.readyState === WebSocket2.CLOSING) {
84440
84450
  if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) {
84441
84451
  this._socket.end();
84442
84452
  }
84443
84453
  return;
84444
84454
  }
84445
- this._readyState = WebSocket.CLOSING;
84455
+ this._readyState = WebSocket2.CLOSING;
84446
84456
  this._sender.close(code, data, !this._isServer, (err) => {
84447
84457
  if (err)
84448
84458
  return;
@@ -84454,14 +84464,14 @@ var require_websocket = __commonJS(function(exports, module) {
84454
84464
  setCloseTimer(this);
84455
84465
  }
84456
84466
  pause() {
84457
- if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) {
84467
+ if (this.readyState === WebSocket2.CONNECTING || this.readyState === WebSocket2.CLOSED) {
84458
84468
  return;
84459
84469
  }
84460
84470
  this._paused = true;
84461
84471
  this._socket.pause();
84462
84472
  }
84463
84473
  ping(data, mask, cb) {
84464
- if (this.readyState === WebSocket.CONNECTING) {
84474
+ if (this.readyState === WebSocket2.CONNECTING) {
84465
84475
  throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
84466
84476
  }
84467
84477
  if (typeof data === "function") {
@@ -84473,7 +84483,7 @@ var require_websocket = __commonJS(function(exports, module) {
84473
84483
  }
84474
84484
  if (typeof data === "number")
84475
84485
  data = data.toString();
84476
- if (this.readyState !== WebSocket.OPEN) {
84486
+ if (this.readyState !== WebSocket2.OPEN) {
84477
84487
  sendAfterClose(this, data, cb);
84478
84488
  return;
84479
84489
  }
@@ -84482,7 +84492,7 @@ var require_websocket = __commonJS(function(exports, module) {
84482
84492
  this._sender.ping(data || EMPTY_BUFFER, mask, cb);
84483
84493
  }
84484
84494
  pong(data, mask, cb) {
84485
- if (this.readyState === WebSocket.CONNECTING) {
84495
+ if (this.readyState === WebSocket2.CONNECTING) {
84486
84496
  throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
84487
84497
  }
84488
84498
  if (typeof data === "function") {
@@ -84494,7 +84504,7 @@ var require_websocket = __commonJS(function(exports, module) {
84494
84504
  }
84495
84505
  if (typeof data === "number")
84496
84506
  data = data.toString();
84497
- if (this.readyState !== WebSocket.OPEN) {
84507
+ if (this.readyState !== WebSocket2.OPEN) {
84498
84508
  sendAfterClose(this, data, cb);
84499
84509
  return;
84500
84510
  }
@@ -84503,7 +84513,7 @@ var require_websocket = __commonJS(function(exports, module) {
84503
84513
  this._sender.pong(data || EMPTY_BUFFER, mask, cb);
84504
84514
  }
84505
84515
  resume() {
84506
- if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) {
84516
+ if (this.readyState === WebSocket2.CONNECTING || this.readyState === WebSocket2.CLOSED) {
84507
84517
  return;
84508
84518
  }
84509
84519
  this._paused = false;
@@ -84511,7 +84521,7 @@ var require_websocket = __commonJS(function(exports, module) {
84511
84521
  this._socket.resume();
84512
84522
  }
84513
84523
  send(data, options, cb) {
84514
- if (this.readyState === WebSocket.CONNECTING) {
84524
+ if (this.readyState === WebSocket2.CONNECTING) {
84515
84525
  throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
84516
84526
  }
84517
84527
  if (typeof options === "function") {
@@ -84520,7 +84530,7 @@ var require_websocket = __commonJS(function(exports, module) {
84520
84530
  }
84521
84531
  if (typeof data === "number")
84522
84532
  data = data.toString();
84523
- if (this.readyState !== WebSocket.OPEN) {
84533
+ if (this.readyState !== WebSocket2.OPEN) {
84524
84534
  sendAfterClose(this, data, cb);
84525
84535
  return;
84526
84536
  }
@@ -84537,48 +84547,48 @@ var require_websocket = __commonJS(function(exports, module) {
84537
84547
  this._sender.send(data || EMPTY_BUFFER, opts, cb);
84538
84548
  }
84539
84549
  terminate() {
84540
- if (this.readyState === WebSocket.CLOSED)
84550
+ if (this.readyState === WebSocket2.CLOSED)
84541
84551
  return;
84542
- if (this.readyState === WebSocket.CONNECTING) {
84552
+ if (this.readyState === WebSocket2.CONNECTING) {
84543
84553
  const msg = "WebSocket was closed before the connection was established";
84544
84554
  abortHandshake(this, this._req, msg);
84545
84555
  return;
84546
84556
  }
84547
84557
  if (this._socket) {
84548
- this._readyState = WebSocket.CLOSING;
84558
+ this._readyState = WebSocket2.CLOSING;
84549
84559
  this._socket.destroy();
84550
84560
  }
84551
84561
  }
84552
84562
  }
84553
- Object.defineProperty(WebSocket, "CONNECTING", {
84563
+ Object.defineProperty(WebSocket2, "CONNECTING", {
84554
84564
  enumerable: true,
84555
84565
  value: readyStates.indexOf("CONNECTING")
84556
84566
  });
84557
- Object.defineProperty(WebSocket.prototype, "CONNECTING", {
84567
+ Object.defineProperty(WebSocket2.prototype, "CONNECTING", {
84558
84568
  enumerable: true,
84559
84569
  value: readyStates.indexOf("CONNECTING")
84560
84570
  });
84561
- Object.defineProperty(WebSocket, "OPEN", {
84571
+ Object.defineProperty(WebSocket2, "OPEN", {
84562
84572
  enumerable: true,
84563
84573
  value: readyStates.indexOf("OPEN")
84564
84574
  });
84565
- Object.defineProperty(WebSocket.prototype, "OPEN", {
84575
+ Object.defineProperty(WebSocket2.prototype, "OPEN", {
84566
84576
  enumerable: true,
84567
84577
  value: readyStates.indexOf("OPEN")
84568
84578
  });
84569
- Object.defineProperty(WebSocket, "CLOSING", {
84579
+ Object.defineProperty(WebSocket2, "CLOSING", {
84570
84580
  enumerable: true,
84571
84581
  value: readyStates.indexOf("CLOSING")
84572
84582
  });
84573
- Object.defineProperty(WebSocket.prototype, "CLOSING", {
84583
+ Object.defineProperty(WebSocket2.prototype, "CLOSING", {
84574
84584
  enumerable: true,
84575
84585
  value: readyStates.indexOf("CLOSING")
84576
84586
  });
84577
- Object.defineProperty(WebSocket, "CLOSED", {
84587
+ Object.defineProperty(WebSocket2, "CLOSED", {
84578
84588
  enumerable: true,
84579
84589
  value: readyStates.indexOf("CLOSED")
84580
84590
  });
84581
- Object.defineProperty(WebSocket.prototype, "CLOSED", {
84591
+ Object.defineProperty(WebSocket2.prototype, "CLOSED", {
84582
84592
  enumerable: true,
84583
84593
  value: readyStates.indexOf("CLOSED")
84584
84594
  });
@@ -84591,10 +84601,10 @@ var require_websocket = __commonJS(function(exports, module) {
84591
84601
  "readyState",
84592
84602
  "url"
84593
84603
  ].forEach((property) => {
84594
- Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
84604
+ Object.defineProperty(WebSocket2.prototype, property, { enumerable: true });
84595
84605
  });
84596
84606
  ["open", "error", "close", "message"].forEach((method) => {
84597
- Object.defineProperty(WebSocket.prototype, `on${method}`, {
84607
+ Object.defineProperty(WebSocket2.prototype, `on${method}`, {
84598
84608
  enumerable: true,
84599
84609
  get() {
84600
84610
  for (const listener of this.listeners(method)) {
@@ -84618,9 +84628,9 @@ var require_websocket = __commonJS(function(exports, module) {
84618
84628
  }
84619
84629
  });
84620
84630
  });
84621
- WebSocket.prototype.addEventListener = addEventListener;
84622
- WebSocket.prototype.removeEventListener = removeEventListener;
84623
- module.exports = WebSocket;
84631
+ WebSocket2.prototype.addEventListener = addEventListener;
84632
+ WebSocket2.prototype.removeEventListener = removeEventListener;
84633
+ module.exports = WebSocket2;
84624
84634
  function initAsClient(websocket, address, protocols, options) {
84625
84635
  const opts = {
84626
84636
  allowSynchronousEvents: true,
@@ -84804,7 +84814,7 @@ var require_websocket = __commonJS(function(exports, module) {
84804
84814
  });
84805
84815
  req.on("upgrade", (res, socket, head) => {
84806
84816
  websocket.emit("upgrade", res);
84807
- if (websocket.readyState !== WebSocket.CONNECTING)
84817
+ if (websocket.readyState !== WebSocket2.CONNECTING)
84808
84818
  return;
84809
84819
  req = websocket._req = null;
84810
84820
  const upgrade = res.headers.upgrade;
@@ -84880,7 +84890,7 @@ var require_websocket = __commonJS(function(exports, module) {
84880
84890
  }
84881
84891
  }
84882
84892
  function emitErrorAndClose(websocket, err) {
84883
- websocket._readyState = WebSocket.CLOSING;
84893
+ websocket._readyState = WebSocket2.CLOSING;
84884
84894
  websocket._errorEmitted = true;
84885
84895
  websocket.emit("error", err);
84886
84896
  websocket.emitClose();
@@ -84897,7 +84907,7 @@ var require_websocket = __commonJS(function(exports, module) {
84897
84907
  return tls.connect(options);
84898
84908
  }
84899
84909
  function abortHandshake(websocket, stream, message) {
84900
- websocket._readyState = WebSocket.CLOSING;
84910
+ websocket._readyState = WebSocket2.CLOSING;
84901
84911
  const err = new Error(message);
84902
84912
  Error.captureStackTrace(err, abortHandshake);
84903
84913
  if (stream.setHeader) {
@@ -84977,10 +84987,10 @@ var require_websocket = __commonJS(function(exports, module) {
84977
84987
  }
84978
84988
  function senderOnError(err) {
84979
84989
  const websocket = this[kWebSocket];
84980
- if (websocket.readyState === WebSocket.CLOSED)
84990
+ if (websocket.readyState === WebSocket2.CLOSED)
84981
84991
  return;
84982
- if (websocket.readyState === WebSocket.OPEN) {
84983
- websocket._readyState = WebSocket.CLOSING;
84992
+ if (websocket.readyState === WebSocket2.OPEN) {
84993
+ websocket._readyState = WebSocket2.CLOSING;
84984
84994
  setCloseTimer(websocket);
84985
84995
  }
84986
84996
  this._socket.end();
@@ -84997,7 +85007,7 @@ var require_websocket = __commonJS(function(exports, module) {
84997
85007
  this.removeListener("close", socketOnClose);
84998
85008
  this.removeListener("data", socketOnData);
84999
85009
  this.removeListener("end", socketOnEnd);
85000
- websocket._readyState = WebSocket.CLOSING;
85010
+ websocket._readyState = WebSocket2.CLOSING;
85001
85011
  if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
85002
85012
  const chunk = this.read(this._readableState.length);
85003
85013
  websocket._receiver.write(chunk);
@@ -85019,7 +85029,7 @@ var require_websocket = __commonJS(function(exports, module) {
85019
85029
  }
85020
85030
  function socketOnEnd() {
85021
85031
  const websocket = this[kWebSocket];
85022
- websocket._readyState = WebSocket.CLOSING;
85032
+ websocket._readyState = WebSocket2.CLOSING;
85023
85033
  websocket._receiver.end();
85024
85034
  this.end();
85025
85035
  }
@@ -85028,7 +85038,7 @@ var require_websocket = __commonJS(function(exports, module) {
85028
85038
  this.removeListener("error", socketOnError);
85029
85039
  this.on("error", NOOP);
85030
85040
  if (websocket) {
85031
- websocket._readyState = WebSocket.CLOSING;
85041
+ websocket._readyState = WebSocket2.CLOSING;
85032
85042
  this.destroy();
85033
85043
  }
85034
85044
  }
@@ -85036,7 +85046,7 @@ var require_websocket = __commonJS(function(exports, module) {
85036
85046
 
85037
85047
  // ../../node_modules/.bun/ws@8.21.0/node_modules/ws/lib/stream.js
85038
85048
  var require_stream = __commonJS(function(exports, module) {
85039
- var WebSocket = require_websocket();
85049
+ var WebSocket2 = require_websocket();
85040
85050
  var { Duplex } = __require("stream");
85041
85051
  function emitClose(stream) {
85042
85052
  stream.emit("close");
@@ -85191,7 +85201,7 @@ var require_websocket_server = __commonJS(function(exports, module) {
85191
85201
  var extension = require_extension();
85192
85202
  var PerMessageDeflate = require_permessage_deflate();
85193
85203
  var subprotocol = require_subprotocol();
85194
- var WebSocket = require_websocket();
85204
+ var WebSocket2 = require_websocket();
85195
85205
  var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants2();
85196
85206
  var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
85197
85207
  var RUNNING = 0;
@@ -85219,7 +85229,7 @@ var require_websocket_server = __commonJS(function(exports, module) {
85219
85229
  host: null,
85220
85230
  path: null,
85221
85231
  port: null,
85222
- WebSocket,
85232
+ WebSocket: WebSocket2,
85223
85233
  ...options
85224
85234
  };
85225
85235
  if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) {
@@ -108645,10 +108655,10 @@ async function fetchLiveProviderModels(provider, apiKey, baseUrl, fetchFn = fetc
108645
108655
  } else if (normalized === "anthropic" || normalized === "claude") {
108646
108656
  endpoint = "https://api.anthropic.com/v1/models";
108647
108657
  headers["anthropic-version"] = "2023-06-01";
108648
- const isOAuth = normalized === "claude" || !apiKey.startsWith("sk-ant-api") || apiKey.startsWith("Bearer ");
108649
- if (isOAuth && !apiKey.startsWith("sk-ant-api")) {
108658
+ const isOAuth = normalized === "claude" || apiKey.startsWith("sk-ant-oat") || apiKey.startsWith("Bearer ");
108659
+ if (isOAuth) {
108650
108660
  headers["Authorization"] = apiKey.startsWith("Bearer ") ? apiKey : `Bearer ${apiKey}`;
108651
- headers["anthropic-beta"] = "oauth-2024-11-18";
108661
+ headers["anthropic-beta"] = "claude-code-20250219,oauth-2024-06-20";
108652
108662
  } else {
108653
108663
  headers["x-api-key"] = apiKey;
108654
108664
  }
@@ -114423,7 +114433,7 @@ function inferManagerFromPath(filePath, realPath) {
114423
114433
  return "npm";
114424
114434
  }
114425
114435
  if (normalized.includes("/.december/bin/december") || normalized.includes("/.december/bin") || normalized.includes("/.local/bin/december") || normalized.includes("/.local/bin/")) {
114426
- return "standalone";
114436
+ return "curl";
114427
114437
  }
114428
114438
  return "unknown";
114429
114439
  }
@@ -114771,7 +114781,7 @@ function isPermissionDeniedError2(errMsg) {
114771
114781
  }
114772
114782
  function detectInstallMethod(options) {
114773
114783
  const configMethod = options?.configInstallMethod;
114774
- const validMethods = ["npm", "bun", "pnpm", "npx", "source"];
114784
+ const validMethods = ["npm", "bun", "pnpm", "npx", "curl", "source"];
114775
114785
  if (configMethod && validMethods.includes(configMethod)) {
114776
114786
  return configMethod;
114777
114787
  }
@@ -114793,6 +114803,9 @@ function detectInstallMethod(options) {
114793
114803
  if (combined.includes("/_npx/") || combined.includes("\\_npx\\") || combined.includes("/.bunx/") || env.npm_config_user_agent?.includes("npx")) {
114794
114804
  return "npx";
114795
114805
  }
114806
+ if (execPath.includes(".december/bin") || combined.includes(".december/bin") || execPath.includes(".local/bin/december") || combined.includes(".local/bin/december") || execPath.endsWith("/december") && !execPath.includes("node_modules") && !execPath.includes(".bun") && !execPath.includes("/node")) {
114807
+ return "curl";
114808
+ }
114796
114809
  if (combined.includes(".bun/bin") || combined.includes("/.bun/") || execPath.includes("/bun") && !execPath.endsWith("/node")) {
114797
114810
  return "bun";
114798
114811
  }
@@ -114803,6 +114816,13 @@ function detectInstallMethod(options) {
114803
114816
  }
114804
114817
  function getUpdateCommand(method, _platform = process.platform) {
114805
114818
  switch (method) {
114819
+ case "curl": {
114820
+ return {
114821
+ command: "curl -fsSL https://trydecember.com/install.sh | bash",
114822
+ manualCmd: "curl -fsSL https://trydecember.com/install.sh | bash",
114823
+ description: "Standalone binary via curl installer"
114824
+ };
114825
+ }
114806
114826
  case "bun": {
114807
114827
  return {
114808
114828
  command: "bun add -g @trydecember/cli@latest",
@@ -114864,8 +114884,8 @@ async function performCliUpdate(options) {
114864
114884
  });
114865
114885
  if (!options?.configInstallMethod && preDiagnosis?.activeBinary?.manager) {
114866
114886
  const activeMgr = preDiagnosis.activeBinary.manager;
114867
- if (activeMgr === "bun" || activeMgr === "npm" || activeMgr === "pnpm" || activeMgr === "source") {
114868
- detectedMethod = activeMgr;
114887
+ if (activeMgr === "bun" || activeMgr === "npm" || activeMgr === "pnpm" || activeMgr === "curl" || activeMgr === "standalone" || activeMgr === "source") {
114888
+ detectedMethod = activeMgr === "standalone" ? "curl" : activeMgr;
114869
114889
  }
114870
114890
  }
114871
114891
  const method = detectedMethod;
@@ -115360,20 +115380,28 @@ Examples:`);
115360
115380
  console.log("No API key provided. Operation cancelled.");
115361
115381
  return;
115362
115382
  }
115363
- await Promise.resolve().then(() => init_models5());
115364
- const defaultModel = getDefaultModelForProvider(provider);
115383
+ const isSTTProvider = provider.toLowerCase() === "deepgram";
115365
115384
  const config = await loadConfig();
115366
115385
  config.providers = config.providers || {};
115367
115386
  config.providers[provider] = finalKey;
115368
- config.activeProvider = provider;
115369
- config.activeModel = defaultModel;
115370
- config.authPriority = "byok";
115371
- await saveConfig(config);
115372
- await Promise.resolve().then(() => init_models5());
115373
- fetchLiveProviderModels(provider, finalKey).catch(() => {});
115374
- console.log(`
115387
+ if (!isSTTProvider) {
115388
+ await Promise.resolve().then(() => init_models5());
115389
+ const defaultModel = getDefaultModelForProvider(provider);
115390
+ config.activeProvider = provider;
115391
+ config.activeModel = defaultModel;
115392
+ config.authPriority = "byok";
115393
+ await saveConfig(config);
115394
+ await Promise.resolve().then(() => init_models5());
115395
+ fetchLiveProviderModels(provider, finalKey).catch(() => {});
115396
+ console.log(`
115375
115397
  ${GREEN}Successfully saved API key for ${provider.toUpperCase()} (Model: ${defaultModel})!${RESET}
115376
115398
  `);
115399
+ } else {
115400
+ await saveConfig(config);
115401
+ console.log(`
115402
+ ${GREEN}Successfully saved API key for DEEPGRAM (Live Voice Dictation)!${RESET}
115403
+ `);
115404
+ }
115377
115405
  }
115378
115406
  async function handleLoginCommand(options) {
115379
115407
  const targetProvider = (options?.provider || "december").toLowerCase().trim();
@@ -119419,7 +119447,7 @@ var init_browser = __esm(() => {
119419
119447
  });
119420
119448
  BrowserTool = {
119421
119449
  name: "browser",
119422
- description: "Use a basic HTTP client to fetch the HTML content of a URL. Strips out scripts and styles to return clean text. It also exposes a VNC stream for UI inspection.",
119450
+ description: "Use an interactive browser engine to navigate to a URL, execute client-side JavaScript, extract rendered page content, and capture console or network errors.",
119423
119451
  inputSchema: browserSchema,
119424
119452
  execute: async ({ url }, context) => {
119425
119453
  try {
@@ -119431,6 +119459,20 @@ var init_browser = __esm(() => {
119431
119459
  return `Failed to fetch URL: ${result.error}`;
119432
119460
  }
119433
119461
  let output = truncateOutput(result.text, 25000, 100).text;
119462
+ if (result.consoleErrors && result.consoleErrors.length > 0) {
119463
+ output += `
119464
+
119465
+ [CONSOLE ERRORS]
119466
+ ` + result.consoleErrors.map((err) => `- ${err}`).join(`
119467
+ `);
119468
+ }
119469
+ if (result.networkErrors && result.networkErrors.length > 0) {
119470
+ output += `
119471
+
119472
+ [FAILED NETWORK REQUESTS]
119473
+ ` + result.networkErrors.map((err) => `- ${err}`).join(`
119474
+ `);
119475
+ }
119434
119476
  if (result.vncUrl) {
119435
119477
  output += `
119436
119478
 
@@ -119444,6 +119486,89 @@ var init_browser = __esm(() => {
119444
119486
  };
119445
119487
  });
119446
119488
 
119489
+ // ../../packages/tools/src/browser_action.ts
119490
+ var browserActionSchema, BrowserActionTool;
119491
+ var init_browser_action = __esm(() => {
119492
+ init_src();
119493
+ init_esm2();
119494
+ browserActionSchema = Type2.Object({
119495
+ action: Type2.Union([
119496
+ Type2.Literal("navigate"),
119497
+ Type2.Literal("click"),
119498
+ Type2.Literal("type"),
119499
+ Type2.Literal("screenshot"),
119500
+ Type2.Literal("get_console_errors")
119501
+ ], { description: "The interactive browser action to execute" }),
119502
+ url: Type2.Optional(Type2.String({ description: 'URL to navigate to (required for action: "navigate")' })),
119503
+ selector: Type2.Optional(Type2.String({ description: 'CSS selector or text target (for action: "click" or "type")' })),
119504
+ text: Type2.Optional(Type2.String({ description: 'Text to input (required for action: "type")' })),
119505
+ fullPage: Type2.Optional(Type2.Boolean({ description: 'Capture full scrollable page (for action: "screenshot")' }))
119506
+ });
119507
+ BrowserActionTool = {
119508
+ name: "browser_action",
119509
+ description: "Interact with a headless browser session. Supports navigating to URLs, clicking elements, typing into inputs, capturing screenshots, and inspecting console or network errors on localhost web apps.",
119510
+ inputSchema: browserActionSchema,
119511
+ execute: async (input, context) => {
119512
+ try {
119513
+ if (!context.operations.browser) {
119514
+ return `Failed to execute browser action: Browser operations are not supported in this environment.`;
119515
+ }
119516
+ if (!context.operations.browser.action) {
119517
+ if (input.action === "navigate" && input.url) {
119518
+ const navResult = await context.operations.browser.navigate(input.url);
119519
+ if (navResult.error) {
119520
+ return `Failed to navigate: ${navResult.error}`;
119521
+ }
119522
+ let out = truncateOutput(navResult.text, 25000, 100).text;
119523
+ if (navResult.consoleErrors && navResult.consoleErrors.length > 0) {
119524
+ out += `
119525
+
119526
+ [CONSOLE ERRORS]
119527
+ ` + navResult.consoleErrors.map((err) => `- ${err}`).join(`
119528
+ `);
119529
+ }
119530
+ if (navResult.networkErrors && navResult.networkErrors.length > 0) {
119531
+ out += `
119532
+
119533
+ [FAILED NETWORK REQUESTS]
119534
+ ` + navResult.networkErrors.map((err) => `- ${err}`).join(`
119535
+ `);
119536
+ }
119537
+ return out;
119538
+ }
119539
+ return `Browser interactive actions ('click', 'type', 'screenshot') require Chrome or Chromium to be installed.`;
119540
+ }
119541
+ const result = await context.operations.browser.action(input);
119542
+ if (result.error) {
119543
+ return `Failed to execute action "${input.action}": ${result.error}`;
119544
+ }
119545
+ let response = result.output || result.text || `Browser action "${input.action}" completed successfully.`;
119546
+ if (result.screenshotPath) {
119547
+ response += `
119548
+ [Screenshot saved: ${result.screenshotPath}]`;
119549
+ }
119550
+ if (result.consoleErrors && result.consoleErrors.length > 0) {
119551
+ response += `
119552
+
119553
+ [CONSOLE ERRORS]
119554
+ ` + result.consoleErrors.map((err) => `- ${err}`).join(`
119555
+ `);
119556
+ }
119557
+ if (result.networkErrors && result.networkErrors.length > 0) {
119558
+ response += `
119559
+
119560
+ [FAILED NETWORK REQUESTS]
119561
+ ` + result.networkErrors.map((err) => `- ${err}`).join(`
119562
+ `);
119563
+ }
119564
+ return response;
119565
+ } catch (error) {
119566
+ return `Failed to execute browser action: ${error.message}`;
119567
+ }
119568
+ }
119569
+ };
119570
+ });
119571
+
119447
119572
  // ../../packages/tools/src/code_intelligence.ts
119448
119573
  function formatDiagnostics(diags, path) {
119449
119574
  const target = path ? ` for ${path}` : "";
@@ -160892,7 +161017,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
160892
161017
  return null;
160893
161018
  }
160894
161019
 
160895
- class WebSocket extends EventTarget {
161020
+ class WebSocket2 extends EventTarget {
160896
161021
  #events = {
160897
161022
  open: null,
160898
161023
  error: null,
@@ -160968,11 +161093,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
160968
161093
  this.#url = new URL(urlRecord.href);
160969
161094
  const client = environmentSettingsObject.settingsObject;
160970
161095
  this.#handler.controller = establishWebSocketConnection(urlRecord, protocols, client, this.#handler, options);
160971
- this.#handler.readyState = WebSocket.CONNECTING;
161096
+ this.#handler.readyState = WebSocket2.CONNECTING;
160972
161097
  this.#binaryType = "blob";
160973
161098
  }
160974
161099
  close(code = undefined, reason = undefined) {
160975
- webidl.brandCheck(this, WebSocket);
161100
+ webidl.brandCheck(this, WebSocket2);
160976
161101
  const prefix = "WebSocket.close";
160977
161102
  if (code !== undefined) {
160978
161103
  code = webidl.converters["unsigned short"](code, prefix, "code", webidl.attributes.Clamp);
@@ -160985,7 +161110,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
160985
161110
  closeWebSocketConnection(this.#handler, code, reason, true);
160986
161111
  }
160987
161112
  send(data) {
160988
- webidl.brandCheck(this, WebSocket);
161113
+ webidl.brandCheck(this, WebSocket2);
160989
161114
  const prefix = "WebSocket.send";
160990
161115
  webidl.argumentLengthCheck(arguments, 1, prefix);
160991
161116
  data = webidl.converters.WebSocketSendData(data, prefix, "data");
@@ -161019,31 +161144,31 @@ var require_websocket2 = __commonJS(function(exports, module) {
161019
161144
  }
161020
161145
  }
161021
161146
  get readyState() {
161022
- webidl.brandCheck(this, WebSocket);
161147
+ webidl.brandCheck(this, WebSocket2);
161023
161148
  return this.#handler.readyState;
161024
161149
  }
161025
161150
  get bufferedAmount() {
161026
- webidl.brandCheck(this, WebSocket);
161151
+ webidl.brandCheck(this, WebSocket2);
161027
161152
  return this.#bufferedAmount;
161028
161153
  }
161029
161154
  get url() {
161030
- webidl.brandCheck(this, WebSocket);
161155
+ webidl.brandCheck(this, WebSocket2);
161031
161156
  return URLSerializer(this.#url);
161032
161157
  }
161033
161158
  get extensions() {
161034
- webidl.brandCheck(this, WebSocket);
161159
+ webidl.brandCheck(this, WebSocket2);
161035
161160
  return this.#extensions;
161036
161161
  }
161037
161162
  get protocol() {
161038
- webidl.brandCheck(this, WebSocket);
161163
+ webidl.brandCheck(this, WebSocket2);
161039
161164
  return this.#protocol;
161040
161165
  }
161041
161166
  get onopen() {
161042
- webidl.brandCheck(this, WebSocket);
161167
+ webidl.brandCheck(this, WebSocket2);
161043
161168
  return this.#events.open;
161044
161169
  }
161045
161170
  set onopen(fn) {
161046
- webidl.brandCheck(this, WebSocket);
161171
+ webidl.brandCheck(this, WebSocket2);
161047
161172
  if (this.#events.open) {
161048
161173
  this.removeEventListener("open", this.#events.open);
161049
161174
  }
@@ -161056,11 +161181,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
161056
161181
  }
161057
161182
  }
161058
161183
  get onerror() {
161059
- webidl.brandCheck(this, WebSocket);
161184
+ webidl.brandCheck(this, WebSocket2);
161060
161185
  return this.#events.error;
161061
161186
  }
161062
161187
  set onerror(fn) {
161063
- webidl.brandCheck(this, WebSocket);
161188
+ webidl.brandCheck(this, WebSocket2);
161064
161189
  if (this.#events.error) {
161065
161190
  this.removeEventListener("error", this.#events.error);
161066
161191
  }
@@ -161073,11 +161198,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
161073
161198
  }
161074
161199
  }
161075
161200
  get onclose() {
161076
- webidl.brandCheck(this, WebSocket);
161201
+ webidl.brandCheck(this, WebSocket2);
161077
161202
  return this.#events.close;
161078
161203
  }
161079
161204
  set onclose(fn) {
161080
- webidl.brandCheck(this, WebSocket);
161205
+ webidl.brandCheck(this, WebSocket2);
161081
161206
  if (this.#events.close) {
161082
161207
  this.removeEventListener("close", this.#events.close);
161083
161208
  }
@@ -161090,11 +161215,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
161090
161215
  }
161091
161216
  }
161092
161217
  get onmessage() {
161093
- webidl.brandCheck(this, WebSocket);
161218
+ webidl.brandCheck(this, WebSocket2);
161094
161219
  return this.#events.message;
161095
161220
  }
161096
161221
  set onmessage(fn) {
161097
- webidl.brandCheck(this, WebSocket);
161222
+ webidl.brandCheck(this, WebSocket2);
161098
161223
  if (this.#events.message) {
161099
161224
  this.removeEventListener("message", this.#events.message);
161100
161225
  }
@@ -161107,11 +161232,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
161107
161232
  }
161108
161233
  }
161109
161234
  get binaryType() {
161110
- webidl.brandCheck(this, WebSocket);
161235
+ webidl.brandCheck(this, WebSocket2);
161111
161236
  return this.#binaryType;
161112
161237
  }
161113
161238
  set binaryType(type) {
161114
- webidl.brandCheck(this, WebSocket);
161239
+ webidl.brandCheck(this, WebSocket2);
161115
161240
  if (type !== "blob" && type !== "arraybuffer") {
161116
161241
  this.#binaryType = "blob";
161117
161242
  } else {
@@ -161227,13 +161352,13 @@ var require_websocket2 = __commonJS(function(exports, module) {
161227
161352
  }
161228
161353
  }
161229
161354
  }
161230
- var { ping } = WebSocket;
161231
- Reflect.deleteProperty(WebSocket, "ping");
161232
- WebSocket.CONNECTING = WebSocket.prototype.CONNECTING = states.CONNECTING;
161233
- WebSocket.OPEN = WebSocket.prototype.OPEN = states.OPEN;
161234
- WebSocket.CLOSING = WebSocket.prototype.CLOSING = states.CLOSING;
161235
- WebSocket.CLOSED = WebSocket.prototype.CLOSED = states.CLOSED;
161236
- Object.defineProperties(WebSocket.prototype, {
161355
+ var { ping } = WebSocket2;
161356
+ Reflect.deleteProperty(WebSocket2, "ping");
161357
+ WebSocket2.CONNECTING = WebSocket2.prototype.CONNECTING = states.CONNECTING;
161358
+ WebSocket2.OPEN = WebSocket2.prototype.OPEN = states.OPEN;
161359
+ WebSocket2.CLOSING = WebSocket2.prototype.CLOSING = states.CLOSING;
161360
+ WebSocket2.CLOSED = WebSocket2.prototype.CLOSED = states.CLOSED;
161361
+ Object.defineProperties(WebSocket2.prototype, {
161237
161362
  CONNECTING: staticPropertyDescriptors,
161238
161363
  OPEN: staticPropertyDescriptors,
161239
161364
  CLOSING: staticPropertyDescriptors,
@@ -161257,7 +161382,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
161257
161382
  configurable: true
161258
161383
  }
161259
161384
  });
161260
- Object.defineProperties(WebSocket, {
161385
+ Object.defineProperties(WebSocket2, {
161261
161386
  CONNECTING: staticPropertyDescriptors,
161262
161387
  OPEN: staticPropertyDescriptors,
161263
161388
  CLOSING: staticPropertyDescriptors,
@@ -161304,7 +161429,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
161304
161429
  return webidl.converters.USVString(V);
161305
161430
  };
161306
161431
  module.exports = {
161307
- WebSocket,
161432
+ WebSocket: WebSocket2,
161308
161433
  ping
161309
161434
  };
161310
161435
  });
@@ -162189,7 +162314,7 @@ function makeDispatcher(fn) {
162189
162314
  }, handler);
162190
162315
  };
162191
162316
  }
162192
- var __filename = "/home/runner/work/december/december/node_modules/.bun/undici@7.28.0/node_modules/undici/index.js", Client, Dispatcher, Pool, BalancedPool, RoundRobinPool, Agent, ProxyAgent, Socks5ProxyAgent, EnvHttpProxyAgent, RetryAgent, H2CClient, errors2, util3, InvalidArgumentError, api, buildConnector, MockClient, MockCallHistory, MockCallHistoryLog, MockAgent, MockPool, SnapshotAgent, mockErrors, RetryHandler, getGlobalDispatcher, setGlobalDispatcher, DecoratorHandler, RedirectHandler, $interceptors, $cacheStores, SqliteCacheStore, fetchImpl, $Headers, $Response, $Request, $FormData, setGlobalOrigin, getGlobalOrigin, CacheStorage, kConstruct, $caches, deleteCookie, getCookies, getSetCookies, setCookie, parseCookie, parseMIMEType, serializeAMimeType, CloseEvent, ErrorEvent, MessageEvent, WebSocket2, ping, $WebSocketStream, $WebSocketError, $request, $stream, $pipeline, $connect, $upgrade, EventSource;
162317
+ var __filename = "/home/runner/work/december/december/node_modules/.bun/undici@7.28.0/node_modules/undici/index.js", Client, Dispatcher, Pool, BalancedPool, RoundRobinPool, Agent, ProxyAgent, Socks5ProxyAgent, EnvHttpProxyAgent, RetryAgent, H2CClient, errors2, util3, InvalidArgumentError, api, buildConnector, MockClient, MockCallHistory, MockCallHistoryLog, MockAgent, MockPool, SnapshotAgent, mockErrors, RetryHandler, getGlobalDispatcher, setGlobalDispatcher, DecoratorHandler, RedirectHandler, $interceptors, $cacheStores, SqliteCacheStore, fetchImpl, $Headers, $Response, $Request, $FormData, setGlobalOrigin, getGlobalOrigin, CacheStorage, kConstruct, $caches, deleteCookie, getCookies, getSetCookies, setCookie, parseCookie, parseMIMEType, serializeAMimeType, CloseEvent, ErrorEvent, MessageEvent, WebSocket3, ping, $WebSocketStream, $WebSocketError, $request, $stream, $pipeline, $connect, $upgrade, EventSource;
162193
162318
  var init_undici = __esm(() => {
162194
162319
  Client = require_client();
162195
162320
  Dispatcher = require_dispatcher();
@@ -162245,7 +162370,7 @@ var init_undici = __esm(() => {
162245
162370
  ({ deleteCookie, getCookies, getSetCookies, setCookie, parseCookie } = require_cookies());
162246
162371
  ({ parseMIMEType, serializeAMimeType } = require_data_url());
162247
162372
  ({ CloseEvent, ErrorEvent, MessageEvent } = require_events());
162248
- ({ WebSocket: WebSocket2, ping } = require_websocket2());
162373
+ ({ WebSocket: WebSocket3, ping } = require_websocket2());
162249
162374
  $WebSocketStream = require_websocketstream().WebSocketStream;
162250
162375
  $WebSocketError = require_websocketerror().WebSocketError;
162251
162376
  $request = makeDispatcher(api.request);
@@ -162835,6 +162960,7 @@ var init_src4 = __esm(() => {
162835
162960
  init_ask_question();
162836
162961
  init_bash();
162837
162962
  init_browser();
162963
+ init_browser_action();
162838
162964
  init_code_intelligence();
162839
162965
  init_deferred_registry();
162840
162966
  init_edit();
@@ -162862,6 +162988,7 @@ var init_src4 = __esm(() => {
162862
162988
  init_manage_task();
162863
162989
  init_manage_memory();
162864
162990
  init_browser();
162991
+ init_browser_action();
162865
162992
  init_web_search();
162866
162993
  init_diff_preview();
162867
162994
  init_deferred_registry();
@@ -162881,11 +163008,26 @@ var init_src4 = __esm(() => {
162881
163008
  CodeIntelligenceTool,
162882
163009
  ManageMemoryTool
162883
163010
  ];
162884
- DEFERRED_TOOLS = [ManageTaskTool, BrowserTool, WebSearchTool];
163011
+ DEFERRED_TOOLS = [ManageTaskTool, BrowserTool, BrowserActionTool, WebSearchTool];
162885
163012
  defaultDeferredRegistry.register(BrowserTool, {
162886
163013
  category: "web",
162887
163014
  keywords: ["browser", "render", "html", "scrape", "page", "url", "browse", "webpage"]
162888
163015
  });
163016
+ defaultDeferredRegistry.register(BrowserActionTool, {
163017
+ category: "web",
163018
+ keywords: [
163019
+ "browser_action",
163020
+ "click",
163021
+ "type",
163022
+ "screenshot",
163023
+ "dom",
163024
+ "test",
163025
+ "input",
163026
+ "form",
163027
+ "ui",
163028
+ "localhost"
163029
+ ]
163030
+ });
162889
163031
  defaultDeferredRegistry.register(WebSearchTool, {
162890
163032
  category: "web",
162891
163033
  keywords: ["search", "google", "duckduckgo", "tavily", "internet", "docs", "websearch"]
@@ -169706,13 +169848,210 @@ var init_headless_runner = __esm(() => {
169706
169848
  };
169707
169849
  });
169708
169850
 
169851
+ // src/utils/workspace-trust.ts
169852
+ import { execSync as execSync2 } from "node:child_process";
169853
+ import fs25 from "node:fs";
169854
+ import os13 from "node:os";
169855
+ import path26 from "node:path";
169856
+ import readline3 from "node:readline";
169857
+ function resolveWorkspaceRoot(targetDir = process.cwd()) {
169858
+ const resolved = path26.resolve(targetDir);
169859
+ try {
169860
+ const stdout = execSync2("git rev-parse --show-toplevel", {
169861
+ cwd: resolved,
169862
+ stdio: ["ignore", "pipe", "ignore"],
169863
+ encoding: "utf8"
169864
+ }).trim();
169865
+ if (stdout && fs25.existsSync(stdout)) {
169866
+ return path26.resolve(stdout);
169867
+ }
169868
+ } catch {}
169869
+ let current = resolved;
169870
+ const homeDir = path26.resolve(os13.homedir());
169871
+ while (true) {
169872
+ if (fs25.existsSync(path26.join(current, ".git"))) {
169873
+ return current;
169874
+ }
169875
+ const parent = path26.dirname(current);
169876
+ if (parent === current || current === homeDir) {
169877
+ break;
169878
+ }
169879
+ current = parent;
169880
+ }
169881
+ return resolved;
169882
+ }
169883
+ function isWorkspaceTrusted(workspaceRoot, config) {
169884
+ const canonicalRoot = path26.resolve(workspaceRoot);
169885
+ const homeDir = path26.resolve(os13.homedir());
169886
+ const trustedList = (config.trustedWorkspaces || []).map((p) => path26.resolve(p));
169887
+ for (const trustedPath of trustedList) {
169888
+ if (canonicalRoot === trustedPath) {
169889
+ return true;
169890
+ }
169891
+ if (trustedPath !== "/" && trustedPath !== homeDir) {
169892
+ const rel = path26.relative(trustedPath, canonicalRoot);
169893
+ if (!rel.startsWith("..") && !path26.isAbsolute(rel)) {
169894
+ return true;
169895
+ }
169896
+ }
169897
+ }
169898
+ return false;
169899
+ }
169900
+ async function addTrustedWorkspace(workspaceRoot) {
169901
+ const config = await loadConfig();
169902
+ const canonical = path26.resolve(workspaceRoot);
169903
+ const existing = (config.trustedWorkspaces || []).map((p) => path26.resolve(p));
169904
+ if (!existing.includes(canonical)) {
169905
+ config.trustedWorkspaces = [...existing, canonical];
169906
+ await saveConfig(config);
169907
+ }
169908
+ }
169909
+ async function promptWorkspaceTrust(workspaceRoot) {
169910
+ if (!process.stdin.isTTY) {
169911
+ return false;
169912
+ }
169913
+ const homeDir = path26.resolve(os13.homedir());
169914
+ const isHomeOrRoot = workspaceRoot === homeDir || workspaceRoot === "/";
169915
+ const BRAND = "\x1B[38;2;137;180;248m";
169916
+ const TEXT = "\x1B[38;2;244;244;245m";
169917
+ const MUTED = "\x1B[38;2;170;170;170m";
169918
+ const GRAY = "\x1B[38;2;119;119;119m";
169919
+ const YELLOW = "\x1B[38;2;253;214;99m";
169920
+ const BOLD = "\x1B[1m";
169921
+ const RESET = "\x1B[0m";
169922
+ const options = isHomeOrRoot ? [
169923
+ { label: "No, exit", value: false },
169924
+ { label: "Yes, trust folder anyway", value: true }
169925
+ ] : [
169926
+ { label: "Yes, trust folder", value: true },
169927
+ { label: "No, exit", value: false }
169928
+ ];
169929
+ return new Promise((resolve) => {
169930
+ let selectedIndex = 0;
169931
+ let renderedLineCount = 0;
169932
+ const wasRaw = process.stdin.isRaw;
169933
+ if (typeof process.stdin.setRawMode === "function") {
169934
+ process.stdin.setRawMode(true);
169935
+ }
169936
+ process.stdin.resume();
169937
+ readline3.emitKeypressEvents(process.stdin);
169938
+ const cleanup = () => {
169939
+ process.stdin.removeListener("keypress", onKeypress);
169940
+ if (typeof process.stdin.setRawMode === "function" && !wasRaw) {
169941
+ process.stdin.setRawMode(false);
169942
+ }
169943
+ process.stdin.pause();
169944
+ process.stdout.write("\x1B[?25h");
169945
+ };
169946
+ const clearRenderedLines = () => {
169947
+ if (renderedLineCount > 0) {
169948
+ readline3.cursorTo(process.stdout, 0);
169949
+ readline3.moveCursor(process.stdout, 0, -renderedLineCount);
169950
+ readline3.clearScreenDown(process.stdout);
169951
+ }
169952
+ };
169953
+ const render = () => {
169954
+ clearRenderedLines();
169955
+ const lines = [];
169956
+ lines.push("");
169957
+ lines.push(` ${BOLD}${TEXT}Trust workspace?${RESET}`);
169958
+ lines.push("");
169959
+ lines.push(` ${GRAY}Directory${RESET} ${BOLD}${BRAND}${workspaceRoot}${RESET}`);
169960
+ if (isHomeOrRoot) {
169961
+ lines.push(` ${YELLOW}Warning${RESET} ${YELLOW}Home or system root directory. Gives access to all personal files.${RESET}`);
169962
+ } else {
169963
+ lines.push(` ${GRAY}Permission${RESET} ${TEXT}Read, edit, and run commands${RESET}`);
169964
+ }
169965
+ lines.push("");
169966
+ for (let i = 0;i < options.length; i++) {
169967
+ const opt = options[i];
169968
+ if (i === selectedIndex) {
169969
+ lines.push(` ${BRAND}❭${RESET} ${BOLD}${TEXT}${opt.label}${RESET}`);
169970
+ } else {
169971
+ lines.push(` ${MUTED}${opt.label}${RESET}`);
169972
+ }
169973
+ }
169974
+ lines.push("");
169975
+ lines.push(` ${BRAND}Enter${RESET} ${MUTED}confirm${RESET} ${MUTED}·${RESET} ${BRAND}Esc${RESET} ${MUTED}cancel${RESET} ${MUTED}·${RESET} ${BRAND}↑↓${RESET} ${MUTED}navigate${RESET}`);
169976
+ lines.push("");
169977
+ renderedLineCount = lines.length;
169978
+ process.stdout.write(lines.join(`
169979
+ `));
169980
+ };
169981
+ const onKeypress = (str, key) => {
169982
+ if (!key && !str)
169983
+ return;
169984
+ const lowerStr = (str || "").toLowerCase();
169985
+ const keyName = key?.name;
169986
+ if (lowerStr === "y" || keyName === "y") {
169987
+ cleanup();
169988
+ clearRenderedLines();
169989
+ resolve(true);
169990
+ return;
169991
+ }
169992
+ if (lowerStr === "n" || keyName === "n") {
169993
+ cleanup();
169994
+ clearRenderedLines();
169995
+ resolve(false);
169996
+ return;
169997
+ }
169998
+ if (keyName === "up" || keyName === "k") {
169999
+ selectedIndex = Math.max(0, selectedIndex - 1);
170000
+ render();
170001
+ } else if (keyName === "down" || keyName === "j" || keyName === "tab") {
170002
+ selectedIndex = Math.min(options.length - 1, selectedIndex + 1);
170003
+ render();
170004
+ } else if (keyName === "return" || keyName === "enter") {
170005
+ cleanup();
170006
+ clearRenderedLines();
170007
+ resolve(options[selectedIndex].value);
170008
+ } else if (keyName === "escape" || key?.ctrl && keyName === "c") {
170009
+ cleanup();
170010
+ clearRenderedLines();
170011
+ resolve(false);
170012
+ }
170013
+ };
170014
+ process.stdout.write("\x1B[?25l");
170015
+ process.stdin.on("keypress", onKeypress);
170016
+ render();
170017
+ });
170018
+ }
170019
+ async function ensureWorkspaceTrust(options = {}) {
170020
+ const workspaceRoot = resolveWorkspaceRoot(options.targetDir || process.cwd());
170021
+ const config = await loadConfig();
170022
+ if (isWorkspaceTrusted(workspaceRoot, config)) {
170023
+ return;
170024
+ }
170025
+ const isNonInteractive = Boolean(options.isHeadless) || !process.stdin.isTTY || Boolean(process.env.NON_INTERACTIVE) || Boolean(process.env.CI);
170026
+ if (isNonInteractive) {
170027
+ const RED = "\x1B[38;2;252;165;165m";
170028
+ const RESET = "\x1B[0m";
170029
+ console.error(`
170030
+ ${RED}Error:${RESET} Workspace "${workspaceRoot}" is not trusted.`);
170031
+ console.error(`Please run "december" interactively first to review and trust this workspace.
170032
+ `);
170033
+ process.exit(1);
170034
+ }
170035
+ const trusted = await promptWorkspaceTrust(workspaceRoot);
170036
+ if (!trusted) {
170037
+ console.log(`
170038
+ Workspace untrusted. Exiting.
170039
+ `);
170040
+ process.exit(0);
170041
+ }
170042
+ await addTrustedWorkspace(workspaceRoot);
170043
+ }
170044
+ var init_workspace_trust = __esm(() => {
170045
+ init_config();
170046
+ });
170047
+
169709
170048
  // src/file-session-repository.ts
169710
170049
  var exports_file_session_repository = {};
169711
170050
  __export(exports_file_session_repository, {
169712
170051
  FileSessionRepository: () => FileSessionRepository
169713
170052
  });
169714
- import fs25 from "node:fs/promises";
169715
- import path26 from "node:path";
170053
+ import fs26 from "node:fs/promises";
170054
+ import path27 from "node:path";
169716
170055
 
169717
170056
  class FileSessionRepository {
169718
170057
  sessionDir;
@@ -169722,22 +170061,22 @@ class FileSessionRepository {
169722
170061
  if (sessionDir) {
169723
170062
  this.sessionDir = sessionDir;
169724
170063
  } else {
169725
- this.sessionDir = path26.join(getConfigDir(), "sessions");
169726
- const legacyDir = path26.join(getLegacyConfigDir(), "sessions");
170064
+ this.sessionDir = path27.join(getConfigDir(), "sessions");
170065
+ const legacyDir = path27.join(getLegacyConfigDir(), "sessions");
169727
170066
  if (legacyDir !== this.sessionDir) {
169728
170067
  this.legacySessionDir = legacyDir;
169729
170068
  }
169730
170069
  }
169731
170070
  }
169732
170071
  async saveContext(sessionId, messages) {
169733
- await fs25.mkdir(this.sessionDir, { recursive: true });
169734
- const historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
170072
+ await fs26.mkdir(this.sessionDir, { recursive: true });
170073
+ const historyPath = path27.join(this.sessionDir, `${sessionId}.jsonl`);
169735
170074
  let existing = this.sessionCache[sessionId];
169736
170075
  if (!existing) {
169737
170076
  existing = {};
169738
170077
  this.sessionCache[sessionId] = existing;
169739
170078
  try {
169740
- const data = await fs25.readFile(historyPath, "utf-8");
170079
+ const data = await fs26.readFile(historyPath, "utf-8");
169741
170080
  const lines = data.split(`
169742
170081
  `).filter((l) => l.trim().length > 0);
169743
170082
  for (const l of lines) {
@@ -169761,19 +170100,19 @@ class FileSessionRepository {
169761
170100
  }
169762
170101
  }
169763
170102
  if (appendContent) {
169764
- await fs25.appendFile(historyPath, appendContent, "utf-8");
170103
+ await fs26.appendFile(historyPath, appendContent, "utf-8");
169765
170104
  }
169766
170105
  }
169767
170106
  async loadContext(sessionId) {
169768
- let historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
170107
+ let historyPath = path27.join(this.sessionDir, `${sessionId}.jsonl`);
169769
170108
  try {
169770
170109
  let data;
169771
170110
  try {
169772
- data = await fs25.readFile(historyPath, "utf-8");
170111
+ data = await fs26.readFile(historyPath, "utf-8");
169773
170112
  } catch (err) {
169774
170113
  if (this.legacySessionDir) {
169775
- const legacyPath = path26.join(this.legacySessionDir, `${sessionId}.jsonl`);
169776
- data = await fs25.readFile(legacyPath, "utf-8");
170114
+ const legacyPath = path27.join(this.legacySessionDir, `${sessionId}.jsonl`);
170115
+ data = await fs26.readFile(legacyPath, "utf-8");
169777
170116
  historyPath = legacyPath;
169778
170117
  } else {
169779
170118
  throw err;
@@ -169814,7 +170153,7 @@ class FileSessionRepository {
169814
170153
  if (msgs.length < lines.length) {
169815
170154
  const compacted = msgs.map((m) => JSON.stringify(m)).join(`
169816
170155
  `);
169817
- await fs25.writeFile(historyPath, compacted, "utf-8").catch(() => {});
170156
+ await fs26.writeFile(historyPath, compacted, "utf-8").catch(() => {});
169818
170157
  }
169819
170158
  const cacheObj = {};
169820
170159
  for (const msg of msgs) {
@@ -169828,12 +170167,12 @@ class FileSessionRepository {
169828
170167
  }
169829
170168
  async listSessions() {
169830
170169
  try {
169831
- await fs25.mkdir(this.sessionDir, { recursive: true });
170170
+ await fs26.mkdir(this.sessionDir, { recursive: true });
169832
170171
  const sessions = [];
169833
170172
  const seenIds = new Set;
169834
170173
  const processDirectory = async (dirPath) => {
169835
170174
  try {
169836
- const dirFiles = await fs25.readdir(dirPath);
170175
+ const dirFiles = await fs26.readdir(dirPath);
169837
170176
  for (const file of dirFiles) {
169838
170177
  if (!file.endsWith(".jsonl"))
169839
170178
  continue;
@@ -169841,10 +170180,10 @@ class FileSessionRepository {
169841
170180
  if (seenIds.has(id))
169842
170181
  continue;
169843
170182
  seenIds.add(id);
169844
- const filePath = path26.join(dirPath, file);
170183
+ const filePath = path27.join(dirPath, file);
169845
170184
  try {
169846
- const stat = await fs25.stat(filePath);
169847
- const data = await fs25.readFile(filePath, "utf-8");
170185
+ const stat = await fs26.stat(filePath);
170186
+ const data = await fs26.readFile(filePath, "utf-8");
169848
170187
  const lines = data.split(`
169849
170188
  `).filter((l) => l.trim().length > 0);
169850
170189
  let preview = "";
@@ -169878,20 +170217,20 @@ class FileSessionRepository {
169878
170217
  }
169879
170218
  }
169880
170219
  async deleteSession(sessionId) {
169881
- const historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
170220
+ const historyPath = path27.join(this.sessionDir, `${sessionId}.jsonl`);
169882
170221
  try {
169883
- await fs25.unlink(historyPath);
170222
+ await fs26.unlink(historyPath);
169884
170223
  } catch (e) {}
169885
170224
  if (this.legacySessionDir) {
169886
170225
  try {
169887
- await fs25.unlink(path26.join(this.legacySessionDir, `${sessionId}.jsonl`));
170226
+ await fs26.unlink(path27.join(this.legacySessionDir, `${sessionId}.jsonl`));
169888
170227
  } catch {}
169889
170228
  }
169890
170229
  }
169891
170230
  async renameSession(oldSessionId, newSessionId) {
169892
- const oldPath = path26.join(this.sessionDir, `${oldSessionId}.jsonl`);
169893
- const newPath = path26.join(this.sessionDir, `${newSessionId}.jsonl`);
169894
- await fs25.rename(oldPath, newPath);
170231
+ const oldPath = path27.join(this.sessionDir, `${oldSessionId}.jsonl`);
170232
+ const newPath = path27.join(this.sessionDir, `${newSessionId}.jsonl`);
170233
+ await fs26.rename(oldPath, newPath);
169895
170234
  }
169896
170235
  }
169897
170236
  var init_file_session_repository = __esm(() => {
@@ -175518,6 +175857,614 @@ var init_task_manager = __esm(() => {
175518
175857
  }
175519
175858
  });
175520
175859
 
175860
+ // src/utils/browser/detector.ts
175861
+ import { execSync as execSync3 } from "node:child_process";
175862
+ import fs27 from "node:fs/promises";
175863
+ import os14 from "node:os";
175864
+ import path28 from "node:path";
175865
+ async function isExecutable(filePath) {
175866
+ try {
175867
+ await fs27.access(filePath, fs27.constants.X_OK);
175868
+ const stats = await fs27.stat(filePath);
175869
+ return stats.isFile();
175870
+ } catch {
175871
+ return false;
175872
+ }
175873
+ }
175874
+ async function findSystemChrome() {
175875
+ if (cachedChromePath !== undefined) {
175876
+ return cachedChromePath;
175877
+ }
175878
+ const envVars = ["CHROME_PATH", "CHROME_BIN", "CHROMIUM_PATH", "PUPPETEER_EXECUTABLE_PATH"];
175879
+ for (const envVar of envVars) {
175880
+ const val = process.env[envVar];
175881
+ if (val && await isExecutable(val)) {
175882
+ cachedChromePath = val;
175883
+ return val;
175884
+ }
175885
+ }
175886
+ const platform = os14.platform();
175887
+ const candidates = [];
175888
+ if (platform === "linux") {
175889
+ candidates.push("/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser", "/snap/bin/chromium", "/usr/bin/brave-browser", "/usr/bin/microsoft-edge", "/usr/bin/microsoft-edge-stable");
175890
+ } else if (platform === "darwin") {
175891
+ const home = os14.homedir();
175892
+ candidates.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium", "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", path28.join(home, "Applications/Google Chrome.app/Contents/MacOS/Google Chrome"), path28.join(home, "Applications/Chromium.app/Contents/MacOS/Chromium"));
175893
+ } else if (platform === "win32") {
175894
+ const localAppData = process.env.LOCALAPPDATA || "";
175895
+ const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
175896
+ const programFilesX86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
175897
+ candidates.push(path28.join(programFiles, "Google\\Chrome\\Application\\chrome.exe"), path28.join(programFilesX86, "Google\\Chrome\\Application\\chrome.exe"), path28.join(localAppData, "Google\\Chrome\\Application\\chrome.exe"), path28.join(programFiles, "Microsoft\\Edge\\Application\\msedge.exe"), path28.join(programFilesX86, "Microsoft\\Edge\\Application\\msedge.exe"), path28.join(localAppData, "BraveSoftware\\Brave-Browser\\Application\\brave.exe"));
175898
+ }
175899
+ for (const candidate of candidates) {
175900
+ if (await isExecutable(candidate)) {
175901
+ cachedChromePath = candidate;
175902
+ return candidate;
175903
+ }
175904
+ }
175905
+ const binaryNames = platform === "win32" ? ["chrome.exe", "msedge.exe", "brave.exe"] : [
175906
+ "google-chrome",
175907
+ "google-chrome-stable",
175908
+ "chromium",
175909
+ "chromium-browser",
175910
+ "brave-browser",
175911
+ "microsoft-edge"
175912
+ ];
175913
+ const whichCmd = platform === "win32" ? "where" : "which";
175914
+ for (const bin of binaryNames) {
175915
+ try {
175916
+ const out = execSync3(`${whichCmd} ${bin}`, {
175917
+ stdio: ["ignore", "pipe", "ignore"],
175918
+ encoding: "utf-8"
175919
+ }).trim();
175920
+ const firstLine = out.split(`
175921
+ `)[0]?.trim();
175922
+ if (firstLine && await isExecutable(firstLine)) {
175923
+ cachedChromePath = firstLine;
175924
+ return firstLine;
175925
+ }
175926
+ } catch {}
175927
+ }
175928
+ cachedChromePath = null;
175929
+ return null;
175930
+ }
175931
+ var cachedChromePath = undefined;
175932
+ var init_detector = () => {};
175933
+
175934
+ // src/utils/browser/cdp-client.ts
175935
+ import { spawn as spawn4 } from "node:child_process";
175936
+ import fs28 from "node:fs/promises";
175937
+ import os15 from "node:os";
175938
+ import path29 from "node:path";
175939
+
175940
+ class CdpBrowserSession {
175941
+ options;
175942
+ chromeProc = null;
175943
+ ws = null;
175944
+ nextId = 1;
175945
+ pendingCommands = new Map;
175946
+ sessionId = null;
175947
+ tmpProfileDir = null;
175948
+ consoleErrors = [];
175949
+ networkErrors = [];
175950
+ isLoaded = false;
175951
+ constructor(options) {
175952
+ this.options = options;
175953
+ }
175954
+ async launch() {
175955
+ this.tmpProfileDir = path29.join(os15.tmpdir(), `december-chrome-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
175956
+ await fs28.mkdir(this.tmpProfileDir, { recursive: true });
175957
+ const isHeadless = this.options.headless ?? (process.env.DECEMBER_HEADED !== "1" && process.env.HEADED !== "1");
175958
+ const args = [
175959
+ ...isHeadless ? ["--headless=new"] : [],
175960
+ "--disable-gpu",
175961
+ "--no-sandbox",
175962
+ "--disable-dev-shm-usage",
175963
+ "--remote-debugging-port=0",
175964
+ "--hide-scrollbars",
175965
+ "--window-size=1280,800",
175966
+ `--user-data-dir=${this.tmpProfileDir}`,
175967
+ "about:blank"
175968
+ ];
175969
+ const proc = spawn4(this.options.chromePath, args, {
175970
+ stdio: ["ignore", "pipe", "pipe"]
175971
+ });
175972
+ this.chromeProc = proc;
175973
+ const wsUrl = await new Promise((resolve, reject) => {
175974
+ let buffer = "";
175975
+ const timeout = setTimeout(() => {
175976
+ reject(new Error("Timeout waiting for Chrome DevTools WebSocket URL."));
175977
+ }, this.options.timeoutMs || 15000);
175978
+ const onData = (chunk) => {
175979
+ buffer += chunk.toString();
175980
+ const match = buffer.match(/DevTools listening on (ws:\/\/[^\s]+)/);
175981
+ if (match && match[1]) {
175982
+ clearTimeout(timeout);
175983
+ proc.stderr?.off("data", onData);
175984
+ resolve(match[1]);
175985
+ }
175986
+ };
175987
+ proc.stderr?.on("data", onData);
175988
+ proc.on("error", (err) => {
175989
+ clearTimeout(timeout);
175990
+ reject(err);
175991
+ });
175992
+ proc.on("exit", (code) => {
175993
+ clearTimeout(timeout);
175994
+ reject(new Error(`Chrome exited prematurely with code ${code}.`));
175995
+ });
175996
+ });
175997
+ await this.connectWs(wsUrl);
175998
+ const createTargetRes = await this.sendCommand("Target.createTarget", {
175999
+ url: "about:blank"
176000
+ });
176001
+ const targetId = createTargetRes.targetId;
176002
+ const attachRes = await this.sendCommand("Target.attachToTarget", {
176003
+ targetId,
176004
+ flatten: true
176005
+ });
176006
+ this.sessionId = attachRes.sessionId;
176007
+ await this.sendSessionCommand("Page.enable");
176008
+ await this.sendSessionCommand("Runtime.enable");
176009
+ await this.sendSessionCommand("Network.enable");
176010
+ }
176011
+ async connectWs(wsUrl) {
176012
+ return new Promise((resolve, reject) => {
176013
+ const ws = new WebSocket(wsUrl);
176014
+ this.ws = ws;
176015
+ ws.onopen = () => resolve();
176016
+ ws.onerror = (err) => reject(err);
176017
+ ws.onmessage = (event) => {
176018
+ try {
176019
+ const msg = JSON.parse(event.data.toString());
176020
+ if (typeof msg.id === "number") {
176021
+ const pending = this.pendingCommands.get(msg.id);
176022
+ if (pending) {
176023
+ this.pendingCommands.delete(msg.id);
176024
+ if (msg.error) {
176025
+ pending.reject(new Error(msg.error.message || "CDP Error"));
176026
+ } else {
176027
+ pending.resolve(msg.result);
176028
+ }
176029
+ }
176030
+ return;
176031
+ }
176032
+ this.handleEvent(msg);
176033
+ } catch {}
176034
+ };
176035
+ });
176036
+ }
176037
+ handleEvent(msg) {
176038
+ const method = msg.method;
176039
+ if (method === "Page.loadEventFired" || method === "Page.domContentEventFired") {
176040
+ this.isLoaded = true;
176041
+ } else if (method === "Runtime.exceptionThrown") {
176042
+ const details = msg.params?.exceptionDetails;
176043
+ const desc = details?.exception?.description || details?.text || "Unknown runtime exception";
176044
+ this.consoleErrors.push(`Exception: ${desc}`);
176045
+ } else if (method === "Runtime.consoleAPICalled") {
176046
+ const type = msg.params?.type;
176047
+ if (type === "error" || type === "warning") {
176048
+ const args = (msg.params?.args || []).map((a) => a.value ?? a.description ?? "").join(" ");
176049
+ this.consoleErrors.push(`[console.${type}] ${args}`);
176050
+ }
176051
+ } else if (method === "Network.responseReceived") {
176052
+ const res = msg.params?.response;
176053
+ if (res && typeof res.status === "number" && res.status >= 400) {
176054
+ this.networkErrors.push(`HTTP ${res.status} ${res.statusText || ""} - ${res.url}`);
176055
+ }
176056
+ } else if (method === "Network.loadingFailed") {
176057
+ const err = msg.params;
176058
+ if (err) {
176059
+ this.networkErrors.push(`Network failure: ${err.errorText || "Unknown error"} (${err.type || "request"})`);
176060
+ }
176061
+ }
176062
+ }
176063
+ sendCommand(method, params) {
176064
+ return new Promise((resolve, reject) => {
176065
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
176066
+ return reject(new Error("WebSocket is not open"));
176067
+ }
176068
+ const id = this.nextId++;
176069
+ this.pendingCommands.set(id, { resolve, reject });
176070
+ this.ws.send(JSON.stringify({ id, method, params }));
176071
+ });
176072
+ }
176073
+ sendSessionCommand(method, params) {
176074
+ return new Promise((resolve, reject) => {
176075
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
176076
+ return reject(new Error("WebSocket is not open"));
176077
+ }
176078
+ const id = this.nextId++;
176079
+ this.pendingCommands.set(id, { resolve, reject });
176080
+ const payload = { id, method, params };
176081
+ if (this.sessionId) {
176082
+ payload.sessionId = this.sessionId;
176083
+ }
176084
+ this.ws.send(JSON.stringify(payload));
176085
+ });
176086
+ }
176087
+ async navigate(url, timeoutMs = 12000) {
176088
+ this.consoleErrors = [];
176089
+ this.networkErrors = [];
176090
+ this.isLoaded = false;
176091
+ await this.sendSessionCommand("Page.navigate", { url });
176092
+ const startTime = Date.now();
176093
+ while (!this.isLoaded && Date.now() - startTime < timeoutMs) {
176094
+ await new Promise((r) => setTimeout(r, 100));
176095
+ }
176096
+ await new Promise((r) => setTimeout(r, 300));
176097
+ const evalRes = await this.sendSessionCommand("Runtime.evaluate", {
176098
+ expression: '(function() { return document.body ? (document.body.innerText || document.documentElement.innerText || "") : ""; })()',
176099
+ returnByValue: true
176100
+ });
176101
+ const text = evalRes?.result?.value || "";
176102
+ return {
176103
+ text: text.trim(),
176104
+ consoleErrors: [...this.consoleErrors],
176105
+ networkErrors: [...this.networkErrors]
176106
+ };
176107
+ }
176108
+ async click(selector) {
176109
+ const expression = `
176110
+ (function(sel) {
176111
+ var el = document.querySelector(sel);
176112
+ if (!el) {
176113
+ var buttons = Array.from(document.querySelectorAll('button, a, input[type=button], input[type=submit], [role=button]'));
176114
+ el = buttons.find(function(b) {
176115
+ return (b.textContent || '').trim().toLowerCase() === sel.trim().toLowerCase();
176116
+ });
176117
+ }
176118
+ if (!el) {
176119
+ return { success: false, error: 'Could not find element matching: ' + sel };
176120
+ }
176121
+ el.scrollIntoView({ behavior: 'instant', block: 'center' });
176122
+ el.click();
176123
+ return {
176124
+ success: true,
176125
+ tagName: el.tagName,
176126
+ text: (el.textContent || '').trim().slice(0, 50)
176127
+ };
176128
+ })(${JSON.stringify(selector)})
176129
+ `;
176130
+ const evalRes = await this.sendSessionCommand("Runtime.evaluate", {
176131
+ expression,
176132
+ returnByValue: true
176133
+ });
176134
+ const result = evalRes?.result?.value;
176135
+ if (!result || !result.success) {
176136
+ return {
176137
+ success: false,
176138
+ output: "",
176139
+ error: result?.error || `Element "${selector}" not found.`,
176140
+ consoleErrors: [...this.consoleErrors],
176141
+ networkErrors: [...this.networkErrors]
176142
+ };
176143
+ }
176144
+ await new Promise((r) => setTimeout(r, 300));
176145
+ return {
176146
+ success: true,
176147
+ output: `Clicked <${result.tagName}>: "${result.text || selector}"`,
176148
+ consoleErrors: [...this.consoleErrors],
176149
+ networkErrors: [...this.networkErrors]
176150
+ };
176151
+ }
176152
+ async type(selector, text) {
176153
+ const expression = `
176154
+ (function(sel, val) {
176155
+ var el = document.querySelector(sel);
176156
+ if (!el) {
176157
+ var inputs = Array.from(document.querySelectorAll('input, textarea'));
176158
+ el = inputs.find(function(i) {
176159
+ return i.name === sel || i.id === sel || i.placeholder === sel;
176160
+ });
176161
+ }
176162
+ if (!el) {
176163
+ return { success: false, error: 'Could not find input element matching: ' + sel };
176164
+ }
176165
+ el.focus();
176166
+ el.value = val;
176167
+ el.dispatchEvent(new Event('input', { bubbles: true }));
176168
+ el.dispatchEvent(new Event('change', { bubbles: true }));
176169
+ return { success: true, tagName: el.tagName, value: el.value };
176170
+ })(${JSON.stringify(selector)}, ${JSON.stringify(text)})
176171
+ `;
176172
+ const evalRes = await this.sendSessionCommand("Runtime.evaluate", {
176173
+ expression,
176174
+ returnByValue: true
176175
+ });
176176
+ const result = evalRes?.result?.value;
176177
+ if (!result || !result.success) {
176178
+ return {
176179
+ success: false,
176180
+ output: "",
176181
+ error: result?.error || `Input element "${selector}" not found.`,
176182
+ consoleErrors: [...this.consoleErrors],
176183
+ networkErrors: [...this.networkErrors]
176184
+ };
176185
+ }
176186
+ return {
176187
+ success: true,
176188
+ output: `Typed "${text}" into <${result.tagName}> (${selector})`,
176189
+ consoleErrors: [...this.consoleErrors],
176190
+ networkErrors: [...this.networkErrors]
176191
+ };
176192
+ }
176193
+ async screenshot(fullPage = false, outputPath) {
176194
+ const params = { format: "png" };
176195
+ if (fullPage) {
176196
+ params.captureBeyondViewport = true;
176197
+ }
176198
+ const res = await this.sendSessionCommand("Page.captureScreenshot", params);
176199
+ const base64Data = res?.data;
176200
+ if (!base64Data) {
176201
+ return {
176202
+ success: false,
176203
+ output: "",
176204
+ error: "Failed to capture screenshot data from Chrome."
176205
+ };
176206
+ }
176207
+ const targetFile = outputPath || path29.join(os15.tmpdir(), `december-screenshot-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.png`);
176208
+ await fs28.writeFile(targetFile, Buffer.from(base64Data, "base64"));
176209
+ return {
176210
+ success: true,
176211
+ output: `Screenshot captured successfully.`,
176212
+ screenshotPath: targetFile,
176213
+ screenshotBase64: base64Data,
176214
+ consoleErrors: [...this.consoleErrors],
176215
+ networkErrors: [...this.networkErrors]
176216
+ };
176217
+ }
176218
+ async getConsoleErrors() {
176219
+ return {
176220
+ consoleErrors: [...this.consoleErrors],
176221
+ networkErrors: [...this.networkErrors]
176222
+ };
176223
+ }
176224
+ async close() {
176225
+ if (this.ws) {
176226
+ try {
176227
+ this.ws.close();
176228
+ } catch {}
176229
+ this.ws = null;
176230
+ }
176231
+ if (this.chromeProc) {
176232
+ try {
176233
+ this.chromeProc.kill("SIGTERM");
176234
+ setTimeout(() => {
176235
+ if (this.chromeProc && !this.chromeProc.killed) {
176236
+ this.chromeProc.kill("SIGKILL");
176237
+ }
176238
+ }, 1000);
176239
+ } catch {}
176240
+ this.chromeProc = null;
176241
+ }
176242
+ if (this.tmpProfileDir) {
176243
+ try {
176244
+ await fs28.rm(this.tmpProfileDir, { recursive: true, force: true });
176245
+ } catch {}
176246
+ this.tmpProfileDir = null;
176247
+ }
176248
+ }
176249
+ }
176250
+ var init_cdp_client = () => {};
176251
+
176252
+ // src/utils/browser/browser-runner.ts
176253
+ class BrowserRunner {
176254
+ activeSession = null;
176255
+ cleanupRegistered = false;
176256
+ registerCleanup() {
176257
+ if (this.cleanupRegistered)
176258
+ return;
176259
+ this.cleanupRegistered = true;
176260
+ const cleanup = () => {
176261
+ if (this.activeSession) {
176262
+ this.activeSession.close().catch(() => {});
176263
+ this.activeSession = null;
176264
+ }
176265
+ };
176266
+ process.once("exit", cleanup);
176267
+ process.once("SIGINT", cleanup);
176268
+ process.once("SIGTERM", cleanup);
176269
+ }
176270
+ async getSession() {
176271
+ if (this.activeSession) {
176272
+ return this.activeSession;
176273
+ }
176274
+ const chromePath = await findSystemChrome();
176275
+ if (!chromePath) {
176276
+ return null;
176277
+ }
176278
+ try {
176279
+ await Promise.resolve().then(() => init_config());
176280
+ const config = await loadConfig().catch(() => null);
176281
+ const hasDisplay = process.platform !== "linux" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
176282
+ const isVisible = hasDisplay && config?.browserVisible !== false && process.env.HEADED !== "0" && process.env.DECEMBER_HEADED !== "0";
176283
+ const session = new CdpBrowserSession({
176284
+ chromePath,
176285
+ headless: !isVisible
176286
+ });
176287
+ await session.launch();
176288
+ this.activeSession = session;
176289
+ this.registerCleanup();
176290
+ return session;
176291
+ } catch {
176292
+ return null;
176293
+ }
176294
+ }
176295
+ async staticFallbackFetch(url) {
176296
+ try {
176297
+ const res = await fetch(url, {
176298
+ headers: {
176299
+ "User-Agent": "Mozilla/5.0 (compatible; DecemberAgent/1.0)"
176300
+ }
176301
+ });
176302
+ const html = await res.text();
176303
+ if (!res.ok) {
176304
+ return {
176305
+ text: "",
176306
+ error: `HTTP Error (${res.status}): ${html}`,
176307
+ networkErrors: [`HTTP ${res.status} - ${url}`]
176308
+ };
176309
+ }
176310
+ const cleanText = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
176311
+ const notice = `
176312
+
176313
+ [Note: Local Chrome/Chromium not detected. Displaying static HTML text. Install Chrome or Chromium for client-side JavaScript rendering and interactive actions.]`;
176314
+ return {
176315
+ text: cleanText + notice,
176316
+ consoleErrors: [],
176317
+ networkErrors: []
176318
+ };
176319
+ } catch (err) {
176320
+ return {
176321
+ text: "",
176322
+ error: err.message,
176323
+ networkErrors: [`Network request failed: ${err.message}`]
176324
+ };
176325
+ }
176326
+ }
176327
+ navigate = async (url) => {
176328
+ const session = await this.getSession();
176329
+ if (session) {
176330
+ try {
176331
+ return await session.navigate(url);
176332
+ } catch {}
176333
+ }
176334
+ return this.staticFallbackFetch(url);
176335
+ };
176336
+ action = async (params) => {
176337
+ const { action, url, selector, text, fullPage } = params;
176338
+ if (action === "navigate") {
176339
+ if (!url) {
176340
+ return {
176341
+ success: false,
176342
+ action: "navigate",
176343
+ error: 'Action "navigate" requires a valid "url" parameter.'
176344
+ };
176345
+ }
176346
+ const navResult = await this.navigate(url);
176347
+ if (navResult.error) {
176348
+ return {
176349
+ success: false,
176350
+ action: "navigate",
176351
+ error: navResult.error,
176352
+ networkErrors: navResult.networkErrors
176353
+ };
176354
+ }
176355
+ return {
176356
+ success: true,
176357
+ action: "navigate",
176358
+ text: navResult.text,
176359
+ consoleErrors: navResult.consoleErrors,
176360
+ networkErrors: navResult.networkErrors
176361
+ };
176362
+ }
176363
+ if (action === "get_console_errors") {
176364
+ if (this.activeSession) {
176365
+ const errors = await this.activeSession.getConsoleErrors();
176366
+ const count = errors.consoleErrors.length + errors.networkErrors.length;
176367
+ return {
176368
+ success: true,
176369
+ action: "get_console_errors",
176370
+ output: count > 0 ? `Captured ${errors.consoleErrors.length} console error(s) and ${errors.networkErrors.length} network error(s).` : "No console or network errors detected.",
176371
+ consoleErrors: errors.consoleErrors,
176372
+ networkErrors: errors.networkErrors
176373
+ };
176374
+ }
176375
+ return {
176376
+ success: true,
176377
+ action: "get_console_errors",
176378
+ output: "No active browser session running."
176379
+ };
176380
+ }
176381
+ const session = await this.getSession();
176382
+ if (!session) {
176383
+ return {
176384
+ success: false,
176385
+ action,
176386
+ error: `Interactive browser action "${action}" requires Google Chrome or Chromium. Please install Chrome or Chromium, or set the CHROME_PATH environment variable.`
176387
+ };
176388
+ }
176389
+ switch (action) {
176390
+ case "click": {
176391
+ if (!selector) {
176392
+ return {
176393
+ success: false,
176394
+ action: "click",
176395
+ error: 'Action "click" requires a "selector" parameter.'
176396
+ };
176397
+ }
176398
+ const res = await session.click(selector);
176399
+ return {
176400
+ success: res.success,
176401
+ action: "click",
176402
+ output: res.output,
176403
+ error: res.error,
176404
+ consoleErrors: res.consoleErrors,
176405
+ networkErrors: res.networkErrors
176406
+ };
176407
+ }
176408
+ case "type": {
176409
+ if (!selector) {
176410
+ return {
176411
+ success: false,
176412
+ action: "type",
176413
+ error: 'Action "type" requires a "selector" parameter.'
176414
+ };
176415
+ }
176416
+ const res = await session.type(selector, text || "");
176417
+ return {
176418
+ success: res.success,
176419
+ action: "type",
176420
+ output: res.output,
176421
+ error: res.error,
176422
+ consoleErrors: res.consoleErrors,
176423
+ networkErrors: res.networkErrors
176424
+ };
176425
+ }
176426
+ case "screenshot": {
176427
+ const res = await session.screenshot(Boolean(fullPage));
176428
+ return {
176429
+ success: res.success,
176430
+ action: "screenshot",
176431
+ output: res.output,
176432
+ screenshotPath: res.screenshotPath,
176433
+ screenshotBase64: res.screenshotBase64,
176434
+ error: res.error,
176435
+ consoleErrors: res.consoleErrors,
176436
+ networkErrors: res.networkErrors
176437
+ };
176438
+ }
176439
+ default:
176440
+ return {
176441
+ success: false,
176442
+ action,
176443
+ error: `Unsupported browser action: ${action}`
176444
+ };
176445
+ }
176446
+ };
176447
+ close = async () => {
176448
+ if (this.activeSession) {
176449
+ await this.activeSession.close();
176450
+ this.activeSession = null;
176451
+ }
176452
+ };
176453
+ }
176454
+ var browserRunner;
176455
+ var init_browser_runner = __esm(() => {
176456
+ init_cdp_client();
176457
+ init_detector();
176458
+ browserRunner = new BrowserRunner;
176459
+ });
176460
+
176461
+ // src/utils/browser/index.ts
176462
+ var init_browser2 = __esm(() => {
176463
+ init_detector();
176464
+ init_cdp_client();
176465
+ init_browser_runner();
176466
+ });
176467
+
175521
176468
  // src/local-operations.ts
175522
176469
  var exports_local_operations = {};
175523
176470
  __export(exports_local_operations, {
@@ -175525,22 +176472,22 @@ __export(exports_local_operations, {
175525
176472
  localOperations: () => localOperations,
175526
176473
  setActiveScopeDir: () => setActiveScopeDir
175527
176474
  });
175528
- import { exec as exec4, spawn as spawn4 } from "node:child_process";
176475
+ import { exec as exec4, spawn as spawn5 } from "node:child_process";
175529
176476
  import nodeFs from "node:fs";
175530
- import fs26 from "node:fs/promises";
175531
- import path27 from "node:path";
176477
+ import fs29 from "node:fs/promises";
176478
+ import path30 from "node:path";
175532
176479
  import { promisify as promisify4 } from "node:util";
175533
176480
  function setActiveScopeDir(dir) {
175534
176481
  activeScopeDir = dir;
175535
176482
  }
175536
176483
  function getScopedCwd() {
175537
176484
  if (activeScopeDir) {
175538
- return path27.resolve(process.cwd(), activeScopeDir);
176485
+ return path30.resolve(process.cwd(), activeScopeDir);
175539
176486
  }
175540
176487
  return process.cwd();
175541
176488
  }
175542
176489
  function isTypeScriptProject(workspaceDir) {
175543
- return nodeFs.existsSync(path27.join(workspaceDir, "tsconfig.json")) || nodeFs.existsSync(path27.join(workspaceDir, "package.json"));
176490
+ return nodeFs.existsSync(path30.join(workspaceDir, "tsconfig.json")) || nodeFs.existsSync(path30.join(workspaceDir, "package.json"));
175544
176491
  }
175545
176492
  async function getOrLaunchLspClient(customWorkspaceDir) {
175546
176493
  const workspaceDir = customWorkspaceDir || getScopedCwd();
@@ -175575,6 +176522,7 @@ var init_local_operations = __esm(() => {
175575
176522
  init_src();
175576
176523
  init_src4();
175577
176524
  init_task_manager();
176525
+ init_browser2();
175578
176526
  import_fast_glob = __toESM(require_out4(), 1);
175579
176527
  execAsync2 = promisify4(exec4);
175580
176528
  localBashOps = createLocalBashOperations();
@@ -175587,7 +176535,7 @@ var init_local_operations = __esm(() => {
175587
176535
  const fallbackTimeoutMs = actualOptions.waitMsBeforeAsync ?? DEFAULT_BG_TIMEOUT_MS;
175588
176536
  const serverDelayMs = actualOptions.serverReadyDelayMs ?? DEFAULT_SERVER_READY_DELAY_MS;
175589
176537
  return new Promise((resolve, reject) => {
175590
- const child = spawn4(command, {
176538
+ const child = spawn5(command, {
175591
176539
  cwd: targetCwd,
175592
176540
  detached: process.platform !== "win32",
175593
176541
  env: actualOptions.env ?? process.env,
@@ -175717,19 +176665,19 @@ Failed to start process: ${err?.message || err}
175717
176665
  fs: {
175718
176666
  readFile: async (filepath) => {
175719
176667
  const root = getScopedCwd();
175720
- const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
175721
- return fs26.readFile(absolutePath, "utf8");
176668
+ const absolutePath = path30.isAbsolute(filepath) ? filepath : path30.resolve(root, filepath);
176669
+ return fs29.readFile(absolutePath, "utf8");
175722
176670
  },
175723
176671
  writeFile: async (filepath, content) => {
175724
176672
  const root = getScopedCwd();
175725
- const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
175726
- await fs26.mkdir(path27.dirname(absolutePath), { recursive: true });
175727
- await fs26.writeFile(absolutePath, content, "utf8");
176673
+ const absolutePath = path30.isAbsolute(filepath) ? filepath : path30.resolve(root, filepath);
176674
+ await fs29.mkdir(path30.dirname(absolutePath), { recursive: true });
176675
+ await fs29.writeFile(absolutePath, content, "utf8");
175728
176676
  },
175729
176677
  readdir: async (dirPath) => {
175730
176678
  const root = getScopedCwd();
175731
- const absolutePath = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
175732
- const entries = await fs26.readdir(absolutePath, { withFileTypes: true });
176679
+ const absolutePath = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
176680
+ const entries = await fs29.readdir(absolutePath, { withFileTypes: true });
175733
176681
  const ignores = getWorkspaceIgnores(root);
175734
176682
  return entries.filter((entry) => !isPathIgnored(entry.name, ignores)).map((entry) => {
175735
176683
  const type = entry.isDirectory() ? "DIR " : "FILE";
@@ -175738,14 +176686,14 @@ Failed to start process: ${err?.message || err}
175738
176686
  },
175739
176687
  mkdir: async (dirPath, options) => {
175740
176688
  const root = getScopedCwd();
175741
- const absolutePath = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
175742
- await fs26.mkdir(absolutePath, options);
176689
+ const absolutePath = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
176690
+ await fs29.mkdir(absolutePath, options);
175743
176691
  },
175744
176692
  exists: async (filepath) => {
175745
176693
  const root = getScopedCwd();
175746
- const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
176694
+ const absolutePath = path30.isAbsolute(filepath) ? filepath : path30.resolve(root, filepath);
175747
176695
  try {
175748
- await fs26.access(absolutePath);
176696
+ await fs29.access(absolutePath);
175749
176697
  return true;
175750
176698
  } catch {
175751
176699
  return false;
@@ -175755,7 +176703,7 @@ Failed to start process: ${err?.message || err}
175755
176703
  search: {
175756
176704
  find: async (dirPath, query) => {
175757
176705
  const root = getScopedCwd();
175758
- const targetDir = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
176706
+ const targetDir = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
175759
176707
  const ignores = getWorkspaceIgnores(root);
175760
176708
  const files = await import_fast_glob.default([query], {
175761
176709
  cwd: targetDir,
@@ -175767,7 +176715,7 @@ Failed to start process: ${err?.message || err}
175767
176715
  },
175768
176716
  grep: async (dirPath, query) => {
175769
176717
  const root = getScopedCwd();
175770
- const targetDir = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
176718
+ const targetDir = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
175771
176719
  const ignores = getWorkspaceIgnores(root);
175772
176720
  try {
175773
176721
  const escapedQuery = query.replace(/"/g, "\\\"");
@@ -175781,7 +176729,7 @@ Failed to start process: ${err?.message || err}
175781
176729
  if (!line.trim())
175782
176730
  return false;
175783
176731
  const rawPath = line.split(":")[0];
175784
- const relPath = path27.isAbsolute(rawPath) ? path27.relative(root, rawPath) : rawPath;
176732
+ const relPath = path30.isAbsolute(rawPath) ? path30.relative(root, rawPath) : rawPath;
175785
176733
  return !isPathIgnored(relPath, ignores);
175786
176734
  });
175787
176735
  return filtered.join(`
@@ -175806,23 +176754,9 @@ Failed to start process: ${err?.message || err}
175806
176754
  }
175807
176755
  },
175808
176756
  browser: {
175809
- navigate: async (url) => {
175810
- try {
175811
- const res = await fetch(url, {
175812
- headers: {
175813
- "User-Agent": "Mozilla/5.0 (compatible; DecemberAgent/1.0)"
175814
- }
175815
- });
175816
- const html = await res.text();
175817
- if (!res.ok) {
175818
- return { text: "", error: `HTTP Error (${res.status}): ${html}` };
175819
- }
175820
- const cleanText = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
175821
- return { text: cleanText };
175822
- } catch (error) {
175823
- return { text: "", error: error.message };
175824
- }
175825
- }
176757
+ navigate: async (url) => browserRunner.navigate(url),
176758
+ action: async (params) => browserRunner.action(params),
176759
+ close: async () => browserRunner.close()
175826
176760
  },
175827
176761
  diagnostics: {
175828
176762
  getDiagnostics: async (filePath) => {
@@ -177515,7 +178449,7 @@ var init_environment = __esm(() => {
177515
178449
 
177516
178450
  // ../../node_modules/.bun/ansi-escapes@7.3.0/node_modules/ansi-escapes/base.js
177517
178451
  import process2 from "node:process";
177518
- import os13 from "node:os";
178452
+ import os16 from "node:os";
177519
178453
  var ESC = "\x1B[", SEP = ";", isTerminalApp, isWindows2, isTmux, cwdFunction, cursorTo = (x, y) => {
177520
178454
  if (typeof x !== "number") {
177521
178455
  throw new TypeError("The `x` argument is required");
@@ -177537,7 +178471,7 @@ var ESC = "\x1B[", SEP = ";", isTerminalApp, isWindows2, isTmux, cwdFunction, cu
177537
178471
  if (isBrowser || !isWindows2) {
177538
178472
  return false;
177539
178473
  }
177540
- const parts = os13.release().split(".");
178474
+ const parts = os16.release().split(".");
177541
178475
  const major = Number(parts[0]);
177542
178476
  const build = Number(parts[2] ?? 0);
177543
178477
  if (major < 10) {
@@ -180224,7 +181158,7 @@ var init_wrap_ansi = __esm(() => {
180224
181158
  // ../../node_modules/.bun/terminal-size@4.0.1/node_modules/terminal-size/index.js
180225
181159
  import process3 from "node:process";
180226
181160
  import { execFileSync } from "node:child_process";
180227
- import fs27 from "node:fs";
181161
+ import fs30 from "node:fs";
180228
181162
  import tty from "node:tty";
180229
181163
  function terminalSize() {
180230
181164
  const { env, stdout, stderr } = process3;
@@ -180272,7 +181206,7 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
180272
181206
  return true;
180273
181207
  }
180274
181208
  try {
180275
- const statContents = fs27.readFileSync("/proc/self/stat", "utf8");
181209
+ const statContents = fs30.readFileSync("/proc/self/stat", "utf8");
180276
181210
  const closingParenthesisIndex = statContents.lastIndexOf(") ");
180277
181211
  if (closingParenthesisIndex === -1) {
180278
181212
  return false;
@@ -180292,8 +181226,8 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
180292
181226
  }
180293
181227
  }, devTty = () => {
180294
181228
  try {
180295
- const flags = process3.platform === "darwin" ? fs27.constants.O_EVTONLY | fs27.constants.O_NONBLOCK : fs27.constants.O_NONBLOCK;
180296
- const { columns, rows } = tty.WriteStream(fs27.openSync("/dev/tty", flags));
181229
+ const flags = process3.platform === "darwin" ? fs30.constants.O_EVTONLY | fs30.constants.O_NONBLOCK : fs30.constants.O_NONBLOCK;
181230
+ const { columns, rows } = tty.WriteStream(fs30.openSync("/dev/tty", flags));
180297
181231
  return { columns, rows };
180298
181232
  } catch {}
180299
181233
  }, tput = () => {
@@ -210285,7 +211219,7 @@ var init_ansi_styles2 = __esm(() => {
210285
211219
 
210286
211220
  // ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
210287
211221
  import process5 from "node:process";
210288
- import os14 from "node:os";
211222
+ import os17 from "node:os";
210289
211223
  import tty2 from "node:tty";
210290
211224
  function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process5.argv) {
210291
211225
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
@@ -210343,7 +211277,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
210343
211277
  return min;
210344
211278
  }
210345
211279
  if (process5.platform === "win32") {
210346
- const osRelease = os14.release().split(".");
211280
+ const osRelease = os17.release().split(".");
210347
211281
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
210348
211282
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
210349
211283
  }
@@ -212495,7 +213429,7 @@ var init_Text = __esm(() => {
212495
213429
  });
212496
213430
 
212497
213431
  // ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/ErrorOverview.js
212498
- import * as fs28 from "node:fs";
213432
+ import * as fs31 from "node:fs";
212499
213433
  import { cwd } from "node:process";
212500
213434
  function ErrorOverview({ error }) {
212501
213435
  const stack = error.stack ? error.stack.split(`
@@ -212505,8 +213439,8 @@ function ErrorOverview({ error }) {
212505
213439
  let excerpt;
212506
213440
  let lineWidth = 0;
212507
213441
  const stackLineCounts = new Map;
212508
- if (filePath && origin?.line && fs28.existsSync(filePath)) {
212509
- const sourceCode = fs28.readFileSync(filePath, "utf8");
213442
+ if (filePath && origin?.line && fs31.existsSync(filePath)) {
213443
+ const sourceCode = fs31.readFileSync(filePath, "utf8");
212510
213444
  excerpt = dist_default4(sourceCode, origin.line);
212511
213445
  if (excerpt) {
212512
213446
  for (const { line } of excerpt) {
@@ -213937,7 +214871,7 @@ var init_render_to_string = __esm(async () => {
213937
214871
  });
213938
214872
 
213939
214873
  // ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/Static.js
213940
- function Static15(props) {
214874
+ function Static16(props) {
213941
214875
  const { items, children: render, style: customStyle } = props;
213942
214876
  const [index, setIndex] = import_react17.useState(0);
213943
214877
  const itemsToRender = import_react17.useMemo(() => {
@@ -215180,9 +216114,9 @@ var init_paste_manager = __esm(() => {
215180
216114
  });
215181
216115
 
215182
216116
  // ../../packages/tui/src/utils/prompt-history.ts
215183
- import fs29 from "node:fs";
215184
- import os15 from "node:os";
215185
- import path28 from "node:path";
216117
+ import fs32 from "node:fs";
216118
+ import os18 from "node:os";
216119
+ import path31 from "node:path";
215186
216120
 
215187
216121
  class PromptHistory {
215188
216122
  historyPath;
@@ -215193,17 +216127,17 @@ class PromptHistory {
215193
216127
  if (customPath) {
215194
216128
  this.historyPath = customPath;
215195
216129
  } else {
215196
- const home = process.env.HOME || process.env.USERPROFILE || os15.homedir();
215197
- const canonicalPath = path28.join(home, ".december", "history");
215198
- const legacyPath = path28.join(home, ".config", "december", "history");
215199
- this.historyPath = fs29.existsSync(canonicalPath) || !fs29.existsSync(legacyPath) ? canonicalPath : legacyPath;
216130
+ const home = process.env.HOME || process.env.USERPROFILE || os18.homedir();
216131
+ const canonicalPath = path31.join(home, ".december", "history");
216132
+ const legacyPath = path31.join(home, ".config", "december", "history");
216133
+ this.historyPath = fs32.existsSync(canonicalPath) || !fs32.existsSync(legacyPath) ? canonicalPath : legacyPath;
215200
216134
  }
215201
216135
  this.load();
215202
216136
  }
215203
216137
  load() {
215204
216138
  try {
215205
- if (fs29.existsSync(this.historyPath)) {
215206
- const raw = fs29.readFileSync(this.historyPath, "utf8");
216139
+ if (fs32.existsSync(this.historyPath)) {
216140
+ const raw = fs32.readFileSync(this.historyPath, "utf8");
215207
216141
  this.entries = raw.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
215208
216142
  }
215209
216143
  } catch {
@@ -215219,11 +216153,11 @@ class PromptHistory {
215219
216153
  this.cursor = this.entries.length;
215220
216154
  this.currentDraft = "";
215221
216155
  try {
215222
- const dir = path28.dirname(this.historyPath);
215223
- if (!fs29.existsSync(dir)) {
215224
- fs29.mkdirSync(dir, { recursive: true });
216156
+ const dir = path31.dirname(this.historyPath);
216157
+ if (!fs32.existsSync(dir)) {
216158
+ fs32.mkdirSync(dir, { recursive: true });
215225
216159
  }
215226
- fs29.appendFileSync(this.historyPath, trimmed + `
216160
+ fs32.appendFileSync(this.historyPath, trimmed + `
215227
216161
  `, "utf8");
215228
216162
  } catch {}
215229
216163
  }
@@ -215262,11 +216196,11 @@ var init_prompt_history = __esm(() => {
215262
216196
 
215263
216197
  // ../../packages/tui/src/utils/clipboard.ts
215264
216198
  import { spawnSync } from "child_process";
215265
- import * as os16 from "os";
216199
+ import * as os19 from "os";
215266
216200
  function writeToClipboard(text) {
215267
- const platform = os16.platform();
216201
+ const platform = os19.platform();
215268
216202
  try {
215269
- const isWsl = platform === "linux" && (!!process.env.WSL_DISTRO_NAME || os16.release().toLowerCase().includes("microsoft"));
216203
+ const isWsl = platform === "linux" && (!!process.env.WSL_DISTRO_NAME || os19.release().toLowerCase().includes("microsoft"));
215270
216204
  if (platform === "darwin") {
215271
216205
  spawnSync("pbcopy", { input: text });
215272
216206
  } else if (platform === "win32") {
@@ -215287,16 +216221,16 @@ var init_clipboard = () => {};
215287
216221
 
215288
216222
  // ../../packages/tui/src/utils/handoff.ts
215289
216223
  import { exec as exec7 } from "node:child_process";
215290
- import fs30 from "node:fs";
215291
- import path29 from "node:path";
216224
+ import fs33 from "node:fs";
216225
+ import path32 from "node:path";
215292
216226
  import { promisify as promisify5 } from "node:util";
215293
216227
  function getHandoffExcludes(baseDir = process.cwd()) {
215294
216228
  const excludes = [...MANDATORY_HANDOFF_EXCLUDES];
215295
216229
  const readIgnoreFile = (fileName) => {
215296
- const filePath = path29.join(baseDir, fileName);
216230
+ const filePath = path32.join(baseDir, fileName);
215297
216231
  try {
215298
- if (fs30.existsSync(filePath)) {
215299
- const lines = fs30.readFileSync(filePath, "utf8").split(`
216232
+ if (fs33.existsSync(filePath)) {
216233
+ const lines = fs33.readFileSync(filePath, "utf8").split(`
215300
216234
  `);
215301
216235
  for (const line of lines) {
215302
216236
  const trimmed = line.trim();
@@ -215365,9 +216299,9 @@ var init_handoff = __esm(() => {
215365
216299
  });
215366
216300
 
215367
216301
  // ../../packages/tui/src/components/command-menu/commands.tsx
215368
- import fs31 from "node:fs";
215369
- import os17 from "node:os";
215370
- import path30 from "node:path";
216302
+ import fs34 from "node:fs";
216303
+ import os20 from "node:os";
216304
+ import path33 from "node:path";
215371
216305
  var COMMANDS;
215372
216306
  var init_commands2 = __esm(() => {
215373
216307
  init_clipboard();
@@ -215470,14 +216404,14 @@ var init_commands2 = __esm(() => {
215470
216404
  action: async (ctx) => {
215471
216405
  const archivePath = ".december-handoff.tar.gz";
215472
216406
  try {
215473
- const home = process.env.HOME || process.env.USERPROFILE || os17.homedir();
215474
- const canonicalConfigPath = path30.join(home, ".december", "config.json");
215475
- const legacyConfigPath = path30.join(home, ".config", "december", "config.json");
215476
- const configPath = fs31.existsSync(canonicalConfigPath) || !fs31.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
216407
+ const home = process.env.HOME || process.env.USERPROFILE || os20.homedir();
216408
+ const canonicalConfigPath = path33.join(home, ".december", "config.json");
216409
+ const legacyConfigPath = path33.join(home, ".config", "december", "config.json");
216410
+ const configPath = fs34.existsSync(canonicalConfigPath) || !fs34.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
215477
216411
  let config = {};
215478
216412
  try {
215479
- if (fs31.existsSync(configPath)) {
215480
- config = JSON.parse(fs31.readFileSync(configPath, "utf8"));
216413
+ if (fs34.existsSync(configPath)) {
216414
+ config = JSON.parse(fs34.readFileSync(configPath, "utf8"));
215481
216415
  }
215482
216416
  } catch {}
215483
216417
  if (!config.decemberToken) {
@@ -215512,7 +216446,7 @@ var init_commands2 = __esm(() => {
215512
216446
  headers: { Authorization: `Bearer ${config.decemberToken}` }
215513
216447
  });
215514
216448
  if (!urlRes.ok) {
215515
- await fs31.promises.unlink(archivePath).catch(() => {});
216449
+ await fs34.promises.unlink(archivePath).catch(() => {});
215516
216450
  if (urlRes.status === 401) {
215517
216451
  ctx.toast.show({
215518
216452
  variant: "error",
@@ -215533,7 +216467,7 @@ var init_commands2 = __esm(() => {
215533
216467
  const urlJson = await urlRes.json();
215534
216468
  const { uploadUrl, objectKey } = urlJson.data || urlJson;
215535
216469
  ctx.toast.show({ variant: "info", message: "Uploading to MinIO..." });
215536
- const fileData = await fs31.promises.readFile(archivePath);
216470
+ const fileData = await fs34.promises.readFile(archivePath);
215537
216471
  const uploadRes = await fetch(uploadUrl, {
215538
216472
  method: "PUT",
215539
216473
  body: fileData
@@ -215557,11 +216491,11 @@ var init_commands2 = __esm(() => {
215557
216491
  const errJson = await sessionRes.json().catch(() => ({}));
215558
216492
  throw new Error(errJson.message || await sessionRes.text());
215559
216493
  }
215560
- await fs31.promises.unlink(archivePath).catch(() => {});
216494
+ await fs34.promises.unlink(archivePath).catch(() => {});
215561
216495
  ctx.toast.show({ variant: "success", message: "Handoff complete! Exiting in 3s." });
215562
216496
  setTimeout(() => ctx.exit(), 3000);
215563
216497
  } catch (e) {
215564
- await fs31.promises.unlink(archivePath).catch(() => {});
216498
+ await fs34.promises.unlink(archivePath).catch(() => {});
215565
216499
  ctx.toast.show({ variant: "error", message: `Handoff failed: ${e.message}` });
215566
216500
  }
215567
216501
  }
@@ -215633,6 +216567,12 @@ var init_commands2 = __esm(() => {
215633
216567
  description: "View quota usage",
215634
216568
  value: "/usage",
215635
216569
  action: (ctx) => {}
216570
+ },
216571
+ {
216572
+ name: "voice",
216573
+ description: "Dictate a prompt using your microphone (Deepgram)",
216574
+ value: "/voice",
216575
+ action: (ctx) => {}
215636
216576
  }
215637
216577
  ];
215638
216578
  });
@@ -216065,6 +217005,7 @@ function TextArea({
216065
217005
  onScrollUp,
216066
217006
  onScrollDown,
216067
217007
  placeholder = "",
217008
+ placeholderColor,
216068
217009
  focus = true,
216069
217010
  disableHistoryNav = false
216070
217011
  }) {
@@ -216318,7 +217259,7 @@ function TextArea({
216318
217259
  });
216319
217260
  if (!value && placeholder) {
216320
217261
  return /* @__PURE__ */ jsx_runtime7.jsxs(Text3, {
216321
- color: THEME.colors.subtle,
217262
+ color: placeholderColor || THEME.colors.subtle,
216322
217263
  wrap: "wrap",
216323
217264
  children: [
216324
217265
  focus ? /* @__PURE__ */ jsx_runtime7.jsx(Text3, {
@@ -216543,9 +217484,13 @@ var init_input_bar = __esm(async () => {
216543
217484
  queuedPrompts,
216544
217485
  tasks,
216545
217486
  onScrollUp,
216546
- onScrollDown
217487
+ onScrollDown,
217488
+ onStartVoice
216547
217489
  }) {
216548
217490
  const [value, setValue] = import_react42.useState("");
217491
+ const [isVoiceActive, setIsVoiceActive] = import_react42.useState(false);
217492
+ const [isTranscribing, setIsTranscribing] = import_react42.useState(false);
217493
+ const voiceSessionRef = import_react42.useRef(null);
216549
217494
  const toast = useToast();
216550
217495
  const dialog = useDialog();
216551
217496
  const columns = useTerminalColumns();
@@ -216553,6 +217498,71 @@ var init_input_bar = __esm(async () => {
216553
217498
  const [allWorkspaceFiles, setAllWorkspaceFiles] = import_react42.useState([]);
216554
217499
  const activeToast = toasts && toasts.length > 0 ? toasts[toasts.length - 1] : toast.currentToast ? { message: toast.currentToast.message, variant: toast.currentToast.variant } : null;
216555
217500
  const handleContentChangeRef = import_react42.useRef(() => {});
217501
+ const handleStopVoice = import_react42.useCallback(async (sendPrompt = false) => {
217502
+ const session = voiceSessionRef.current;
217503
+ if (!session) {
217504
+ setIsVoiceActive(false);
217505
+ setIsTranscribing(false);
217506
+ return;
217507
+ }
217508
+ voiceSessionRef.current = null;
217509
+ setIsVoiceActive(false);
217510
+ if (sendPrompt) {
217511
+ setIsTranscribing(true);
217512
+ try {
217513
+ const finalTranscript = await session.stop();
217514
+ const promptToSend = (finalTranscript || value).trim();
217515
+ setValue("");
217516
+ handleContentChangeRef.current("");
217517
+ if (promptToSend) {
217518
+ onSubmit(promptToSend);
217519
+ }
217520
+ } finally {
217521
+ setIsTranscribing(false);
217522
+ }
217523
+ } else {
217524
+ session.cancel();
217525
+ setValue("");
217526
+ handleContentChangeRef.current("");
217527
+ setIsTranscribing(false);
217528
+ }
217529
+ }, [onSubmit, value]);
217530
+ const handleStartVoice = import_react42.useCallback(async () => {
217531
+ if (!onStartVoice) {
217532
+ onSubmit("/voice");
217533
+ return;
217534
+ }
217535
+ setIsVoiceActive(true);
217536
+ setValue("");
217537
+ handleContentChangeRef.current("");
217538
+ setIsTranscribing(false);
217539
+ try {
217540
+ const session = await onStartVoice({
217541
+ onTranscript: (text) => {
217542
+ setValue(text);
217543
+ handleContentChangeRef.current(text);
217544
+ },
217545
+ onError: (err) => {
217546
+ setIsVoiceActive(false);
217547
+ setIsTranscribing(false);
217548
+ voiceSessionRef.current = null;
217549
+ toast.show({
217550
+ variant: "error",
217551
+ message: err.message || "Voice dictation error."
217552
+ });
217553
+ }
217554
+ });
217555
+ if (!session) {
217556
+ setIsVoiceActive(false);
217557
+ setIsTranscribing(false);
217558
+ return;
217559
+ }
217560
+ voiceSessionRef.current = session;
217561
+ } catch {
217562
+ setIsVoiceActive(false);
217563
+ setIsTranscribing(false);
217564
+ }
217565
+ }, [onStartVoice, onSubmit, toast]);
216556
217566
  const handleAutocompleteCommand = import_react42.useCallback((completedText) => {
216557
217567
  setValue(completedText);
216558
217568
  handleContentChangeRef.current(completedText);
@@ -216618,7 +217628,10 @@ var init_input_bar = __esm(async () => {
216618
217628
  handleContentChange,
216619
217629
  grillMode,
216620
217630
  onSubmit,
216621
- onCopy
217631
+ onCopy,
217632
+ isVoiceActive,
217633
+ isTranscribing,
217634
+ handleStopVoice
216622
217635
  });
216623
217636
  stateRef.current = {
216624
217637
  showFileMenu,
@@ -216628,11 +217641,26 @@ var init_input_bar = __esm(async () => {
216628
217641
  handleContentChange,
216629
217642
  grillMode,
216630
217643
  onSubmit,
216631
- onCopy
217644
+ onCopy,
217645
+ isVoiceActive,
217646
+ isTranscribing,
217647
+ handleStopVoice
216632
217648
  };
216633
217649
  const isCtrlW = import_react42.useRef(false);
216634
217650
  use_input_default((input, key) => {
216635
217651
  const state = stateRef.current;
217652
+ if (key.escape) {
217653
+ if (state.isVoiceActive || state.isTranscribing || voiceSessionRef.current) {
217654
+ state.handleStopVoice(false);
217655
+ return;
217656
+ }
217657
+ if (state.showFileMenu && state.matchingFiles.length > 0) {
217658
+ const nextVal = state.value.replace(/@\S*$/, "");
217659
+ setValue(nextVal);
217660
+ state.handleContentChange(nextVal);
217661
+ return;
217662
+ }
217663
+ }
216636
217664
  if (state.showFileMenu && state.matchingFiles.length > 0) {
216637
217665
  if (key.upArrow) {
216638
217666
  setSelectedFileIndex((prev) => Math.max(0, prev - 1));
@@ -216652,12 +217680,6 @@ var init_input_bar = __esm(async () => {
216652
217680
  }
216653
217681
  return;
216654
217682
  }
216655
- if (key.escape) {
216656
- const nextVal = state.value.replace(/@\S*$/, "");
216657
- setValue(nextVal);
216658
- state.handleContentChange(nextVal);
216659
- return;
216660
- }
216661
217683
  }
216662
217684
  if (key.ctrl && input === "w") {
216663
217685
  isCtrlW.current = true;
@@ -216748,6 +217770,12 @@ var init_input_bar = __esm(async () => {
216748
217770
  "/handoff",
216749
217771
  "/docs"
216750
217772
  ];
217773
+ if (command.value === "/voice") {
217774
+ setValue("");
217775
+ handleContentChange("");
217776
+ handleStartVoice();
217777
+ return;
217778
+ }
216751
217779
  if (forwardCommands.includes(command.value) || !command.action) {
216752
217780
  if (hasArguments) {
216753
217781
  onSubmit(currentValue);
@@ -216766,10 +217794,24 @@ var init_input_bar = __esm(async () => {
216766
217794
  onUpdateSuccess
216767
217795
  });
216768
217796
  }
216769
- }, [toast, dialog, agent, resetChat, onUpdateSuccess, handleContentChange, onSubmit, value]);
217797
+ }, [
217798
+ toast,
217799
+ dialog,
217800
+ agent,
217801
+ resetChat,
217802
+ onUpdateSuccess,
217803
+ handleContentChange,
217804
+ onSubmit,
217805
+ value,
217806
+ handleStartVoice
217807
+ ]);
216770
217808
  const handleSubmit = import_react42.useCallback(async (text) => {
216771
217809
  if (disabled)
216772
217810
  return;
217811
+ if (isVoiceActive || isTranscribing) {
217812
+ await handleStopVoice(true);
217813
+ return;
217814
+ }
216773
217815
  if (showCommandMenu) {
216774
217816
  const command = resolveCommand(selectedIndex);
216775
217817
  handleCommand(command);
@@ -216781,6 +217823,12 @@ var init_input_bar = __esm(async () => {
216781
217823
  const rawTrimmed = text.trim();
216782
217824
  if (rawTrimmed.length === 0)
216783
217825
  return;
217826
+ if (rawTrimmed === "/voice") {
217827
+ setValue("");
217828
+ handleContentChange("");
217829
+ handleStartVoice();
217830
+ return;
217831
+ }
216784
217832
  const trimmed = expandPastes(rawTrimmed);
216785
217833
  defaultPromptHistory.append(trimmed);
216786
217834
  defaultPromptHistory.resetCursor();
@@ -216939,7 +217987,8 @@ var init_input_bar = __esm(async () => {
216939
217987
  onHistoryDown: handleHistoryDown,
216940
217988
  onScrollUp,
216941
217989
  onScrollDown,
216942
- placeholder: grillMode ? "" : planRefineMode ? "Enter feedback to refine plan (or Esc to cancel)..." : interactivePlanGoalMode ? "Enter your goal (e.g. add dark mode) or Esc to cancel..." : placeholder,
217990
+ placeholder: isTranscribing ? "Transcribing speech..." : isVoiceActive ? "Listening..." : grillMode ? "" : planRefineMode ? "Enter feedback to refine plan (or Esc to cancel)..." : interactivePlanGoalMode ? "Enter your goal (e.g. add dark mode) or Esc to cancel..." : placeholder,
217991
+ placeholderColor: isVoiceActive ? THEME.colors.brand : undefined,
216943
217992
  focus: !disabled && !dialog.isOpen,
216944
217993
  disableHistoryNav: showCommandMenu || showFileMenu || showShortcutsMenu
216945
217994
  })
@@ -217254,7 +218303,7 @@ var init_scroll_viewport = __esm(async () => {
217254
218303
  });
217255
218304
 
217256
218305
  // ../../packages/tui/src/utils/terminal-link.ts
217257
- import path31 from "node:path";
218306
+ import path34 from "node:path";
217258
218307
  function terminalLink(text, uri) {
217259
218308
  if (!uri)
217260
218309
  return text;
@@ -217263,7 +218312,7 @@ function terminalLink(text, uri) {
217263
218312
  function fileLink(displayPath, targetPath, line, col) {
217264
218313
  if (!targetPath)
217265
218314
  return displayPath;
217266
- const absPath = path31.isAbsolute(targetPath) ? targetPath : path31.resolve(process.cwd(), targetPath);
218315
+ const absPath = path34.isAbsolute(targetPath) ? targetPath : path34.resolve(process.cwd(), targetPath);
217267
218316
  const lineFragment = line ? `#L${line}${col ? `:${col}` : ""}` : "";
217268
218317
  const uri = `file://${absPath}${lineFragment}`;
217269
218318
  return terminalLink(displayPath, uri);
@@ -217272,9 +218321,9 @@ function toRelativePath(filePath, cwd = process.cwd()) {
217272
218321
  if (!filePath)
217273
218322
  return "";
217274
218323
  const trimmed = filePath.trim();
217275
- if (!path31.isAbsolute(trimmed))
218324
+ if (!path34.isAbsolute(trimmed))
217276
218325
  return trimmed;
217277
- const rel = path31.relative(cwd, trimmed);
218326
+ const rel = path34.relative(cwd, trimmed);
217278
218327
  if (!rel || rel.startsWith("..")) {
217279
218328
  return trimmed;
217280
218329
  }
@@ -263642,27 +264691,27 @@ ${r}`;
263642
264691
  });
263643
264692
 
263644
264693
  // ../../packages/tui/src/components/header.tsx
263645
- import fs32 from "node:fs";
264694
+ import fs35 from "node:fs";
263646
264695
  import { homedir } from "node:os";
263647
- import path32 from "node:path";
264696
+ import path35 from "node:path";
263648
264697
  function getGitBranch(workspaceRoot = process.cwd()) {
263649
264698
  if (cachedBranch !== undefined) {
263650
264699
  return cachedBranch;
263651
264700
  }
263652
264701
  try {
263653
- const gitPath = path32.resolve(workspaceRoot, ".git");
263654
- if (fs32.existsSync(gitPath)) {
263655
- const stat = fs32.statSync(gitPath);
263656
- let headPath = path32.resolve(gitPath, "HEAD");
264702
+ const gitPath = path35.resolve(workspaceRoot, ".git");
264703
+ if (fs35.existsSync(gitPath)) {
264704
+ const stat = fs35.statSync(gitPath);
264705
+ let headPath = path35.resolve(gitPath, "HEAD");
263657
264706
  if (stat.isFile()) {
263658
- const content = fs32.readFileSync(gitPath, "utf-8").trim();
264707
+ const content = fs35.readFileSync(gitPath, "utf-8").trim();
263659
264708
  if (content.startsWith("gitdir:")) {
263660
264709
  const gitDir = content.slice(7).trim();
263661
- headPath = path32.resolve(workspaceRoot, gitDir, "HEAD");
264710
+ headPath = path35.resolve(workspaceRoot, gitDir, "HEAD");
263662
264711
  }
263663
264712
  }
263664
- if (fs32.existsSync(headPath)) {
263665
- const headContent = fs32.readFileSync(headPath, "utf-8").trim();
264713
+ if (fs35.existsSync(headPath)) {
264714
+ const headContent = fs35.readFileSync(headPath, "utf-8").trim();
263666
264715
  if (headContent.startsWith("ref: refs/heads/")) {
263667
264716
  cachedBranch = headContent.replace("ref: refs/heads/", "").trim();
263668
264717
  return cachedBranch;
@@ -263936,7 +264985,7 @@ var init_message_list = __esm(async () => {
263936
264985
  flexDirection: "column",
263937
264986
  width: "100%",
263938
264987
  children: [
263939
- staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static15, {
264988
+ staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static16, {
263940
264989
  items: staticMessages,
263941
264990
  style: { width: "100%" },
263942
264991
  children: (msg, index) => renderSingleMessage(msg, index, staticMessages, cliVersion, latestVersion, userEmail, expandCommands, announcement)
@@ -264504,6 +265553,7 @@ function ByokKeyMenu(props) {
264504
265553
  const { selectedProvider, apiKey, setApiKey, handleKeySubmit, isStreaming, authError } = props;
264505
265554
  const formattedProvider = formatProviderName2(selectedProvider);
264506
265555
  const keyUrl = selectedProvider ? PROVIDER_KEY_URLS[selectedProvider] : undefined;
265556
+ const keyNote = selectedProvider ? PROVIDER_KEY_NOTES[selectedProvider.toLowerCase().trim()] : undefined;
264507
265557
  return /* @__PURE__ */ jsx_runtime25.jsxs(Box_default, {
264508
265558
  flexDirection: "column",
264509
265559
  paddingX: THEME.padding.paddingX,
@@ -264528,7 +265578,8 @@ function ByokKeyMenu(props) {
264528
265578
  /* @__PURE__ */ jsx_runtime25.jsx(Text3, {
264529
265579
  color: THEME.colors.brand,
264530
265580
  children: keyUrl
264531
- })
265581
+ }),
265582
+ keyNote
264532
265583
  ]
264533
265584
  })
264534
265585
  ]
@@ -264573,7 +265624,7 @@ function ByokKeyMenu(props) {
264573
265624
  ]
264574
265625
  });
264575
265626
  }
264576
- var jsx_runtime25, PROVIDER_NAMES, PROVIDER_KEY_URLS;
265627
+ var jsx_runtime25, PROVIDER_NAMES, PROVIDER_KEY_URLS, PROVIDER_KEY_NOTES;
264577
265628
  var init_byok_key_menu = __esm(async () => {
264578
265629
  init_theme();
264579
265630
  await __promiseAll([
@@ -264619,6 +265670,7 @@ var init_byok_key_menu = __esm(async () => {
264619
265670
  december: "December Cloud",
264620
265671
  december_proxy: "December Cloud",
264621
265672
  deepseek: "DeepSeek",
265673
+ deepgram: "Deepgram",
264622
265674
  fireworks: "Fireworks AI",
264623
265675
  google: "Google",
264624
265676
  gemini: "Google AI Studio",
@@ -264737,6 +265789,7 @@ var init_byok_key_menu = __esm(async () => {
264737
265789
  deepinfra: "https://deepinfra.com/dash/api_keys",
264738
265790
  deepseek: "https://platform.deepseek.com/api_keys",
264739
265791
  digitalocean: "https://cloud.digitalocean.com/account/api/tokens",
265792
+ deepgram: "https://console.deepgram.com",
264740
265793
  dinference: "https://dinference.com/",
264741
265794
  drun: "https://d.run/",
264742
265795
  ebcloud: "https://ebcloud.com/",
@@ -264910,6 +265963,9 @@ var init_byok_key_menu = __esm(async () => {
264910
265963
  zhipu: "https://open.bigmodel.cn/usercenter/apikeys",
264911
265964
  zhipuai: "https://open.bigmodel.cn/usercenter/apikeys"
264912
265965
  };
265966
+ PROVIDER_KEY_NOTES = {
265967
+ deepgram: " ($200 free credit on signup)"
265968
+ };
264913
265969
  });
264914
265970
 
264915
265971
  // ../../packages/tui/src/components/menus/context-select-menu.tsx
@@ -266835,6 +267891,7 @@ function SettingsMainMenu(props) {
266835
267891
  settingsPathGuard = true,
266836
267892
  settingsSteeringMode = "all",
266837
267893
  settingsFollowUpMode = "all",
267894
+ settingsBrowserVisible = true,
266838
267895
  handleSettingsMainSelect
266839
267896
  } = props;
266840
267897
  const providerKey = selectedProvider || activeProvider || "";
@@ -266873,6 +267930,10 @@ function SettingsMainMenu(props) {
266873
267930
  {
266874
267931
  label: `Follow-Up Mode [${settingsFollowUpMode}]`,
266875
267932
  value: "followUpMode"
267933
+ },
267934
+ {
267935
+ label: `Browser Window [${settingsBrowserVisible !== false ? "on" : "off"}]`,
267936
+ value: "browserVisible"
266876
267937
  }
266877
267938
  ];
266878
267939
  return /* @__PURE__ */ jsx_runtime34.jsxs(Box_default, {
@@ -269025,25 +270086,25 @@ var init_global_shortcuts = __esm(async () => {
269025
270086
 
269026
270087
  // ../../packages/tui/src/utils/pager.ts
269027
270088
  import * as cp3 from "node:child_process";
269028
- import fs33 from "node:fs";
269029
- import os18 from "node:os";
269030
- import path33 from "node:path";
270089
+ import fs36 from "node:fs";
270090
+ import os21 from "node:os";
270091
+ import path36 from "node:path";
269031
270092
  function openPlanInPager(planText, title = "december-plan") {
269032
270093
  if (Boolean(process.env.BUN_TEST) || Boolean(process.env.VITEST)) {
269033
270094
  return Promise.resolve();
269034
270095
  }
269035
270096
  return new Promise((resolve) => {
269036
270097
  try {
269037
- const tmpFile = path33.join(os18.tmpdir(), `${title}-${Date.now()}.md`);
269038
- fs33.writeFileSync(tmpFile, planText, "utf8");
269039
- const pager = process.env.PAGER || process.env.EDITOR || (os18.platform() === "win32" ? "notepad" : "less -R");
270098
+ const tmpFile = path36.join(os21.tmpdir(), `${title}-${Date.now()}.md`);
270099
+ fs36.writeFileSync(tmpFile, planText, "utf8");
270100
+ const pager = process.env.PAGER || process.env.EDITOR || (os21.platform() === "win32" ? "notepad" : "less -R");
269040
270101
  const child = cp3.spawn(pager, [tmpFile], {
269041
270102
  stdio: "inherit",
269042
270103
  shell: true
269043
270104
  });
269044
270105
  child.on("exit", () => {
269045
270106
  try {
269046
- fs33.unlinkSync(tmpFile);
270107
+ fs36.unlinkSync(tmpFile);
269047
270108
  } catch {}
269048
270109
  resolve();
269049
270110
  });
@@ -269203,6 +270264,7 @@ function ChatApp({
269203
270264
  onSubmit: handleFormSubmit,
269204
270265
  onScrollUp: handleScrollUp,
269205
270266
  onScrollDown: handleScrollDown,
270267
+ onStartVoice: session.onStartVoice,
269206
270268
  disabled: authMode !== "none" || Boolean(workflowUI) && !session.customInputMode,
269207
270269
  onCopy: () => {
269208
270270
  Promise.resolve().then(() => (init_clipboard(), {})).then((cb) => {
@@ -269579,6 +270641,8 @@ var init_store = __esm(() => {
269579
270641
  setSettingsSteeringMode: (settingsSteeringMode) => set({ settingsSteeringMode }),
269580
270642
  settingsFollowUpMode: "all",
269581
270643
  setSettingsFollowUpMode: (settingsFollowUpMode) => set({ settingsFollowUpMode }),
270644
+ settingsBrowserVisible: true,
270645
+ setSettingsBrowserVisible: (settingsBrowserVisible) => set({ settingsBrowserVisible }),
269582
270646
  tasksData: [],
269583
270647
  setTasksData: (tasksData) => set({ tasksData }),
269584
270648
  taskSelectedIndex: 0,
@@ -269612,9 +270676,9 @@ var init_store = __esm(() => {
269612
270676
  });
269613
270677
 
269614
270678
  // src/store/interceptors.ts
269615
- import { execSync as execSync2 } from "node:child_process";
269616
- import fs34 from "node:fs/promises";
269617
- import path34 from "node:path";
270679
+ import { execSync as execSync4 } from "node:child_process";
270680
+ import fs37 from "node:fs/promises";
270681
+ import path37 from "node:path";
269618
270682
  async function computeToolCallDiff(toolCall) {
269619
270683
  if (toolCall.input?.diff) {
269620
270684
  return toolCall.input.diff;
@@ -269623,11 +270687,11 @@ async function computeToolCallDiff(toolCall) {
269623
270687
  if (!targetPath)
269624
270688
  return;
269625
270689
  const cwd = process.cwd();
269626
- const resolved = path34.resolve(cwd, targetPath);
269627
- const relative = path34.relative(cwd, resolved);
270690
+ const resolved = path37.resolve(cwd, targetPath);
270691
+ const relative = path37.relative(cwd, resolved);
269628
270692
  let oldContent = "";
269629
270693
  try {
269630
- oldContent = await fs34.readFile(resolved, "utf-8");
270694
+ oldContent = await fs37.readFile(resolved, "utf-8");
269631
270695
  } catch {}
269632
270696
  if (toolCall.name === "write_to_file" || toolCall.name === "write") {
269633
270697
  const newContent = toolCall.input?.CodeContent ?? toolCall.input?.content ?? "";
@@ -269691,9 +270755,9 @@ function setupAgentInterceptors(agent, storeState) {
269691
270755
  ];
269692
270756
  if (fileTools.includes(toolCall.name)) {
269693
270757
  const cwd = process.cwd();
269694
- const resolved = path34.resolve(cwd, targetPath);
269695
- const relative = path34.relative(cwd, resolved);
269696
- const isOutside = relative.startsWith("..") || path34.isAbsolute(relative);
270758
+ const resolved = path37.resolve(cwd, targetPath);
270759
+ const relative = path37.relative(cwd, resolved);
270760
+ const isOutside = relative.startsWith("..") || path37.isAbsolute(relative);
269697
270761
  if (!config.nonWorkspaceAccess && isOutside) {
269698
270762
  return {
269699
270763
  block: true,
@@ -269757,10 +270821,10 @@ function setupAgentInterceptors(agent, storeState) {
269757
270821
  const targetPath = toolCall.input?.TargetFile || toolCall.input?.AbsolutePath || toolCall.input?.filePath || toolCall.input?.path;
269758
270822
  if (targetPath) {
269759
270823
  const cwd = process.cwd();
269760
- const resolved = path34.resolve(cwd, targetPath);
269761
- const relative = path34.relative(cwd, resolved);
270824
+ const resolved = path37.resolve(cwd, targetPath);
270825
+ const relative = path37.relative(cwd, resolved);
269762
270826
  try {
269763
- execSync2(`git ls-files --error-unmatch "${relative}"`, {
270827
+ execSync4(`git ls-files --error-unmatch "${relative}"`, {
269764
270828
  cwd,
269765
270829
  stdio: "pipe"
269766
270830
  });
@@ -269785,7 +270849,7 @@ var init_interceptors = __esm(() => {
269785
270849
  });
269786
270850
 
269787
270851
  // src/utils/direct-shell.ts
269788
- import { spawn as spawn6 } from "node:child_process";
270852
+ import { spawn as spawn7 } from "node:child_process";
269789
270853
  function startDirectCommand(command, options = {}) {
269790
270854
  const { timeoutMs = 20000, serverReadyDelayMs = 2500, onData, onBackground } = options;
269791
270855
  let output = "";
@@ -269795,7 +270859,7 @@ function startDirectCommand(command, options = {}) {
269795
270859
  const isWindows = process.platform === "win32";
269796
270860
  const shellCmd = isWindows ? "cmd.exe" : "/bin/bash";
269797
270861
  const shellArgs = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
269798
- const child = spawn6(shellCmd, shellArgs, {
270862
+ const child = spawn7(shellCmd, shellArgs, {
269799
270863
  cwd: process.cwd(),
269800
270864
  env: process.env,
269801
270865
  stdio: ["pipe", "pipe", "pipe"],
@@ -269947,15 +271011,15 @@ var init_direct_shell = __esm(() => {
269947
271011
  });
269948
271012
 
269949
271013
  // src/utils/usage-tracker.ts
269950
- import fs35 from "node:fs/promises";
269951
- import path35 from "node:path";
271014
+ import fs38 from "node:fs/promises";
271015
+ import path38 from "node:path";
269952
271016
  function getUsageFilePath() {
269953
- return path35.join(getConfigDir(), "usage.json");
271017
+ return path38.join(getConfigDir(), "usage.json");
269954
271018
  }
269955
271019
  async function loadUsageLedger() {
269956
271020
  const filePath = getUsageFilePath();
269957
271021
  try {
269958
- const raw = await fs35.readFile(filePath, "utf-8");
271022
+ const raw = await fs38.readFile(filePath, "utf-8");
269959
271023
  const parsed = JSON.parse(raw);
269960
271024
  if (parsed && typeof parsed === "object" && parsed.version === 1 && parsed.days) {
269961
271025
  return parsed;
@@ -269968,8 +271032,8 @@ async function loadUsageLedger() {
269968
271032
  }
269969
271033
  async function saveUsageLedger(ledger) {
269970
271034
  const configDir = getConfigDir();
269971
- const filePath = path35.join(configDir, "usage.json");
269972
- const tmpPath = path35.join(configDir, `usage.json.tmp.${Date.now()}`);
271035
+ const filePath = path38.join(configDir, "usage.json");
271036
+ const tmpPath = path38.join(configDir, `usage.json.tmp.${Date.now()}`);
269973
271037
  const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
269974
271038
  const prunedDays = {};
269975
271039
  for (const [dateKey, dayRecord] of Object.entries(ledger.days)) {
@@ -269979,12 +271043,12 @@ async function saveUsageLedger(ledger) {
269979
271043
  }
269980
271044
  ledger.days = prunedDays;
269981
271045
  try {
269982
- await fs35.mkdir(configDir, { recursive: true });
269983
- await fs35.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
269984
- await fs35.rename(tmpPath, filePath);
271046
+ await fs38.mkdir(configDir, { recursive: true });
271047
+ await fs38.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
271048
+ await fs38.rename(tmpPath, filePath);
269985
271049
  } catch {
269986
271050
  try {
269987
- await fs35.unlink(tmpPath);
271051
+ await fs38.unlink(tmpPath);
269988
271052
  } catch {}
269989
271053
  }
269990
271054
  }
@@ -271421,14 +272485,14 @@ function extractJsonArray(rawText) {
271421
272485
  }
271422
272486
 
271423
272487
  // src/utils/project-context.ts
271424
- import fs36 from "node:fs";
271425
- import path36 from "node:path";
272488
+ import fs39 from "node:fs";
272489
+ import path39 from "node:path";
271426
272490
  function getProjectContext(workspaceDir = process.cwd()) {
271427
272491
  const parts = [];
271428
272492
  try {
271429
- const pkgPath = path36.join(workspaceDir, "package.json");
271430
- if (fs36.existsSync(pkgPath)) {
271431
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf8"));
272493
+ const pkgPath = path39.join(workspaceDir, "package.json");
272494
+ if (fs39.existsSync(pkgPath)) {
272495
+ const pkg = JSON.parse(fs39.readFileSync(pkgPath, "utf8"));
271432
272496
  const deps = Object.keys(pkg.dependencies || {}).slice(0, 15).join(", ");
271433
272497
  const devDeps = Object.keys(pkg.devDependencies || {}).slice(0, 10).join(", ");
271434
272498
  parts.push(`Project: ${pkg.name || "Unnamed"}`);
@@ -271437,13 +272501,13 @@ function getProjectContext(workspaceDir = process.cwd()) {
271437
272501
  if (devDeps)
271438
272502
  parts.push(`DevDependencies: ${devDeps}`);
271439
272503
  }
271440
- const agentsPath = path36.join(workspaceDir, "AGENTS.md");
271441
- if (fs36.existsSync(agentsPath)) {
271442
- const content = fs36.readFileSync(agentsPath, "utf8");
272504
+ const agentsPath = path39.join(workspaceDir, "AGENTS.md");
272505
+ if (fs39.existsSync(agentsPath)) {
272506
+ const content = fs39.readFileSync(agentsPath, "utf8");
271443
272507
  parts.push(`Guidelines (from AGENTS.md):
271444
272508
  ${content.slice(0, 500)}`);
271445
272509
  }
271446
- const entries = fs36.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
272510
+ const entries = fs39.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
271447
272511
  if (entries.length > 0) {
271448
272512
  parts.push(`Workspace layout: ${entries.join(", ")}`);
271449
272513
  }
@@ -271598,14 +272662,14 @@ async function fetchProviderBalance(provider, apiKey, timeoutMs = 2000) {
271598
272662
  }
271599
272663
 
271600
272664
  // src/utils/formatters.ts
271601
- import path37 from "node:path";
272665
+ import path40 from "node:path";
271602
272666
  function toRelativePath2(filePath, cwd = process.cwd()) {
271603
272667
  if (!filePath)
271604
272668
  return "";
271605
272669
  const trimmed = filePath.trim();
271606
- if (!path37.isAbsolute(trimmed))
272670
+ if (!path40.isAbsolute(trimmed))
271607
272671
  return trimmed;
271608
- const rel = path37.relative(cwd, trimmed);
272672
+ const rel = path40.relative(cwd, trimmed);
271609
272673
  if (!rel || rel.startsWith("..")) {
271610
272674
  return trimmed;
271611
272675
  }
@@ -272328,6 +273392,30 @@ ${formatSubscriptionCard(bundle, targetModel)}`
272328
273392
  if (!trimmedKey)
272329
273393
  return;
272330
273394
  const selectedProvider = useCliStore.getState().selectedProvider;
273395
+ if (selectedProvider === "deepgram") {
273396
+ setIsStreaming(true);
273397
+ setAuthError(null);
273398
+ try {
273399
+ const res = await fetch("https://api.deepgram.com/v1/projects", {
273400
+ headers: { Authorization: `Token ${trimmedKey}` }
273401
+ });
273402
+ if (!res.ok) {
273403
+ throw new Error("Invalid Deepgram API key. Please check your key from console.deepgram.com.");
273404
+ }
273405
+ const config = await loadConfig();
273406
+ config.providers = config.providers || {};
273407
+ config.providers.deepgram = trimmedKey;
273408
+ await saveConfig(config);
273409
+ setApiKey("");
273410
+ setAuthMode("none");
273411
+ addToast("Saved Deepgram API key for voice dictation.", "success");
273412
+ } catch (err) {
273413
+ setAuthError(err.message || "Failed to verify Deepgram API key.");
273414
+ } finally {
273415
+ setIsStreaming(false);
273416
+ }
273417
+ return;
273418
+ }
272331
273419
  setIsStreaming(true);
272332
273420
  setAuthError(null);
272333
273421
  let testProvider;
@@ -272684,6 +273772,8 @@ function useSettingsHandlers() {
272684
273772
  setSettingsSteeringMode,
272685
273773
  settingsFollowUpMode,
272686
273774
  setSettingsFollowUpMode,
273775
+ settingsBrowserVisible,
273776
+ setSettingsBrowserVisible,
272687
273777
  settingsAuthPriority,
272688
273778
  setSettingsAuthPriority,
272689
273779
  setAuthMethod,
@@ -272694,7 +273784,7 @@ function useSettingsHandlers() {
272694
273784
  setOllamaModels,
272695
273785
  agent,
272696
273786
  addToast
272697
- } = useCliStore();
273787
+ } = useCliStore.getState();
272698
273788
  const handleSettingsMainSelect = async (item) => {
272699
273789
  const config = await loadConfig();
272700
273790
  let updated = false;
@@ -272802,6 +273892,15 @@ function useSettingsHandlers() {
272802
273892
  updated = true;
272803
273893
  break;
272804
273894
  }
273895
+ case "browserVisible": {
273896
+ const current = useCliStore.getState().settingsBrowserVisible;
273897
+ const nextVal = !(current !== false);
273898
+ config.browserVisible = nextVal;
273899
+ setSettingsBrowserVisible(nextVal);
273900
+ addToast(`Browser window set to ${nextVal ? "visible (on)" : "headless (off)"}`);
273901
+ updated = true;
273902
+ break;
273903
+ }
272805
273904
  case "authPriority": {
272806
273905
  const priorities = [
272807
273906
  "subscription",
@@ -272858,6 +273957,503 @@ var init_use_settings_handlers = __esm(() => {
272858
273957
  init_provider_factory();
272859
273958
  });
272860
273959
 
273960
+ // src/utils/audio/detector.ts
273961
+ import { execSync as execSync5 } from "node:child_process";
273962
+ function checkBinary(name) {
273963
+ try {
273964
+ const isWindows = process.platform === "win32";
273965
+ const checkCmd = isWindows ? `where ${name}` : `which ${name}`;
273966
+ const stdout = execSync5(checkCmd, {
273967
+ stdio: ["ignore", "pipe", "ignore"],
273968
+ encoding: "utf8"
273969
+ }).trim();
273970
+ return stdout ? stdout.split(`
273971
+ `)[0].trim() : null;
273972
+ } catch {
273973
+ return null;
273974
+ }
273975
+ }
273976
+ function detectAudioBinary() {
273977
+ const ffmpegPath = checkBinary("ffmpeg");
273978
+ if (ffmpegPath) {
273979
+ return { available: true, binary: "ffmpeg", path: ffmpegPath };
273980
+ }
273981
+ const soxPath = checkBinary("rec") || checkBinary("sox");
273982
+ if (soxPath) {
273983
+ return { available: true, binary: "sox", path: soxPath };
273984
+ }
273985
+ if (process.platform === "linux") {
273986
+ const arecordPath = checkBinary("arecord");
273987
+ if (arecordPath) {
273988
+ return { available: true, binary: "arecord", path: arecordPath };
273989
+ }
273990
+ }
273991
+ return {
273992
+ available: false,
273993
+ binary: null,
273994
+ error: process.platform === "darwin" ? 'No audio recording binary found. Please install ffmpeg or sox (e.g. "brew install ffmpeg").' : process.platform === "linux" ? 'No audio recording binary found. Please install ffmpeg, sox, or alsa-utils (e.g. "sudo apt install ffmpeg").' : "No audio recording binary found. Please install ffmpeg or sox in your system PATH."
273995
+ };
273996
+ }
273997
+ var init_detector2 = () => {};
273998
+
273999
+ // src/utils/audio/recorder.ts
274000
+ import { spawn as spawn8 } from "node:child_process";
274001
+ import { EventEmitter as EventEmitter4 } from "node:events";
274002
+ var AudioRecorder;
274003
+ var init_recorder = __esm(() => {
274004
+ init_detector2();
274005
+ AudioRecorder = class AudioRecorder extends EventEmitter4 {
274006
+ process = null;
274007
+ isRecording = false;
274008
+ start(options = {}) {
274009
+ if (this.isRecording)
274010
+ return true;
274011
+ const detection = detectAudioBinary();
274012
+ if (!detection.available || !detection.binary) {
274013
+ this.emit("error", new Error(detection.error || "Audio recording binary not found."));
274014
+ return false;
274015
+ }
274016
+ const sampleRate = options.sampleRate ?? 16000;
274017
+ const channels = options.channels ?? 1;
274018
+ try {
274019
+ this.process = this.spawnRecorderProcess(detection.binary, sampleRate, channels);
274020
+ this.isRecording = true;
274021
+ this.process.stdout.on("data", (chunk) => {
274022
+ this.emit("data", chunk);
274023
+ });
274024
+ this.process.stderr.on("data", (data) => {
274025
+ const msg = data.toString();
274026
+ if (msg.includes("No such device") || msg.includes("Device or resource busy")) {
274027
+ this.emit("error", new Error(msg.trim()));
274028
+ }
274029
+ });
274030
+ this.process.on("error", (err) => {
274031
+ this.emit("error", err);
274032
+ this.stop();
274033
+ });
274034
+ this.process.on("close", (code) => {
274035
+ this.isRecording = false;
274036
+ this.process = null;
274037
+ this.emit("close", code);
274038
+ });
274039
+ return true;
274040
+ } catch (err) {
274041
+ const error = err instanceof Error ? err : new Error(String(err));
274042
+ this.emit("error", error);
274043
+ this.stop();
274044
+ return false;
274045
+ }
274046
+ }
274047
+ spawnRecorderProcess(binary, sampleRate, channels) {
274048
+ if (binary === "ffmpeg") {
274049
+ const isLinux = process.platform === "linux";
274050
+ const isMac = process.platform === "darwin";
274051
+ const isWindows = process.platform === "win32";
274052
+ const args = [
274053
+ "-loglevel",
274054
+ "error",
274055
+ "-fflags",
274056
+ "nobuffer",
274057
+ "-flags",
274058
+ "low_delay"
274059
+ ];
274060
+ if (isLinux) {
274061
+ args.push("-f", "pulse", "-i", "default");
274062
+ } else if (isMac) {
274063
+ args.push("-f", "avfoundation", "-i", ":default");
274064
+ } else if (isWindows) {
274065
+ args.push("-f", "dshow", "-i", "audio=default");
274066
+ }
274067
+ args.push("-ac", channels.toString(), "-ar", sampleRate.toString(), "-f", "s16le", "-flush_packets", "1", "-");
274068
+ return spawn8("ffmpeg", args);
274069
+ }
274070
+ if (binary === "sox") {
274071
+ const detection = detectAudioBinary();
274072
+ const binaryName = detection.path?.endsWith("rec") ? "rec" : "sox";
274073
+ const args = binaryName === "rec" ? [
274074
+ "-q",
274075
+ "-c",
274076
+ channels.toString(),
274077
+ "-r",
274078
+ sampleRate.toString(),
274079
+ "-b",
274080
+ "16",
274081
+ "-e",
274082
+ "signed",
274083
+ "-t",
274084
+ "raw",
274085
+ "-"
274086
+ ] : [
274087
+ "-d",
274088
+ "-q",
274089
+ "-c",
274090
+ channels.toString(),
274091
+ "-r",
274092
+ sampleRate.toString(),
274093
+ "-b",
274094
+ "16",
274095
+ "-e",
274096
+ "signed",
274097
+ "-t",
274098
+ "raw",
274099
+ "-"
274100
+ ];
274101
+ return spawn8(binaryName, args);
274102
+ }
274103
+ return spawn8("arecord", [
274104
+ "-q",
274105
+ "-c",
274106
+ channels.toString(),
274107
+ "-r",
274108
+ sampleRate.toString(),
274109
+ "-f",
274110
+ "S16_LE",
274111
+ "-t",
274112
+ "raw"
274113
+ ]);
274114
+ }
274115
+ stop() {
274116
+ if (!this.isRecording && !this.process)
274117
+ return;
274118
+ this.isRecording = false;
274119
+ if (this.process) {
274120
+ try {
274121
+ this.process.kill("SIGTERM");
274122
+ } catch {}
274123
+ this.process = null;
274124
+ }
274125
+ }
274126
+ get recording() {
274127
+ return this.isRecording;
274128
+ }
274129
+ };
274130
+ });
274131
+
274132
+ // src/utils/audio/stt.ts
274133
+ import dns from "node:dns";
274134
+ async function resolveSTTKey() {
274135
+ const envDeepgram = process.env.DEEPGRAM_API_KEY?.trim();
274136
+ if (envDeepgram)
274137
+ return { provider: "deepgram", key: envDeepgram };
274138
+ const config = await loadConfig();
274139
+ const configDeepgram = config.providers?.deepgram?.trim();
274140
+ if (configDeepgram)
274141
+ return { provider: "deepgram", key: configDeepgram };
274142
+ return { provider: null, key: null };
274143
+ }
274144
+ function appendTranscript(base, next) {
274145
+ const trimmedBase = base.trim();
274146
+ const trimmedNext = next.trim();
274147
+ if (!trimmedBase)
274148
+ return trimmedNext;
274149
+ if (!trimmedNext)
274150
+ return trimmedBase;
274151
+ if (/^[.,!?;:]/.test(trimmedNext)) {
274152
+ return `${trimmedBase}${trimmedNext}`;
274153
+ }
274154
+ const baseWords = trimmedBase.split(/\s+/);
274155
+ const nextWords = trimmedNext.split(/\s+/);
274156
+ const maxOverlap = Math.min(3, baseWords.length, nextWords.length);
274157
+ let overlapCount = 0;
274158
+ const cleanWord = (w) => w.toLowerCase().replace(/[^a-z0-9]/g, "");
274159
+ for (let count = maxOverlap;count >= 1; count--) {
274160
+ const baseSlice = baseWords.slice(baseWords.length - count).map(cleanWord);
274161
+ const nextSlice = nextWords.slice(0, count).map(cleanWord);
274162
+ if (baseSlice.length === nextSlice.length && baseSlice.every((w, i) => w.length > 0 && w === nextSlice[i])) {
274163
+ overlapCount = count;
274164
+ break;
274165
+ }
274166
+ }
274167
+ if (overlapCount > 0) {
274168
+ const remainingNextWords = nextWords.slice(overlapCount);
274169
+ if (remainingNextWords.length === 0) {
274170
+ return trimmedBase;
274171
+ }
274172
+ return `${trimmedBase} ${remainingNextWords.join(" ")}`;
274173
+ }
274174
+ return `${trimmedBase} ${trimmedNext}`;
274175
+ }
274176
+
274177
+ class STTSession {
274178
+ key = null;
274179
+ ws = null;
274180
+ finalTranscript = "";
274181
+ interimTranscript = "";
274182
+ pendingChunks = [];
274183
+ heartbeatTimer = null;
274184
+ onTranscript;
274185
+ onError;
274186
+ isClosed = false;
274187
+ constructor(options) {
274188
+ this.onTranscript = options.onTranscript;
274189
+ this.onError = options.onError;
274190
+ }
274191
+ async init() {
274192
+ const resolved = await resolveSTTKey();
274193
+ if (!resolved.key) {
274194
+ this.onError(new Error("No Deepgram API key found. Set DEEPGRAM_API_KEY or configure via BYOK key menu."));
274195
+ return false;
274196
+ }
274197
+ this.key = resolved.key;
274198
+ return this.initDeepgram(this.key);
274199
+ }
274200
+ initDeepgram(key) {
274201
+ return new Promise((resolve) => {
274202
+ try {
274203
+ const url = "wss://api.deepgram.com/v1/listen?model=nova-2&smart_format=true&interim_results=true&endpointing=300&encoding=linear16&sample_rate=16000&channels=1";
274204
+ this.ws = new WebSocket(url, ["token", key]);
274205
+ let isHandshakeComplete = false;
274206
+ const handshakeTimeout = setTimeout(() => {
274207
+ if (!isHandshakeComplete) {
274208
+ isHandshakeComplete = true;
274209
+ try {
274210
+ if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) {
274211
+ this.ws.close();
274212
+ }
274213
+ } catch {}
274214
+ this.ws = null;
274215
+ this.onError(new Error("Deepgram WebSocket connection timed out. Check your internet connection."));
274216
+ resolve(false);
274217
+ }
274218
+ }, 1e4);
274219
+ this.ws.onopen = () => {
274220
+ if (isHandshakeComplete)
274221
+ return;
274222
+ isHandshakeComplete = true;
274223
+ clearTimeout(handshakeTimeout);
274224
+ this.startHeartbeat();
274225
+ this.flushPendingChunks();
274226
+ resolve(true);
274227
+ };
274228
+ this.ws.onmessage = (event) => {
274229
+ if (this.isClosed)
274230
+ return;
274231
+ try {
274232
+ const data = typeof event.data === "string" ? JSON.parse(event.data) : null;
274233
+ if (!data || data.type !== "Results")
274234
+ return;
274235
+ const alt = data.channel?.alternatives?.[0];
274236
+ const transcript = alt?.transcript?.trim();
274237
+ if (!transcript) {
274238
+ if (data.is_final) {
274239
+ this.interimTranscript = "";
274240
+ }
274241
+ return;
274242
+ }
274243
+ if (data.is_final) {
274244
+ this.finalTranscript = appendTranscript(this.finalTranscript, transcript);
274245
+ this.interimTranscript = "";
274246
+ this.onTranscript(this.finalTranscript, true);
274247
+ } else {
274248
+ this.interimTranscript = transcript;
274249
+ const fullText = appendTranscript(this.finalTranscript, this.interimTranscript);
274250
+ this.onTranscript(fullText, false);
274251
+ }
274252
+ } catch {}
274253
+ };
274254
+ this.ws.onerror = (_err) => {
274255
+ if (!isHandshakeComplete) {
274256
+ isHandshakeComplete = true;
274257
+ clearTimeout(handshakeTimeout);
274258
+ this.onError(new Error("Deepgram WebSocket connection error."));
274259
+ resolve(false);
274260
+ return;
274261
+ }
274262
+ if (!this.isClosed) {
274263
+ this.onError(new Error("Deepgram WebSocket connection error."));
274264
+ }
274265
+ };
274266
+ this.ws.onclose = () => {
274267
+ this.stopHeartbeat();
274268
+ };
274269
+ } catch (err) {
274270
+ const error = err instanceof Error ? err : new Error(String(err));
274271
+ this.onError(error);
274272
+ resolve(false);
274273
+ }
274274
+ });
274275
+ }
274276
+ sendAudioChunk(chunk) {
274277
+ if (this.isClosed)
274278
+ return;
274279
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
274280
+ this.ws.send(chunk);
274281
+ } else if (!this.ws || this.ws.readyState === WebSocket.CONNECTING) {
274282
+ this.pendingChunks.push(chunk);
274283
+ }
274284
+ }
274285
+ flushPendingChunks() {
274286
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
274287
+ return;
274288
+ while (this.pendingChunks.length > 0) {
274289
+ const chunk = this.pendingChunks.shift();
274290
+ if (chunk) {
274291
+ try {
274292
+ this.ws.send(chunk);
274293
+ } catch {}
274294
+ }
274295
+ }
274296
+ }
274297
+ startHeartbeat() {
274298
+ this.stopHeartbeat();
274299
+ this.heartbeatTimer = setInterval(() => {
274300
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
274301
+ try {
274302
+ this.ws.send(JSON.stringify({ type: "KeepAlive" }));
274303
+ } catch {}
274304
+ }
274305
+ }, 4000);
274306
+ }
274307
+ stopHeartbeat() {
274308
+ if (this.heartbeatTimer) {
274309
+ clearInterval(this.heartbeatTimer);
274310
+ this.heartbeatTimer = null;
274311
+ }
274312
+ }
274313
+ async finish() {
274314
+ this.isClosed = true;
274315
+ this.stopHeartbeat();
274316
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
274317
+ try {
274318
+ this.ws.send(JSON.stringify({ type: "CloseStream" }));
274319
+ } catch {}
274320
+ await new Promise((resolve) => setTimeout(resolve, 150));
274321
+ try {
274322
+ if (this.ws.readyState === WebSocket.OPEN) {
274323
+ this.ws.close();
274324
+ }
274325
+ } catch {}
274326
+ this.ws = null;
274327
+ } else if (this.ws) {
274328
+ try {
274329
+ this.ws.close();
274330
+ } catch {}
274331
+ this.ws = null;
274332
+ }
274333
+ this.pendingChunks = [];
274334
+ const fullText = appendTranscript(this.finalTranscript, this.interimTranscript);
274335
+ return fullText.trim();
274336
+ }
274337
+ cancel() {
274338
+ this.isClosed = true;
274339
+ this.stopHeartbeat();
274340
+ this.pendingChunks = [];
274341
+ if (this.ws) {
274342
+ try {
274343
+ this.ws.close();
274344
+ } catch {}
274345
+ this.ws = null;
274346
+ }
274347
+ this.finalTranscript = "";
274348
+ this.interimTranscript = "";
274349
+ }
274350
+ }
274351
+ var init_stt = __esm(() => {
274352
+ init_config();
274353
+ try {
274354
+ dns.setDefaultResultOrder?.("ipv4first");
274355
+ } catch {}
274356
+ });
274357
+
274358
+ // src/utils/audio/index.ts
274359
+ async function isVoiceConfigured() {
274360
+ const binaryDetection = detectAudioBinary();
274361
+ const sttConfig = await resolveSTTKey();
274362
+ if (!binaryDetection.available) {
274363
+ return {
274364
+ available: false,
274365
+ hasBinary: false,
274366
+ hasKey: Boolean(sttConfig.key),
274367
+ error: binaryDetection.error
274368
+ };
274369
+ }
274370
+ if (!sttConfig.key) {
274371
+ return {
274372
+ available: false,
274373
+ hasBinary: true,
274374
+ hasKey: false,
274375
+ binary: binaryDetection.binary,
274376
+ error: "Deepgram API key not configured."
274377
+ };
274378
+ }
274379
+ return {
274380
+ available: true,
274381
+ hasBinary: true,
274382
+ hasKey: true,
274383
+ binary: binaryDetection.binary,
274384
+ provider: "deepgram"
274385
+ };
274386
+ }
274387
+ function getVoiceSetupNotice(status) {
274388
+ if (!status.hasBinary) {
274389
+ return [
274390
+ "Voice dictation requires an audio recording tool.",
274391
+ "",
274392
+ "Install:",
274393
+ " • macOS: brew install ffmpeg",
274394
+ " • Linux: sudo apt install ffmpeg"
274395
+ ].join(`
274396
+ `);
274397
+ }
274398
+ return [
274399
+ "Voice dictation requires a Deepgram API key for real-time live streaming.",
274400
+ "",
274401
+ "Get a free key ($200 credit) at https://console.deepgram.com",
274402
+ "",
274403
+ "Configure key:",
274404
+ " december key deepgram <key>"
274405
+ ].join(`
274406
+ `);
274407
+ }
274408
+ async function startVoiceDictation(options) {
274409
+ const configCheck = await isVoiceConfigured();
274410
+ if (!configCheck.available) {
274411
+ options.onError(new Error(configCheck.error || "Voice dictation is not available."));
274412
+ return null;
274413
+ }
274414
+ const stt = new STTSession({
274415
+ onTranscript: options.onTranscript,
274416
+ onError: options.onError
274417
+ });
274418
+ const initialized = await stt.init();
274419
+ if (!initialized) {
274420
+ return null;
274421
+ }
274422
+ const recorder = new AudioRecorder;
274423
+ recorder.on("data", (chunk) => {
274424
+ stt.sendAudioChunk(chunk);
274425
+ });
274426
+ recorder.on("error", (err) => {
274427
+ options.onError(err);
274428
+ stt.cancel();
274429
+ });
274430
+ const started = recorder.start();
274431
+ if (!started) {
274432
+ stt.cancel();
274433
+ return null;
274434
+ }
274435
+ return {
274436
+ stop: async () => {
274437
+ recorder.stop();
274438
+ return await stt.finish();
274439
+ },
274440
+ cancel: () => {
274441
+ recorder.stop();
274442
+ stt.cancel();
274443
+ },
274444
+ get isRecording() {
274445
+ return recorder.recording;
274446
+ }
274447
+ };
274448
+ }
274449
+ var init_audio2 = __esm(() => {
274450
+ init_detector2();
274451
+ init_recorder();
274452
+ init_stt();
274453
+ init_detector2();
274454
+ init_recorder();
274455
+ });
274456
+
272861
274457
  // src/hooks/use-agent-session.ts
272862
274458
  var exports_use_agent_session = {};
272863
274459
  __export(exports_use_agent_session, {
@@ -272993,7 +274589,9 @@ function useAgentSession({
272993
274589
  settingsSteeringMode,
272994
274590
  setSettingsSteeringMode,
272995
274591
  settingsFollowUpMode,
272996
- setSettingsFollowUpMode
274592
+ setSettingsFollowUpMode,
274593
+ settingsBrowserVisible,
274594
+ setSettingsBrowserVisible
272997
274595
  } = state;
272998
274596
  const [expandCommands, setExpandCommands] = import_react71.useState(false);
272999
274597
  const toggleExpandCommands = import_react71.useCallback(() => {
@@ -274046,6 +275644,36 @@ ${decStatus}
274046
275644
  }
274047
275645
  return;
274048
275646
  }
275647
+ if (text.trim() === "/voice") {
275648
+ await Promise.resolve().then(() => init_audio2());
275649
+ const status = await isVoiceConfigured();
275650
+ if (!status.available) {
275651
+ const userMsg = {
275652
+ id: getNextMsgId(),
275653
+ role: "user",
275654
+ displayText: "/voice",
275655
+ text: "/voice"
275656
+ };
275657
+ const noticeMsg = {
275658
+ id: getNextMsgId(),
275659
+ role: "assistant",
275660
+ blocks: [
275661
+ {
275662
+ type: "text",
275663
+ content: getVoiceSetupNotice(status)
275664
+ }
275665
+ ]
275666
+ };
275667
+ setStaticMessages((prev) => [
275668
+ ...prev,
275669
+ ...useCliStore.getState().activeMessages,
275670
+ userMsg,
275671
+ noticeMsg
275672
+ ]);
275673
+ setActiveMessages([]);
275674
+ return;
275675
+ }
275676
+ }
274049
275677
  if (text.trim() === "/copy") {
274050
275678
  try {
274051
275679
  await init_src7();
@@ -274433,6 +276061,7 @@ ${decStatus}
274433
276061
  setSettingsThinkingLevel(config.thinkingLevel ?? "auto");
274434
276062
  setSettingsSteeringMode(config.steeringMode ?? "all");
274435
276063
  setSettingsFollowUpMode(config.followUpMode ?? "all");
276064
+ setSettingsBrowserVisible(config.browserVisible !== false);
274436
276065
  setAuthMode("settings_main");
274437
276066
  });
274438
276067
  return;
@@ -274837,7 +276466,46 @@ If it doesn't open automatically, please click here:
274837
276466
  setStaticMessages
274838
276467
  ]);
274839
276468
  const handleContextSelect = () => {};
276469
+ const handleStartVoice = import_react71.useCallback(async (callbacks) => {
276470
+ await Promise.resolve().then(() => init_audio2());
276471
+ const status = await isVoiceConfigured();
276472
+ if (!status.hasBinary) {
276473
+ const userMsg = {
276474
+ id: getNextMsgId(),
276475
+ role: "user",
276476
+ displayText: "/voice",
276477
+ text: "/voice"
276478
+ };
276479
+ const noticeMsg = {
276480
+ id: getNextMsgId(),
276481
+ role: "assistant",
276482
+ blocks: [
276483
+ {
276484
+ type: "text",
276485
+ content: getVoiceSetupNotice(status)
276486
+ }
276487
+ ]
276488
+ };
276489
+ setStaticMessages((prev) => [
276490
+ ...prev,
276491
+ ...useCliStore.getState().activeMessages,
276492
+ userMsg,
276493
+ noticeMsg
276494
+ ]);
276495
+ setActiveMessages([]);
276496
+ return null;
276497
+ }
276498
+ if (!status.hasKey) {
276499
+ useCliStore.getState().setSelectedProvider("deepgram");
276500
+ useCliStore.getState().setApiKey("");
276501
+ useCliStore.getState().setAuthError(null);
276502
+ useCliStore.getState().setAuthMode("byok_key");
276503
+ return null;
276504
+ }
276505
+ return await startVoiceDictation(callbacks);
276506
+ }, [setStaticMessages, setActiveMessages]);
274840
276507
  return {
276508
+ onStartVoice: handleStartVoice,
274841
276509
  currentPlannedPrompt,
274842
276510
  setCurrentPlannedPrompt,
274843
276511
  currentPlanText,
@@ -274932,6 +276600,8 @@ If it doesn't open automatically, please click here:
274932
276600
  setSettingsSteeringMode,
274933
276601
  settingsFollowUpMode,
274934
276602
  setSettingsFollowUpMode,
276603
+ settingsBrowserVisible,
276604
+ setSettingsBrowserVisible,
274935
276605
  handleSubmit,
274936
276606
  sessionRepository,
274937
276607
  handleSettingsMainSelect,
@@ -275008,7 +276678,7 @@ var init_use_agent_session = __esm(async () => {
275008
276678
 
275009
276679
  // src/index.ts
275010
276680
  init_package();
275011
- import path38 from "node:path";
276681
+ import path41 from "node:path";
275012
276682
 
275013
276683
  // src/args.ts
275014
276684
  import { parseArgs } from "node:util";
@@ -275146,12 +276816,12 @@ Options:
275146
276816
  }
275147
276817
  // src/constants/release-highlights.json
275148
276818
  var release_highlights_default = {
275149
- version: "0.4.4",
275150
- title: "What's New in v0.4.4",
276819
+ version: "0.4.6",
276820
+ title: "What's New in v0.4.6",
275151
276821
  bullets: [
275152
- "*(agent)* Passive workspace memory ingestion and manage_memory tool",
275153
- "*(agent)* Lsp client integration for definition and reference navigation",
275154
- "*(tools)* Prefer non-import lines when resolving symbol definition in code_intelligence"
276822
+ "*(providers,scripts)* Fix anthropic user key auth and update email scripts",
276823
+ "*(docs,web)* Clean redundant png assets, add marathi readme, and update landing hero",
276824
+ "*(docs)* Update brand blue banner, navbar links, and profile cards"
275155
276825
  ],
275156
276826
  url: "https://trydecember.com/docs/changelog"
275157
276827
  };
@@ -275161,6 +276831,7 @@ init_env_sanitizer();
275161
276831
  init_env_sanitizer();
275162
276832
  init_commands();
275163
276833
  init_headless_runner();
276834
+ init_workspace_trust();
275164
276835
  purgeProjectEnvApiKeys();
275165
276836
  async function readPipedStdin() {
275166
276837
  if (process.stdin.isTTY)
@@ -275279,6 +276950,11 @@ To define project-specific instructions, create an AGENTS.md file in your projec
275279
276950
  }
275280
276951
  parsedArgs.prompt = question;
275281
276952
  }
276953
+ await Promise.resolve().then(() => init_workspace_trust());
276954
+ await ensureWorkspaceTrust({
276955
+ targetDir: parsedArgs.scope,
276956
+ isHeadless: Boolean(parsedArgs.prompt)
276957
+ });
275282
276958
  if (parsedArgs.prompt) {
275283
276959
  const [
275284
276960
  { AgentHarness },
@@ -275352,7 +277028,7 @@ To define project-specific instructions, create an AGENTS.md file in your projec
275352
277028
  sessionRepository,
275353
277029
  sessionId,
275354
277030
  runtime: "cli",
275355
- workspaceDir: parsedArgs.scope ? path38.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
277031
+ workspaceDir: parsedArgs.scope ? path41.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
275356
277032
  thinkingLevel: config.thinkingLevel || "auto",
275357
277033
  steeringMode: config.steeringMode || "all",
275358
277034
  followUpMode: config.followUpMode || "all"
@@ -275456,7 +277132,7 @@ Executing Headless Task: "${parsedArgs.prompt}"
275456
277132
  sessionRepository,
275457
277133
  sessionId,
275458
277134
  runtime: "cli",
275459
- workspaceDir: parsedArgs.scope ? path38.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
277135
+ workspaceDir: parsedArgs.scope ? path41.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
275460
277136
  thinkingLevel: config.thinkingLevel || "auto",
275461
277137
  steeringMode: config.steeringMode || "all",
275462
277138
  followUpMode: config.followUpMode || "all"
@@ -275541,6 +277217,8 @@ Executing Headless Task: "${parsedArgs.prompt}"
275541
277217
  }
275542
277218
  main().catch(console.error);
275543
277219
  export {
277220
+ addTrustedWorkspace,
277221
+ ensureWorkspaceTrust,
275544
277222
  getHelpText,
275545
277223
  handleAuthCommand,
275546
277224
  handleDoctorCommand,
@@ -275550,9 +277228,11 @@ export {
275550
277228
  handleLogoutCommand,
275551
277229
  handleSwitchCommand,
275552
277230
  handleUpdateCommand,
277231
+ isWorkspaceTrusted,
275553
277232
  parseCliArgs,
275554
277233
  purgeProjectEnvApiKeys,
275555
277234
  readPipedStdin,
277235
+ resolveWorkspaceRoot,
275556
277236
  restoreConsole,
275557
277237
  runHeadlessTask,
275558
277238
  suppressConsole