imtoagent 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +97 -97
  2. package/bin/imtoagent-real +197 -153
  3. package/bin/imtoagent.cjs +13 -5
  4. package/index.ts +106 -106
  5. package/modules/agent/claude-adapter.ts +6 -6
  6. package/modules/agent/claude.ts +6 -6
  7. package/modules/agent/codex-adapter.ts +13 -13
  8. package/modules/agent/codex-exec-server.ts +11 -11
  9. package/modules/agent/codex.ts +29 -29
  10. package/modules/agent/opencode-adapter.ts +17 -17
  11. package/modules/agent/opencode.ts +10 -10
  12. package/modules/capabilities.ts +33 -33
  13. package/modules/cli/setup.ts +164 -164
  14. package/modules/core/config.ts +5 -5
  15. package/modules/core/error.ts +8 -8
  16. package/modules/core/runtime.ts +10 -10
  17. package/modules/core/session.ts +4 -4
  18. package/modules/core/stats.ts +14 -14
  19. package/modules/core/types.ts +7 -7
  20. package/modules/im/feishu.ts +56 -56
  21. package/modules/im/telegram.ts +23 -23
  22. package/modules/im/wechat.ts +54 -54
  23. package/modules/im/wecom.ts +50 -50
  24. package/modules/media/feishu-inbound-adapter.ts +4 -4
  25. package/modules/media/resolver.ts +11 -11
  26. package/modules/media/telegram-inbound-adapter.ts +8 -8
  27. package/modules/prompt-builder.ts +12 -12
  28. package/modules/proxy/anthropic-proxy.ts +31 -31
  29. package/modules/proxy/codex-proxy.ts +18 -18
  30. package/modules/utils/backend-check.ts +12 -12
  31. package/modules/utils/paths.ts +8 -8
  32. package/package.json +1 -1
  33. package/scripts/postinstall.cjs +10 -10
  34. package/scripts/postinstall.ts +13 -13
  35. package/templates/soul.template/identity.md +5 -5
  36. package/templates/soul.template/profile.md +7 -7
  37. package/templates/soul.template/rules.md +5 -5
  38. package/templates/soul.template/skills.md +2 -2
  39. package/templates/soul.template/workspace.md +3 -3
@@ -66,7 +66,7 @@ export class ClaudeAgentModule {
66
66
  const session = ctx.sessions.get(chatId);
67
67
  if (!session || session.running) return;
68
68
  session.running = true;
69
- console.log(`[${ctx.name}] Claude 循环启动 chat=${chatId.slice(-8)}`);
69
+ console.log(`[${ctx.name}] Claude loop started chat=${chatId.slice(-8)}`);
70
70
 
71
71
  try {
72
72
  const modelSpec = ctx.activeModel;
@@ -134,22 +134,22 @@ export class ClaudeAgentModule {
134
134
  });
135
135
 
136
136
  if (result.subtype === 'error' || result.subtype === 'cancelled') {
137
- await ctx.reply(chatId, `❌ ${result.error || result.result || '未知错误'}`);
137
+ await ctx.reply(chatId, `❌ ${result.error || result.result || 'Unknown error'}`);
138
138
  } else if (fullResponse) {
139
139
  await ctx.sendFormattedReply(chatId, fullResponse);
140
140
  } else {
141
- await ctx.reply(chatId, `✅ 已完成 (${toolCalls} 步操作)`);
141
+ await ctx.reply(chatId, `✅ Completed (${toolCalls} steps)`);
142
142
  }
143
143
 
144
144
  ctx.flushToolLog(chatId);
145
- const costStr = callCost > 0 ? `费用 $${callCost.toFixed(4)}\n` : '';
145
+ const costStr = callCost > 0 ? `Cost $${callCost.toFixed(4)}\n` : '';
146
146
  await ctx.sendProgress(chatId,
147
- `✅ 完成 (${toolCalls} )\n输入 ${callInput.toLocaleString()} Token\n输出 ${callOutput.toLocaleString()} Token\n${costStr}耗时 ${(callDur/1000).toFixed(1)}s`);
147
+ `✅ Completed (${toolCalls} steps)\nInput ${callInput.toLocaleString()} Token\nOutput ${callOutput.toLocaleString()} Token\n${costStr}Duration ${(callDur/1000).toFixed(1)}s`);
148
148
  fullResponse = ''; toolCalls = 0;
149
149
  }
150
150
  }
