@suzuke/agend 1.0.2 → 1.1.1

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.
@@ -1,6 +1,8 @@
1
- import type { CliBackend, CliBackendConfig } from "./types.js";
1
+ import { type CliBackend, type CliBackendConfig } from "./types.js";
2
2
  export declare class ClaudeCodeBackend implements CliBackend {
3
3
  private instanceDir;
4
+ readonly binaryName = "claude";
5
+ private binaryPath;
4
6
  constructor(instanceDir: string);
5
7
  buildCommand(config: CliBackendConfig): string;
6
8
  writeConfig(config: CliBackendConfig): void;
@@ -9,5 +11,7 @@ export declare class ClaudeCodeBackend implements CliBackend {
9
11
  cleanup(_config: CliBackendConfig): void;
10
12
  /** Pre-approve ANTHROPIC_API_KEY in ~/.claude.json to skip the interactive prompt */
11
13
  private preApproveApiKey;
14
+ /** Check if user has an active OAuth session in ~/.claude.json */
15
+ private hasOAuthSession;
12
16
  private writeStatusLineScript;
13
17
  }
@@ -1,21 +1,25 @@
1
1
  import { join } from "node:path";
2
2
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
+ import { resolveBinary } from "./types.js";
4
5
  export class ClaudeCodeBackend {
5
6
  instanceDir;
7
+ binaryName = "claude";
8
+ binaryPath;
6
9
  constructor(instanceDir) {
7
10
  this.instanceDir = instanceDir;
11
+ this.binaryPath = resolveBinary("claude");
8
12
  }
9
13
  buildCommand(config) {
10
14
  const settingsPath = join(this.instanceDir, "claude-settings.json");
11
15
  const mcpConfigPath = join(this.instanceDir, "mcp-config.json");
12
- // Forward Anthropic env vars to the CLI process (tmux shell doesn't inherit daemon's env)
13
16
  const envPrefix = ["CMUX_CLAUDE_HOOKS_DISABLED=1", "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"];
14
- for (const key of ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY"]) {
15
- if (process.env[key])
16
- envPrefix.push(`${key}=${process.env[key]}`);
17
+ if (process.env.ANTHROPIC_BASE_URL)
18
+ envPrefix.push(`ANTHROPIC_BASE_URL=${process.env.ANTHROPIC_BASE_URL}`);
19
+ if (process.env.ANTHROPIC_API_KEY && !this.hasOAuthSession()) {
20
+ envPrefix.push(`ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY}`);
17
21
  }
18
- let cmd = `${envPrefix.join(" ")} claude --settings ${settingsPath} --mcp-config ${mcpConfigPath} --dangerously-skip-permissions`;
22
+ let cmd = `${envPrefix.join(" ")} ${this.binaryPath} --settings ${settingsPath} --mcp-config ${mcpConfigPath} --dangerously-skip-permissions`;
19
23
  const sessionIdFile = join(this.instanceDir, "session-id");
20
24
  if (existsSync(sessionIdFile)) {
21
25
  const sid = readFileSync(sessionIdFile, "utf-8").trim();
@@ -96,6 +100,16 @@ export class ClaudeCodeBackend {
96
100
  writeFileSync(claudeJsonPath, JSON.stringify(claudeCfg, null, 2));
97
101
  }
98
102
  }
103
+ /** Check if user has an active OAuth session in ~/.claude.json */
104
+ hasOAuthSession() {
105
+ try {
106
+ const cfg = JSON.parse(readFileSync(join(homedir(), ".claude.json"), "utf-8"));
107
+ return !!cfg.oauthAccount?.accountUuid;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ }
99
113
  writeStatusLineScript() {
100
114
  const statusFile = join(this.instanceDir, "statusline.json");
101
115
  // Use a Node.js script instead of bash to avoid shell injection via statusFile path
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/backend/claude-code.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/backend/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAA0C,aAAa,EAAE,MAAM,YAAY,CAAC;AAGnF,MAAM,OAAO,iBAAiB;IAIR;IAHX,UAAU,GAAG,QAAQ,CAAC;IACvB,UAAU,CAAS;IAE3B,YAAoB,WAAmB;QAAnB,gBAAW,GAAX,WAAW,CAAQ;QACrC,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAED,YAAY,CAAC,MAAwB;QACnC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,sBAAsB,CAAC,CAAC;QACpE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,CAAC,8BAA8B,EAAE,wCAAwC,CAAC,CAAC;QAC7F,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;YAAE,SAAS,CAAC,IAAI,CAAC,sBAAsB,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;QAC3G,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAC7D,SAAS,CAAC,IAAI,CAAC,qBAAqB,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,IAAI,GAAG,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,eAAe,YAAY,iBAAiB,aAAa,iCAAiC,CAAC;QAE9I,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC3D,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YACxD,IAAI,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,GAAG,IAAI,aAAa,GAAG,EAAE,CAAC;QACrE,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,GAAG,IAAI,YAAY,MAAM,CAAC,KAAK,EAAE,CAAC;QACpC,CAAC;QAED,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;YAC9D,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;YAC/C,GAAG,IAAI,qBAAqB,UAAU,GAAG,CAAC;QAC5C,CAAC;QAED,OAAO,GAAG,CAAC;IACb,CAAC;IAED,WAAW,CAAC,MAAwB;QAClC,qEAAqE;QACrE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;QACpD,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjE,6BAA6B;QAC7B,MAAM,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAEvD,wFAAwF;QACxF,MAAM,QAAQ,GAA4B;YACxC,UAAU,EAAE;gBACV,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,iBAAiB;aAC3B;SACF,CAAC;QACF,aAAa,CACX,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,sBAAsB,CAAC,EAC9C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;QAEF,+DAA+D;QAC/D,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,eAAe;QACb,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,cAAc,EAAE,eAAe,IAAI,IAAI,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,4EAA4E;YAC5E,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,YAAY;QACV,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAyB;QAC/B,0EAA0E;IAC5E,CAAC;IAED,qFAAqF;IAC7E,gBAAgB,CAAC,OAAyB;QAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC7C,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACpE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;QAEvD,IAAI,SAAS,GAA4B,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC,CAAC,6BAA6B,CAAC,CAAC;QAEzC,MAAM,QAAQ,GAAG,SAAS,CAAC,qBAAiF,CAAC;QAC7G,MAAM,QAAQ,GAAG,QAAQ,EAAE,QAAQ,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,SAAS,CAAC,qBAAqB,GAAG;gBAChC,QAAQ,EAAE,CAAC,GAAG,QAAQ,EAAE,WAAW,CAAC;gBACpC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE;aACnC,CAAC;YACF,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,kEAAkE;IAC1D,eAAe;QACrB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YAC/E,OAAO,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,qBAAqB;QAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAC7D,oFAAoF;QACpF,MAAM,MAAM,GAAG;YACb,qBAAqB;YACrB,2BAA2B;YAC3B,iBAAiB;YACjB,4CAA4C;YAC5C,oDAAoD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,kCAAkC;SACjH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAC3D,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnD,OAAO,UAAU,CAAC;IACpB,CAAC;CACF"}
@@ -1,6 +1,8 @@
1
- import type { CliBackend, CliBackendConfig } from "./types.js";
1
+ import { type CliBackend, type CliBackendConfig } from "./types.js";
2
2
  export declare class CodexBackend implements CliBackend {
3
3
  private instanceDir;
4
+ readonly binaryName = "codex";
5
+ private binaryPath;
4
6
  constructor(instanceDir: string);
5
7
  buildCommand(config: CliBackendConfig): string;
6
8
  writeConfig(config: CliBackendConfig): void;
@@ -1,12 +1,16 @@
1
1
  import { join } from "node:path";
2
2
  import { readFileSync, writeFileSync } from "node:fs";
3
+ import { resolveBinary } from "./types.js";
3
4
  export class CodexBackend {
4
5
  instanceDir;
6
+ binaryName = "codex";
7
+ binaryPath;
5
8
  constructor(instanceDir) {
6
9
  this.instanceDir = instanceDir;
10
+ this.binaryPath = resolveBinary("codex");
7
11
  }
8
12
  buildCommand(config) {
9
- let cmd = "codex --full-auto";
13
+ let cmd = `${this.binaryPath} --full-auto`;
10
14
  if (config.model) {
11
15
  cmd += ` -c model="${config.model}"`;
12
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/backend/codex.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/backend/codex.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
- import type { CliBackend, CliBackendConfig } from "./types.js";
1
+ import { type CliBackend, type CliBackendConfig } from "./types.js";
2
2
  export declare class GeminiCliBackend implements CliBackend {
3
3
  private instanceDir;
4
+ readonly binaryName = "gemini";
5
+ private binaryPath;
4
6
  constructor(instanceDir: string);
5
7
  buildCommand(config: CliBackendConfig): string;
6
8
  writeConfig(config: CliBackendConfig): void;
@@ -1,12 +1,16 @@
1
1
  import { join } from "node:path";
2
2
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
3
+ import { resolveBinary } from "./types.js";
3
4
  export class GeminiCliBackend {
4
5
  instanceDir;
6
+ binaryName = "gemini";
7
+ binaryPath;
5
8
  constructor(instanceDir) {
6
9
  this.instanceDir = instanceDir;
10
+ this.binaryPath = resolveBinary("gemini");
7
11
  }
8
12
  buildCommand(config) {
9
- let cmd = "gemini --yolo";
13
+ let cmd = `${this.binaryPath} --yolo`;
10
14
  const sessionIdFile = join(this.instanceDir, "session-id");
11
15
  if (existsSync(sessionIdFile)) {
12
16
  const sid = readFileSync(sessionIdFile, "utf-8").trim();
@@ -1 +1 @@
1
- {"version":3,"file":"gemini-cli.js","sourceRoot":"","sources":["../../src/backend/gemini-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAG7E,MAAM,OAAO,gBAAgB;IACP;IAApB,YAAoB,WAAmB;QAAnB,gBAAW,GAAX,WAAW,CAAQ;IAAG,CAAC;IAE3C,YAAY,CAAC,MAAwB;QACnC,IAAI,GAAG,GAAG,eAAe,CAAC;QAE1B,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC3D,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YACxD,IAAI,GAAG;gBAAE,GAAG,IAAI,aAAa,GAAG,EAAE,CAAC;QACrC,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,GAAG,IAAI,YAAY,MAAM,CAAC,KAAK,EAAE,CAAC;QACpC,CAAC;QAED,OAAO,GAAG,CAAC;IACb,CAAC;IAED,WAAW,CAAC,MAAwB;QAClC,oDAAoD;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;QAC3D,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;QACtD,IAAI,QAAQ,GAA4B,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAE1B,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACxC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAE/D,8BAA8B;QAC9B,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,eAAe;QACb,qDAAqD;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY;QACV,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC/C,OAAO,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;IAC1B,CAAC;IAED,OAAO,CAAC,MAAwB;QAC9B,6CAA6C;QAC7C,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;YAC/E,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;gBACjE,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;oBACxB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;wBAClD,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;oBACnC,CAAC;oBACD,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC/B,CAAC;CACF"}
1
+ {"version":3,"file":"gemini-cli.js","sourceRoot":"","sources":["../../src/backend/gemini-cli.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
- import type { CliBackend, CliBackendConfig } from "./types.js";
1
+ import { type CliBackend, type CliBackendConfig } from "./types.js";
2
2
  export declare class OpenCodeBackend implements CliBackend {
3
3
  private instanceDir;
4
+ readonly binaryName = "opencode";
5
+ private binaryPath;
4
6
  constructor(instanceDir: string);
5
7
  buildCommand(_config: CliBackendConfig): string;
6
8
  writeConfig(config: CliBackendConfig): void;
@@ -1,12 +1,16 @@
1
1
  import { join } from "node:path";
2
2
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { resolveBinary } from "./types.js";
3
4
  export class OpenCodeBackend {
4
5
  instanceDir;
6
+ binaryName = "opencode";
7
+ binaryPath;
5
8
  constructor(instanceDir) {
6
9
  this.instanceDir = instanceDir;
10
+ this.binaryPath = resolveBinary("opencode");
7
11
  }
8
12
  buildCommand(_config) {
9
- return "opencode";
13
+ return this.binaryPath;
10
14
  }
11
15
  writeConfig(config) {
12
16
  // OpenCode uses opencode.json in the working directory
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/backend/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlE,MAAM,OAAO,eAAe;IACN;IAApB,YAAoB,WAAmB;QAAnB,gBAAW,GAAX,WAAW,CAAQ;IAAG,CAAC;IAE3C,YAAY,CAAC,OAAyB;QACpC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,MAAwB;QAClC,uDAAuD;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;QAClE,IAAI,EAAE,GAA4B,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAE1B,cAAc;QACd,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7D,EAAE,CAAC,GAA+B,CAAC,IAAI,CAAC,GAAG;gBAC1C,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;gBACvC,GAAG,EAAE,KAAK,CAAC,GAAG;aACf,CAAC;QACJ,CAAC;QAED,iCAAiC;QACjC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,EAAE,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACxC,CAAC;QAED,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY;QACV,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC/C,OAAO,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;IAC1B,CAAC;IAED,OAAO,CAAC,MAAwB;QAC9B,qCAAqC;QACrC,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;YAClE,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;gBACzD,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC;oBACX,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;wBAClD,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACtB,CAAC;oBACD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC/B,CAAC;CACF"}
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/backend/opencode.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,8 @@ export interface CliBackendConfig {
13
13
  model?: string;
14
14
  }
15
15
  export interface CliBackend {
16
+ /** The CLI binary name (e.g. "claude", "gemini", "codex") */
17
+ readonly binaryName: string;
16
18
  /** Build the shell command string to launch the CLI in a tmux window. */
17
19
  buildCommand(config: CliBackendConfig): string;
18
20
  /** Write all config files the CLI needs before launch. */
@@ -24,3 +26,9 @@ export interface CliBackend {
24
26
  /** Clean up config files on shutdown. */
25
27
  cleanup?(config: CliBackendConfig): void;
26
28
  }
29
+ /**
30
+ * Resolve the full path to a CLI binary.
31
+ * tmux new-window runs commands in a minimal shell without user PATH,
32
+ * so we resolve at daemon startup time when the full PATH is available.
33
+ */
34
+ export declare function resolveBinary(name: string): string;
@@ -1,2 +1,15 @@
1
- export {};
1
+ import { execFileSync } from "node:child_process";
2
+ /**
3
+ * Resolve the full path to a CLI binary.
4
+ * tmux new-window runs commands in a minimal shell without user PATH,
5
+ * so we resolve at daemon startup time when the full PATH is available.
6
+ */
7
+ export function resolveBinary(name) {
8
+ try {
9
+ return execFileSync("which", [name], { encoding: "utf-8" }).trim();
10
+ }
11
+ catch {
12
+ return name; // fallback to bare name
13
+ }
14
+ }
2
15
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAsClD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,wBAAwB;IACvC,CAAC;AACH,CAAC"}
package/dist/daemon.d.ts CHANGED
@@ -3,12 +3,14 @@ import type { InstanceConfig, RotationSnapshot } from "./types.js";
3
3
  import { MessageBus } from "./channel/message-bus.js";
4
4
  import type { CliBackend } from "./backend/types.js";
5
5
  import { HangDetector } from "./hang-detector.js";
6
+ import type { TmuxControlClient } from "./tmux-control.js";
6
7
  export declare class Daemon extends EventEmitter {
7
8
  private name;
8
9
  private config;
9
10
  private instanceDir;
10
11
  private topicMode;
11
12
  private backend?;
13
+ private controlClient?;
12
14
  private logger;
13
15
  private tmux;
14
16
  private ipcServer;
@@ -39,7 +41,8 @@ export declare class Daemon extends EventEmitter {
39
41
  private recentUserMessages;
40
42
  private recentEvents;
41
43
  private recentToolActivity;
42
- constructor(name: string, config: InstanceConfig, instanceDir: string, topicMode?: boolean, backend?: CliBackend | undefined);
44
+ private pasteLock;
45
+ constructor(name: string, config: InstanceConfig, instanceDir: string, topicMode?: boolean, backend?: CliBackend | undefined, controlClient?: TmuxControlClient | undefined);
43
46
  start(): Promise<void>;
44
47
  private startHealthCheck;
45
48
  stop(): Promise<void>;
@@ -58,6 +61,9 @@ export declare class Daemon extends EventEmitter {
58
61
  * Otherwise send to the instance's own session (this.name).
59
62
  */
60
63
  pushChannelMessage(content: string, meta: Record<string, string>, _targetSession?: string): void;
64
+ /** Deliver a single message: wait for idle, then paste */
65
+ private deliverMessage;
66
+ private getWindowId;
61
67
  /** Find the IPC socket for a given sessionName */
62
68
  private findSocketBySession;
63
69
  /**
@@ -69,8 +75,20 @@ export declare class Daemon extends EventEmitter {
69
75
  private buildBackendConfig;
70
76
  /** Combine fleet context with user-configured system prompt + previous session snapshot */
71
77
  private buildSystemPrompt;
72
- /** Spawn (or respawn) a Claude window in tmux */
78
+ /** Spawn (or respawn) a CLI window in tmux */
73
79
  private spawnClaudeWindow;
80
+ /**
81
+ * Spawn a CLI window and verify it reaches a ready state.
82
+ * Uses control mode to wait for output, then checks pane content.
83
+ * Handles confirmation dialogs (trust folder, bypass permissions).
84
+ * Returns true if CLI is ready, false if it failed or got stuck.
85
+ */
86
+ private trySpawn;
87
+ /**
88
+ * Repeatedly check pane content, dismiss any confirmation dialogs,
89
+ * and return true once CLI reaches a ready prompt.
90
+ */
91
+ private dismissDialogsUntilReady;
74
92
  private saveSessionId;
75
93
  private readContextPercentage;
76
94
  /** Set a model override for next spawn (used by failover logic) */
package/dist/daemon.js CHANGED
@@ -19,6 +19,7 @@ export class Daemon extends EventEmitter {
19
19
  instanceDir;
20
20
  topicMode;
21
21
  backend;
22
+ controlClient;
22
23
  logger;
23
24
  tmux = null;
24
25
  ipcServer = null;
@@ -57,13 +58,15 @@ export class Daemon extends EventEmitter {
57
58
  recentUserMessages = [];
58
59
  recentEvents = [];
59
60
  recentToolActivity = [];
60
- constructor(name, config, instanceDir, topicMode = false, backend) {
61
+ pasteLock = Promise.resolve();
62
+ constructor(name, config, instanceDir, topicMode = false, backend, controlClient) {
61
63
  super();
62
64
  this.name = name;
63
65
  this.config = config;
64
66
  this.instanceDir = instanceDir;
65
67
  this.topicMode = topicMode;
66
68
  this.backend = backend;
69
+ this.controlClient = controlClient;
67
70
  this.logger = createLogger(config.log_level);
68
71
  this.messageBus = new MessageBus();
69
72
  this.messageBus.setLogger(this.logger);
@@ -472,15 +475,31 @@ export class Daemon extends EventEmitter {
472
475
  const threadId = meta.thread_id || "";
473
476
  formatted = `[user:${user} chat_id:${chatId} thread_id:${threadId}] ${content}\n(Reply using the reply tool with chat_id="${chatId}" — do NOT respond with direct text)`;
474
477
  }
475
- this.tmux.pasteText(formatted).catch(async (err) => {
478
+ // Serialize deliveries: each message waits for the previous to complete,
479
+ // and each waits for the CLI to be idle before pasting.
480
+ this.pasteLock = this.pasteLock.then(() => this.deliverMessage(formatted));
481
+ this.logger.debug({ user: meta.user, text: content.slice(0, 100) }, "Queued channel message for delivery");
482
+ }
483
+ /** Deliver a single message: wait for idle, then paste */
484
+ async deliverMessage(formatted) {
485
+ const windowId = this.getWindowId();
486
+ if (windowId && this.controlClient) {
487
+ const idle = await this.controlClient.waitForIdle(windowId);
488
+ if (!idle) {
489
+ this.logger.warn("Delivering message after idle timeout (CLI may be busy)");
490
+ }
491
+ }
492
+ const ok = await this.tmux.pasteText(formatted);
493
+ if (!ok) {
476
494
  // Window ID may be stale after crash/respawn — try to find by name
477
- this.logger.warn({ err }, "pasteText failed, looking up window by name");
495
+ this.logger.warn("pasteText failed, looking up window by name");
478
496
  try {
479
497
  const windows = await TmuxManager.listWindows("agend");
480
498
  const match = windows.find(w => w.name === this.name);
481
499
  if (match) {
482
500
  this.tmux = new TmuxManager("agend", match.id);
483
501
  writeFileSync(join(this.instanceDir, "window-id"), match.id);
502
+ await this.controlClient?.registerWindow(match.id);
484
503
  await this.tmux.pasteText(formatted);
485
504
  this.logger.info({ windowId: match.id }, "Recovered window ID and delivered message");
486
505
  }
@@ -488,8 +507,15 @@ export class Daemon extends EventEmitter {
488
507
  catch (retryErr) {
489
508
  this.logger.error({ err: retryErr }, "Failed to recover window for message delivery");
490
509
  }
491
- });
492
- this.logger.debug({ user: meta.user, text: content.slice(0, 100) }, "Pushed channel message via tmux");
510
+ }
511
+ }
512
+ getWindowId() {
513
+ try {
514
+ return readFileSync(join(this.instanceDir, "window-id"), "utf-8").trim() || undefined;
515
+ }
516
+ catch {
517
+ return undefined;
518
+ }
493
519
  }
494
520
  /** Find the IPC socket for a given sessionName */
495
521
  findSocketBySession(sessionName) {
@@ -636,37 +662,110 @@ export class Daemon extends EventEmitter {
636
662
  }
637
663
  return prompt;
638
664
  }
639
- /** Spawn (or respawn) a Claude window in tmux */
665
+ /** Spawn (or respawn) a CLI window in tmux */
640
666
  async spawnClaudeWindow() {
641
667
  this.spawning = true;
642
668
  try {
643
- // Clear tool status from previous session
644
669
  this.toolStatusLines = [];
645
670
  this.toolStatusMessageId = null;
646
671
  if (!this.backend) {
647
- throw new Error("No backend configured — cannot spawn Claude window");
672
+ throw new Error("No backend configured — cannot spawn CLI window");
648
673
  }
649
- const backendConfig = this.buildBackendConfig();
650
- this.backend.writeConfig(backendConfig);
651
- // Inject AGEND_INSTANCE_NAME via shell env (not .mcp.json) so internal sessions
652
- // are distinguishable from external sessions sharing the same .mcp.json
653
- let claudeCmd = `AGEND_INSTANCE_NAME=${this.name} ` + this.backend.buildCommand(backendConfig);
654
- const windowId = await this.tmux.createWindow(claudeCmd, this.config.working_directory, this.name);
655
- const windowIdFile = join(this.instanceDir, "window-id");
656
- writeFileSync(windowIdFile, windowId);
657
- // Fixed grace period — smart detection was unreliable across different CLIs.
658
- // Wait for CLI to fully initialize, then press Enter to dismiss any prompts.
659
- await new Promise(r => setTimeout(r, 10_000));
660
- try {
661
- await this.tmux.sendSpecialKey("Enter");
674
+ const alive = await this.trySpawn();
675
+ if (!alive) {
676
+ // First attempt failed (stale --resume, crash, rate limit, etc.)
677
+ // Clean slate: clear session-id and retry once.
678
+ this.logger.warn("CLI startup failed clearing session-id and retrying");
679
+ const sidFile = join(this.instanceDir, "session-id");
680
+ try {
681
+ unlinkSync(sidFile);
682
+ }
683
+ catch { /* may not exist */ }
684
+ await this.tmux.killWindow();
685
+ const retryAlive = await this.trySpawn();
686
+ if (!retryAlive) {
687
+ await this.tmux.killWindow();
688
+ throw new Error("CLI failed to start after retry");
689
+ }
662
690
  }
663
- catch { /* window may have exited */ }
664
691
  this.lastSpawnAt = Date.now();
665
692
  }
666
693
  finally {
667
694
  this.spawning = false;
668
695
  }
669
696
  }
697
+ /**
698
+ * Spawn a CLI window and verify it reaches a ready state.
699
+ * Uses control mode to wait for output, then checks pane content.
700
+ * Handles confirmation dialogs (trust folder, bypass permissions).
701
+ * Returns true if CLI is ready, false if it failed or got stuck.
702
+ */
703
+ async trySpawn() {
704
+ const backendConfig = this.buildBackendConfig();
705
+ this.backend.writeConfig(backendConfig);
706
+ const cmd = `AGEND_INSTANCE_NAME=${this.name} ` + this.backend.buildCommand(backendConfig);
707
+ const windowId = await this.tmux.createWindow(cmd, this.config.working_directory, this.name);
708
+ writeFileSync(join(this.instanceDir, "window-id"), windowId);
709
+ // Register with control client and wait for output + idle
710
+ await this.controlClient?.registerWindow(windowId);
711
+ if (this.controlClient) {
712
+ const hasOutput = await this.controlClient.waitForOutput(windowId, 15_000);
713
+ if (!hasOutput)
714
+ return false;
715
+ await this.controlClient.waitForIdle(windowId, 10_000);
716
+ }
717
+ else {
718
+ await new Promise(r => setTimeout(r, 10_000));
719
+ }
720
+ // Dismiss confirmation dialogs and verify CLI reached prompt
721
+ if (!await this.tmux.isWindowAlive())
722
+ return false;
723
+ return this.dismissDialogsUntilReady(3);
724
+ }
725
+ /**
726
+ * Repeatedly check pane content, dismiss any confirmation dialogs,
727
+ * and return true once CLI reaches a ready prompt.
728
+ */
729
+ async dismissDialogsUntilReady(maxAttempts) {
730
+ for (let i = 0; i < maxAttempts; i++) {
731
+ try {
732
+ const pane = await this.tmux.capturePane();
733
+ // CLI is ready
734
+ if (/❯|bypass permissions|ok\s*$/m.test(pane))
735
+ return true;
736
+ // Confirmation dialog: "Yes, I accept" / "Yes, I trust this folder"
737
+ // Navigate to the "Yes" option and confirm
738
+ if (/No, exit|I accept|I trust/i.test(pane)) {
739
+ this.logger.debug("Dismissing confirmation dialog");
740
+ // If "No" is selected (❯ on No), press Down to select Yes
741
+ if (/❯\s*\d+\.\s*No/m.test(pane)) {
742
+ await this.tmux.sendSpecialKey("Down");
743
+ await new Promise(r => setTimeout(r, 200));
744
+ }
745
+ await this.tmux.sendSpecialKey("Enter");
746
+ // Wait for next screen to render
747
+ if (this.controlClient) {
748
+ const wid = readFileSync(join(this.instanceDir, "window-id"), "utf-8").trim();
749
+ await this.controlClient.waitForIdle(wid, 10_000);
750
+ }
751
+ else {
752
+ await new Promise(r => setTimeout(r, 3_000));
753
+ }
754
+ if (!await this.tmux.isWindowAlive())
755
+ return false;
756
+ continue;
757
+ }
758
+ // Resume Session picker or command not found
759
+ if (/Resume Session|command not found|not found/i.test(pane))
760
+ return false;
761
+ }
762
+ catch {
763
+ return false;
764
+ }
765
+ }
766
+ // Exhausted attempts — assume ok for unknown CLI prompts
767
+ return true;
768
+ }
670
769
  saveSessionId() {
671
770
  const sid = this.backend?.getSessionId();
672
771
  if (sid) {