lody 0.43.1 → 0.44.0

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 (2) hide show
  1. package/dist/index.js +553 -126
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -36820,7 +36820,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
36820
36820
  return client;
36821
36821
  }
36822
36822
  const name = "lody";
36823
- const version$4 = "0.43.1";
36823
+ const version$4 = "0.44.0";
36824
36824
  const description = "Lody Agent CLI tool for managing remote command execution";
36825
36825
  const type = "module";
36826
36826
  const main$3 = "dist/index.js";
@@ -36863,7 +36863,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
36863
36863
  "node": ">=18.0.0"
36864
36864
  };
36865
36865
  const optionalDependencies = {
36866
- "acp-extension-claude": "0.25.0",
36866
+ "@agentclientprotocol/claude-agent-acp": "0.29.0",
36867
36867
  "acp-extension-codex": "0.11.1"
36868
36868
  };
36869
36869
  const devDependencies = {
@@ -64620,34 +64620,78 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
64620
64620
  SITE_URL = "https://lody.ai";
64621
64621
  SITE_APP_BASE_PATH = process.env["SITE_APP_BASE_PATH"] ?? "";
64622
64622
  };
64623
+ const MACHINE_ID_FILE_NAME = "machine-id";
64624
+ function getMachineIdFilePath() {
64625
+ return path__default.join(os__default.homedir(), ".lody", MACHINE_ID_FILE_NAME);
64626
+ }
64627
+ function isRunningInDocker() {
64628
+ try {
64629
+ if (fs__default.existsSync("/.dockerenv")) {
64630
+ return true;
64631
+ }
64632
+ const cgroup = fs__default.readFileSync("/proc/self/cgroup", "utf-8");
64633
+ return cgroup.includes("docker") || /[0-9a-f]{64}/.test(cgroup);
64634
+ } catch {
64635
+ return false;
64636
+ }
64637
+ }
64623
64638
  function getSystemMachineId() {
64624
64639
  try {
64640
+ const inDocker = isRunningInDocker();
64641
+ if (inDocker) {
64642
+ const machineIdPath = getMachineIdFilePath();
64643
+ if (fs__default.existsSync(machineIdPath)) {
64644
+ const id = fs__default.readFileSync(machineIdPath, "utf-8").trim();
64645
+ if (id) return id;
64646
+ }
64647
+ }
64648
+ let baseId = null;
64625
64649
  if (process.platform === "linux") {
64626
64650
  if (fs__default.existsSync("/etc/machine-id")) {
64627
- return fs__default.readFileSync("/etc/machine-id", "utf-8").trim();
64628
- }
64629
- if (fs__default.existsSync("/var/lib/dbus/machine-id")) {
64630
- return fs__default.readFileSync("/var/lib/dbus/machine-id", "utf-8").trim();
64651
+ baseId = fs__default.readFileSync("/etc/machine-id", "utf-8").trim();
64652
+ } else if (fs__default.existsSync("/var/lib/dbus/machine-id")) {
64653
+ baseId = fs__default.readFileSync("/var/lib/dbus/machine-id", "utf-8").trim();
64631
64654
  }
64632
64655
  } else if (process.platform === "darwin") {
64633
64656
  try {
64634
64657
  const uuid2 = execSync("ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID", {
64635
64658
  encoding: "utf-8"
64636
64659
  }).match(/"IOPlatformUUID" = "(.+)"/)?.[1];
64637
- return uuid2 || null;
64660
+ baseId = uuid2 ?? null;
64638
64661
  } catch {
64639
- return null;
64662
+ baseId = null;
64640
64663
  }
64641
64664
  } else if (process.platform === "win32") {
64642
64665
  try {
64643
64666
  const uuid2 = execSync("wmic csproduct get UUID", {
64644
64667
  encoding: "utf-8"
64645
64668
  }).split("\n")[1]?.trim();
64646
- return uuid2 || null;
64669
+ baseId = uuid2 ?? null;
64670
+ } catch {
64671
+ baseId = null;
64672
+ }
64673
+ }
64674
+ if (!baseId) return null;
64675
+ if (inDocker) {
64676
+ const suffix = require$$3$4.randomUUID().replace(/-/g, "").slice(0, 12);
64677
+ const dockerMachineId = `${baseId}-docker-${suffix}`;
64678
+ try {
64679
+ const machineIdPath = getMachineIdFilePath();
64680
+ fs__default.mkdirSync(path__default.dirname(machineIdPath), {
64681
+ recursive: true
64682
+ });
64683
+ fs__default.writeFileSync(machineIdPath, dockerMachineId, "utf-8");
64684
+ if (process.platform !== "win32") {
64685
+ try {
64686
+ fs__default.chmodSync(machineIdPath, 384);
64687
+ } catch {
64688
+ }
64689
+ }
64647
64690
  } catch {
64648
- return null;
64649
64691
  }
64692
+ return dockerMachineId;
64650
64693
  }
64694
+ return baseId;
64651
64695
  } catch {
64652
64696
  }
64653
64697
  return null;
@@ -66603,6 +66647,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
66603
66647
  if (!normalizedSegment) return "";
66604
66648
  return `/${normalizedSegment}`;
66605
66649
  }
66650
+ const LODY_FULL_ACCESS_MODE_ID = "lodyFullAccess";
66651
+ const isFullAccessModeId = (modeId) => modeId === LODY_FULL_ACCESS_MODE_ID || modeId === "bypassPermissions" || modeId === "full-access";
66606
66652
  const ACP_CAPABILITY_CACHE_VERSION = 1;
66607
66653
  const getAcpCapabilityCacheKey = (cliType, agentType) => `${cliType}:${agentType}`;
66608
66654
  const getAcpCapabilityCacheStaleReason = (entry2, expectedSourceVersion) => {
@@ -66618,6 +66664,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
66618
66664
  return void 0;
66619
66665
  };
66620
66666
  const isBuiltinAgentType = (agentType) => agentType === "claude" || agentType === "codex";