151
151
  } catch (e: any) {
152
- console.error(`[${ctx.name}] Claude 错误: ${e.message}`);
152
+ console.error(`[${ctx.name}] Claude error: ${e.message}`);
153
153
  await ctx.reply(chatId, `❌ ${e.message}`);
154
154
  } finally {
155
155
  session.running = false;
@@ -64,12 +64,12 @@ async function spawnCodexExec(cwd: string, prompt: string): Promise<{ threadId:
64
64
  let stdout = '', stderr = '';
65
65
  try {
66
66
  [stdout, stderr] = await Promise.all([
67
- new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout 读取失败: ${e?.message || e}`); }),
68
- new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr 读取失败: ${e?.message || e}`); }),
67
+ new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout read failed: ${e?.message || e}`); }),
68
+ new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr read failed: ${e?.message || e}`); }),
69
69
  ]);
70
70
  } catch (ioErr: any) {
71
71
  try { child.kill('SIGKILL'); } catch {}
72
- throw new Error(`codex exec I/O 异常: ${ioErr.message}`);
72
+ throw new Error(`codex exec I/O error: ${ioErr.message}`);
73
73
  }
74
74
 
75
75
  const code = await child.exited.catch(() => -1);
@@ -87,12 +87,12 @@ async function spawnCodexResume(cwd: string, threadId: string, prompt: string):
87
87
  let stdout = '', stderr = '';
88
88
  try {
89
89
  [stdout, stderr] = await Promise.all([
90
- new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout 读取失败: ${e?.message || e}`); }),
91
- new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr 读取失败: ${e?.message || e}`); }),
90
+ new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout read failed: ${e?.message || e}`); }),
91
+ new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr read failed: ${e?.message || e}`); }),
92
92
  ]);
93
93
  } catch (ioErr: any) {
94
94
  try { child.kill('SIGKILL'); } catch {}
95
- throw new Error(`codex exec resume I/O 异常: ${ioErr.message}`);
95
+ throw new Error(`codex exec resume I/O error: ${ioErr.message}`);
96
96
  }
97
97
 
98
98
  const code = await child.exited.catch(() => -1);
@@ -118,7 +118,7 @@ async function runViaAppServer(
118
118
  sessionAny.codexThreadId = await client.startThread(cwd);
119
119
  sessionAny._appServerGen = currentGen;
120
120
  session.metadata.codexThreadId = sessionAny.codexThreadId;
121
- console.log(`[CodexAdapter] app-server 全新 thread=${sessionAny.codexThreadId.slice(-8)}${threadExpired ? ' (进程重启)' : ''}`);
121
+ console.log(`[CodexAdapter] app-server new thread=${sessionAny.codexThreadId.slice(-8)}${threadExpired ? ' (process restarted)' : ''}`);
122
122
  }
123
123
 
124
124
  await client.sendPrompt(sessionAny.codexThreadId, prompt, cwd);
