agent-yes 1.43.0 → 1.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.
package/dist/cli.js CHANGED
@@ -20719,6 +20719,7 @@ class PidStore {
20719
20719
  cli TEXT NOT NULL,
20720
20720
  args TEXT NOT NULL,
20721
20721
  prompt TEXT,
20722
+ cwd TEXT NOT NULL,
20722
20723
  logFile TEXT NOT NULL,
20723
20724
  fifoFile TEXT NOT NULL,
20724
20725
  status TEXT NOT NULL DEFAULT 'active',
@@ -20740,7 +20741,8 @@ class PidStore {
20740
20741
  pid,
20741
20742
  cli,
20742
20743
  args,
20743
- prompt
20744
+ prompt,
20745
+ cwd
20744
20746
  }) {
20745
20747
  const now = Date.now();
20746
20748
  const argsJson = JSON.stringify(args);
@@ -20748,16 +20750,16 @@ class PidStore {
20748
20750
  const fifoFile = this.getFifoPath(pid);
20749
20751
  try {
20750
20752
  this.db.run(`
20751
- INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
20752
- VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
20753
- `, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
20753
+ INSERT INTO pid_records (pid, cli, args, prompt, cwd, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
20754
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
20755
+ `, [pid, cli, argsJson, prompt, cwd, logFile, fifoFile, now, now]);
20754
20756
  } catch (error) {
20755
20757
  if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
20756
20758
  this.db.run(`
20757
20759
  UPDATE pid_records
20758
- SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
20760
+ SET cli = ?, args = ?, prompt = ?, cwd = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
20759
20761
  WHERE pid = ?
20760
- `, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
20762
+ `, [cli, argsJson, prompt, cwd, logFile, fifoFile, now, now, pid]);
20761
20763
  } else {
20762
20764
  throw error;
20763
20765
  }
@@ -20782,6 +20784,9 @@ class PidStore {
20782
20784
  }
20783
20785
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
20784
20786
  }
20787
+ getAllRecords() {
20788
+ return this.db.query("SELECT * FROM pid_records");
20789
+ }
20785
20790
  getLogDir() {
20786
20791
  return path10.resolve(this.storeDir, "logs");
20787
20792
  }
@@ -21237,83 +21242,7 @@ class AgentContext {
21237
21242
  }
21238
21243
  var init_context = () => {};
21239
21244
 
21240
- // ts/core/responders.ts
21241
- async function createAutoResponseHandler(line, lineIndex, options) {
21242
- const { ctx, conf, cli, workingDir, exitAgent } = options;
21243
- logger.debug(`stdout|${line}`);
21244
- if (conf.ready?.some((rx) => line.match(rx))) {
21245
- logger.debug(`ready |${line}`);
21246
- if (cli === "gemini" && lineIndex <= 80)
21247
- return;
21248
- ctx.stdinReady.ready();
21249
- ctx.stdinFirstReady.ready();
21250
- }
21251
- if (conf.enter?.some((rx) => line.match(rx))) {
21252
- logger.debug(`sendEnter matched|${line}`);
21253
- return await sendEnter(ctx.messageContext, 400);
21254
- }
21255
- const typingResponded = await src_default(Object.entries(conf.typingRespond ?? {})).filter(([_sendString, onThePatterns]) => onThePatterns.some((rx) => line.match(rx))).map(async ([sendString]) => await sendMessage3(ctx.messageContext, sendString, { waitForReady: false })).toCount();
21256
- if (typingResponded)
21257
- return;
21258
- if (conf.fatal?.some((rx) => line.match(rx))) {
21259
- logger.debug(`fatal |${line}`);
21260
- ctx.isFatal = true;
21261
- await exitAgent();
21262
- }
21263
- if (conf.restartWithoutContinueArg?.some((rx) => line.match(rx))) {
21264
- logger.debug(`restart-without-continue|${line}`);
21265
- ctx.shouldRestartWithoutContinue = true;
21266
- ctx.isFatal = true;
21267
- await exitAgent();
21268
- }
21269
- if (cli === "codex") {
21270
- const sessionId = extractSessionId(line);
21271
- if (sessionId) {
21272
- logger.debug(`session|captured session ID: ${sessionId}`);
21273
- await storeSessionForCwd(workingDir, sessionId);
21274
- }
21275
- }
21276
- }
21277
- var init_responders = __esm(() => {
21278
- init_dist4();
21279
- init_logger();
21280
- init_messaging();
21281
- init_codexSessionManager();
21282
- });
21283
-
21284
21245
  // ts/core/streamHelpers.ts
21285
- function handleConsoleControlCodes(text, shell, terminalRender, cli, verbose) {
21286
- terminalRender.write(text);
21287
- if (text.includes("\x1B[c") || text.includes("\x1B[0c")) {
21288
- shell.write("\x1B[?1;2c");
21289
- if (verbose) {
21290
- logger.debug("device|respond DA: VT100 with Advanced Video Option");
21291
- }
21292
- return;
21293
- }
21294
- if (process.stdin.isTTY)
21295
- return;
21296
- if (!text.includes("\x1B[6n"))
21297
- return;
21298
- const { col, row } = terminalRender.getCursorPosition();
21299
- shell.write(`\x1B[${row};${col}R`);
21300
- logger.debug(`cursor|respond position: row=${String(row)}, col=${String(col)}`);
21301
- }
21302
- function createTerminateSignalHandler(stdinReady, onAbort) {
21303
- let aborted2 = false;
21304
- return (chunk) => {
21305
- if (!aborted2 && chunk === "\x1A") {
21306
- return "";
21307
- }
21308
- if (!aborted2 && !stdinReady.isReady && chunk === "\x03") {
21309
- logger.error("User aborted: SIGINT");
21310
- onAbort(130);
21311
- aborted2 = true;
21312
- return chunk;
21313
- }
21314
- return chunk;
21315
- };
21316
- }
21317
21246
  function createTerminatorStream(exitPromise) {
21318
21247
  return new TransformStream({
21319
21248
  start: function terminator(ctrl) {
@@ -21323,8 +21252,48 @@ function createTerminatorStream(exitPromise) {
21323
21252
  flush: (ctrl) => ctrl.terminate()
21324
21253
  });
21325
21254
  }
21326
- var init_streamHelpers = __esm(() => {
21327
- init_logger();
21255
+
21256
+ // ts/agentRegistry.ts
21257
+ class AgentRegistry {
21258
+ agents = new Map;
21259
+ register(pid, instance) {
21260
+ this.agents.set(pid, instance);
21261
+ }
21262
+ unregister(pid) {
21263
+ this.agents.delete(pid);
21264
+ }
21265
+ get(pid) {
21266
+ return this.agents.get(pid);
21267
+ }
21268
+ list() {
21269
+ return Array.from(this.agents.values());
21270
+ }
21271
+ appendStdout(pid, data) {
21272
+ const instance = this.agents.get(pid);
21273
+ if (!instance) {
21274
+ return;
21275
+ }
21276
+ const lines2 = data.split(`
21277
+ `);
21278
+ instance.stdoutBuffer.push(...lines2);
21279
+ if (instance.stdoutBuffer.length > MAX_BUFFER_SIZE) {
21280
+ instance.stdoutBuffer = instance.stdoutBuffer.slice(-MAX_BUFFER_SIZE);
21281
+ }
21282
+ }
21283
+ getStdout(pid, tail) {
21284
+ const instance = this.agents.get(pid);
21285
+ if (!instance) {
21286
+ return [];
21287
+ }
21288
+ if (tail !== undefined) {
21289
+ return instance.stdoutBuffer.slice(-tail);
21290
+ }
21291
+ return instance.stdoutBuffer;
21292
+ }
21293
+ }
21294
+ var MAX_BUFFER_SIZE = 1000, globalAgentRegistry;
21295
+ var init_agentRegistry = __esm(() => {
21296
+ globalAgentRegistry = new AgentRegistry;
21328
21297
  });
21329
21298
 
21330
21299
  // ts/defineConfig.ts
@@ -21367,6 +21336,7 @@ function getDefaultConfig() {
21367
21336
  clis: {
21368
21337
  claude: {
21369
21338
  promptArg: "last-arg",
21339
+ systemPrompt: "--append-system-prompt",
21370
21340
  install: {
21371
21341
  powershell: "irm https://claude.ai/install.ps1 | iex",
21372
21342
  bash: "curl -fsSL https://claude.ai/install.sh | bash",
@@ -21379,6 +21349,7 @@ function getDefaultConfig() {
21379
21349
  /^>[ \u00A0]/,
21380
21350
  /──────────+/
21381
21351
  ],
21352
+ working: [/esc to interrupt/, /to run in background/],
21382
21353
  typingRespond: {
21383
21354
  "1\n": [/│ Do you want to use this API key\?/]
21384
21355
  },
@@ -21534,16 +21505,385 @@ var require_get_caller_file = __commonJS((exports, module) => {
21534
21505
  };
21535
21506
  });
21536
21507
 
21508
+ // ts/mcp-server.ts
21509
+ var exports_mcp_server = {};
21510
+ __export(exports_mcp_server, {
21511
+ startMcpServer: () => startMcpServer
21512
+ });
21513
+ import { Server } from "@modelcontextprotocol/sdk/server";
21514
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
21515
+ import {
21516
+ CallToolRequestSchema,
21517
+ ListToolsRequestSchema
21518
+ } from "@modelcontextprotocol/sdk/types";
21519
+ import { readFile as readFile4 } from "fs/promises";
21520
+ function getTerminalDimensions() {
21521
+ if (!process.stdout.isTTY)
21522
+ return { cols: 80, rows: 24 };
21523
+ return {
21524
+ cols: Math.max(20, process.stdout.columns),
21525
+ rows: process.stdout.rows
21526
+ };
21527
+ }
21528
+ async function startMcpServer() {
21529
+ try {
21530
+ const config2 = await init_agent_yes_config().then(() => exports_agent_yes_config);
21531
+ CLIS_CONFIG2 = config2.default.clis;
21532
+ } catch (error) {
21533
+ console.error("[MCP] Failed to load agent-yes.config.ts:", error);
21534
+ process.exit(1);
21535
+ }
21536
+ const server = new Server({ name: "agent-yes-mcp-server", version: "1.0.0" }, { capabilities: { tools: {} } });
21537
+ const tools = [
21538
+ {
21539
+ name: "spawn-agent",
21540
+ description: "Spawn a new AI agent with specified CLI and prompt",
21541
+ inputSchema: {
21542
+ type: "object",
21543
+ properties: {
21544
+ cwd: {
21545
+ type: "string",
21546
+ description: "Working directory for the agent"
21547
+ },
21548
+ cli: {
21549
+ type: "string",
21550
+ enum: Object.keys(CLIS_CONFIG2),
21551
+ description: "AI CLI to spawn (e.g., 'claude', 'codex', 'gemini')"
21552
+ },
21553
+ prompt: {
21554
+ type: "string",
21555
+ description: "Initial prompt to send to the agent (optional)"
21556
+ }
21557
+ },
21558
+ required: ["cwd", "cli"]
21559
+ }
21560
+ },
21561
+ {
21562
+ name: "kill-agent",
21563
+ description: "Terminate a running agent by PID",
21564
+ inputSchema: {
21565
+ type: "object",
21566
+ properties: {
21567
+ pid: {
21568
+ type: "number",
21569
+ description: "Process ID of the agent to kill"
21570
+ }
21571
+ },
21572
+ required: ["pid"]
21573
+ }
21574
+ },
21575
+ {
21576
+ name: "read-stdout",
21577
+ description: "Read stdout output from an agent (live or historical)",
21578
+ inputSchema: {
21579
+ type: "object",
21580
+ properties: {
21581
+ pid: {
21582
+ type: "number",
21583
+ description: "Process ID of the agent"
21584
+ },
21585
+ tail: {
21586
+ type: "number",
21587
+ description: "Number of lines to return from the end (default: 100)"
21588
+ }
21589
+ },
21590
+ required: ["pid"]
21591
+ }
21592
+ },
21593
+ {
21594
+ name: "write-stdin",
21595
+ description: "Send a message to an agent's stdin",
21596
+ inputSchema: {
21597
+ type: "object",
21598
+ properties: {
21599
+ pid: {
21600
+ type: "number",
21601
+ description: "Process ID of the agent"
21602
+ },
21603
+ message: {
21604
+ type: "string",
21605
+ description: "Message to send to the agent"
21606
+ }
21607
+ },
21608
+ required: ["pid", "message"]
21609
+ }
21610
+ },
21611
+ {
21612
+ name: "list-agents",
21613
+ description: "List all agents in a working directory (live and historical)",
21614
+ inputSchema: {
21615
+ type: "object",
21616
+ properties: {
21617
+ cwd: {
21618
+ type: "string",
21619
+ description: "Working directory to list agents from (defaults to current directory)"
21620
+ }
21621
+ }
21622
+ }
21623
+ }
21624
+ ];
21625
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
21626
+ tools
21627
+ }));
21628
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21629
+ const { name, arguments: args } = request.params;
21630
+ try {
21631
+ switch (name) {
21632
+ case "spawn-agent":
21633
+ return await handleSpawnAgent(args);
21634
+ case "kill-agent":
21635
+ return await handleKillAgent(args);
21636
+ case "read-stdout":
21637
+ return await handleReadStdout(args);
21638
+ case "write-stdin":
21639
+ return await handleWriteStdin(args);
21640
+ case "list-agents":
21641
+ return await handleListAgents(args);
21642
+ default:
21643
+ throw new Error(`Unknown tool: ${name}`);
21644
+ }
21645
+ } catch (error) {
21646
+ console.error(`[MCP] Error handling tool ${name}:`, error);
21647
+ return {
21648
+ content: [
21649
+ {
21650
+ type: "text",
21651
+ text: JSON.stringify({ error: error.message || String(error) })
21652
+ }
21653
+ ]
21654
+ };
21655
+ }
21656
+ });
21657
+ const transport = new StdioServerTransport;
21658
+ await server.connect(transport);
21659
+ console.error("[agent-yes-mcp] Server started on stdio");
21660
+ }
21661
+ async function handleSpawnAgent(args) {
21662
+ const { cwd, cli, prompt } = args;
21663
+ if (!CLIS_CONFIG2[cli]) {
21664
+ throw new Error(`Unknown CLI: ${cli}. Available: ${Object.keys(CLIS_CONFIG2).join(", ")}`);
21665
+ }
21666
+ const cliConf = CLIS_CONFIG2[cli];
21667
+ const ptyOptions = {
21668
+ name: "xterm-color",
21669
+ ...getTerminalDimensions(),
21670
+ cwd,
21671
+ env: { ...process.env, AGENT_YES_MCP: "true" }
21672
+ };
21673
+ const shell = spawnAgent({
21674
+ cli,
21675
+ cliConf,
21676
+ cliArgs: [],
21677
+ verbose: false,
21678
+ install: false,
21679
+ ptyOptions
21680
+ });
21681
+ const pidStore = new PidStore(cwd);
21682
+ await pidStore.init();
21683
+ try {
21684
+ await pidStore.registerProcess({ pid: shell.pid, cli, args: [], prompt, cwd });
21685
+ } catch (error) {
21686
+ console.error(`[MCP] Failed to register process ${shell.pid}:`, error);
21687
+ }
21688
+ const logPaths = await initializeLogPaths(pidStore, shell.pid);
21689
+ setupDebugLogging(logPaths.debuggingLogsPath);
21690
+ const ctx = new AgentContext({
21691
+ shell,
21692
+ pidStore,
21693
+ logPaths,
21694
+ cli,
21695
+ cliConf,
21696
+ verbose: false,
21697
+ robust: false
21698
+ });
21699
+ const pendingExitCode = Promise.withResolvers();
21700
+ const outputStream = src_default(createTerminatorStream(pendingExitCode.promise)).writable;
21701
+ const outputWriter = outputStream.getWriter();
21702
+ shell.onData(async (data) => {
21703
+ await outputWriter.write(data);
21704
+ globalAgentRegistry.appendStdout(shell.pid, data);
21705
+ });
21706
+ shell.onExit(async ({ exitCode }) => {
21707
+ globalAgentRegistry.unregister(shell.pid);
21708
+ try {
21709
+ await pidStore.updateStatus(shell.pid, "exited", {
21710
+ exitReason: exitCode === 0 ? "normal" : "crash",
21711
+ exitCode: exitCode ?? undefined
21712
+ });
21713
+ } catch (error) {
21714
+ console.error(`[MCP] Failed to update status for PID ${shell.pid}:`, error);
21715
+ }
21716
+ pendingExitCode.resolve(exitCode);
21717
+ });
21718
+ try {
21719
+ globalAgentRegistry.register(shell.pid, {
21720
+ pid: shell.pid,
21721
+ context: ctx,
21722
+ cwd,
21723
+ cli,
21724
+ prompt,
21725
+ startTime: Date.now(),
21726
+ stdoutBuffer: []
21727
+ });
21728
+ } catch (error) {
21729
+ console.error(`[MCP] Failed to register agent ${shell.pid} in registry:`, error);
21730
+ }
21731
+ if (prompt) {
21732
+ shell.write(prompt + `
21733
+ `);
21734
+ }
21735
+ return {
21736
+ content: [
21737
+ {
21738
+ type: "text",
21739
+ text: JSON.stringify({ pid: shell.pid, status: "spawned", cli, cwd })
21740
+ }
21741
+ ]
21742
+ };
21743
+ }
21744
+ async function handleKillAgent(args) {
21745
+ const { pid } = args;
21746
+ const instance = globalAgentRegistry.get(pid);
21747
+ if (!instance) {
21748
+ return {
21749
+ content: [
21750
+ {
21751
+ type: "text",
21752
+ text: JSON.stringify({ status: "not_running", pid })
21753
+ }
21754
+ ]
21755
+ };
21756
+ }
21757
+ instance.context.shell.kill("SIGTERM");
21758
+ globalAgentRegistry.unregister(pid);
21759
+ return {
21760
+ content: [{ type: "text", text: JSON.stringify({ status: "killed", pid }) }]
21761
+ };
21762
+ }
21763
+ async function handleReadStdout(args) {
21764
+ const { pid, tail = 100 } = args;
21765
+ const instance = globalAgentRegistry.get(pid);
21766
+ if (instance) {
21767
+ const lines2 = instance.stdoutBuffer.slice(-tail);
21768
+ return {
21769
+ content: [{ type: "text", text: lines2.join(`
21770
+ `) }]
21771
+ };
21772
+ }
21773
+ try {
21774
+ const possibleDirs = [process.cwd(), args.cwd || process.cwd()];
21775
+ for (const dir of possibleDirs) {
21776
+ const pidStore = new PidStore(dir);
21777
+ await pidStore.init();
21778
+ const record = pidStore.getAllRecords().find((r) => r.pid === pid);
21779
+ if (record && record.logFile) {
21780
+ const content = await readFile4(record.logFile, "utf8");
21781
+ const lines2 = content.split(`
21782
+ `);
21783
+ const tailedLines = lines2.slice(-tail);
21784
+ return {
21785
+ content: [{ type: "text", text: tailedLines.join(`
21786
+ `) }]
21787
+ };
21788
+ }
21789
+ }
21790
+ return {
21791
+ content: [{ type: "text", text: "" }]
21792
+ };
21793
+ } catch (error) {
21794
+ return {
21795
+ content: [
21796
+ {
21797
+ type: "text",
21798
+ text: JSON.stringify({ error: `Failed to read stdout: ${error.message}` })
21799
+ }
21800
+ ]
21801
+ };
21802
+ }
21803
+ }
21804
+ async function handleWriteStdin(args) {
21805
+ const { pid, message } = args;
21806
+ const instance = globalAgentRegistry.get(pid);
21807
+ if (!instance) {
21808
+ return {
21809
+ content: [
21810
+ {
21811
+ type: "text",
21812
+ text: JSON.stringify({ error: `Agent with PID ${pid} not found (not running)` })
21813
+ }
21814
+ ]
21815
+ };
21816
+ }
21817
+ try {
21818
+ await sendMessage3(instance.context.messageContext, message);
21819
+ return {
21820
+ content: [{ type: "text", text: JSON.stringify({ status: "sent", pid, message }) }]
21821
+ };
21822
+ } catch (error) {
21823
+ return {
21824
+ content: [
21825
+ {
21826
+ type: "text",
21827
+ text: JSON.stringify({ error: `Failed to send message: ${error.message}` })
21828
+ }
21829
+ ]
21830
+ };
21831
+ }
21832
+ }
21833
+ async function handleListAgents(args) {
21834
+ const cwd = args.cwd || process.cwd();
21835
+ const liveAgents = globalAgentRegistry.list().filter((a2) => a2.cwd === cwd).map((a2) => ({
21836
+ pid: a2.pid,
21837
+ cli: a2.cli,
21838
+ cwd: a2.cwd,
21839
+ status: "running",
21840
+ startTime: a2.startTime,
21841
+ prompt: a2.prompt
21842
+ }));
21843
+ const pidStore = new PidStore(cwd);
21844
+ await pidStore.init();
21845
+ const dbAgents = pidStore.getAllRecords().filter((r) => r.cwd === cwd).map((r) => ({
21846
+ pid: r.pid,
21847
+ cli: r.cli,
21848
+ cwd: r.cwd,
21849
+ status: r.status,
21850
+ exitCode: r.exitCode,
21851
+ startTime: r.startedAt,
21852
+ prompt: r.prompt
21853
+ }));
21854
+ const allAgents = [...liveAgents];
21855
+ const livePids = new Set(liveAgents.map((a2) => a2.pid));
21856
+ for (const dbAgent of dbAgents) {
21857
+ if (!livePids.has(dbAgent.pid)) {
21858
+ allAgents.push(dbAgent);
21859
+ }
21860
+ }
21861
+ return {
21862
+ content: [{ type: "text", text: JSON.stringify({ agents: allAgents, cwd }) }]
21863
+ };
21864
+ }
21865
+ var CLIS_CONFIG2;
21866
+ var init_mcp_server = __esm(async () => {
21867
+ init_agentRegistry();
21868
+ init_pidStore();
21869
+ init_messaging();
21870
+ init_logging();
21871
+ init_context();
21872
+ init_dist4();
21873
+ await init_spawner();
21874
+ });
21875
+
21537
21876
  // ts/index.ts
21538
21877
  var exports_ts = {};
21539
21878
  __export(exports_ts, {
21540
21879
  removeControlCharacters: () => removeControlCharacters,
21541
21880
  default: () => agentYes2,
21542
21881
  config: () => config2,
21543
- CLIS_CONFIG: () => CLIS_CONFIG2
21882
+ CLIS_CONFIG: () => CLIS_CONFIG3,
21883
+ AgentContext: () => AgentContext
21544
21884
  });
21545
- import { fromReadable as fromReadable2, fromWritable as fromWritable2 } from "from-node-stream";
21546
- import { mkdir as mkdir9, readFile as readFile4, writeFile as writeFile8 } from "fs/promises";
21885
+ import { fromWritable as fromWritable2 } from "from-node-stream";
21886
+ import { mkdir as mkdir9, readFile as readFile5, writeFile as writeFile8 } from "fs/promises";
21547
21887
  import path15 from "path";
21548
21888
  async function agentYes2({
21549
21889
  cli,
@@ -21564,7 +21904,7 @@ async function agentYes2({
21564
21904
  }) {
21565
21905
  if (!cli)
21566
21906
  throw new Error(`cli is required`);
21567
- const conf = CLIS_CONFIG2[cli] || phpdie_default(`Unsupported cli tool: ${cli}, current process.argv: ${process.argv.join(" ")}`);
21907
+ const conf = CLIS_CONFIG3[cli] || phpdie_default(`Unsupported cli tool: ${cli}, current process.argv: ${process.argv.join(" ")}`);
21568
21908
  const workingDir = cwd ?? process.cwd();
21569
21909
  if (queue) {
21570
21910
  if (queue && shouldUseLock(workingDir)) {
@@ -21603,14 +21943,18 @@ async function agentYes2({
21603
21943
  stdinFirstReady.ready();
21604
21944
  });
21605
21945
  const nextStdout = new ReadyManager;
21946
+ if (verbose)
21947
+ logger.debug(`[stdin] isTTY: ${process.stdin.isTTY}, setRawMode available: ${!!process.stdin.setRawMode}`);
21606
21948
  process.stdin.setRawMode?.(true);
21949
+ if (verbose)
21950
+ logger.debug(`[stdin] Raw mode set, isRaw: ${process.stdin.isRaw}`);
21607
21951
  const shellOutputStream = new TransformStream;
21608
21952
  const outputWriter = shellOutputStream.writable.getWriter();
21609
21953
  logger.debug(`Using ${ptyPackage} for pseudo terminal management.`);
21610
21954
  const isSubAgent = !!process.env.CLAUDE_PPID;
21611
21955
  if (isSubAgent)
21612
21956
  logger.info(`[${cli}-yes] Running as sub-agent (CLAUDE_PPID=${process.env.CLAUDE_PPID})`);
21613
- const cliConf = CLIS_CONFIG2[cli] || {};
21957
+ const cliConf = CLIS_CONFIG3[cli] || {};
21614
21958
  cliArgs = cliConf.defaultArgs ? [...cliConf.defaultArgs, ...cliArgs] : cliArgs;
21615
21959
  try {
21616
21960
  const workingDir2 = cwd ?? process.cwd();
@@ -21630,7 +21974,7 @@ async function agentYes2({
21630
21974
  const searchLimit = gitRoot || path15.parse(currentDir).root;
21631
21975
  while (true) {
21632
21976
  const skillPath = path15.resolve(currentDir, "SKILL.md");
21633
- const md = await readFile4(skillPath, "utf8").catch(() => null);
21977
+ const md = await readFile5(skillPath, "utf8").catch(() => null);
21634
21978
  if (md) {
21635
21979
  const headerMatch = md.match(/^[\s\S]*?(?=\n##\s)/);
21636
21980
  const headerRaw = (headerMatch ? headerMatch[0] : md).trim();
@@ -21708,7 +22052,7 @@ ${prompt}` : prefix;
21708
22052
  const ptyEnv = { ...env2 ?? process.env };
21709
22053
  const ptyOptions = {
21710
22054
  name: "xterm-color",
21711
- ...getTerminalDimensions(),
22055
+ ...getTerminalDimensions2(),
21712
22056
  cwd: cwd ?? process.cwd(),
21713
22057
  env: ptyEnv
21714
22058
  };
@@ -21720,7 +22064,11 @@ ${prompt}` : prefix;
21720
22064
  install,
21721
22065
  ptyOptions
21722
22066
  });
21723
- await pidStore.registerProcess({ pid: shell.pid, cli, args: cliArgs, prompt });
22067
+ try {
22068
+ await pidStore.registerProcess({ pid: shell.pid, cli, args: cliArgs, prompt, cwd: workingDir });
22069
+ } catch (error) {
22070
+ logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
22071
+ }
21724
22072
  const logPaths = await initializeLogPaths(pidStore, shell.pid);
21725
22073
  setupDebugLogging(logPaths.debuggingLogsPath);
21726
22074
  const ctx = new AgentContext({
@@ -21732,6 +22080,19 @@ ${prompt}` : prefix;
21732
22080
  verbose,
21733
22081
  robust
21734
22082
  });
22083
+ try {
22084
+ globalAgentRegistry.register(shell.pid, {
22085
+ pid: shell.pid,
22086
+ context: ctx,
22087
+ cwd: workingDir,
22088
+ cli,
22089
+ prompt,
22090
+ startTime: Date.now(),
22091
+ stdoutBuffer: []
22092
+ });
22093
+ } catch (error) {
22094
+ logger.warn(`[agentRegistry] Failed to register agent ${shell.pid}:`, error);
22095
+ }
21735
22096
  sleep3(1e4).then(() => {
21736
22097
  if (!ctx.stdinReady.isReady)
21737
22098
  ctx.stdinReady.ready();
@@ -21741,16 +22102,22 @@ ${prompt}` : prefix;
21741
22102
  const pendingExitCode = Promise.withResolvers();
21742
22103
  async function onData(data) {
21743
22104
  await outputWriter.write(data);
22105
+ globalAgentRegistry.appendStdout(shell.pid, data);
21744
22106
  }
21745
22107
  shell.onData(onData);
21746
22108
  shell.onExit(async function onExit2({ exitCode: exitCode2 }) {
22109
+ globalAgentRegistry.unregister(shell.pid);
21747
22110
  ctx.stdinReady.unready();
21748
22111
  const agentCrashed = exitCode2 !== 0;
21749
22112
  if (ctx.shouldRestartWithoutContinue) {
21750
- await pidStore.updateStatus(shell.pid, "exited", {
21751
- exitReason: "restarted",
21752
- exitCode: exitCode2 ?? undefined
21753
- });
22113
+ try {
22114
+ await pidStore.updateStatus(shell.pid, "exited", {
22115
+ exitReason: "restarted",
22116
+ exitCode: exitCode2 ?? undefined
22117
+ });
22118
+ } catch (error) {
22119
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22120
+ }
21754
22121
  ctx.shouldRestartWithoutContinue = false;
21755
22122
  ctx.isFatal = false;
21756
22123
  const cliCommand = cliConf?.binary || cli;
@@ -21761,32 +22128,58 @@ ${prompt}` : prefix;
21761
22128
  logger.info(`Restarting ${cli} ${JSON.stringify([bin, ...args])}`);
21762
22129
  const restartPtyOptions = {
21763
22130
  name: "xterm-color",
21764
- ...getTerminalDimensions(),
22131
+ ...getTerminalDimensions2(),
21765
22132
  cwd: cwd ?? process.cwd(),
21766
22133
  env: ptyEnv
21767
22134
  };
21768
22135
  shell = pty_default.spawn(bin, args, restartPtyOptions);
21769
- await pidStore.registerProcess({ pid: shell.pid, cli, args, prompt });
22136
+ try {
22137
+ await pidStore.registerProcess({ pid: shell.pid, cli, args, prompt, cwd: workingDir });
22138
+ } catch (error) {
22139
+ logger.warn(`[pidStore] Failed to register restarted process ${shell.pid}:`, error);
22140
+ }
22141
+ ctx.shell = shell;
22142
+ try {
22143
+ globalAgentRegistry.register(shell.pid, {
22144
+ pid: shell.pid,
22145
+ context: ctx,
22146
+ cwd: workingDir,
22147
+ cli,
22148
+ prompt,
22149
+ startTime: Date.now(),
22150
+ stdoutBuffer: []
22151
+ });
22152
+ } catch (error) {
22153
+ logger.warn(`[agentRegistry] Failed to register restarted agent ${shell.pid}:`, error);
22154
+ }
21770
22155
  shell.onData(onData);
21771
22156
  shell.onExit(onExit2);
21772
22157
  return;
21773
22158
  }
21774
22159
  if (agentCrashed && robust && conf?.restoreArgs) {
21775
22160
  if (!conf.restoreArgs) {
21776
- logger.warn(`robust is only supported for ${Object.entries(CLIS_CONFIG2).filter(([_, v]) => v.restoreArgs).map(([k]) => k).join(", ")} currently, not ${cli}`);
22161
+ logger.warn(`robust is only supported for ${Object.entries(CLIS_CONFIG3).filter(([_, v]) => v.restoreArgs).map(([k]) => k).join(", ")} currently, not ${cli}`);
21777
22162
  return;
21778
22163
  }
21779
22164
  if (ctx.isFatal) {
22165
+ try {
22166
+ await pidStore.updateStatus(shell.pid, "exited", {
22167
+ exitReason: "fatal",
22168
+ exitCode: exitCode2 ?? undefined
22169
+ });
22170
+ } catch (error) {
22171
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22172
+ }
22173
+ return pendingExitCode.resolve(exitCode2);
22174
+ }
22175
+ try {
21780
22176
  await pidStore.updateStatus(shell.pid, "exited", {
21781
- exitReason: "fatal",
22177
+ exitReason: "restarted",
21782
22178
  exitCode: exitCode2 ?? undefined
21783
22179
  });
21784
- return pendingExitCode.resolve(exitCode2);
22180
+ } catch (error) {
22181
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
21785
22182
  }
21786
- await pidStore.updateStatus(shell.pid, "exited", {
21787
- exitReason: "restarted",
21788
- exitCode: exitCode2 ?? undefined
21789
- });
21790
22183
  logger.info(`${cli} crashed, restarting...`);
21791
22184
  let restoreArgs = conf.restoreArgs;
21792
22185
  if (cli === "codex") {
@@ -21800,45 +22193,155 @@ ${prompt}` : prefix;
21800
22193
  }
21801
22194
  const restorePtyOptions = {
21802
22195
  name: "xterm-color",
21803
- ...getTerminalDimensions(),
22196
+ ...getTerminalDimensions2(),
21804
22197
  cwd: cwd ?? process.cwd(),
21805
22198
  env: ptyEnv
21806
22199
  };
21807
22200
  shell = pty_default.spawn(cli, restoreArgs, restorePtyOptions);
21808
- await pidStore.registerProcess({ pid: shell.pid, cli, args: restoreArgs, prompt });
22201
+ try {
22202
+ await pidStore.registerProcess({ pid: shell.pid, cli, args: restoreArgs, prompt, cwd: workingDir });
22203
+ } catch (error) {
22204
+ logger.warn(`[pidStore] Failed to register restored process ${shell.pid}:`, error);
22205
+ }
22206
+ ctx.shell = shell;
22207
+ try {
22208
+ globalAgentRegistry.register(shell.pid, {
22209
+ pid: shell.pid,
22210
+ context: ctx,
22211
+ cwd: workingDir,
22212
+ cli,
22213
+ prompt,
22214
+ startTime: Date.now(),
22215
+ stdoutBuffer: []
22216
+ });
22217
+ } catch (error) {
22218
+ logger.warn(`[agentRegistry] Failed to register restored agent ${shell.pid}:`, error);
22219
+ }
21809
22220
  shell.onData(onData);
21810
22221
  shell.onExit(onExit2);
21811
22222
  return;
21812
22223
  }
21813
22224
  const exitReason = agentCrashed ? "crash" : "normal";
21814
- await pidStore.updateStatus(shell.pid, "exited", {
21815
- exitReason,
21816
- exitCode: exitCode2 ?? undefined
21817
- });
22225
+ try {
22226
+ await pidStore.updateStatus(shell.pid, "exited", {
22227
+ exitReason,
22228
+ exitCode: exitCode2 ?? undefined
22229
+ });
22230
+ } catch (error) {
22231
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22232
+ }
21818
22233
  return pendingExitCode.resolve(exitCode2);
21819
22234
  });
21820
22235
  process.stdout.on("resize", () => {
21821
- const { cols, rows } = getTerminalDimensions();
22236
+ const { cols, rows } = getTerminalDimensions2();
21822
22237
  shell.resize(cols, rows);
21823
22238
  });
21824
22239
  const terminalRender = new TerminalTextRender;
21825
- const isStillWorkingQ = () => terminalRender.render().replace(/\s+/g, " ").match(/esc to interrupt|to run in background/);
22240
+ const isStillWorkingQ = () => {
22241
+ const rendered = terminalRender.tail(24).replace(/\s+/g, " ");
22242
+ return conf.working?.some((rgx) => rgx.test(rendered));
22243
+ };
22244
+ let lastHeartbeatRendered = "";
22245
+ const heartbeatInterval = setInterval(async () => {
22246
+ try {
22247
+ const rendered = removeControlCharacters(terminalRender.tail(12));
22248
+ if (rendered === lastHeartbeatRendered)
22249
+ return;
22250
+ lastHeartbeatRendered = rendered;
22251
+ const lines2 = rendered.split(`
22252
+ `).filter((line) => line.trim());
22253
+ for (const line of lines2) {
22254
+ if (conf.ready?.some((rx) => rx.test(line))) {
22255
+ logger.debug(`heartbeat|ready |${line}`);
22256
+ ctx.stdinReady.ready();
22257
+ ctx.stdinFirstReady.ready();
22258
+ }
22259
+ if (conf.enter?.some((rx) => rx.test(line))) {
22260
+ logger.debug(`heartbeat|sendEnter matched|${line}`);
22261
+ await sendEnter(ctx.messageContext, 400);
22262
+ continue;
22263
+ }
22264
+ const typeingRespondMatched = Object.entries(conf.typingRespond ?? {}).filter(([_sendString, onThePatterns]) => onThePatterns.some((rx) => rx.test(line)));
22265
+ if (typeingRespondMatched.length) {
22266
+ await src_default(typeingRespondMatched).map(async ([sendString]) => await sendMessage3(ctx.messageContext, sendString, { waitForReady: false })).toCount();
22267
+ continue;
22268
+ }
22269
+ if (conf.fatal?.some((rx) => rx.test(line))) {
22270
+ logger.debug(`heartbeat|fatal |${line}`);
22271
+ ctx.isFatal = true;
22272
+ await exitAgent();
22273
+ break;
22274
+ }
22275
+ if (conf.restartWithoutContinueArg?.some((rx) => rx.test(line))) {
22276
+ logger.debug(`heartbeat|restart-without-continue|${line}`);
22277
+ ctx.shouldRestartWithoutContinue = true;
22278
+ ctx.isFatal = true;
22279
+ await exitAgent();
22280
+ break;
22281
+ }
22282
+ if (cli === "codex") {
22283
+ const sessionId = extractSessionId(line);
22284
+ if (sessionId) {
22285
+ logger.debug(`heartbeat|session|captured session ID: ${sessionId}`);
22286
+ await storeSessionForCwd(workingDir, sessionId);
22287
+ }
22288
+ }
22289
+ }
22290
+ } catch (error) {
22291
+ logger.debug(`heartbeat|error: ${error}`);
22292
+ }
22293
+ }, 800);
22294
+ const cleanupHeartbeat = () => clearInterval(heartbeatInterval);
22295
+ shell.onExit(cleanupHeartbeat);
21826
22296
  if (exitOnIdle)
21827
22297
  ctx.idleWaiter.wait(exitOnIdle).then(async () => {
21828
22298
  await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
21829
22299
  if (isStillWorkingQ()) {
21830
- logger.warn("[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet");
22300
+ logger.warn(`[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet`);
21831
22301
  return;
21832
22302
  }
21833
- logger.info("[${cli}-yes] ${cli} is idle, exiting...");
22303
+ logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
21834
22304
  await exitAgent();
21835
22305
  });
21836
- await src_default(fromReadable2(process.stdin)).map((buffer2) => buffer2.toString()).forkTo(function handleTerminateSignals(s) {
21837
- const handler = createTerminateSignalHandler(ctx.stdinReady, (exitCode2) => {
22306
+ const stdinStream = new ReadableStream({
22307
+ start(controller) {
22308
+ process.stdin.resume();
22309
+ const dataHandler = (chunk) => {
22310
+ try {
22311
+ controller.enqueue(chunk);
22312
+ } catch (err) {}
22313
+ };
22314
+ const endHandler = () => {
22315
+ controller.close();
22316
+ };
22317
+ const errorHandler = (err) => {
22318
+ controller.error(err);
22319
+ };
22320
+ process.stdin.on("data", dataHandler);
22321
+ process.stdin.on("end", endHandler);
22322
+ process.stdin.on("close", endHandler);
22323
+ process.stdin.on("error", errorHandler);
22324
+ },
22325
+ cancel(reason) {
22326
+ process.stdin.pause();
22327
+ }
22328
+ });
22329
+ let aborted2 = false;
22330
+ await src_default(stdinStream).map((buffer2) => {
22331
+ const str = buffer2.toString();
22332
+ const CTRL_Z = "\x1A";
22333
+ const CTRL_C = "\x03";
22334
+ if (!aborted2 && str === CTRL_Z) {
22335
+ return "";
22336
+ }
22337
+ if (!aborted2 && !ctx.stdinReady.isReady && str === CTRL_C) {
22338
+ logger.error("User aborted: SIGINT");
21838
22339
  shell.kill("SIGINT");
21839
- pendingExitCode.resolve(exitCode2);
21840
- });
21841
- return s.map(handler);
22340
+ pendingExitCode.resolve(130);
22341
+ aborted2 = true;
22342
+ return str;
22343
+ }
22344
+ return str;
21842
22345
  }).onStart(async function promptOnStart() {
21843
22346
  logger.debug("Sending prompt message: " + JSON.stringify(prompt));
21844
22347
  if (prompt)
@@ -21866,15 +22369,62 @@ ${prompt}` : prefix;
21866
22369
  }).run();
21867
22370
  }).catch(() => f.run());
21868
22371
  }).by(function consoleResponder(e) {
21869
- if (cli === "codex")
21870
- shell.write(`\x1B[1;1R`);
21871
- return e.forEach((text) => handleConsoleControlCodes(text, shell, terminalRender, cli, verbose));
21872
- }).forkTo(function autoResponse(e) {
21873
- return e.map((e2) => removeControlCharacters(e2)).by((s) => {
21874
- if (conf.noEOL)
21875
- return s;
21876
- return s.lines({ EOL: "NONE" });
21877
- }).forEach(async (line, lineIndex) => createAutoResponseHandler(line, lineIndex, { ctx, conf, cli, workingDir, exitAgent })).run();
22372
+ let lastRendered = "";
22373
+ return e.forEach((chunk) => {
22374
+ terminalRender.write(chunk);
22375
+ if (chunk.includes("\x1B[c") || chunk.includes("\x1B[0c")) {
22376
+ shell.write("\x1B[?1;2c");
22377
+ if (verbose) {
22378
+ logger.debug("device|respond DA: VT100 with Advanced Video Option");
22379
+ }
22380
+ return;
22381
+ }
22382
+ if (process.stdin.isTTY)
22383
+ return;
22384
+ if (!chunk.includes("\x1B[6n"))
22385
+ return;
22386
+ const { col, row } = terminalRender.getCursorPosition();
22387
+ shell.write(`\x1B[${row};${col}R`);
22388
+ logger.debug(`cursor|respond position: row=${String(row)}, col=${String(col)}`);
22389
+ }).forEach(async (line, lineIndex) => {
22390
+ const rendered = terminalRender.tail(24);
22391
+ if (rendered === lastRendered)
22392
+ return;
22393
+ logger.debug(`stdout|${line}`);
22394
+ if (conf.ready?.some((rx) => line.match(rx))) {
22395
+ logger.debug(`ready |${line}`);
22396
+ if (cli === "gemini" && lineIndex <= 80)
22397
+ return;
22398
+ ctx.stdinReady.ready();
22399
+ ctx.stdinFirstReady.ready();
22400
+ }
22401
+ if (conf.enter?.some((rx) => line.match(rx))) {
22402
+ logger.debug(`sendEnter matched|${line}`);
22403
+ return await sendEnter(ctx.messageContext, 400);
22404
+ }
22405
+ const typeingRespondMatched = Object.entries(conf.typingRespond ?? {}).filter(([_sendString, onThePatterns]) => onThePatterns.some((rx) => line.match(rx)));
22406
+ const typingResponded = typeingRespondMatched.length && await src_default(typeingRespondMatched).map(async ([sendString]) => await sendMessage3(ctx.messageContext, sendString, { waitForReady: false })).toCount();
22407
+ if (typingResponded)
22408
+ return;
22409
+ if (conf.fatal?.some((rx) => line.match(rx))) {
22410
+ logger.debug(`fatal |${line}`);
22411
+ ctx.isFatal = true;
22412
+ await exitAgent();
22413
+ }
22414
+ if (conf.restartWithoutContinueArg?.some((rx) => line.match(rx))) {
22415
+ logger.debug(`restart-without-continue|${line}`);
22416
+ ctx.shouldRestartWithoutContinue = true;
22417
+ ctx.isFatal = true;
22418
+ await exitAgent();
22419
+ }
22420
+ if (cli === "codex") {
22421
+ const sessionId = extractSessionId(line);
22422
+ if (sessionId) {
22423
+ logger.debug(`session|captured session ID: ${sessionId}`);
22424
+ await storeSessionForCwd(workingDir, sessionId);
22425
+ }
22426
+ }
22427
+ });
21878
22428
  }).by((s) => removeControlCharactersFromStdout ? s.map((e) => removeControlCharacters(e)) : s).by(createTerminatorStream(pendingExitCode.promise)).to(fromWritable2(process.stdout));
21879
22429
  await saveLogFile(ctx.logPaths.logPath, terminalRender.render());
21880
22430
  const exitCode = await pendingExitCode.promise;
@@ -21898,7 +22448,7 @@ ${prompt}` : prefix;
21898
22448
  }, 5000))
21899
22449
  ]);
21900
22450
  }
21901
- function getTerminalDimensions() {
22451
+ function getTerminalDimensions2() {
21902
22452
  if (!process.stdout.isTTY)
21903
22453
  return { cols: 80, rows: 24 };
21904
22454
  return {
@@ -21910,7 +22460,7 @@ ${prompt}` : prefix;
21910
22460
  function sleep3(ms2) {
21911
22461
  return new Promise((resolve5) => setTimeout(resolve5, ms2));
21912
22462
  }
21913
- var config2, CLIS_CONFIG2;
22463
+ var config2, CLIS_CONFIG3;
21914
22464
  var init_ts = __esm(async () => {
21915
22465
  init_execa();
21916
22466
  init_dist();
@@ -21923,14 +22473,13 @@ var init_ts = __esm(async () => {
21923
22473
  init_messaging();
21924
22474
  init_logging();
21925
22475
  init_context();
21926
- init_responders();
21927
- init_streamHelpers();
22476
+ init_agentRegistry();
21928
22477
  await __promiseAll([
21929
22478
  init_pty(),
21930
22479
  init_spawner()
21931
22480
  ]);
21932
22481
  config2 = await init_agent_yes_config().then(() => exports_agent_yes_config).then((mod) => mod.default || mod);
21933
- CLIS_CONFIG2 = config2.clis;
22482
+ CLIS_CONFIG3 = config2.clis;
21934
22483
  });
21935
22484
 
21936
22485
  // ts/index.ts
@@ -21945,13 +22494,12 @@ init_pidStore();
21945
22494
  init_messaging();
21946
22495
  init_logging();
21947
22496
  init_context();
21948
- init_responders();
21949
- init_streamHelpers();
22497
+ init_agentRegistry();
21950
22498
  await __promiseAll([
21951
22499
  init_pty(),
21952
22500
  init_spawner()
21953
22501
  ]);
21954
- import { fromReadable, fromWritable } from "from-node-stream";
22502
+ import { fromWritable } from "from-node-stream";
21955
22503
  import { mkdir as mkdir7, readFile as readFile3, writeFile as writeFile5 } from "fs/promises";
21956
22504
  import path13 from "path";
21957
22505
  var config = await init_agent_yes_config().then(() => exports_agent_yes_config).then((mod) => mod.default || mod);
@@ -22014,7 +22562,11 @@ async function agentYes({
22014
22562
  stdinFirstReady.ready();
22015
22563
  });
22016
22564
  const nextStdout = new ReadyManager;
22565
+ if (verbose)
22566
+ logger.debug(`[stdin] isTTY: ${process.stdin.isTTY}, setRawMode available: ${!!process.stdin.setRawMode}`);
22017
22567
  process.stdin.setRawMode?.(true);
22568
+ if (verbose)
22569
+ logger.debug(`[stdin] Raw mode set, isRaw: ${process.stdin.isRaw}`);
22018
22570
  const shellOutputStream = new TransformStream;
22019
22571
  const outputWriter = shellOutputStream.writable.getWriter();
22020
22572
  logger.debug(`Using ${ptyPackage} for pseudo terminal management.`);
@@ -22131,7 +22683,11 @@ ${prompt}` : prefix;
22131
22683
  install,
22132
22684
  ptyOptions
22133
22685
  });
22134
- await pidStore.registerProcess({ pid: shell.pid, cli, args: cliArgs, prompt });
22686
+ try {
22687
+ await pidStore.registerProcess({ pid: shell.pid, cli, args: cliArgs, prompt, cwd: workingDir });
22688
+ } catch (error) {
22689
+ logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
22690
+ }
22135
22691
  const logPaths = await initializeLogPaths(pidStore, shell.pid);
22136
22692
  setupDebugLogging(logPaths.debuggingLogsPath);
22137
22693
  const ctx = new AgentContext({
@@ -22143,6 +22699,19 @@ ${prompt}` : prefix;
22143
22699
  verbose,
22144
22700
  robust
22145
22701
  });
22702
+ try {
22703
+ globalAgentRegistry.register(shell.pid, {
22704
+ pid: shell.pid,
22705
+ context: ctx,
22706
+ cwd: workingDir,
22707
+ cli,
22708
+ prompt,
22709
+ startTime: Date.now(),
22710
+ stdoutBuffer: []
22711
+ });
22712
+ } catch (error) {
22713
+ logger.warn(`[agentRegistry] Failed to register agent ${shell.pid}:`, error);
22714
+ }
22146
22715
  sleep2(1e4).then(() => {
22147
22716
  if (!ctx.stdinReady.isReady)
22148
22717
  ctx.stdinReady.ready();
@@ -22152,16 +22721,22 @@ ${prompt}` : prefix;
22152
22721
  const pendingExitCode = Promise.withResolvers();
22153
22722
  async function onData(data) {
22154
22723
  await outputWriter.write(data);
22724
+ globalAgentRegistry.appendStdout(shell.pid, data);
22155
22725
  }
22156
22726
  shell.onData(onData);
22157
22727
  shell.onExit(async function onExit2({ exitCode: exitCode2 }) {
22728
+ globalAgentRegistry.unregister(shell.pid);
22158
22729
  ctx.stdinReady.unready();
22159
22730
  const agentCrashed = exitCode2 !== 0;
22160
22731
  if (ctx.shouldRestartWithoutContinue) {
22161
- await pidStore.updateStatus(shell.pid, "exited", {
22162
- exitReason: "restarted",
22163
- exitCode: exitCode2 ?? undefined
22164
- });
22732
+ try {
22733
+ await pidStore.updateStatus(shell.pid, "exited", {
22734
+ exitReason: "restarted",
22735
+ exitCode: exitCode2 ?? undefined
22736
+ });
22737
+ } catch (error) {
22738
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22739
+ }
22165
22740
  ctx.shouldRestartWithoutContinue = false;
22166
22741
  ctx.isFatal = false;
22167
22742
  const cliCommand = cliConf?.binary || cli;
@@ -22177,7 +22752,25 @@ ${prompt}` : prefix;
22177
22752
  env: ptyEnv
22178
22753
  };
22179
22754
  shell = pty_default.spawn(bin, args, restartPtyOptions);
22180
- await pidStore.registerProcess({ pid: shell.pid, cli, args, prompt });
22755
+ try {
22756
+ await pidStore.registerProcess({ pid: shell.pid, cli, args, prompt, cwd: workingDir });
22757
+ } catch (error) {
22758
+ logger.warn(`[pidStore] Failed to register restarted process ${shell.pid}:`, error);
22759
+ }
22760
+ ctx.shell = shell;
22761
+ try {
22762
+ globalAgentRegistry.register(shell.pid, {
22763
+ pid: shell.pid,
22764
+ context: ctx,
22765
+ cwd: workingDir,
22766
+ cli,
22767
+ prompt,
22768
+ startTime: Date.now(),
22769
+ stdoutBuffer: []
22770
+ });
22771
+ } catch (error) {
22772
+ logger.warn(`[agentRegistry] Failed to register restarted agent ${shell.pid}:`, error);
22773
+ }
22181
22774
  shell.onData(onData);
22182
22775
  shell.onExit(onExit2);
22183
22776
  return;
@@ -22188,16 +22781,24 @@ ${prompt}` : prefix;
22188
22781
  return;
22189
22782
  }
22190
22783
  if (ctx.isFatal) {
22784
+ try {
22785
+ await pidStore.updateStatus(shell.pid, "exited", {
22786
+ exitReason: "fatal",
22787
+ exitCode: exitCode2 ?? undefined
22788
+ });
22789
+ } catch (error) {
22790
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22791
+ }
22792
+ return pendingExitCode.resolve(exitCode2);
22793
+ }
22794
+ try {
22191
22795
  await pidStore.updateStatus(shell.pid, "exited", {
22192
- exitReason: "fatal",
22796
+ exitReason: "restarted",
22193
22797
  exitCode: exitCode2 ?? undefined
22194
22798
  });
22195
- return pendingExitCode.resolve(exitCode2);
22799
+ } catch (error) {
22800
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22196
22801
  }
22197
- await pidStore.updateStatus(shell.pid, "exited", {
22198
- exitReason: "restarted",
22199
- exitCode: exitCode2 ?? undefined
22200
- });
22201
22802
  logger.info(`${cli} crashed, restarting...`);
22202
22803
  let restoreArgs = conf.restoreArgs;
22203
22804
  if (cli === "codex") {
@@ -22216,16 +22817,38 @@ ${prompt}` : prefix;
22216
22817
  env: ptyEnv
22217
22818
  };
22218
22819
  shell = pty_default.spawn(cli, restoreArgs, restorePtyOptions);
22219
- await pidStore.registerProcess({ pid: shell.pid, cli, args: restoreArgs, prompt });
22820
+ try {
22821
+ await pidStore.registerProcess({ pid: shell.pid, cli, args: restoreArgs, prompt, cwd: workingDir });
22822
+ } catch (error) {
22823
+ logger.warn(`[pidStore] Failed to register restored process ${shell.pid}:`, error);
22824
+ }
22825
+ ctx.shell = shell;
22826
+ try {
22827
+ globalAgentRegistry.register(shell.pid, {
22828
+ pid: shell.pid,
22829
+ context: ctx,
22830
+ cwd: workingDir,
22831
+ cli,
22832
+ prompt,
22833
+ startTime: Date.now(),
22834
+ stdoutBuffer: []
22835
+ });
22836
+ } catch (error) {
22837
+ logger.warn(`[agentRegistry] Failed to register restored agent ${shell.pid}:`, error);
22838
+ }
22220
22839
  shell.onData(onData);
22221
22840
  shell.onExit(onExit2);
22222
22841
  return;
22223
22842
  }
22224
22843
  const exitReason = agentCrashed ? "crash" : "normal";
22225
- await pidStore.updateStatus(shell.pid, "exited", {
22226
- exitReason,
22227
- exitCode: exitCode2 ?? undefined
22228
- });
22844
+ try {
22845
+ await pidStore.updateStatus(shell.pid, "exited", {
22846
+ exitReason,
22847
+ exitCode: exitCode2 ?? undefined
22848
+ });
22849
+ } catch (error) {
22850
+ logger.warn(`[pidStore] Failed to update status for PID ${shell.pid}:`, error);
22851
+ }
22229
22852
  return pendingExitCode.resolve(exitCode2);
22230
22853
  });
22231
22854
  process.stdout.on("resize", () => {
@@ -22233,23 +22856,111 @@ ${prompt}` : prefix;
22233
22856
  shell.resize(cols, rows);
22234
22857
  });
22235
22858
  const terminalRender = new TerminalTextRender;
22236
- const isStillWorkingQ = () => terminalRender.render().replace(/\s+/g, " ").match(/esc to interrupt|to run in background/);
22859
+ const isStillWorkingQ = () => {
22860
+ const rendered = terminalRender.tail(24).replace(/\s+/g, " ");
22861
+ return conf.working?.some((rgx) => rgx.test(rendered));
22862
+ };
22863
+ let lastHeartbeatRendered = "";
22864
+ const heartbeatInterval = setInterval(async () => {
22865
+ try {
22866
+ const rendered = removeControlCharacters(terminalRender.tail(12));
22867
+ if (rendered === lastHeartbeatRendered)
22868
+ return;
22869
+ lastHeartbeatRendered = rendered;
22870
+ const lines2 = rendered.split(`
22871
+ `).filter((line) => line.trim());
22872
+ for (const line of lines2) {
22873
+ if (conf.ready?.some((rx) => rx.test(line))) {
22874
+ logger.debug(`heartbeat|ready |${line}`);
22875
+ ctx.stdinReady.ready();
22876
+ ctx.stdinFirstReady.ready();
22877
+ }
22878
+ if (conf.enter?.some((rx) => rx.test(line))) {
22879
+ logger.debug(`heartbeat|sendEnter matched|${line}`);
22880
+ await sendEnter(ctx.messageContext, 400);
22881
+ continue;
22882
+ }
22883
+ const typeingRespondMatched = Object.entries(conf.typingRespond ?? {}).filter(([_sendString, onThePatterns]) => onThePatterns.some((rx) => rx.test(line)));
22884
+ if (typeingRespondMatched.length) {
22885
+ await src_default(typeingRespondMatched).map(async ([sendString]) => await sendMessage3(ctx.messageContext, sendString, { waitForReady: false })).toCount();
22886
+ continue;
22887
+ }
22888
+ if (conf.fatal?.some((rx) => rx.test(line))) {
22889
+ logger.debug(`heartbeat|fatal |${line}`);
22890
+ ctx.isFatal = true;
22891
+ await exitAgent();
22892
+ break;
22893
+ }
22894
+ if (conf.restartWithoutContinueArg?.some((rx) => rx.test(line))) {
22895
+ logger.debug(`heartbeat|restart-without-continue|${line}`);
22896
+ ctx.shouldRestartWithoutContinue = true;
22897
+ ctx.isFatal = true;
22898
+ await exitAgent();
22899
+ break;
22900
+ }
22901
+ if (cli === "codex") {
22902
+ const sessionId = extractSessionId(line);
22903
+ if (sessionId) {
22904
+ logger.debug(`heartbeat|session|captured session ID: ${sessionId}`);
22905
+ await storeSessionForCwd(workingDir, sessionId);
22906
+ }
22907
+ }
22908
+ }
22909
+ } catch (error) {
22910
+ logger.debug(`heartbeat|error: ${error}`);
22911
+ }
22912
+ }, 800);
22913
+ const cleanupHeartbeat = () => clearInterval(heartbeatInterval);
22914
+ shell.onExit(cleanupHeartbeat);
22237
22915
  if (exitOnIdle)
22238
22916
  ctx.idleWaiter.wait(exitOnIdle).then(async () => {
22239
22917
  await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
22240
22918
  if (isStillWorkingQ()) {
22241
- logger.warn("[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet");
22919
+ logger.warn(`[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet`);
22242
22920
  return;
22243
22921
  }
22244
- logger.info("[${cli}-yes] ${cli} is idle, exiting...");
22922
+ logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
22245
22923
  await exitAgent();
22246
22924
  });
22247
- await src_default(fromReadable(process.stdin)).map((buffer2) => buffer2.toString()).forkTo(function handleTerminateSignals(s) {
22248
- const handler = createTerminateSignalHandler(ctx.stdinReady, (exitCode2) => {
22925
+ const stdinStream = new ReadableStream({
22926
+ start(controller) {
22927
+ process.stdin.resume();
22928
+ const dataHandler = (chunk) => {
22929
+ try {
22930
+ controller.enqueue(chunk);
22931
+ } catch (err) {}
22932
+ };
22933
+ const endHandler = () => {
22934
+ controller.close();
22935
+ };
22936
+ const errorHandler = (err) => {
22937
+ controller.error(err);
22938
+ };
22939
+ process.stdin.on("data", dataHandler);
22940
+ process.stdin.on("end", endHandler);
22941
+ process.stdin.on("close", endHandler);
22942
+ process.stdin.on("error", errorHandler);
22943
+ },
22944
+ cancel(reason) {
22945
+ process.stdin.pause();
22946
+ }
22947
+ });
22948
+ let aborted2 = false;
22949
+ await src_default(stdinStream).map((buffer2) => {
22950
+ const str = buffer2.toString();
22951
+ const CTRL_Z = "\x1A";
22952
+ const CTRL_C = "\x03";
22953
+ if (!aborted2 && str === CTRL_Z) {
22954
+ return "";
22955
+ }
22956
+ if (!aborted2 && !ctx.stdinReady.isReady && str === CTRL_C) {
22957
+ logger.error("User aborted: SIGINT");
22249
22958
  shell.kill("SIGINT");
22250
- pendingExitCode.resolve(exitCode2);
22251
- });
22252
- return s.map(handler);
22959
+ pendingExitCode.resolve(130);
22960
+ aborted2 = true;
22961
+ return str;
22962
+ }
22963
+ return str;
22253
22964
  }).onStart(async function promptOnStart() {
22254
22965
  logger.debug("Sending prompt message: " + JSON.stringify(prompt));
22255
22966
  if (prompt)
@@ -22277,15 +22988,62 @@ ${prompt}` : prefix;
22277
22988
  }).run();
22278
22989
  }).catch(() => f.run());
22279
22990
  }).by(function consoleResponder(e) {
22280
- if (cli === "codex")
22281
- shell.write(`\x1B[1;1R`);
22282
- return e.forEach((text) => handleConsoleControlCodes(text, shell, terminalRender, cli, verbose));
22283
- }).forkTo(function autoResponse(e) {
22284
- return e.map((e2) => removeControlCharacters(e2)).by((s) => {
22285
- if (conf.noEOL)
22286
- return s;
22287
- return s.lines({ EOL: "NONE" });
22288
- }).forEach(async (line, lineIndex) => createAutoResponseHandler(line, lineIndex, { ctx, conf, cli, workingDir, exitAgent })).run();
22991
+ let lastRendered = "";
22992
+ return e.forEach((chunk) => {
22993
+ terminalRender.write(chunk);
22994
+ if (chunk.includes("\x1B[c") || chunk.includes("\x1B[0c")) {
22995
+ shell.write("\x1B[?1;2c");
22996
+ if (verbose) {
22997
+ logger.debug("device|respond DA: VT100 with Advanced Video Option");
22998
+ }
22999
+ return;
23000
+ }
23001
+ if (process.stdin.isTTY)
23002
+ return;
23003
+ if (!chunk.includes("\x1B[6n"))
23004
+ return;
23005
+ const { col, row } = terminalRender.getCursorPosition();
23006
+ shell.write(`\x1B[${row};${col}R`);
23007
+ logger.debug(`cursor|respond position: row=${String(row)}, col=${String(col)}`);
23008
+ }).forEach(async (line, lineIndex) => {
23009
+ const rendered = terminalRender.tail(24);
23010
+ if (rendered === lastRendered)
23011
+ return;
23012
+ logger.debug(`stdout|${line}`);
23013
+ if (conf.ready?.some((rx) => line.match(rx))) {
23014
+ logger.debug(`ready |${line}`);
23015
+ if (cli === "gemini" && lineIndex <= 80)
23016
+ return;
23017
+ ctx.stdinReady.ready();
23018
+ ctx.stdinFirstReady.ready();
23019
+ }
23020
+ if (conf.enter?.some((rx) => line.match(rx))) {
23021
+ logger.debug(`sendEnter matched|${line}`);
23022
+ return await sendEnter(ctx.messageContext, 400);
23023
+ }
23024
+ const typeingRespondMatched = Object.entries(conf.typingRespond ?? {}).filter(([_sendString, onThePatterns]) => onThePatterns.some((rx) => line.match(rx)));
23025
+ const typingResponded = typeingRespondMatched.length && await src_default(typeingRespondMatched).map(async ([sendString]) => await sendMessage3(ctx.messageContext, sendString, { waitForReady: false })).toCount();
23026
+ if (typingResponded)
23027
+ return;
23028
+ if (conf.fatal?.some((rx) => line.match(rx))) {
23029
+ logger.debug(`fatal |${line}`);
23030
+ ctx.isFatal = true;
23031
+ await exitAgent();
23032
+ }
23033
+ if (conf.restartWithoutContinueArg?.some((rx) => line.match(rx))) {
23034
+ logger.debug(`restart-without-continue|${line}`);
23035
+ ctx.shouldRestartWithoutContinue = true;
23036
+ ctx.isFatal = true;
23037
+ await exitAgent();
23038
+ }
23039
+ if (cli === "codex") {
23040
+ const sessionId = extractSessionId(line);
23041
+ if (sessionId) {
23042
+ logger.debug(`session|captured session ID: ${sessionId}`);
23043
+ await storeSessionForCwd(workingDir, sessionId);
23044
+ }
23045
+ }
23046
+ });
22289
23047
  }).by((s) => removeControlCharactersFromStdout ? s.map((e) => removeControlCharacters(e)) : s).by(createTerminatorStream(pendingExitCode.promise)).to(fromWritable(process.stdout));
22290
23048
  await saveLogFile(ctx.logPaths.logPath, terminalRender.render());
22291
23049
  const exitCode = await pendingExitCode.promise;
@@ -27688,7 +28446,7 @@ var package_default = {
27688
28446
  registry: "https://registry.npmjs.org/"
27689
28447
  },
27690
28448
  scripts: {
27691
- build: "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
28449
+ build: "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun --external=@modelcontextprotocol/sdk",
27692
28450
  postbuild: "bun ./ts/postbuild.ts",
27693
28451
  demo: "bun run build && bun link && claude-yes -- demo",
27694
28452
  dev: "bun ts/index.ts",
@@ -27702,10 +28460,12 @@ var package_default = {
27702
28460
  },
27703
28461
  dependencies: {
27704
28462
  "@snomiao/bun-pty": "^0.3.4",
28463
+ "@snomiao/keyv-sqlite": "^5.0.4",
27705
28464
  "bun-pty": "^0.4.8",
27706
28465
  "from-node-stream": "^0.1.2"
27707
28466
  },
27708
28467
  devDependencies: {
28468
+ "@modelcontextprotocol/sdk": "^1.26.0",
27709
28469
  "@semantic-release/exec": "^7.1.0",
27710
28470
  "@types/bun": "^1.3.6",
27711
28471
  "@types/jest": "^30.0.0",
@@ -27728,7 +28488,8 @@ var package_default = {
27728
28488
  "terminal-render": "^1.3.0",
27729
28489
  vitest: "^4.0.17",
27730
28490
  winston: "^3.19.0",
27731
- yargs: "^18.0.0"
28491
+ yargs: "^18.0.0",
28492
+ zod: "^3.23.0"
27732
28493
  },
27733
28494
  peerDependencies: {
27734
28495
  "node-pty": "latest",
@@ -27930,6 +28691,7 @@ class PidStore2 {
27930
28691
  cli TEXT NOT NULL,
27931
28692
  args TEXT NOT NULL,
27932
28693
  prompt TEXT,
28694
+ cwd TEXT NOT NULL,
27933
28695
  logFile TEXT NOT NULL,
27934
28696
  fifoFile TEXT NOT NULL,
27935
28697
  status TEXT NOT NULL DEFAULT 'active',
@@ -27951,7 +28713,8 @@ class PidStore2 {
27951
28713
  pid,
27952
28714
  cli,
27953
28715
  args,
27954
- prompt
28716
+ prompt,
28717
+ cwd
27955
28718
  }) {
27956
28719
  const now = Date.now();
27957
28720
  const argsJson = JSON.stringify(args);
@@ -27959,16 +28722,16 @@ class PidStore2 {
27959
28722
  const fifoFile = this.getFifoPath(pid);
27960
28723
  try {
27961
28724
  this.db.run(`
27962
- INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
27963
- VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
27964
- `, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
28725
+ INSERT INTO pid_records (pid, cli, args, prompt, cwd, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
28726
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
28727
+ `, [pid, cli, argsJson, prompt, cwd, logFile, fifoFile, now, now]);
27965
28728
  } catch (error) {
27966
28729
  if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
27967
28730
  this.db.run(`
27968
28731
  UPDATE pid_records
27969
- SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
28732
+ SET cli = ?, args = ?, prompt = ?, cwd = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
27970
28733
  WHERE pid = ?
27971
- `, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
28734
+ `, [cli, argsJson, prompt, cwd, logFile, fifoFile, now, now, pid]);
27972
28735
  } else {
27973
28736
  throw error;
27974
28737
  }
@@ -27993,6 +28756,9 @@ class PidStore2 {
27993
28756
  }
27994
28757
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
27995
28758
  }
28759
+ getAllRecords() {
28760
+ return this.db.query("SELECT * FROM pid_records");
28761
+ }
27996
28762
  getLogDir() {
27997
28763
  return path14.resolve(this.storeDir, "logs");
27998
28764
  }
@@ -28072,6 +28838,12 @@ fifo/
28072
28838
  }
28073
28839
 
28074
28840
  // ts/cli.ts
28841
+ var rawArgs = process.argv.slice(2);
28842
+ if (rawArgs[0] === "mcp" && rawArgs[1] === "serve") {
28843
+ const { startMcpServer: startMcpServer2 } = await init_mcp_server().then(() => exports_mcp_server);
28844
+ await startMcpServer2();
28845
+ process.exit(0);
28846
+ }
28075
28847
  var config3 = parseCliArgs(process.argv);
28076
28848
  if (config3.appendPrompt) {
28077
28849
  const ipcPath = await PidStore2.findActiveFifo(process.cwd());
@@ -28126,5 +28898,5 @@ var { exitCode } = await cliYes(config3);
28126
28898
  console.log("exiting process");
28127
28899
  process.exit(exitCode ?? 1);
28128
28900
 
28129
- //# debugId=5E51725C76510FBE64756E2164756E21
28901
+ //# debugId=138DDE1799AB280064756E2164756E21
28130
28902
  //# sourceMappingURL=cli.js.map