66667
+ const isAllowPermissionKind = (kind) => typeof kind === "string" && kind.startsWith("allow");
66668
+ const isDenyOrRejectPermissionKind = (kind) => typeof kind === "string" && (kind.startsWith("deny") || kind.startsWith("reject"));
66669
+ const getPermissionOptionKind = (option2) => option2.kind;
66670
+ function selectAutoApprovePermissionDecision(options) {
66671
+ const selected = options.find((option2) => getPermissionOptionKind(option2) === "allow_always") ?? options.find((option2) => getPermissionOptionKind(option2) === "allow_once") ?? options.find((option2) => isAllowPermissionKind(getPermissionOptionKind(option2)));
66672
+ if (selected) {
66673
+ return {
66674
+ outcome: {
66675
+ outcome: "selected",
66676
+ optionId: selected.optionId
66677
+ },
66678
+ option: selected,
66679
+ reason: "allow"
66680
+ };
66681
+ }
66682
+ const fallback2 = options.find((option2) => {
66683
+ const kind = getPermissionOptionKind(option2);
66684
+ return typeof kind === "string" && !isDenyOrRejectPermissionKind(kind);
66685
+ });
66686
+ if (fallback2) {
66687
+ return {
66688
+ outcome: {
66689
+ outcome: "selected",
66690
+ optionId: fallback2.optionId
66691
+ },
66692
+ option: fallback2,
66693
+ reason: "fallback"
66694
+ };
66695
+ }
66696
+ return null;
66697
+ }
66621
66698
  function getBuiltinTitleGenerationDefaults(agentType) {
66622
66699
  if (agentType === "claude") {
66623
66700
  return {
@@ -76319,6 +76396,37 @@ ${tailedOutput}` : null;
76319
76396
  machineId
76320
76397
  });
76321
76398
  }
76399
+ async loginWithApiKey(apiKey, machineName) {
76400
+ const accessToken = apiKey.trim();
76401
+ if (!accessToken) {
76402
+ return {
76403
+ success: false,
76404
+ error: "Missing API key for --auth login"
76405
+ };
76406
+ }
76407
+ const validation2 = await this.validateToken(accessToken);
76408
+ if (!validation2.valid || !validation2.user || !validation2.userId) {
76409
+ return {
76410
+ success: false,
76411
+ error: "Invalid API key. Generate a new key and retry."
76412
+ };
76413
+ }
76414
+ const machineId = getSystemMachineId() || v4();
76415
+ const authInfo = {
76416
+ token: accessToken,
76417
+ user: validation2.user,
76418
+ machine: {
76419
+ machineName,
76420
+ machineId
76421
+ }
76422
+ };
76423
+ await saveAuthInfo(accessToken, authInfo.user, authInfo.machine);
76424
+ setSentryUser(authInfo.user);
76425
+ return {
76426
+ success: true,
76427
+ ...authInfo
76428
+ };
76429
+ }
76322
76430
  async login(machineName) {
76323
76431
  const machineId = getSystemMachineId() || v4();
76324
76432
  this.logger.debug(`[device-auth] siteUrl=${this.siteUrl} serverUrl=${this.serverUrl}`);
@@ -76629,6 +76737,25 @@ ${tailedOutput}` : null;
76629
76737
  machine: loginResult.machine
76630
76738
  };
76631
76739
  }
76740
+ async function performLoginWithApiKey(authClient, logger2, options) {
76741
+ const machineNameOverride = options.machineName?.trim();
76742
+ const machineName = machineNameOverride || os__default.hostname();
76743
+ logger2.info("Using provided API key for non-interactive login.");
76744
+ logger2.info(" Machine Name: " + chalk.cyan(machineName));
76745
+ const loginResult = await authClient.loginWithApiKey(options.apiKey, machineName);
76746
+ if (!loginResult.success) {
76747
+ return {
76748
+ success: false,
76749
+ error: loginResult.error
76750
+ };
76751
+ }
76752
+ return {
76753
+ success: true,
76754
+ token: loginResult.token,
76755
+ user: loginResult.user,
76756
+ machine: loginResult.machine
76757
+ };
76758
+ }
76632
76759
  const version$1 = "1.25.4";
76633
76760
  var lookup = [];
76634
76761
  var revLookup = [];
@@ -88969,12 +89096,11 @@ ${val.stack}`;
88969
89096
  const flock = this.metaFlock;
88970
89097
  const currentVersion = flock.version();
88971
89098
  if (this.lastPersistedVersion && this.versionsEqual(currentVersion, this.lastPersistedVersion)) return;
88972
- const fullBundle = flock.exportJson();
88973
- const encoded = Flock.fromJson(fullBundle, flock.peerId()).exportFile();
88974
89099
  if (!this.storage) {
88975
89100
  this.lastPersistedVersion = currentVersion;
88976
89101
  return;
88977
89102
  }
89103
+ const encoded = flock.exportFile();
88978
89104
  await this.storage.save({
88979
89105
  type: "meta",
88980
89106
  update: encoded
@@ -104038,7 +104164,7 @@ stream:${scope2.streamId}`;
104038
104164
  const DEFAULT_GATEWAY_BASE_URL = "https://streams-api.loro.dev";
104039
104165
  const JSON_RPC_VERSION$1 = "2.0";
104040
104166
  const LORO_STREAMS_RPC_VERSION = "1";
104041
- const LORO_STREAMS_RPC_RETENTION_SECONDS = 3600;
104167
+ const LORO_STREAMS_RPC_RETENTION_SECONDS = 86400;
104042
104168
  const LORO_RPC_REQUEST_STREAM_SEGMENT = "rpc:req";
104043
104169
  const getLoroMachineRpcRequestStreamId = (workspaceId, machineId) => `${workspaceId}:${LORO_RPC_REQUEST_STREAM_SEGMENT}:${machineId}`;
104044
104170
  const normalizeLoroGatewayBaseUrl = (baseUrl) => {
@@ -105181,8 +105307,9 @@ stream:${scope2.streamId}`;
105181
105307
  const filtered = stripToolCallContentForHistory(kind ?? null, content);
105182
105308
  return filtered.length ? filtered : void 0;
105183
105309
  };
105184
- const ensurePermissionRequestOnToolCall = async (doc, requestId, request, model) => {
105310
+ const ensurePermissionRequestOnToolCall = async (doc, requestId, request, _model) => {
105185
105311
  const toolCallId = request.toolCall.toolCallId;
105312
+ let persisted = false;
105186
105313
  await doc.updateHistory((history) => {
105187
105314
  let updated = false;
105188
105315
  history.forEach((entry2) => {
@@ -105197,25 +105324,23 @@ stream:${scope2.streamId}`;
105197
105324
  return content;
105198
105325
  });
105199
105326
  if (entryUpdated) {
105327
+ persisted = true;
105200
105328
  writeEntryItems(entry2, nextContents);
105201
105329
  }
105202
105330
  });