@@ -130,7 +130,7 @@ async function runViaAppServer(
130
130
 
131
131
  for await (const event of client.receiveEvents()) {
132
132
  if (Date.now() - startTime > MAX_DURATION) {
133
- console.error('[CodexAdapter] app-server 任务超时 (10min)');
133
+ console.error('[CodexAdapter] app-server task timed out (10min)');
134
134
  break;
135
135
  }
136
136
 
@@ -145,7 +145,7 @@ async function runViaAppServer(
145
145
  totalUsage.outputTokens += event.usage?.outputTokens || 0;
146
146
  break;
147
147
  case 'error':
148
- throw new Error(`app-server 错误: ${event.error}`);
148
+ throw new Error(`app-server error: ${event.error}`);
149
149
  }
150
150
  }
151
151
 
@@ -177,7 +177,7 @@ export class CodexAdapter implements AgentAdapter {
177
177
  }
178
178
 
179
179
  if (session.codexMode === 'plan') {
180
- effectiveText = `[模式: 先计划后执行] 请先制定一个清晰的计划,等我确认后再执行。用户请求: ${effectiveText}`;
180
+ effectiveText = `[Mode: Plan then execute] Please create a clear plan first, wait for my confirmation before executing. User request: ${effectiveText}`;
181
181
  }
182
182
 
183
183
  const isFresh = session.startFresh || !sessionAny.codexThreadId;
@@ -194,7 +194,7 @@ export class CodexAdapter implements AgentAdapter {
194
194
  execServerUsage = r.usage;
195
195
  } catch (appErr: any) {
196
196
  const errMsg = appErr.message || '';
197
- console.error(`[CodexAdapter] app-server 失败: ${errMsg}`);
197
+ console.error(`[CodexAdapter] app-server failed: ${errMsg}`);
198
198
 
199
199
  if (errMsg.includes('thread not found') || errMsg.includes('Thread not found')) {
200
200
  try {
@@ -202,7 +202,7 @@ export class CodexAdapter implements AgentAdapter {
202
202
  const r2 = await runViaAppServer(cwd, effectiveText, input.chatId, session, true);
203
203
  response = r2.response;
204
204
  execServerUsage = r2.usage;
205
- console.error(`[CodexAdapter] app-server thread 重建成功`);
205
+ console.error(`[CodexAdapter] app-server thread rebuilt successfully`);
206
206
  } catch {
207
207
  useExecFallback = true;
208
208
  }
@@ -225,7 +225,7 @@ export class CodexAdapter implements AgentAdapter {
225
225
  }
226
226
 
227
227
  return {
228
- text: response || '✅ 已完成',
228
+ text: response || '✅ Completed',
229
229
  usage: execServerUsage || undefined,
230
230
  };
231
231
  }
@@ -81,7 +81,7 @@ export class CodexAppServerClient {
81
81
  if (!this._turnActive) return true;
82
82
  this._turnToolCallCount++;
83
83
  if (this._turnToolCallCount > _config.maxToolCallsPerTurn) {
84
- console.error(`[app-server] ⚠️ tool-call loop 检测! chat=${this.chatId.slice(-8)}: ${this._turnToolCallCount} > 上限 ${_config.maxToolCallsPerTurn}`);
84
+ console.error(`[app-server] ⚠️ tool-call loop detected! chat=${this.chatId.slice(-8)}: ${this._turnToolCallCount} times > limit ${_config.maxToolCallsPerTurn}`);
85
85
  return false;
86
86
  }
87
87
  return true;
@@ -107,7 +107,7 @@ export class CodexAppServerClient {
107
107
  approvalPolicy: 'never',
108
108
  });
109
109
  const threadId = result?.thread?.id || '';
110
- if (!threadId) throw new Error('thread/start 未返回 thread.id');
110
+ if (!threadId) throw new Error('thread/start did not return thread.id');
111
111
  console.log(`[app-server] thread started=${threadId.slice(-8)} chat=${this.chatId.slice(-8)}`);
112
112
  return threadId;
113
113
  }
@@ -202,7 +202,7 @@ export class CodexAppServerClient {
202
202
  const req = JSON.stringify({ jsonrpc: '2.0', id, method, params });
203
203
  const timer = setTimeout(() => {
204
204
  this.pendingRequests.delete(id);
205
- reject(new Error(`app-server 请求超时: ${method}`));
205
+ reject(new Error(`app-server request timeout: ${method}`));
206
206
  }, 300000);
207
207
  this.pendingRequests.set(id, { resolve, reject });
208
208
 
@@ -210,7 +210,7 @@ export class CodexAppServerClient {
210
210
  if (!ok) {
211
211
  clearTimeout(timer);
212
212
  this.pendingRequests.delete(id);
213
- reject(new Error(`app-server stdin 写入失败: ${method}`));
213
+ reject(new Error(`app-server stdin write failed: ${method}`));
214
214
  }
215
215
  });
216
216
  }
@@ -231,7 +231,7 @@ class CodexAppServerManager {
231
231
  private _generation = 0; // 每次进程重启递增,用于判断 thread 是否过期
232
232
 
233
233
  async ensureRunning(): Promise<void> {
234
- if (this._shuttingDown) throw new Error('app-server 正在关闭');
234
+ if (this._shuttingDown) throw new Error('app-server is shutting down');
235
235
  if (this.process && !this.process.killed) return;
236
236
  if (this._startPromise) { await this._startPromise; return; }
237
237
  this._startPromise = this._spawn();
@@ -286,7 +286,7 @@ class CodexAppServerManager {
286
286
  } catch {}
287
287
  }
288
288
  this.process = null;
289
- console.log('[app-server] 已关闭');
289
+ console.log('[app-server] shut down');
290
290
  }
291
291
  /** 健康检查:进程存活但 readLoop 已停止 */
292
292
  needsRestart(): boolean {
@@ -295,7 +295,7 @@ class CodexAppServerManager {
295
295
 
296
296
  /** 强制重启 app-server(用于健康检查自动恢复) */
297
297
  async forceRestart(): Promise<void> {
298
- console.warn('[app-server] 健康检查触发强制重启...');
298
+ console.warn('[app-server] Health check triggered forced restart...');
299
299
  await this.shutdown();
300
300
  this._shuttingDown = false;
301
301
  this._initialized = false;
@@ -307,7 +307,7 @@ class CodexAppServerManager {
307
307
  }
308
308
 
309
309
  private async _spawn(): Promise<void> {
310
- console.log('[app-server] 启动 codex app-server (stdio)...');
310
+ console.log('[app-server] starting codex app-server (stdio)...');
311
311
  this.process = Bun.spawn(
312
312
  ['codex', 'app-server',
313
313
  '--listen', 'stdio://',
@@ -322,7 +322,7 @@ class CodexAppServerManager {
322
322
  });
323
323
  this.process.exited
324
324
  .then(async (code: number | null) => {
325
- console.error(`[app-server] 进程退出 code=${code}`);
325
+ console.error(`[app-server] process exited code=${code}`);
326
326
  this.process = null;
327
327
  this.readLoopRunning = false;
328
328
  this._initialized = false;
@@ -347,7 +347,7 @@ class CodexAppServerManager {
347
347
  // 给 app-server 短暂时间完成内部初始化
348
348
  await new Promise(r => setTimeout(r, 500));
349
349
  this._startReadLoop();
350
- console.log('[app-server] 已就绪 (stdio)');
350
+ console.log('[app-server] ready (stdio)');
351
351
  }
352
352
 
353
353
  private _startReadLoop(): void {
@@ -425,7 +425,7 @@ class CodexAppServerManager {
425
425
  // 超限,发送 error 事件强制终止本轮
426
426
  client.dispatchEvent({
427
427
  type: 'error',
428
- error: `⚠️ Tool-call loop 检测:本 turn 已达 ${client.turnToolCallCount} 次工具调用(上限 ${_config.maxToolCallsPerTurn}),已强制终止。建议拆分为更小任务。`,
428
+ error: `⚠️ Tool-call loop detected: this turn has reached ${client.turnToolCallCount} tool calls (limit ${_config.maxToolCallsPerTurn}), forcefully terminated. Consider breaking into smaller tasks.`,
429
429
  });
430
430
  }
431
431
  }
@@ -19,14 +19,14 @@ interface CodexJsonEvent {
19
19
  }
20
20
 
21
21
  const TOOL_NAMES: Record<string, string> = {
22
- Bash: '执行命令', Read: '读取文件', Edit: '编辑文件', Write: '写入文件',
23
- Glob: '搜索文件', Grep: '搜索内容', WebSearch: '搜索网页', WebFetch: '抓取网页',
24
- NotebookEdit: '编辑 Notebook',
25
- // Codex 工具名
26
- command_execution: '执行命令', exec_command: '执行命令', write_stdin: '写入文件', update_plan: '更新计划',
27
- request_user_input: '请求输入', apply_patch: '应用补丁', view_image: '查看图片',
28
- spawn_agent: '启动代理', send_input: '发送输入', resume_agent: '恢复代理',
29
- wait_agent: '等待代理', close_agent: '关闭代理',
22
+ Bash: 'Execute command', Read: 'Read file', Edit: 'Edit file', Write: 'Write file',
23
+ Glob: 'Search files', Grep: 'Search content', WebSearch: 'Web search', WebFetch: 'Fetch webpage',
24
+ NotebookEdit: 'Edit Notebook',
25
+ // Codex tool names
26
+ command_execution: 'Execute command', exec_command: 'Execute command', write_stdin: 'Write to stdin', update_plan: 'Update plan',
27
+ request_user_input: 'Request input', apply_patch: 'Apply patch', view_image: 'View image',
28
+ spawn_agent: 'Spawn agent', send_input: 'Send input', resume_agent: 'Resume agent',
29
+ wait_agent: 'Wait agent', close_agent: 'Close agent',
30
30
  };
31
31
 
32
32
  // ================================================================
@@ -64,17 +64,17 @@ async function spawnCodexExec(
64
64
  cwd, stdout: 'pipe', stderr: 'pipe',
65
65
  });
66
66
 
67
- // 安全读取 stdout/stderr:捕获子进程被 kill 等异常,确保 reject Error 对象
67
+ // Safe read stdout/stderr: catch subprocess kill exceptions, ensure reject carries Error object
68
68
  let stdout = '', stderr = '';
69
69
  try {
70
70
  [stdout, stderr] = await Promise.all([
71
- new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout 读取失败: ${e?.message || e}`); }),
72
- new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr 读取失败: ${e?.message || e}`); }),
71
+ new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout read failed: ${e?.message || e}`); }),
72
+ new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr read failed: ${e?.message || e}`); }),
73
73
  ]);
74
74
  } catch (ioErr: any) {
75
- // 子进程可能已被 kill,尝试获取退出码
75
+ // Subprocess may have been killed, try to get exit code
76
76
  try { child.kill('SIGKILL'); } catch {}
77
- throw new Error(`codex exec I/O 异常: ${ioErr.message}`);
77
+ throw new Error(`codex exec I/O error: ${ioErr.message}`);
78
78
  }
79
79
 
80
80
  const code = await child.exited.catch(() => -1);
@@ -97,12 +97,12 @@ async function spawnCodexResume(
97
97
  let stdout = '', stderr = '';
98
98
  try {
99
99
  [stdout, stderr] = await Promise.all([
100
- new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout 读取失败: ${e?.message || e}`); }),
101
- new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr 读取失败: ${e?.message || e}`); }),
100
+ new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout read failed: ${e?.message || e}`); }),
101
+ new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr read failed: ${e?.message || e}`); }),
102
102
  ]);