105203
105331
  if (!updated) {
105204
- history.push({
105205
- id: v4(),
105206
- role: "assistant",
105207
- items: [
105332
+ const latestEntry = history[history.length - 1];
105333
+ if (latestEntry?.role === "assistant" && latestEntry.finished !== true && typeof latestEntry.endedAt !== "number") {
105334
+ persisted = true;
105335
+ writeEntryItems(latestEntry, [
105336
+ ...readEntryItems(latestEntry),
105208
105337
  buildToolCallFromPermissionRequest(requestId, request)
105209
- ],
105210
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
105211
- read: void 0,
105212
- userId: void 0,
105213
- modelInfo: model,
105214
- fileDiff: []
105215
- });
105338
+ ]);
105339
+ }
105216
105340
  }
105217
105341
  return history;
105218
105342
  });
105343
+ return persisted;
105219
105344
  };
105220
105345
  const updatePermissionOutcomeInHistory = async (doc, requestId, outcome, _logger) => {
105221
105346
  await doc.updateHistory((history) => {
@@ -106300,9 +106425,9 @@ stream:${scope2.streamId}`;
106300
106425
  };
106301
106426
  const BuiltinACPSetting = {
106302
106427
  claude: {
106303
- packageName: "acp-extension-claude",
106304
- version: "0.25.0",
106305
- binName: "acp-extension-claude"
106428
+ packageName: "@agentclientprotocol/claude-agent-acp",
106429
+ version: "0.29.0",
106430
+ binName: "claude-agent-acp"
106306
106431
  },
106307
106432
  codex: {
106308
106433
  packageName: "acp-extension-codex",
@@ -106454,7 +106579,10 @@ stream:${scope2.streamId}`;
106454
106579
  if (fs__default.existsSync(path__default.join(installScopedNodeModules, "package.json"))) {
106455
106580
  return true;
106456
106581
  }
106457
- const installNodeModulesRoot = path__default.dirname(packageRoot);
106582
+ let installNodeModulesRoot = path__default.dirname(packageRoot);
106583
+ if (path__default.basename(installNodeModulesRoot).startsWith("@")) {
106584
+ installNodeModulesRoot = path__default.dirname(installNodeModulesRoot);
106585
+ }
106458
106586
  const hoistedDependencyPath = path__default.join(installNodeModulesRoot, ...depSegments);
106459
106587
  return fs__default.existsSync(path__default.join(hoistedDependencyPath, "package.json"));
106460
106588
  }
@@ -107830,6 +107958,19 @@ main().catch(() => {});
107830
107958
  return `!node "${helperPath}"`;
107831
107959
  };
107832
107960
  const SAFE_SESSION_ID_RE$1 = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
107961
+ const COMMON_BASE_BRANCH_NAMES = /* @__PURE__ */ new Set([
107962
+ "main",
107963
+ "master",
107964
+ "dev",
107965
+ "develop",
107966
+ "development",
107967
+ "trunk",
107968
+ "release",
107969
+ "staging",
107970
+ "stage",
107971
+ "prod",
107972
+ "production"
107973
+ ]);
107833
107974
  function assertSafeSessionId$1(sessionId) {
107834
107975
  if (!SAFE_SESSION_ID_RE$1.test(sessionId)) {
107835
107976
  throw new Error(`Invalid sessionId ${JSON.stringify(sessionId)}: expected /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/`);
@@ -108517,6 +108658,23 @@ path=/${options.repoFullName}.git
108517
108658
  getDefaultSessionBranchName(sessionId) {
108518
108659
  return `session/${sessionId.slice(0, 8)}`;
108519
108660
  }
108661
+ normalizeBranchName(branchName) {
108662
+ return branchName.trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "");
108663
+ }
108664
+ isCommonBaseBranchName(branchName) {
108665
+ const normalized = this.normalizeBranchName(branchName).toLowerCase();
108666
+ if (!normalized) {
108667
+ return false;
108668
+ }
108669
+ if (COMMON_BASE_BRANCH_NAMES.has(normalized)) {
108670
+ return true;
108671
+ }
108672
+ return normalized.startsWith("release/") || normalized.startsWith("releases/");
108673
+ }
108674
+ shouldReuseExistingBaseBranch(branchName) {
108675
+ const trimmed = branchName?.trim();
108676
+ return !!trimmed && !this.isCommonBaseBranchName(trimmed);
108677
+ }
108520
108678
  async hasLocalBranch(branchName) {
108521
108679
  const sanitized = branchName.trim();
108522
108680
  if (!sanitized) {
@@ -108544,6 +108702,33 @@ path=/${options.repoFullName}.git
108544
108702
  }
108545
108703
  return null;
108546
108704
  }
108705
+ async resolveReusableBaseBranch(baseBranch) {
108706
+ const trimmed = baseBranch?.trim();
108707
+ if (!this.shouldReuseExistingBaseBranch(trimmed)) {
108708
+ return null;
108709
+ }
108710
+ if (await this.hasLocalBranch(trimmed)) {
108711
+ return {
108712
+ branchName: trimmed
108713
+ };
108714
+ }
108715
+ const remoteBranch = `origin/${trimmed}`;
108716
+ if (this.repoUrl && await this.hasCommitish(remoteBranch)) {
108717
+ return {
108718
+ branchName: trimmed,
108719
+ startPoint: remoteBranch
108720
+ };
108721
+ }
108722
+ return null;
108723
+ }
108724
+ shouldPreserveRemovedBranch(branchName, options) {
108725
+ const baseBranchName = options?.baseBranchName?.trim();
108726
+ return !!baseBranchName && branchName === baseBranchName && this.shouldReuseExistingBaseBranch(baseBranchName);
108727
+ }
108728
+ isBranchAlreadyCheckedOutError(error2) {
108729
+ const message = formatErrorMessage(error2).toLowerCase();
108730
+ return message.includes("is already checked out at");
108731
+ }
108547
108732
  async createWorktree(sessionId, baseBranch, restoreBranchName) {
108548
108733
  return withRepoLock(this.repoId, async () => {
108549
108734
  assertSafeSessionId$1(sessionId);
@@ -108551,6 +108736,7 @@ path=/${options.repoFullName}.git
108551
108736
  const worktreePath = this.getWorktreeHostPath(sessionId);
108552
108737
  const branchName = this.getDefaultSessionBranchName(sessionId);
108553
108738
  const resolvedBase = await this.resolveBaseRef(baseBranch);
108739
+ const reusableBaseBranch = await this.resolveReusableBaseBranch(baseBranch);
108554
108740
  if (fs$4.existsSync(worktreePath)) {
108555
108741
  this.ensureWorktreeGitdirIsRelative(sessionId);
108556
108742
  const info2 = await this.getWorktreeInfo(sessionId);
@@ -108566,6 +108752,40 @@ path=/${options.repoFullName}.git
108566
108752
  worktreePath,
108567
108753
  existingBranchName
108568
108754
  ], this.bareGitDir);
108755
+ } else if (reusableBaseBranch) {
108756
+ this.logger.debug(`[${this.repoId}] Creating worktree from selected branch: ${reusableBaseBranch.branchName}`);
108757
+ try {
108758
+ if (reusableBaseBranch.startPoint) {
108759
+ await this.runGit([
108760
+ "worktree",
108761
+ "add",
108762
+ "-b",
108763
+ reusableBaseBranch.branchName,
108764
+ worktreePath,
108765
+ reusableBaseBranch.startPoint
108766
+ ], this.bareGitDir);
108767
+ } else {
108768
+ await this.runGit([
108769
+ "worktree",
108770
+ "add",
108771
+ worktreePath,
108772
+ reusableBaseBranch.branchName
108773
+ ], this.bareGitDir);
108774
+ }
108775
+ } catch (error2) {
108776
+ if (!this.isBranchAlreadyCheckedOutError(error2)) {
108777
+ throw error2;
108778
+ }
108779
+ this.logger.debug(`[${this.repoId}] Selected branch already checked out; creating session branch instead (branch=${branchName} base=${resolvedBase})`);
108780
+ await this.runGit([
108781
+ "worktree",
108782
+ "add",
108783
+ "-b",
108784
+ branchName,
108785
+ worktreePath,
108786
+ resolvedBase
108787
+ ], this.bareGitDir);
108788
+ }
108569
108789
  } else {
108570
108790
  this.logger.debug(`[${this.repoId}] Creating new worktree (sessionId=${sessionId} branch=${branchName} base=${resolvedBase}): ${worktreePath}`);
108571
108791
  await this.runGit([
@@ -108614,7 +108834,7 @@ path=/${options.repoFullName}.git
108614
108834
  isClean
108615
108835
  };
108616
108836
  }
108617
- async removeWorktree(sessionId, force = false, branchName) {
108837
+ async removeWorktree(sessionId, force = false, branchName, options) {
108618
108838
  return withRepoLock(this.repoId, async () => {
108619
108839
  const resolvedBranchName = await this.removeWorktreeInternal(sessionId, {
108620
108840
  force,
@@ -108622,6 +108842,10 @@ path=/${options.repoFullName}.git
108622
108842
  branchName
108623
108843
  });
108624
108844
  if (resolvedBranchName) {
108845
+ if (this.shouldPreserveRemovedBranch(resolvedBranchName, options)) {
108846
+ this.logger.debug(`[${this.repoId}] Preserving reused base branch after worktree removal: ${resolvedBranchName}`);
108847
+ return;
108848
+ }
108625
108849
  await this.cleanupBranch(resolvedBranchName);
108626
108850
  }
108627
108851
  });
@@ -108897,6 +109121,7 @@ path=/${options.repoFullName}.git
108897
109121
  pendingContextWindowHandlers: /* @__PURE__ */ new Set(),
108898
109122
  pendingUsageHandlers: /* @__PURE__ */ new Set(),
108899
109123
  permissionWaitMs: 0,
109124
+ autoApprovePermissions: false,
108900
109125
  pendingUnread: false,
108901
109126
  heartbeat: null,
108902
109127
  lastActivityMs: Date.now(),
@@ -120291,6 +120516,15 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
120291
120516
  if (dispatchAction.type !== "dispatch") {
120292
120517
  return;
120293
120518
  }
120519
+ const requesterUserId = nextUserTurn.userId ?? freshMeta.userId;
120520
+ const access = await this.deps.canUseMachine({
120521
+ sessionId,
120522
+ requesterUserId
120523
+ });
120524
+ if (!access.allowed) {
120525
+ await this.markDispatchAccessDenied(sessionId, sessionDoc, nextUserTurn.id, access.reason);
120526
+ return;
120527
+ }
120294
120528
  if (dispatchAction.mode === "create") {
120295
120529
  const createRequest = await this.buildCreateRequestFromHistoryEntry(freshMeta, nextUserTurn);
120296
120530
  await this.deps.executionService.startSession(createRequest);
@@ -120299,6 +120533,38 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
120299
120533
  await this.deps.executionService.continueSession(chatRequest);
120300
120534
  }
120301
120535
  }
120536
+ getAccessDeniedMessage(reason) {
120537
+ switch (reason) {
120538
+ case "requester_not_member":
120539
+ return "The requester is not a member of this workspace.";
120540
+ case "machine_not_registered":
120541
+ return "This machine is not registered for workspace access.";
120542
+ case "not_visible":
120543
+ return "This machine is private to its owner.";
120544
+ case "access_unavailable":
120545
+ return "Machine access could not be verified.";
120546
+ }
120547
+ }
120548
+ async markDispatchAccessDenied(sessionId, sessionDoc, userTurnId, reason) {
120549
+ const message = this.getAccessDeniedMessage(reason);
120550
+ this.deps.logger.warn(`[${sessionId}] Refusing dispatch: ${message}`);
120551
+ await sessionDoc.updateHistory((history) => history.map((entry2) => entry2.id === userTurnId && entry2.role === "user" ? {
120552
+ ...entry2,
120553
+ status: "failed",
120554
+ read: getLegacyReadForSessionHistoryStatus("failed")
120555
+ } : entry2));
120556
+ await this.deps.workspaceDocument.repo.upsertDocMeta?.(getSessionRoomId(sessionId), {
120557
+ latestUserMsgId: userTurnId,
120558
+ lastHandledUserMsgId: userTurnId,
120559
+ processingUserMsgId: void 0,
120560
+ dispatchError: {
120561
+ code: "machine_access_denied",
120562
+ message,
120563
+ at: getServerNow()
120564
+ }
120565
+ });
120566
+ await sessionDoc.setStatus(SessionStatusFactory.idle());
120567
+ }
120302
120568
  async maybeHandleCancelRequest(sessionId) {
120303
120569
  const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
120304
120570
  const meta = await sessionDoc.getMetaState();
@@ -120869,6 +121135,121 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
120869
121135
  }
120870
121136
  }
120871
121137
  }
121138
+ const WorkspaceSummarySchema = object({
121139
+ id: string$2(),
121140
+ name: string$2(),
121141
+ slug: string$2().nullable(),
121142
+ role: string$2()
121143
+ });
121144
+ const WorkspaceGitHubRepositorySchema = object({
121145
+ id: number$3(),
121146
+ name: string$2(),
121147
+ fullName: string$2(),
121148
+ private: boolean()
121149
+ });
121150
+ const WorkspaceListResultSchema = discriminatedUnion("valid", [
121151
+ object({
121152
+ valid: literal(false),
121153
+ userId: _null(),
121154
+ workspaces: array$3(WorkspaceSummarySchema)
121155
+ }),
121156
+ object({
121157
+ valid: literal(true),
121158
+ userId: string$2(),
121159
+ workspaces: array$3(WorkspaceSummarySchema)
121160
+ })
121161
+ ]);
121162
+ const RegisterSessionOwnerResultSchema = object({
121163
+ success: literal(true),
121164
+ existing: boolean()
121165
+ });
121166
+ const RegisterMachineAccessResultSchema = object({
121167
+ success: literal(true),
121168
+ existing: boolean(),
121169
+ sharedWithTeam: boolean()
121170
+ });
121171
+ const MachineAccessCheckResultSchema = discriminatedUnion("allowed", [
121172
+ object({
121173
+ allowed: literal(true)
121174
+ }),
121175
+ object({
121176
+ allowed: literal(false),
121177
+ reason: _enum([
121178
+ "requester_not_member",
121179
+ "machine_not_registered",
121180
+ "not_visible"
121181
+ ])
121182
+ })
121183
+ ]);
121184
+ const WorkspaceGitHubRepositoryListResultSchema = discriminatedUnion("valid", [
121185
+ object({
121186
+ valid: literal(false),
121187
+ repositories: array$3(WorkspaceGitHubRepositorySchema)
121188
+ }),
121189
+ object({
121190
+ valid: literal(true),
121191
+ repositories: array$3(WorkspaceGitHubRepositorySchema)
121192
+ })
121193
+ ]);
121194
+ function createAuthConvexClient() {
121195
+ if (!LODY_AUTH_URL) {
121196
+ throw new Error("LODY_AUTH_URL is not defined.");
121197
+ }
121198
+ return new ConvexHttpClient(LODY_AUTH_URL);
121199
+ }
121200
+ async function listWorkspacesForToken(token2) {
121201
+ const client = createAuthConvexClient();
121202
+ const raw = await client.query(api.deviceAuth.listMyWorkspacesForCliToken, {
121203
+ token: token2
121204
+ });
121205
+ const parsed = WorkspaceListResultSchema.parse(raw);
121206
+ if (!parsed.valid) {
121207
+ throw new Error("CLI token is invalid or expired. Run `lody login` again.");
121208
+ }
121209
+ return parsed.workspaces;
121210
+ }
121211
+ async function listWorkspaceGitHubRepositoriesForCliToken(input2) {
121212
+ const client = createAuthConvexClient();
121213
+ const raw = await client.query(api.github.listWorkspaceRepositoriesForCliToken, {
121214
+ cliToken: input2.token,
121215
+ workspaceId: input2.workspaceId
121216
+ });
121217
+ const parsed = WorkspaceGitHubRepositoryListResultSchema.parse(raw);
121218
+ if (!parsed.valid) {
121219
+ throw new Error("CLI token is invalid or expired. Run `lody login` again.");
121220
+ }
121221
+ return parsed.repositories;
121222
+ }
121223
+ async function registerSessionOwnerForCliToken(input2) {
121224
+ const client = createAuthConvexClient();
121225
+ const raw = await client.mutation(api.sessions.registerSessionOwnerFromCliToken, {
121226
+ cliToken: input2.token,
121227
+ workspaceId: input2.workspaceId,
121228
+ sessionId: input2.sessionId,
121229
+ cliType: input2.cliType,
121230
+ repoFullName: input2.repoFullName
121231
+ });
121232
+ return RegisterSessionOwnerResultSchema.parse(raw);
121233
+ }
121234
+ async function registerMachineAccessForCliToken(input2) {
121235
+ const client = createAuthConvexClient();
121236
+ const raw = await client.mutation(api.machines.upsertMachineRegistrationFromCliToken, {
121237
+ cliToken: input2.token,
121238
+ workspaceId: input2.workspaceId,
121239
+ machineId: input2.machineId
121240
+ });
121241
+ return RegisterMachineAccessResultSchema.parse(raw);
121242
+ }
121243
+ async function canUseMachineForCliToken(input2) {
121244
+ const client = createAuthConvexClient();
121245
+ const raw = await client.query(api.machines.canUseMachineFromCliToken, {
121246
+ cliToken: input2.token,
121247
+ workspaceId: input2.workspaceId,
121248
+ machineId: input2.machineId,
121249
+ requesterUserId: input2.requesterUserId
121250
+ });
121251
+ return MachineAccessCheckResultSchema.parse(raw);
121252
+ }
120872
121253
  const SESSION_IMAGE_MIME_TYPE_BY_EXTENSION = {
120873
121254
  png: "image/png",
120874
121255
  jpg: "image/jpeg",
@@ -121013,7 +121394,23 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121013
121394
  workspaceId: this.workspaceId,
121014
121395
  workspaceDocument: this.workspaceDocument,
121015
121396
  sessionManager: this.sessionManager,
121016
- executionService: this.executionService
121397
+ executionService: this.executionService,
121398
+ canUseMachine: async ({ requesterUserId, sessionId }) => {
121399
+ try {
121400
+ return await canUseMachineForCliToken({
121401
+ token: this.token,
121402
+ workspaceId: this.workspaceId,
121403
+ machineId: this.machineId,
121404
+ requesterUserId
121405
+ });
121406
+ } catch (error2) {
121407
+ this.logger.error(`[${sessionId}] Failed to verify machine access: ${formatErrorMessage(error2)}`);
121408
+ return {
121409
+ allowed: false,
121410
+ reason: "access_unavailable"
121411
+ };
121412
+ }
121413
+ }
121017
121414
  });
121018
121415
  this.setupSessionEventHandlers();
121019
121416
  this.setupArchiveWatcher();
@@ -121313,13 +121710,25 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121313
121710
  }
121314
121711
  const modeId = config2.modeId;
121315
121712
  const modelId = config2.modelId;
121713
+ const agentConfigOptions = session.agentClient.getConfigOptions?.() ?? [];
121714
+ const modeConfigId = agentConfigOptions.find((o) => o.category === "mode")?.id ?? "mode";
121715
+ const modelConfigId = agentConfigOptions.find((o) => o.category === "model")?.id ?? "model";
121716
+ const configModeId = config2.configOptionValues?.[modeConfigId];
121717
+ const effectiveModeId = modeId ?? configModeId;
121718
+ const autoApprovePermissions = isFullAccessModeId(effectiveModeId);
121719
+ this.store.get(sessionId).autoApprovePermissions = autoApprovePermissions;
121720
+ this.logger.debug(`[${sessionId}] applyAcpModeAndModel: autoApprovePermissions=${autoApprovePermissions}`);
121316
121721
  if (modeId) {
121317
- this.logger.debug(`[${sessionId}] applyAcpModeAndModel: setting mode (modeId=${modeId})`);
121318
- try {
121319
- await session.agentClient.setSessionMode?.(acpSessionId, modeId);
121320
- this.logger.debug(`[${sessionId}] applyAcpModeAndModel: mode set complete`);
121321
- } catch (err2) {
121322
- this.logger.debug(`[${sessionId}] applyAcpModeAndModel: setSessionMode failed for "${modeId}", skipping (mode may not be supported by agent): ${err2}`);
121722
+ if (modeId === LODY_FULL_ACCESS_MODE_ID) {
121723
+ this.logger.debug(`[${sessionId}] applyAcpModeAndModel: using Lody Full Access mode without sending setSessionMode to agent`);
121724
+ } else {
121725
+ this.logger.debug(`[${sessionId}] applyAcpModeAndModel: setting mode (modeId=${modeId})`);
121726
+ try {
121727
+ await session.agentClient.setSessionMode?.(acpSessionId, modeId);
121728
+ this.logger.debug(`[${sessionId}] applyAcpModeAndModel: mode set complete`);
121729
+ } catch (err2) {
121730
+ this.logger.debug(`[${sessionId}] applyAcpModeAndModel: setSessionMode failed for "${modeId}", skipping (mode may not be supported by agent): ${err2}`);
121731
+ }
121323
121732
  }
121324
121733
  }
121325
121734
  if (modelId) {
@@ -121332,12 +121741,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121332
121741
  }
121333
121742
  }
121334
121743
  if (config2.configOptionValues && session.agentClient) {
121335
- const agentConfigOptions = session.agentClient.getConfigOptions();
121336
- const modeConfigId = agentConfigOptions.find((o) => o.category === "mode")?.id;
121337
- const modelConfigId = agentConfigOptions.find((o) => o.category === "model")?.id;
121338
121744
  for (const [configId, value] of Object.entries(config2.configOptionValues)) {
121339
121745
  if (configId === modeConfigId) {
121340
- if (!modeId) {
121746
+ if (!modeId && value !== LODY_FULL_ACCESS_MODE_ID) {
121341
121747
  this.logger.debug(`[${sessionId}] applyAcpModeAndModel: setting mode via setSessionMode from configOption (${configId}=${value})`);
121342
121748
  try {
121343
121749
  await session.agentClient.setSessionMode?.(acpSessionId, value);
@@ -121650,6 +122056,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121650
122056
  needToArchiveSessions: {},
121651
122057
  raceLimits: {}
121652
122058
  });
122059
+ await this.registerMachineAccess();
121653
122060
  this.logger.debug("Machine re-registered in machine meta");
121654
122061
  } catch (error2) {
121655
122062
  this.logger.error(`Failed to ensure machine registration after reconnect: ${error2}`);
@@ -121927,6 +122334,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121927
122334
  }
121928
122335
  const repoFullName = sessionMeta?.project?.kind === "local" ? void 0 : sessionMeta?.repoFullName ?? (request && typeof request === "object" && "repoFullName" in request ? request.repoFullName : void 0);
121929
122336
  const branchName = sessionMeta?.branchName ?? (request && typeof request === "object" && "branchName" in request ? request.branchName : void 0);
122337
+ const baseBranchName = sessionMeta?.baseBranch?.trim() || void 0;
121930
122338
  if (repoFullName) {
121931
122339
  try {
121932
122340
  const repoId = deriveRepoIdFromGitHubRepo$1(repoFullName);
@@ -121934,7 +122342,9 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
121934
122342
  repoId,
121935
122343
  logger: this.logger
121936
122344
  });
121937
- await worktreeManager.removeWorktree(sessionId, true, branchName);
122345
+ await worktreeManager.removeWorktree(sessionId, true, branchName, {
122346
+ baseBranchName
122347
+ });
121938
122348
  } catch (error2) {
121939
122349
  this.logger.debug(`[${sessionId}] Failed to remove worktree: ${formatErrorMessage(error2)}`);
121940
122350
  }
@@ -122286,6 +122696,17 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122286
122696
  this.store.beginTurn(sessionId, turnId);
122287
122697
  return turnId;
122288
122698
  }
122699
+ async registerMachineAccess() {
122700
+ try {
122701
+ await registerMachineAccessForCliToken({
122702
+ token: this.token,
122703
+ workspaceId: this.workspaceId,
122704
+ machineId: this.machineId
122705
+ });
122706
+ } catch (error2) {
122707
+ this.logger.error(`Failed to register machine access: ${formatErrorMessage(error2)}`);
122708
+ }
122709
+ }
122289
122710
  registerMachine() {
122290
122711
  void (async () => {
122291
122712
  const supportsStreamsRpc = !!this.machineRpcServer;
@@ -122310,6 +122731,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122310
122731
  lastSeen: machineMeta?.lastSeen,
122311
122732
  raceLimits: machineMeta?.raceLimits ?? {}
122312
122733
  });
122734
+ await this.registerMachineAccess();
122313
122735
  this.startMachineHeartbeat();
122314
122736
  this.logger.debug(`Machine registered with name: ${this.machineName}`);
122315
122737
  })().catch((error2) => {
@@ -122686,6 +123108,22 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122686
123108
  cpuUsagePercent
122687
123109
  };
122688
123110
  }
123111
+ tryAutoApprovePermission(sessionId, request) {
123112
+ const state2 = this.store.get(sessionId);
123113
+ if (!state2.autoApprovePermissions) {
123114
+ return null;
123115
+ }
123116
+ const decision = selectAutoApprovePermissionDecision(request.options);
123117
+ if (!decision) {
123118
+ this.logger.debug(`[${sessionId}] Full Access auto-approval skipped because no allow or fallback-safe option was available`);
123119
+ return null;
123120
+ }
123121
+ if (decision.reason === "fallback") {
123122
+ const kind = decision.option.kind;
123123
+ this.logger.warn(`[${sessionId}] Full Access auto-approval selected a non-standard permission option (optionId=${decision.option.optionId}, kind=${String(kind)})`);
123124
+ }
123125
+ return decision.outcome;
123126
+ }
122689
123127
  async handleAgentPermissionRequest(sessionId, requestId, request, model) {
122690
123128
  const toolTitle = request.toolCall.title?.trim();
122691
123129
  const toolKind = typeof request.toolCall.kind === "string" ? request.toolCall.kind.trim() : void 0;
@@ -122696,10 +123134,10 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122696
123134
  let sessionTitle;
122697
123135
  let metaUserId;
122698
123136
  let historyUserId;
123137
+ let permissionRequestPersisted = false;
122699
123138
  try {
122700
- await ensurePermissionRequestOnToolCall(doc, requestId, request, model);
123139
+ permissionRequestPersisted = await ensurePermissionRequestOnToolCall(doc, requestId, request, model);
122701
123140
  await doc.setLastMessageAt();
122702
- await doc.setStatus(SessionStatusFactory.requestPermission(requestId, request.toolCall.toolCallId, request.toolCall.title ?? void 0));
122703
123141
  const meta = await doc.getMetaState();
122704
123142
  sessionTitle = meta?.title;
122705
123143
  metaUserId = meta?.userId;
@@ -122716,6 +123154,31 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
122716
123154
  } catch (error2) {
122717
123155
  this.logger.error(`[${sessionId}] Failed to append permission request to history: ${formatErrorMessage(error2)}`);
122718
123156
  }
123157
+ const autoApproveOutcome = this.tryAutoApprovePermission(sessionId, request);
123158
+ if (autoApproveOutcome) {
123159
+ try {
123160
+ await updatePermissionOutcomeInHistory(doc, requestId, autoApproveOutcome, this.logger);
123161
+ } catch (error2) {
123162
+ this.logger.error(`[${sessionId}] Failed to persist auto-approved permission outcome: ${formatErrorMessage(error2)}`);
123163
+ }
123164
+ this.logger.info(`[${sessionId}] Permission auto-approved by Full Access for ${permissionLabel}: optionId=${autoApproveOutcome.outcome === "selected" ? autoApproveOutcome.optionId : "cancelled"}`);
123165
+ return {
123166
+ outcome: autoApproveOutcome
123167
+ };
123168
+ }
123169
+ if (!permissionRequestPersisted) {
123170
+ this.logger.warn(`[${sessionId}] Permission request ${requestId} for tool call ${request.toolCall.toolCallId} could not be attached to an active assistant entry; cancelling to avoid waiting for an unobservable permission outcome`);
123171
+ return {
123172
+ outcome: {
123173
+ outcome: "cancelled"
123174
+ }
123175
+ };
123176
+ }
123177
+ try {
123178
+ await doc.setStatus(SessionStatusFactory.requestPermission(requestId, request.toolCall.toolCallId, request.toolCall.title ?? void 0));
123179
+ } catch (error2) {
123180
+ this.logger.error(`[${sessionId}] Failed to mark session as waiting for permission: ${formatErrorMessage(error2)}`);
123181
+ }
122719
123182
  if (this.notificationService) {
122720
123183
  const workspaceSlug = this.workspaceSlug?.trim() || this.workspaceId;
122721
123184
  const userId = historyUserId ?? metaUserId ?? this.userId;
@@ -133469,7 +133932,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
133469
133932
  ];
133470
133933
  }
133471
133934
  return previous.concat(value);
133472
- }).option("--debug", "enable debug output").option("--heartbeat-log", "output current timestamp every 5 seconds").action(async (options) => {
133935
+ }).option("--debug", "enable debug output").option("--heartbeat-log", "output current timestamp every 5 seconds").option("--auth <api-key>", "Use a CLI API key for non-interactive authentication").action(async (options) => {
133473
133936
  createHybridLogger({
133474
133937
  level: options.debug ? "debug" : "info"
133475
133938
  });
@@ -133497,6 +133960,7 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
133497
133960
  runtimeStateReporter.setStartupStage("bootstrap");
133498
133961
  const electronBootstrapEnabled = process.env[ELECTRON_BOOTSTRAP_ENV] === "1";
133499
133962
  const electronSessionToken = process.env[ELECTRON_SESSION_TOKEN_ENV]?.trim();
133963
+ const providedAuth = options.auth?.trim();
133500
133964
  const cliAvailability = {
133501
133965
  claude: checkClaude(),
133502
133966
  codex: checkCodex()
@@ -133543,7 +134007,25 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
133543
134007
  let machineId;
133544
134008
  let machineName;
133545
134009
  const existingAuth = authClient.getAuthInfo();
133546
- if (existingAuth) {
134010
+ if (options.auth !== void 0 && !providedAuth) {
134011
+ logger2.error("Missing API key for --auth.");
134012
+ process.exit(1);
134013
+ }
134014
+ if (providedAuth) {
134015
+ const loginResult = await performLoginWithApiKey(authClient, logger2, {
134016
+ apiKey: providedAuth,
134017
+ machineName: defaultMachineName
134018
+ });
134019
+ if (!loginResult.success) {
134020
+ logger2.error(`Login failed: ${loginResult.error}`);
134021
+ process.exit(1);
134022
+ }
134023
+ token2 = loginResult.token;
134024
+ userId = loginResult.user.id;
134025
+ machineId = loginResult.machine.machineId;
134026
+ machineName = loginResult.machine.machineName;
134027
+ logger2.success("Login successful via --auth.");
134028
+ } else if (existingAuth) {
133547
134029
  logger2.debug("Found existing authentication, checking validity...");
133548
134030
  const validation2 = await authClient.validateToken(existingAuth.token);
133549
134031
  if (validation2.valid) {
@@ -133703,13 +134185,36 @@ Received ${signal}, shutting down gracefully...` : "\nShutting down gracefully..
133703
134185
  throw error2;
133704
134186
  }
133705
134187
  }
133706
- const loginCommand = new Command("login").description("Login to Lody using device authorization flow").option("-d, --debug", "enable debug output").option("--machine-name <name>", "Machine name to register (defaults to hostname)").action(async (options) => {
134188
+ const loginCommand = new Command("login").description("Login to Lody using device authorization flow or an API key").option("-d, --debug", "enable debug output").option("--machine-name <name>", "Machine name to register (defaults to hostname)").option("--auth <api-key>", "Use a CLI API key for non-interactive login").action(async (options) => {
133707
134189
  if (options.debug) {
133708
134190
  rootLogger.setDebug(true);
133709
134191
  }
133710
134192
  const logger2 = getLogger("login");
133711
134193
  const authClient = new AuthClient(logger2);
134194
+ const providedAuth = options.auth?.trim();
133712
134195
  try {
134196
+ if (options.auth !== void 0 && !providedAuth) {
134197
+ logger2.error("Missing API key for --auth.");
134198
+ process.exit(1);
134199
+ }
134200
+ if (providedAuth) {
134201
+ const result2 = await performLoginWithApiKey(authClient, logger2, {
134202
+ apiKey: providedAuth,
134203
+ machineName: options.machineName
134204
+ });
134205
+ if (result2.success) {
134206
+ logger2.success("\n" + chalk.green("\u2713") + " Successfully logged in!");
134207
+ logger2.info(" User: " + chalk.cyan(result2.user.name || result2.user.email));
134208
+ logger2.info(" Email: " + chalk.cyan(result2.user.email));
134209
+ } else {
134210
+ await reportError("login", result2.error, {
134211
+ message: "Login failed using --auth",
134212
+ logger: logger2
134213
+ });
134214
+ process.exit(1);
134215
+ }
134216
+ return;
134217
+ }
133713
134218
  const existingAuth = authClient.getAuthInfo();
133714
134219
  if (existingAuth) {
133715
134220
  logger2.debug("Found existing authentication, checking validity...");
@@ -159300,84 +159805,6 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
159300
159805
  restoreDefaultPrompts,
159301
159806
  Separator
159302
159807
  };
159303
- const WorkspaceSummarySchema = object({
159304
- id: string$2(),
159305
- name: string$2(),
159306
- slug: string$2().nullable(),
159307
- role: string$2()
159308
- });
159309
- const WorkspaceGitHubRepositorySchema = object({
159310
- id: number$3(),
159311
- name: string$2(),
159312
- fullName: string$2(),
159313
- private: boolean()
159314
- });
159315
- const WorkspaceListResultSchema = discriminatedUnion("valid", [
159316
- object({
159317
- valid: literal(false),
159318
- userId: _null(),
159319
- workspaces: array$3(WorkspaceSummarySchema)
159320
- }),
159321
- object({
159322
- valid: literal(true),
159323
- userId: string$2(),
159324
- workspaces: array$3(WorkspaceSummarySchema)
159325
- })
159326
- ]);
159327
- const RegisterSessionOwnerResultSchema = object({
159328
- success: literal(true),
159329
- existing: boolean()
159330
- });
159331
- const WorkspaceGitHubRepositoryListResultSchema = discriminatedUnion("valid", [
159332
- object({
159333
- valid: literal(false),
159334
- repositories: array$3(WorkspaceGitHubRepositorySchema)
159335
- }),
159336
- object({
159337
- valid: literal(true),
159338
- repositories: array$3(WorkspaceGitHubRepositorySchema)
159339
- })
159340
- ]);
159341
- function createAuthConvexClient() {
159342
- if (!LODY_AUTH_URL) {
159343
- throw new Error("LODY_AUTH_URL is not defined.");
159344
- }
159345
- return new ConvexHttpClient(LODY_AUTH_URL);
159346
- }
159347
- async function listWorkspacesForToken(token2) {
159348
- const client = createAuthConvexClient();
159349
- const raw = await client.query(api.deviceAuth.listMyWorkspacesForCliToken, {
159350
- token: token2
159351
- });
159352
- const parsed = WorkspaceListResultSchema.parse(raw);
159353
- if (!parsed.valid) {
159354
- throw new Error("CLI token is invalid or expired. Run `lody login` again.");
159355
- }
159356
- return parsed.workspaces;
159357
- }
159358
- async function listWorkspaceGitHubRepositoriesForCliToken(input2) {
159359
- const client = createAuthConvexClient();
159360
- const raw = await client.query(api.github.listWorkspaceRepositoriesForCliToken, {
159361
- cliToken: input2.token,
159362
- workspaceId: input2.workspaceId
159363
- });
159364
- const parsed = WorkspaceGitHubRepositoryListResultSchema.parse(raw);
159365
- if (!parsed.valid) {
159366
- throw new Error("CLI token is invalid or expired. Run `lody login` again.");
159367
- }
159368
- return parsed.repositories;
159369
- }
159370
- async function registerSessionOwnerForCliToken(input2) {
159371
- const client = createAuthConvexClient();
159372
- const raw = await client.mutation(api.sessions.registerSessionOwnerFromCliToken, {
159373
- cliToken: input2.token,
159374
- workspaceId: input2.workspaceId,
159375
- sessionId: input2.sessionId,
159376
- cliType: input2.cliType,
159377
- repoFullName: input2.repoFullName
159378
- });
159379
- return RegisterSessionOwnerResultSchema.parse(raw);
159380
- }
159381
159808
  const SESSION_CONTROL_PATH = "/session-control";
159382
159809
  const LOCAL_CONTROL_HEADER = "x-lody-local-control";
159383
159810
  const DEFAULT_LOCAL_CONTROL_TIMEOUT_MS = 3e4;
@@ -163250,7 +163677,7 @@ ${entry2.text}`).join("\n\n");
163250
163677
  }
163251
163678
  function resolveBuiltinFullAccessModeId(cliType, agentType) {
163252
163679
  if (cliType !== "builtin") {
163253
- return void 0;
163680
+ return LODY_FULL_ACCESS_MODE_ID;
163254
163681
  }
163255
163682
  if (agentType === "claude") {
163256
163683
  return "bypassPermissions";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.43.1",
3
+ "version": "0.44.0",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "node": ">=18.0.0"
21
21
  },
22
22
  "optionalDependencies": {
23
- "acp-extension-claude": "0.25.0",
23
+ "@agentclientprotocol/claude-agent-acp": "0.29.0",
24
24
  "acp-extension-codex": "0.11.1"
25
25
  },
26
26
  "devDependencies": {
@@ -56,7 +56,7 @@
56
56
  "jiti": "^2.6.1",
57
57
  "loro-crdt": "^1.11.0",
58
58
  "loro-mirror": "1.2.1",
59
- "loro-repo": "^0.16.4",
59
+ "loro-repo": "^0.16.5",
60
60
  "ora": "^8.2.0",
61
61
  "prettier": "^3.6.2",
62
62
  "proxy-from-env": "^1.1.0",
@@ -72,11 +72,11 @@
72
72
  "winston-transport": "^4.7.1",
73
73
  "ws": "^8.18.3",
74
74
  "zod": "^4.1.5",
75
+ "@lody/cli-supervisor": "0.0.1",
75
76
  "@lody/convex": "0.0.1",
76
77
  "@lody/loro-streams-rpc": "0.0.1",
77
78
  "@lody/shared": "0.0.1",
78
- "loro-code": "0.0.1",
79
- "@lody/cli-supervisor": "0.0.1"
79
+ "loro-code": "0.0.1"
80
80
  },
81
81
  "files": [
82
82
  "dist",