103
103
  } catch (ioErr: any) {
104
104
  try { child.kill('SIGKILL'); } catch {}
105
- throw new Error(`codex exec resume I/O 异常: ${ioErr.message}`);
105
+ throw new Error(`codex exec resume I/O error: ${ioErr.message}`);
106
106
  }
107
107
 
108
108
  const code = await child.exited.catch(() => -1);
@@ -129,7 +129,7 @@ async function runViaAppServer(
129
129
  if (isFresh || !session.codexThreadId || threadExpired) {
130
130
  session.codexThreadId = await client.startThread(cwd);
131
131
  session._appServerGen = currentGen;
132
- console.log(`[Codex] app-server 全新线程 thread=${session.codexThreadId.slice(-8)}${threadExpired ? ' (进程重启)' : ''}`);
132
+ console.log(`[Codex] app-server new thread=${session.codexThreadId.slice(-8)}${threadExpired ? ' (process restarted)' : ''}`);
133
133
  }
134
134
  // 后续消息直接 turn/start(同线程延续上下文)
135
135
 
@@ -143,7 +143,7 @@ async function runViaAppServer(
143
143
  for await (const event of client.receiveEvents()) {
144
144
  // 超时保护
145
145
  if (Date.now() - startTime > MAX_DURATION) {
146
- console.error('[Codex] app-server 任务超时 (10min)');
146
+ console.error('[Codex] app-server task timed out (10min)');
147
147
  break;
148
148
  }
149
149
 
@@ -160,7 +160,7 @@ async function runViaAppServer(
160
160
  totalUsage.outputTokens += event.usage?.outputTokens || 0;
161
161
  break;
162
162
  case 'error':
163
- throw new Error(`app-server 错误: ${event.error}`);
163
+ throw new Error(`app-server error: ${event.error}`);
164
164
  }
165
165
  }
166
166
 
@@ -193,7 +193,7 @@ export class CodexAgentModule {
193
193
  try {
194
194
  let effectiveText = text;
195
195
  if (session.codexMode === 'plan') {
196
- effectiveText = `[模式: 先计划后执行] 请先制定一个清晰的计划,等我确认后再执行。用户请求: ${text}`;
196
+ effectiveText = `[Mode: Plan then execute] Please create a clear plan first, wait for my confirmation before executing. User request: ${text}`;
197
197
  }
198
198
 
199
199
  const isFresh = session.startFresh || !session.codexThreadId;
@@ -201,10 +201,10 @@ export class CodexAgentModule {
201
201
  let execServerUsage: { inputTokens: number; outputTokens: number } | null = null;
202
202
 
203
203
  session.startFresh = false;
204
- await ctx.sendProgress(chatId, '💭 思考中...');
204
+ await ctx.sendProgress(chatId, '💭 Thinking...');
205
205
 
206
206
  // 优先尝试 app-server
207
- console.error(`[${ctx.name}] DEBUG 进入 app-server 分支, isFresh=${isFresh}, threadId=${session.codexThreadId?.slice(-8)}`);
207
+ console.error(`[${ctx.name}] DEBUG entering app-server branch, isFresh=${isFresh}, threadId=${session.codexThreadId?.slice(-8)}`);
208
208
  let useExecFallback = false;
209
209
  try {
210
210
  const r = await runViaAppServer(cwd, effectiveText, chatId, session, onTool, isFresh);
@@ -212,7 +212,7 @@ export class CodexAgentModule {
212
212
  execServerUsage = r.usage;
213
213
  } catch (appErr: any) {
214
214
  const errMsg = appErr.message || '';
215
- console.error(`[${ctx.name}] app-server 失败: ${errMsg}`);
215
+ console.error(`[${ctx.name}] app-server failed: ${errMsg}`);
216
216
 
217
217
  // thread not found → app-server 进程内线程丢了,尝试重新创建
218
218
  if (errMsg.includes('thread not found') || errMsg.includes('Thread not found')) {
@@ -221,7 +221,7 @@ export class CodexAgentModule {
221
221
  const r2 = await runViaAppServer(cwd, effectiveText, chatId, session, onTool, true);
222
222
  response = r2.response;
223
223
  execServerUsage = r2.usage;
224
- console.error(`[${ctx.name}] app-server thread 重建成功`);
224
+ console.error(`[${ctx.name}] app-server thread rebuilt successfully`);
225
225
  } catch {
226
226
  useExecFallback = true;
227
227
  }
@@ -236,7 +236,7 @@ export class CodexAgentModule {
236
236
  const r = await spawnCodexExec(cwd, effectiveText, onTool);
237
237
  session.codexThreadId = r.threadId;
238
238
  response = r.response;
239
- console.log(`[${ctx.name}] 全新会话 thread=${r.threadId.slice(-8)}`);
239
+ console.log(`[${ctx.name}] Fresh session thread=${r.threadId.slice(-8)}`);
240
240
  } else {
241
241
  const r = await spawnCodexResume(cwd, session.codexThreadId, effectiveText, onTool);
242
242
  response = r.response;
@@ -250,16 +250,16 @@ export class CodexAgentModule {
250
250
  const cost = calculateCost(ctx.activeModel, usage.inputTokens, usage.outputTokens);
251
251
  ctx.accumulateStats(session, { ...usage, costUSD: cost });
252
252
  await ctx.sendProgress(chatId,
253
- `输入 ${usage.inputTokens.toLocaleString()} Token\n输出 ${usage.outputTokens.toLocaleString()} Token\n费用 $${cost.toFixed(4)}`);
253
+ `Input ${usage.inputTokens.toLocaleString()} Token\nOutput ${usage.outputTokens.toLocaleString()} Token\nCost $${cost.toFixed(4)}`);
254
254
  }
255
255
 
256
256
  if (response) {
257
257
  await ctx.sendFormattedReply(chatId, response);
258
258
  }
259
- else await ctx.reply(chatId, '✅ 已完成');
259
+ else await ctx.reply(chatId, '✅ Completed');
260
260
  ctx.persistSession(chatId, session);
261
261
  } catch (e: any) {
262
- console.error(`[${ctx.name}] Codex 错误: ${e.message}`);
262
+ console.error(`[${ctx.name}] Codex error: ${e.message}`);
263
263
  session.codexThreadId = undefined;
264
264
  try {
265
265
  const r = await spawnCodexExec(cwd, text, onTool);
@@ -268,7 +268,7 @@ export class CodexAgentModule {
268
268
  await ctx.sendFormattedReply(chatId, r.response);
269
269
  }
270
270
  } catch (e2: any) {
271
- await ctx.reply(chatId, `❌ 处理失败: ${e2.message}`);
271
+ await ctx.reply(chatId, `❌ Processing failed: ${e2.message}`);
272
272
  }
273
273
  }
274
274
  }
@@ -63,8 +63,8 @@ async function ocSendPrompt(
63
63
  onTool?: (name: string, args: Record<string, any>) => void
64
64
  ): Promise<{ response: string; toolCalls: Array<{ name: string; summary: string }> }> {
65
65
  const MAX_TURNS = 50;
66
- const TURN_TIMEOUT = 300_000; // 每轮 5 分钟
67
- const MAX_DURATION = 600_000; // 总超时 10 分钟
66
+ const TURN_TIMEOUT = 300_000; // 5 min per turn
67
+ const MAX_DURATION = 600_000; // total timeout 10 min
68
68
  const startTime = Date.now();
69
69
 
70
70
  let promptText = initialText;
@@ -74,7 +74,7 @@ async function ocSendPrompt(
74
74
 
75
75
  while (turn < MAX_TURNS) {
76
76
  if (Date.now() - startTime > MAX_DURATION) {
77
- console.error('[OpenCodeAdapter] 任务总超时 (10min)');
77
+ console.error('[OpenCodeAdapter] Task timed out (10min)');
78
78
  break;
79
79
  }
80
80
  turn++;
@@ -118,22 +118,22 @@ async function ocSendPrompt(
118
118
 
119
119
  // 有文本回复 → 任务完成(OpenCode 内部已完成多轮 agent loop)
120
120
  if (hasText) {
121
- console.log(`[OpenCodeAdapter] ✅ 完成于 turn ${turn}/${MAX_TURNS}`);
121
+ console.log(`[OpenCodeAdapter] ✅ completed at turn ${turn}/${MAX_TURNS}`);
122
122
  break;
123
123
  }
124
124
 
125
125
  // 无文本且无 tool_call → 空响应,结束
126
126
  if (!hasToolCall) {
127
- console.log(`[OpenCodeAdapter] ⚠️ 空响应,结束于 turn ${turn}/${MAX_TURNS}`);
127
+ console.log(`[OpenCodeAdapter] ⚠️ empty response, ending at turn ${turn}/${MAX_TURNS}`);
128
128
  break;
129
129
  }
130
130
 
131
131
  // 仅有 tool_call 无文本 → OpenCode 无法自行执行,推进下一轮
132
- promptText = '继续执行,完成剩余任务';
132
+ promptText = 'Continue executing, complete remaining tasks';
133
133
  }
134
134
 
135
135
  if (turn >= MAX_TURNS) {
136
- console.warn(`[OpenCodeAdapter] ⚠️ 达到最大轮次 ${MAX_TURNS}`);
136
+ console.warn(`[OpenCodeAdapter] ⚠️ reached max turns ${MAX_TURNS}`);
137
137
  }
138
138
 
139
139
  return { response: accumulatedResponse, toolCalls: allToolCalls };
@@ -170,7 +170,7 @@ export class OpenCodeAdapter implements AgentAdapter {
170
170
  }
171
171
 
172
172
  if (session.codexMode === 'plan') {
173
- effectiveText = `[模式: 先计划后执行] 请先制定一个清晰的计划,等我确认后再执行。用户请求: ${effectiveText}`;
173
+ effectiveText = `[Mode: Plan then execute] Please create a clear plan first, wait for my confirmation before executing. User request: ${effectiveText}`;
174
174
  }
175
175
 
176
176
  // 清理标记
@@ -181,11 +181,11 @@ export class OpenCodeAdapter implements AgentAdapter {
181
181
  if (shouldClear || !sessionAny.ocSessionId) {
182
182
  if (sessionAny.ocSessionId) {
183
183
  await ocDeleteSession(serverUrl, sessionAny.ocSessionId);
184
- console.log(`[OpenCodeAdapter] 已清除 oc session=${sessionAny.ocSessionId.slice(-8)}`);
184
+ console.log(`[OpenCodeAdapter] Cleared oc session=${sessionAny.ocSessionId.slice(-8)}`);
185
185
  }
186
186
  sessionAny.ocSessionId = await ocCreateSession(serverUrl, input.chatId);
187
187
  session.metadata.ocSessionId = sessionAny.ocSessionId;
188
- console.log(`[OpenCodeAdapter] 新建 oc session=${sessionAny.ocSessionId.slice(-8)}`);
188
+ console.log(`[OpenCodeAdapter] Created oc session=${sessionAny.ocSessionId.slice(-8)}`);
189
189
  }
190
190
 
191
191
  // 构建系统提示词
@@ -209,7 +209,7 @@ export class OpenCodeAdapter implements AgentAdapter {
209
209
  );
210
210
 
211
211
  return {
212
- text: response || '✅ 已完成',
212
+ text: response || '✅ Completed',
213
213
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
214
214
  };
215
215
  }
@@ -242,12 +242,12 @@ export async function startOpenCodeServer(): Promise<void> {
242
242
  try {
243
243
  const res = await fetch(`${OC_URL}/global/health`, { signal: AbortSignal.timeout(3000) });
244
244
  if (res.ok) {
245
- console.log(`[OpenCodeAdapter] 检测到已有服务运行在 ${OC_URL},复用`);
245
+ console.log(`[OpenCodeAdapter] Detected existing service running at ${OC_URL}, reusing`);
246
246
  return;
247
247
  }
248
248
  } catch {}
249
249
 
250
- console.log('[OpenCodeAdapter] 启动 opencode serve...');
250
+ console.log('[OpenCodeAdapter] starting opencode serve...');
251
251
  const child = Bun.spawn(
252
252
  ['opencode', 'serve', '--port', String(OC_PORT), '--hostname', '127.0.0.1'],
253
253
  {
@@ -279,13 +279,13 @@ export async function startOpenCodeServer(): Promise<void> {
279
279
  const timeout = 15000;
280
280
  while (Date.now() - start < timeout) {
281
281
  if (child.exitCode !== undefined && child.exitCode !== null) {
282
- throw new Error(`OpenCode 进程异常退出,exitCode=${child.exitCode}`);
282
+ throw new Error(`OpenCode process exited unexpectedly, exitCode=${child.exitCode}`);
283
283
  }
284
284
  try {
285
285
  const res = await fetch(`${OC_URL}/global/health`, { signal: AbortSignal.timeout(2000) });
286
286
  if (res.ok) {
287
287
  _ocProcess = child;
288
- console.log(`[OpenCodeAdapter] 服务启动成功 (PID=${child.pid}, ${OC_URL})`);
288
+ console.log(`[OpenCodeAdapter] Service started successfully (PID=${child.pid}, ${OC_URL})`);
289
289
  return;
290
290
  }
291
291
  } catch {}
@@ -294,13 +294,13 @@ export async function startOpenCodeServer(): Promise<void> {
294
294
 
295
295
  // 超时
296
296
  child.kill('SIGTERM');
297
- throw new Error(`OpenCode 服务启动超时 (${timeout}ms)`);
297
+ throw new Error(`OpenCode service startup timed out (${timeout}ms)`);
298
298
  }
299
299
 
300
300
  /** 停止 OpenCode serve 进程 */
301
301
  export async function stopOpenCodeServer(): Promise<void> {
302
302
  if (_ocProcess) {
303
- console.log('[OpenCodeAdapter] 停止 OpenCode 服务...');
303
+ console.log('[OpenCodeAdapter] stopping OpenCode service...');
304
304
  _ocProcess.kill('SIGTERM');
305
305
  await new Promise(r => setTimeout(r, 1000));
306
306
  _ocProcess = null;
@@ -96,7 +96,7 @@ async function ocSendPrompt(
96
96
 
97
97
  while (turn < MAX_TURNS) {
98
98
  if (Date.now() - startTime > MAX_DURATION) {
99
- console.error('[OpenCode] 任务总超时 (10min)');
99
+ console.error('[OpenCode] Task timed out (10min)');
100
100
  break;
101
101
  }
102
102
  turn++;
@@ -133,7 +133,7 @@ async function ocSendPrompt(
133
133
  if (!hasToolCall) break;
134
134
 
135
135
  // 有 tool_call,继续推进(空 prompt)
136
- promptText = '继续';
136
+ promptText = 'Continue';
137
137
  }
138
138
 
139
139
  return { response: accumulatedResponse };
@@ -176,7 +176,7 @@ export class OpenCodeAgentModule {
176
176
  // ① Plan 模式处理
177
177
  let effectiveText = text;
178
178
  if (session.codexMode === 'plan') {
179
- effectiveText = `[模式: 先计划后执行] 请先制定一个清晰的计划,等我确认后再执行。用户请求: ${text}`;
179
+ effectiveText = `[Mode: Plan then execute] Please create a clear plan first, wait for my confirmation before executing. User request: ${text}`;
180
180
  }
181
181
 
182
182
  // ② 清理标记
@@ -187,14 +187,14 @@ export class OpenCodeAgentModule {
187
187
  if (shouldClear || !session.ocSessionId) {
188
188
  if (session.ocSessionId) {
189
189
  await ocDeleteSession(session.ocSessionId);
190
- console.log(`[${ctx.name}] 已清除 oc session=${session.ocSessionId.slice(-8)}`);
190
+ console.log(`[${ctx.name}] Cleared oc session=${session.ocSessionId.slice(-8)}`);
191
191
  }
192
192
  session.ocSessionId = await ocCreateSession(chatId);
193
- console.log(`[${ctx.name}] 新建 oc session=${session.ocSessionId.slice(-8)}`);
193
+ console.log(`[${ctx.name}] Created oc session=${session.ocSessionId.slice(-8)}`);
194
194
  }
195
195
 
196
196
  // ④ 发送进度提示
197
- await ctx.sendProgress(chatId, '💭 思考中...');
197
+ await ctx.sendProgress(chatId, '💭 Thinking...');
198
198
 
199
199
  // ④.⑤ 构建系统提示词
200
200
  const systemPrompt = buildSystemPrompt({
@@ -218,7 +218,7 @@ export class OpenCodeAgentModule {
218
218
  if (response) {
219
219
  await ctx.sendFormattedReply(chatId, response);
220
220
  } else {
221
- await ctx.reply(chatId, '✅ 已完成');
221
+ await ctx.reply(chatId, '✅ Completed');
222
222
  }
223
223
 
224
224
  // ⑧ 统计
@@ -228,15 +228,15 @@ export class OpenCodeAgentModule {
228
228
  const cost = calculateCost(ctx.activeModel, lastUsage.inputTokens, lastUsage.outputTokens);
229
229
  ctx.accumulateStats(session, { ...lastUsage, costUSD: cost });
230
230
  await ctx.sendProgress(chatId,
231
- `输入 ${lastUsage.inputTokens.toLocaleString()} Token\n输出 ${lastUsage.outputTokens.toLocaleString()} Token\n费用 $${cost.toFixed(4)}`);
231
+ `Input ${lastUsage.inputTokens.toLocaleString()} Token\nOutput ${lastUsage.outputTokens.toLocaleString()} Token\nCost $${cost.toFixed(4)}`);
232
232
  }
233
233
 
234
234
  // ⑨ 持久化会话
235
235
  ctx.persistSession(chatId, session);
236
236
 
237
237
  } catch (err: any) {
238
- console.error(`[${ctx.name}] OpenCode 错误: ${err.message}`);
239
- await ctx.reply(chatId, `⚠️ OpenCode 出错:${err.message}`);
238
+ console.error(`[${ctx.name}] OpenCode error: ${err.message}`);
239
+ await ctx.reply(chatId, `⚠️ OpenCode error: ${err.message}`);
240
240
  }
241
241
  }
242
242