shennian 0.2.88 → 0.2.90

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 (143) hide show
  1. package/dist/assets/wechat-channel/macos/manifest.json +22 -0
  2. package/dist/assets/wechat-channel/macos/shennian-wechat-channel-helper +0 -0
  3. package/dist/bin/shennian.js +1 -1
  4. package/dist/publish-build-manifest.json +548 -0
  5. package/dist/scripts/wechat-rpa-confirmation.mjs +5 -97
  6. package/dist/src/agent-env.js +4 -105
  7. package/dist/src/agents/adapter.d.ts +6 -0
  8. package/dist/src/agents/adapter.js +1 -19
  9. package/dist/src/agents/claude.js +8 -305
  10. package/dist/src/agents/codex-control.d.ts +35 -0
  11. package/dist/src/agents/codex-control.js +2 -0
  12. package/dist/src/agents/codex-utils.js +7 -200
  13. package/dist/src/agents/codex.d.ts +8 -0
  14. package/dist/src/agents/codex.js +15 -863
  15. package/dist/src/agents/command-spec.js +2 -413
  16. package/dist/src/agents/config-status.js +1 -226
  17. package/dist/src/agents/cursor.js +1 -249
  18. package/dist/src/agents/custom.js +4 -271
  19. package/dist/src/agents/detect.js +1 -56
  20. package/dist/src/agents/external-channel-instructions.js +10 -94
  21. package/dist/src/agents/gemini.js +1 -173
  22. package/dist/src/agents/manager.js +13 -157
  23. package/dist/src/agents/model-registry/cache.js +1 -37
  24. package/dist/src/agents/model-registry/discovery.js +2 -187
  25. package/dist/src/agents/model-registry/parsers.js +4 -447
  26. package/dist/src/agents/model-registry/runner.js +1 -30
  27. package/dist/src/agents/model-registry/service.js +1 -78
  28. package/dist/src/agents/model-registry/types.js +1 -8
  29. package/dist/src/agents/model-registry.js +1 -18
  30. package/dist/src/agents/openclaw.js +2 -275
  31. package/dist/src/agents/opencode.js +1 -231
  32. package/dist/src/agents/pi-context.js +12 -217
  33. package/dist/src/agents/pi.js +14 -723
  34. package/dist/src/agents/platform-instructions.js +9 -54
  35. package/dist/src/channels/base.d.ts +4 -1
  36. package/dist/src/channels/base.js +1 -3
  37. package/dist/src/channels/registry.js +1 -30
  38. package/dist/src/channels/reply-split.js +10 -89
  39. package/dist/src/channels/runtime.d.ts +1 -0
  40. package/dist/src/channels/runtime.js +5 -533
  41. package/dist/src/channels/secret-registry.d.ts +1 -0
  42. package/dist/src/channels/secret-registry.js +1 -46
  43. package/dist/src/channels/websocket.js +8 -378
  44. package/dist/src/channels/wechat-channel/anchor.d.ts +10 -0
  45. package/dist/src/channels/wechat-channel/anchor.js +1 -0
  46. package/dist/src/channels/wechat-channel/client.d.ts +74 -0
  47. package/dist/src/channels/wechat-channel/client.js +1 -0
  48. package/dist/src/channels/wechat-channel/cooldown.d.ts +15 -0
  49. package/dist/src/channels/wechat-channel/cooldown.js +1 -0
  50. package/dist/src/channels/wechat-channel/fingerprint.d.ts +28 -0
  51. package/dist/src/channels/wechat-channel/fingerprint.js +1 -0
  52. package/dist/src/channels/wechat-channel/helper-assets.d.ts +37 -0
  53. package/dist/src/channels/wechat-channel/helper-assets.js +1 -0
  54. package/dist/src/channels/wechat-channel/helper-client.d.ts +25 -0
  55. package/dist/src/channels/wechat-channel/helper-client.js +3 -0
  56. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +84 -0
  57. package/dist/src/channels/wechat-channel/helper-protocol.js +1 -0
  58. package/dist/src/channels/wechat-channel/index.d.ts +17 -0
  59. package/dist/src/channels/wechat-channel/index.js +1 -0
  60. package/dist/src/channels/wechat-channel/ledger.d.ts +33 -0
  61. package/dist/src/channels/wechat-channel/ledger.js +1 -0
  62. package/dist/src/channels/wechat-channel/media-resolver.d.ts +32 -0
  63. package/dist/src/channels/wechat-channel/media-resolver.js +1 -0
  64. package/dist/src/channels/wechat-channel/message-key.d.ts +19 -0
  65. package/dist/src/channels/wechat-channel/message-key.js +1 -0
  66. package/dist/src/channels/wechat-channel/observer.d.ts +64 -0
  67. package/dist/src/channels/wechat-channel/observer.js +1 -0
  68. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +69 -0
  69. package/dist/src/channels/wechat-channel/outbound-ledger.js +2 -0
  70. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +26 -0
  71. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -0
  72. package/dist/src/channels/wechat-channel/preflight.d.ts +37 -0
  73. package/dist/src/channels/wechat-channel/preflight.js +1 -0
  74. package/dist/src/channels/wechat-channel/runner.d.ts +34 -0
  75. package/dist/src/channels/wechat-channel/runner.js +1 -0
  76. package/dist/src/channels/wechat-channel/runtime.d.ts +45 -0
  77. package/dist/src/channels/wechat-channel/runtime.js +1 -0
  78. package/dist/src/channels/wechat-channel/scheduler.d.ts +35 -0
  79. package/dist/src/channels/wechat-channel/scheduler.js +1 -0
  80. package/dist/src/channels/wechat-rpa/macos-flow.js +1 -96
  81. package/dist/src/channels/wechat-rpa/macos.js +6 -48
  82. package/dist/src/channels/wechat-rpa/normalizer.js +7 -127
  83. package/dist/src/channels/wechat-rpa.d.ts +21 -0
  84. package/dist/src/channels/wechat-rpa.js +6 -1022
  85. package/dist/src/channels/wecom.js +4 -357
  86. package/dist/src/commands/agent.js +6 -131
  87. package/dist/src/commands/daemon-windows.js +8 -48
  88. package/dist/src/commands/daemon.js +19 -1013
  89. package/dist/src/commands/external-attachments.js +1 -51
  90. package/dist/src/commands/external.js +1 -137
  91. package/dist/src/commands/manager.js +2 -389
  92. package/dist/src/commands/pair-qr.js +1 -6
  93. package/dist/src/commands/pair.js +9 -287
  94. package/dist/src/commands/tools.js +1 -34
  95. package/dist/src/commands/upgrade.js +1 -198
  96. package/dist/src/config/index.js +1 -35
  97. package/dist/src/daemon-log.js +6 -58
  98. package/dist/src/env-path.js +1 -64
  99. package/dist/src/fs/boundary.js +1 -126
  100. package/dist/src/fs/handler.js +1 -130
  101. package/dist/src/fs/security.js +1 -32
  102. package/dist/src/fs/text-decoder.d.ts +10 -0
  103. package/dist/src/fs/text-decoder.js +1 -0
  104. package/dist/src/index.js +2 -404
  105. package/dist/src/log-reporter.js +1 -16
  106. package/dist/src/manager/prompt.js +29 -34
  107. package/dist/src/manager/registry.js +2 -269
  108. package/dist/src/manager/runtime.js +19 -1003
  109. package/dist/src/native-fusion/config.js +1 -5
  110. package/dist/src/native-fusion/opencode-parser.js +3 -123
  111. package/dist/src/native-fusion/parser-common.js +8 -264
  112. package/dist/src/native-fusion/parsers.js +8 -729
  113. package/dist/src/native-fusion/service.d.ts +10 -0
  114. package/dist/src/native-fusion/service.js +2 -198
  115. package/dist/src/native-fusion/state.js +1 -22
  116. package/dist/src/native-fusion/types.js +1 -1
  117. package/dist/src/region.js +1 -88
  118. package/dist/src/relay/client.js +1 -343
  119. package/dist/src/session/archive-zip.js +1 -220
  120. package/dist/src/session/handlers/agent-config.js +1 -150
  121. package/dist/src/session/handlers/agents.js +1 -55
  122. package/dist/src/session/handlers/chat.js +2 -733
  123. package/dist/src/session/handlers/control.js +1 -55
  124. package/dist/src/session/handlers/fs.js +1 -747
  125. package/dist/src/session/handlers/session-refresh.js +1 -35
  126. package/dist/src/session/handlers/skills.js +1 -121
  127. package/dist/src/session/handlers/title.js +1 -60
  128. package/dist/src/session/handlers/tool-detail.d.ts +3 -0
  129. package/dist/src/session/handlers/tool-detail.js +1 -0
  130. package/dist/src/session/manager.d.ts +3 -0
  131. package/dist/src/session/manager.js +1 -261
  132. package/dist/src/session/projection.js +1 -54
  133. package/dist/src/session/queue.js +4 -317
  134. package/dist/src/session/remote-attachments.js +1 -72
  135. package/dist/src/session/store.js +3 -109
  136. package/dist/src/session/types.d.ts +4 -0
  137. package/dist/src/session/types.js +1 -4
  138. package/dist/src/skills/registry.js +15 -148
  139. package/dist/src/skills/setup.js +1 -101
  140. package/dist/src/tools/markdown-to-pdf.js +10 -346
  141. package/dist/src/upgrade/engine.js +3 -347
  142. package/package.json +3 -2
  143. package/dist/scripts/wechat-rpa-download-candidates.mjs +0 -105
@@ -1,863 +1,15 @@
1
- // @arch docs/architecture/cli/agent-adapters.md
2
- // @test src/__tests__/codex-adapter.test.ts
3
- import { createInterface } from 'node:readline';
4
- import { randomUUID } from 'node:crypto';
5
- import { AgentAdapter, registerAgent } from './adapter.js';
6
- import { resolveBuiltinCommand, spawnAgentCommand } from './command-spec.js';
7
- import { buildAgentProcessEnv } from '../agent-env.js';
8
- import { ensurePlatformInstructionsFile } from './platform-instructions.js';
9
- import { CODEX_APP_SERVER_CLIENT_INFO, extractAppServerErrorMessage, formatCodexErrorMessage, formatTransientCodexStatus, isCodexCollabAgentItem, isCodexCollabAgentToolName, isCodexLegacyCollabAgentItem, isCodexUnsupportedEffortError, isMissingCodexRolloutError, isTransientCodexErrorMessage, looksLikeCodexInteractiveAuthPrompt, looksLikeFatalCodexStderr, makeThreadTitle, normalizeCodexModelId, normalizeCodexReasoningEffort, normalizeCodexStderr, normalizeTerminalText, safeStringify, stripGitDirectiveArtifacts, } from './codex-utils.js';
10
- export { isCodexUnsupportedEffortError, isMissingCodexRolloutError, normalizeCodexModelId, normalizeCodexReasoningEffort, } from './codex-utils.js';
11
- function buildCodexTextInput(text, attachments) {
12
- const images = attachments
13
- ?.filter((attachment) => attachment.kind === 'image')
14
- .filter((attachment) => !/^https?:\/\//i.test(attachment.path))
15
- .map((attachment) => {
16
- if (attachment.previewData) {
17
- return {
18
- path: attachment.path,
19
- data: attachment.previewData,
20
- mimeType: attachment.mimeType,
21
- };
22
- }
23
- return { path: attachment.path };
24
- })
25
- .filter((attachment) => attachment.path);
26
- return {
27
- type: 'text',
28
- text,
29
- text_elements: [],
30
- ...(images?.length ? { local_images: images } : {}),
31
- };
32
- }
33
- export class CodexAdapter extends AgentAdapter {
34
- options;
35
- type = 'codex';
36
- process = null;
37
- workDir = null;
38
- seq = 0;
39
- runId = '';
40
- pendingUsage;
41
- agentSessionId = null;
42
- hasEmittedText = false;
43
- textTail = '';
44
- terminalState = 'open';
45
- forceCloseTimer = null;
46
- stderrBuf = '';
47
- rpcSeq = 1;
48
- pendingRequests = new Map();
49
- appServerReady = null;
50
- appServerStartupError = null;
51
- activeTurnId = null;
52
- namedThread = false;
53
- deltaItemIds = new Set();
54
- lastTransientStatus = '';
55
- constructor(options = {}) {
56
- super();
57
- this.options = options;
58
- }
59
- externalChannel = null;
60
- shennianSessionId = null;
61
- extraEnv = {};
62
- configure(options) {
63
- this.shennianSessionId = options.sessionId ?? null;
64
- this.externalChannel = options.externalChannel ?? null;
65
- this.extraEnv = options.env ?? {};
66
- }
67
- async start(_sessionId, workDir, agentSessionId) {
68
- this.workDir = workDir;
69
- this.seq = 0;
70
- if (agentSessionId)
71
- this.agentSessionId = agentSessionId;
72
- }
73
- async send(text, modelId, reasoningEffort, attachments) {
74
- if (this.activeTurnId) {
75
- await this.interruptActiveTurn().catch(() => { });
76
- await this.killProcess();
77
- }
78
- const codexModelId = normalizeCodexModelId(modelId);
79
- const codexReasoningEffort = normalizeCodexReasoningEffort(reasoningEffort);
80
- this.runId = randomUUID();
81
- this.seq = 0;
82
- this.resetRunState();
83
- await this.ensureAppServer(codexModelId);
84
- const threadId = this.agentSessionId;
85
- if (!threadId)
86
- throw new Error('codex app-server did not create a thread');
87
- this.emitEvent({ state: 'start', agentSessionId: threadId });
88
- if (!this.namedThread) {
89
- await this.sendRpc('thread/name/set', {
90
- threadId,
91
- name: makeThreadTitle(text),
92
- }).catch(() => { });
93
- this.namedThread = true;
94
- }
95
- const response = await this.startTurnWithRecovery(threadId, text, codexModelId, codexReasoningEffort, attachments);
96
- this.activeTurnId = response.turn?.id ?? null;
97
- }
98
- async resume(agentSessionId) {
99
- await this.killProcess();
100
- this.agentSessionId = agentSessionId;
101
- this.namedThread = true;
102
- this.resetTextState();
103
- }
104
- async setTitle(agentSessionId, title, workDir) {
105
- const threadId = agentSessionId.trim();
106
- const name = title.replace(/\s+/g, ' ').trim();
107
- if (!threadId || !name)
108
- return;
109
- this.agentSessionId = threadId;
110
- if (workDir?.trim())
111
- this.workDir = workDir;
112
- await this.ensureAppServer();
113
- await this.sendRpc('thread/name/set', { threadId, name });
114
- this.namedThread = true;
115
- }
116
- async stop() {
117
- await this.interruptActiveTurn().catch(() => { });
118
- await this.killProcess();
119
- }
120
- spawnCodex(args) {
121
- const spec = resolveBuiltinCommand('codex');
122
- if (!spec) {
123
- this.emit('error', new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));
124
- return;
125
- }
126
- const proc = spawnAgentCommand(spec, args, {
127
- cwd: this.workDir ?? undefined,
128
- stdio: ['ignore', 'pipe', 'pipe'],
129
- env: buildAgentProcessEnv(this.extraEnv),
130
- });
131
- this.process = proc;
132
- const rl = createInterface({ input: proc.stdout });
133
- rl.on('line', (line) => {
134
- if (!line.trim())
135
- return;
136
- try {
137
- const obj = JSON.parse(line);
138
- this.handleStreamEvent(obj);
139
- }
140
- catch {
141
- this.emitEvent({ state: 'delta', text: line });
142
- }
143
- });
144
- let stderrBuf = '';
145
- proc.stderr?.on('data', (chunk) => {
146
- stderrBuf += chunk.toString();
147
- });
148
- proc.on('close', (code) => {
149
- if (this.process !== proc)
150
- return;
151
- this.process = null;
152
- this.clearForceCloseTimer();
153
- this.handleProcessClose(code, stderrBuf);
154
- });
155
- proc.on('error', (err) => {
156
- if (this.process !== proc)
157
- return;
158
- this.process = null;
159
- if (err.code === 'ENOENT') {
160
- this.emit('error', new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));
161
- }
162
- else {
163
- this.emit('error', err);
164
- }
165
- });
166
- }
167
- spawnAppServer() {
168
- const spec = resolveBuiltinCommand('codex');
169
- if (!spec) {
170
- this.emit('error', new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));
171
- return;
172
- }
173
- const args = ['app-server', '--listen', 'stdio://'];
174
- const modelInstructionsFile = this.options.modelInstructionsFile ?? ensurePlatformInstructionsFile(this.workDir ?? process.cwd(), this.externalChannel, this.shennianSessionId ?? undefined);
175
- if (modelInstructionsFile) {
176
- args.push('-c', `model_instructions_file=${JSON.stringify(modelInstructionsFile)}`);
177
- }
178
- const proc = spawnAgentCommand(spec, args, {
179
- cwd: this.workDir ?? undefined,
180
- stdio: ['pipe', 'pipe', 'pipe'],
181
- env: buildAgentProcessEnv({ NO_COLOR: '1', ...this.extraEnv }),
182
- });
183
- this.process = proc;
184
- this.stderrBuf = '';
185
- this.appServerStartupError = null;
186
- const rl = createInterface({ input: proc.stdout });
187
- rl.on('line', (line) => {
188
- if (!line.trim())
189
- return;
190
- let msg;
191
- try {
192
- msg = JSON.parse(line);
193
- }
194
- catch {
195
- this.handleAppServerNonJsonStdout(line);
196
- return;
197
- }
198
- this.handleAppServerMessage(msg);
199
- });
200
- proc.stderr?.on('data', (chunk) => {
201
- this.stderrBuf += chunk.toString();
202
- });
203
- proc.on('close', (code) => {
204
- if (this.process !== proc)
205
- return;
206
- const hadActiveTurn = this.activeTurnId != null;
207
- this.process = null;
208
- this.appServerReady = null;
209
- this.activeTurnId = null;
210
- this.rejectAllPending(new Error(normalizeCodexStderr(this.stderrBuf) || `codex app-server exited with code ${code}`));
211
- if (this.terminalState === 'open' && hadActiveTurn) {
212
- this.handleProcessClose(code, this.stderrBuf);
213
- }
214
- });
215
- proc.on('error', (err) => {
216
- if (this.process !== proc)
217
- return;
218
- this.process = null;
219
- this.rejectAllPending(err);
220
- if (err.code === 'ENOENT') {
221
- this.emit('error', new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));
222
- }
223
- else {
224
- this.emit('error', err);
225
- }
226
- });
227
- }
228
- async ensureAppServer(modelId) {
229
- if (this.appServerReady)
230
- return this.appServerReady;
231
- this.spawnAppServer();
232
- this.appServerReady = this.initializeAppServer(modelId).catch(async (error) => {
233
- this.appServerReady = null;
234
- await this.killProcess();
235
- throw error;
236
- });
237
- return this.appServerReady;
238
- }
239
- async initializeAppServer(modelId) {
240
- const codexModelId = normalizeCodexModelId(modelId);
241
- await this.sendRpc('initialize', {
242
- clientInfo: CODEX_APP_SERVER_CLIENT_INFO,
243
- capabilities: { experimentalApi: true },
244
- });
245
- if (this.agentSessionId) {
246
- const response = await this.sendRpc('thread/resume', {
247
- threadId: this.agentSessionId,
248
- cwd: this.workDir,
249
- approvalPolicy: 'never',
250
- sandbox: 'danger-full-access',
251
- persistExtendedHistory: true,
252
- ...(this.options.hidden ? { threadSource: 'subagent' } : {}),
253
- ...(codexModelId ? { model: codexModelId } : {}),
254
- });
255
- this.agentSessionId = response.thread?.id ?? this.agentSessionId;
256
- this.namedThread = !!response.thread?.name;
257
- return;
258
- }
259
- const response = await this.sendRpc('thread/start', {
260
- cwd: this.workDir,
261
- approvalPolicy: 'never',
262
- sandbox: 'danger-full-access',
263
- serviceName: 'Shennian',
264
- threadSource: this.options.hidden ? 'subagent' : 'user',
265
- experimentalRawEvents: false,
266
- persistExtendedHistory: true,
267
- ...(codexModelId ? { model: codexModelId } : {}),
268
- });
269
- const threadId = response.thread?.id;
270
- if (!threadId)
271
- throw new Error('codex app-server thread/start returned no thread id');
272
- this.agentSessionId = threadId;
273
- this.namedThread = !!response.thread?.name;
274
- }
275
- async startTurnWithRecovery(threadId, text, codexModelId, reasoningEffort, attachments) {
276
- try {
277
- return await this.startTurn(threadId, text, codexModelId, reasoningEffort, attachments);
278
- }
279
- catch (error) {
280
- if (!isMissingCodexRolloutError(error))
281
- throw error;
282
- await this.killProcess();
283
- await this.ensureAppServer(codexModelId);
284
- return await this.startTurn(threadId, text, codexModelId, reasoningEffort, attachments);
285
- }
286
- }
287
- async startTurn(threadId, text, codexModelId, reasoningEffort, attachments) {
288
- try {
289
- return await this.sendRpc('turn/start', {
290
- threadId,
291
- input: [buildCodexTextInput(text, attachments)],
292
- approvalPolicy: 'never',
293
- sandboxPolicy: { type: 'dangerFullAccess' },
294
- ...(codexModelId ? { model: codexModelId } : {}),
295
- ...(reasoningEffort ? { effort: reasoningEffort } : {}),
296
- });
297
- }
298
- catch (error) {
299
- if (reasoningEffort && isCodexUnsupportedEffortError(error)) {
300
- throw new Error(`Codex app-server does not accept reasoning effort "${reasoningEffort}" for this turn. Refresh models or upgrade Codex CLI, then retry.`, { cause: error });
301
- }
302
- throw error;
303
- }
304
- }
305
- async interruptActiveTurn() {
306
- const threadId = this.agentSessionId;
307
- const turnId = this.activeTurnId;
308
- if (!threadId || !turnId || !this.process)
309
- return;
310
- await this.sendRpc('turn/interrupt', { threadId, turnId }, 5_000);
311
- this.activeTurnId = null;
312
- }
313
- sendRpc(method, params, timeoutMs = 60_000) {
314
- if (this.appServerStartupError)
315
- return Promise.reject(this.appServerStartupError);
316
- const proc = this.process;
317
- if (!proc?.stdin)
318
- return Promise.reject(new Error('codex app-server is not running'));
319
- const id = this.rpcSeq++;
320
- const payload = JSON.stringify({ id, method, params });
321
- return new Promise((resolve, reject) => {
322
- const timer = setTimeout(() => {
323
- this.pendingRequests.delete(id);
324
- reject(new Error(`codex app-server request timed out: ${method}`));
325
- }, timeoutMs);
326
- this.pendingRequests.set(id, { resolve, reject, timer });
327
- proc.stdin.write(`${payload}\n`, (error) => {
328
- if (!error)
329
- return;
330
- clearTimeout(timer);
331
- this.pendingRequests.delete(id);
332
- reject(error);
333
- });
334
- });
335
- }
336
- handleAppServerMessage(msg) {
337
- if (msg.id != null) {
338
- const pending = this.pendingRequests.get(msg.id);
339
- if (pending) {
340
- clearTimeout(pending.timer);
341
- this.pendingRequests.delete(msg.id);
342
- if (msg.error) {
343
- pending.reject(new Error(msg.error.message || `codex app-server error ${msg.error.code ?? ''}`.trim()));
344
- }
345
- else {
346
- pending.resolve(msg.result);
347
- }
348
- }
349
- }
350
- if (!msg.method)
351
- return;
352
- const params = msg.params ?? {};
353
- switch (msg.method) {
354
- case 'item/agentMessage/delta': {
355
- const itemId = typeof params.itemId === 'string' ? params.itemId : '';
356
- if (itemId)
357
- this.deltaItemIds.add(itemId);
358
- if (typeof params.delta === 'string')
359
- this.emitText(params.delta);
360
- break;
361
- }
362
- case 'item/completed':
363
- this.handleAppServerCompletedItem(params);
364
- break;
365
- case 'thread/tokenUsage/updated':
366
- this.handleAppServerUsage(params);
367
- break;
368
- case 'turn/completed': {
369
- const turn = typeof params.turn === 'object' && params.turn !== null
370
- ? params.turn
371
- : null;
372
- const turnId = typeof turn?.id === 'string' ? turn.id : null;
373
- if (!this.activeTurnId || turnId === this.activeTurnId) {
374
- this.activeTurnId = null;
375
- this.emitFinalIfOpen();
376
- }
377
- break;
378
- }
379
- case 'error': {
380
- const message = extractAppServerErrorMessage(params);
381
- if (isTransientCodexErrorMessage(message)) {
382
- this.emitTransientStatus(message);
383
- return;
384
- }
385
- this.emitErrorIfOpen(message);
386
- break;
387
- }
388
- }
389
- }
390
- handleAppServerNonJsonStdout(line) {
391
- const normalized = normalizeTerminalText(line);
392
- if (!looksLikeCodexInteractiveAuthPrompt(normalized))
393
- return;
394
- this.failAppServerStartup(new Error('Codex 未登录。请先在这台电脑的终端运行 `codex`,按提示完成 ChatGPT 登录或配置 API key,然后再回到神念发送消息。'));
395
- }
396
- failAppServerStartup(error) {
397
- if (this.appServerStartupError)
398
- return;
399
- this.appServerStartupError = error;
400
- this.rejectAllPending(error);
401
- this.process?.kill('SIGTERM');
402
- }
403
- handleAppServerCompletedItem(params) {
404
- const item = typeof params.item === 'object' && params.item !== null
405
- ? params.item
406
- : null;
407
- if (!item || typeof item.type !== 'string')
408
- return;
409
- if (isCodexCollabAgentItem(item))
410
- return;
411
- switch (item.type) {
412
- case 'agentMessage': {
413
- const id = typeof item.id === 'string' ? item.id : '';
414
- if (id && this.deltaItemIds.has(id))
415
- return;
416
- if (typeof item.text === 'string')
417
- this.emitText(item.text, 'block');
418
- break;
419
- }
420
- case 'commandExecution': {
421
- const command = typeof item.command === 'string' ? item.command : '';
422
- const output = typeof item.aggregatedOutput === 'string' ? item.aggregatedOutput : '';
423
- if (command) {
424
- this.emitEvent({ state: 'tool-call', name: 'command_execution', args: { command } });
425
- }
426
- this.emitEvent({
427
- state: 'tool-result',
428
- name: 'command_execution',
429
- result: [output, item.exitCode != null ? `\n(exit ${item.exitCode})` : ''].join('').trim(),
430
- });
431
- break;
432
- }
433
- case 'mcpToolCall':
434
- case 'dynamicToolCall': {
435
- const tool = typeof item.tool === 'string' ? item.tool : item.type;
436
- if (isCodexCollabAgentToolName(tool))
437
- return;
438
- const args = typeof item.arguments === 'object' && item.arguments !== null
439
- ? item.arguments
440
- : {};
441
- this.emitEvent({ state: 'tool-call', name: tool, args });
442
- this.emitEvent({
443
- state: 'tool-result',
444
- name: tool,
445
- result: safeStringify(item.error ?? item.result ?? item.contentItems ?? ''),
446
- });
447
- break;
448
- }
449
- case 'fileChange': {
450
- const changes = Array.isArray(item.changes) ? item.changes : [];
451
- const summary = changes
452
- .map((change) => {
453
- if (typeof change !== 'object' || change === null)
454
- return '';
455
- const c = change;
456
- const path = typeof c.path === 'string' ? c.path : typeof c.file === 'string' ? c.file : '';
457
- const kind = typeof c.kind === 'string' ? c.kind : typeof c.type === 'string' ? c.type : 'change';
458
- return path ? `${kind}: ${path}` : '';
459
- })
460
- .filter(Boolean)
461
- .join('\n');
462
- if (summary)
463
- this.emitText(summary, 'block');
464
- break;
465
- }
466
- case 'approvalRequest':
467
- case 'approval_request':
468
- case 'permissionRequest':
469
- case 'permission_request':
470
- case 'userInputRequired':
471
- case 'user_input_required': {
472
- this.emitApprovalPending(item);
473
- break;
474
- }
475
- }
476
- }
477
- handleAppServerUsage(params) {
478
- const tokenUsage = typeof params.tokenUsage === 'object' && params.tokenUsage !== null
479
- ? params.tokenUsage
480
- : null;
481
- const last = typeof tokenUsage?.last === 'object' && tokenUsage.last !== null
482
- ? tokenUsage.last
483
- : null;
484
- const total = typeof tokenUsage?.total === 'object' && tokenUsage.total !== null
485
- ? tokenUsage.total
486
- : null;
487
- const usage = last ?? total;
488
- if (!usage)
489
- return;
490
- const input = typeof usage.inputTokens === 'number' ? usage.inputTokens : undefined;
491
- const output = typeof usage.outputTokens === 'number' ? usage.outputTokens : undefined;
492
- if (input != null || output != null) {
493
- this.pendingUsage = {
494
- inputTokens: input ?? 0,
495
- outputTokens: output ?? 0,
496
- };
497
- }
498
- }
499
- handleCodexItemLine(obj) {
500
- const item = obj.item;
501
- if (!item?.type)
502
- return;
503
- if (isCodexLegacyCollabAgentItem(item))
504
- return;
505
- switch (item.type) {
506
- case 'agent_message':
507
- case 'reasoning': {
508
- if (obj.type === 'item.completed' && item.text) {
509
- this.emitText(item.text, 'block');
510
- }
511
- break;
512
- }
513
- case 'command_execution': {
514
- if (obj.type === 'item.started' && item.command) {
515
- this.emitEvent({
516
- state: 'tool-call',
517
- name: 'command_execution',
518
- args: { command: item.command },
519
- });
520
- }
521
- if (obj.type === 'item.completed') {
522
- const parts = [
523
- item.aggregated_output ?? '',
524
- item.exit_code != null ? `\n(exit ${item.exit_code})` : '',
525
- ];
526
- this.emitEvent({
527
- state: 'tool-result',
528
- name: 'command_execution',
529
- result: parts.join('').trim() || String(item.status ?? ''),
530
- });
531
- }
532
- break;
533
- }
534
- case 'mcp_tool_call': {
535
- if (item.tool && isCodexCollabAgentToolName(item.tool))
536
- return;
537
- if (obj.type === 'item.started') {
538
- this.emitEvent({
539
- state: 'tool-call',
540
- name: item.tool ?? 'mcp_tool',
541
- args: {
542
- server: item.server,
543
- ...(item.arguments && typeof item.arguments === 'object' ? item.arguments : {}),
544
- },
545
- });
546
- }
547
- if (obj.type === 'item.completed') {
548
- const errMsg = item.error && typeof item.error === 'object' ? item.error.message : undefined;
549
- this.emitEvent({
550
- state: 'tool-result',
551
- name: item.tool ?? 'mcp_tool',
552
- result: errMsg ?? safeStringify(item.result),
553
- });
554
- }
555
- break;
556
- }
557
- case 'file_change': {
558
- if (obj.type === 'item.completed' && item.changes?.length) {
559
- const summary = item.changes.map((c) => `${c.kind}: ${c.path}`).join('\n');
560
- this.emitText(summary, 'block');
561
- }
562
- break;
563
- }
564
- case 'web_search': {
565
- if (obj.type === 'item.completed' && item.query) {
566
- this.emitText(`[web_search] ${item.query}`, 'block');
567
- }
568
- break;
569
- }
570
- case 'todo_list': {
571
- if (obj.type === 'item.completed' && item.items?.length) {
572
- const lines = item.items.map((t) => `- [${t.completed ? 'x' : ' '}] ${t.text}`).join('\n');
573
- this.emitText(lines, 'block');
574
- }
575
- break;
576
- }
577
- case 'approval_request':
578
- case 'approvalRequest':
579
- case 'permission_request':
580
- case 'permissionRequest':
581
- case 'user_input_required':
582
- case 'userInputRequired': {
583
- if (obj.type === 'item.started' || obj.type === 'item.completed') {
584
- this.emitApprovalPending(item);
585
- }
586
- break;
587
- }
588
- case 'error': {
589
- if (obj.type === 'item.completed' && item.message) {
590
- this.emitEvent({ state: 'error', message: item.message });
591
- }
592
- break;
593
- }
594
- default:
595
- break;
596
- }
597
- }
598
- handleStreamEvent(obj) {
599
- const topType = obj.type ?? obj.event;
600
- if (topType?.startsWith('item.')) {
601
- this.handleCodexItemLine(obj);
602
- }
603
- switch (topType) {
604
- case 'turn.completed': {
605
- const u = obj.usage;
606
- if (u) {
607
- const input = u.input_tokens ?? u.prompt_tokens;
608
- const output = u.output_tokens ?? u.completion_tokens;
609
- if (input != null || output != null) {
610
- this.pendingUsage = {
611
- inputTokens: input ?? 0,
612
- outputTokens: output ?? 0,
613
- };
614
- }
615
- }
616
- this.emitFinalIfOpen();
617
- return;
618
- }
619
- case 'turn.failed': {
620
- const msg = typeof obj.error === 'object' && obj.error?.message
621
- ? obj.error.message
622
- : typeof obj.error === 'string'
623
- ? obj.error
624
- : 'turn failed';
625
- this.emitErrorIfOpen(msg);
626
- return;
627
- }
628
- case 'error': {
629
- const m = typeof obj.message === 'string' ? obj.message : '';
630
- if (m.includes('Reconnecting'))
631
- return;
632
- this.emitErrorIfOpen(m || (typeof obj.error === 'string' ? obj.error : 'stream error'));
633
- return;
634
- }
635
- case 'thread.started': {
636
- const sid = obj.thread_id ?? obj.session_id ?? obj.id;
637
- if (typeof sid === 'string' && sid) {
638
- this.agentSessionId = sid;
639
- this.emitEvent({ state: 'start', agentSessionId: this.agentSessionId });
640
- }
641
- return;
642
- }
643
- case 'turn.started':
644
- return;
645
- }
646
- if (topType?.startsWith('item.'))
647
- return;
648
- const eventType = obj.type ?? obj.event;
649
- switch (eventType) {
650
- case 'message':
651
- case 'text':
652
- case 'content': {
653
- const text = obj.content ?? obj.text;
654
- if (text) {
655
- this.emitText(text);
656
- }
657
- break;
658
- }
659
- case 'function_call':
660
- case 'tool_call': {
661
- if (this.isApprovalLikeName(obj.name)) {
662
- this.emitApprovalPending(obj);
663
- break;
664
- }
665
- if (obj.name && isCodexCollabAgentToolName(obj.name))
666
- return;
667
- this.emitEvent({
668
- state: 'tool-call',
669
- name: obj.name,
670
- args: obj.arguments ?? obj.input,
671
- });
672
- break;
673
- }
674
- case 'function_call_output':
675
- case 'tool_result': {
676
- if (obj.name && isCodexCollabAgentToolName(obj.name))
677
- return;
678
- this.emitEvent({
679
- state: 'tool-result',
680
- name: obj.name,
681
- result: obj.output ?? obj.result,
682
- });
683
- break;
684
- }
685
- case 'approval_request':
686
- case 'approvalRequest':
687
- case 'permission_request':
688
- case 'permissionRequest':
689
- case 'user_input_required':
690
- case 'userInputRequired': {
691
- this.emitApprovalPending(obj);
692
- break;
693
- }
694
- case 'error': {
695
- break;
696
- }
697
- case 'done':
698
- case 'completed': {
699
- if (obj.usage) {
700
- this.pendingUsage = {
701
- inputTokens: obj.usage.prompt_tokens ?? obj.usage.input_tokens ?? 0,
702
- outputTokens: obj.usage.completion_tokens ?? obj.usage.output_tokens ?? 0,
703
- };
704
- }
705
- this.emitFinalIfOpen();
706
- break;
707
- }
708
- }
709
- }
710
- emitEvent(partial) {
711
- const event = {
712
- ...partial,
713
- runId: this.runId,
714
- seq: this.seq++,
715
- };
716
- this.emit('agentEvent', event);
717
- }
718
- isApprovalLikeName(name) {
719
- if (typeof name !== 'string')
720
- return false;
721
- return /approval|permission|confirm|user[_-]?input|request[_-]?user/i.test(name);
722
- }
723
- emitApprovalPending(source) {
724
- const command = typeof source.command === 'string' ? source.command : '';
725
- const title = typeof source.title === 'string' && source.title.trim()
726
- ? source.title
727
- : '需要用户确认';
728
- const rawContent = typeof source.prompt === 'string' ? source.prompt
729
- : typeof source.reason === 'string' ? source.reason
730
- : typeof source.message === 'string' ? source.message
731
- : command ? `Codex 请求执行命令:${command}`
732
- : 'Codex 正在等待用户确认后继续。';
733
- this.emitEvent({
734
- state: 'approval-pending',
735
- approval: {
736
- title,
737
- content: rawContent,
738
- source: 'codex',
739
- actionHint: '当前神念暂不支持远程确认,请回到运行 Codex 的电脑/终端完成确认。',
740
- },
741
- });
742
- }
743
- async killProcess() {
744
- const proc = this.process;
745
- this.appServerReady = null;
746
- this.activeTurnId = null;
747
- if (!proc) {
748
- this.rejectAllPending(new Error('codex app-server stopped'));
749
- return;
750
- }
751
- this.process = null;
752
- this.clearForceCloseTimer();
753
- proc.kill('SIGTERM');
754
- await new Promise((resolve) => {
755
- proc.on('close', resolve);
756
- setTimeout(() => {
757
- proc.kill('SIGKILL');
758
- resolve();
759
- }, 3000);
760
- });
761
- this.rejectAllPending(new Error('codex app-server stopped'));
762
- }
763
- rejectAllPending(error) {
764
- for (const [id, pending] of this.pendingRequests.entries()) {
765
- clearTimeout(pending.timer);
766
- pending.reject(error);
767
- this.pendingRequests.delete(id);
768
- }
769
- }
770
- handleProcessClose(code, stderrBuf) {
771
- const stderr = normalizeCodexStderr(stderrBuf);
772
- if (code !== 0 && code !== null) {
773
- const msg = stderr || `codex exited with code ${code}`;
774
- this.emitErrorIfOpen(msg);
775
- }
776
- else if (looksLikeFatalCodexStderr(stderr)) {
777
- // Codex sometimes surfaces fatal API/auth errors on stderr without
778
- // emitting a protocol-level `turn.failed`, and may still exit 0.
779
- this.emitErrorIfOpen(stderr);
780
- }
781
- else {
782
- this.emitFinalIfOpen();
783
- }
784
- this.pendingUsage = undefined;
785
- }
786
- emitFinalIfOpen() {
787
- if (this.terminalState !== 'open')
788
- return;
789
- this.terminalState = 'final';
790
- this.emitEvent({
791
- state: 'final',
792
- usage: this.pendingUsage,
793
- agentSessionId: this.agentSessionId ?? undefined,
794
- });
795
- }
796
- emitErrorIfOpen(message) {
797
- if (this.terminalState !== 'open')
798
- return;
799
- this.terminalState = 'error';
800
- this.emitEvent({ state: 'error', message: formatCodexErrorMessage(message) });
801
- this.scheduleProcessClose();
802
- }
803
- resetRunState() {
804
- this.pendingUsage = undefined;
805
- this.terminalState = 'open';
806
- this.deltaItemIds.clear();
807
- this.lastTransientStatus = '';
808
- this.clearForceCloseTimer();
809
- this.resetTextState();
810
- }
811
- emitTransientStatus(message) {
812
- const formatted = formatTransientCodexStatus(message);
813
- if (!formatted || formatted === this.lastTransientStatus || this.terminalState !== 'open')
814
- return;
815
- this.lastTransientStatus = formatted;
816
- this.emitEvent({ state: 'delta', text: `${formatted}\n`, thinking: true });
817
- }
818
- resetTextState() {
819
- this.hasEmittedText = false;
820
- this.textTail = '';
821
- }
822
- emitText(text, mode = 'delta') {
823
- if (!text)
824
- return;
825
- let next = stripGitDirectiveArtifacts(text);
826
- if (!next)
827
- return;
828
- if (mode === 'block' && this.hasEmittedText && !next.startsWith('\n')) {
829
- if (this.textTail.endsWith('\n')) {
830
- next = `\n${next}`;
831
- }
832
- else if (!this.textTail.endsWith('\n\n')) {
833
- next = `\n\n${next}`;
834
- }
835
- }
836
- this.hasEmittedText = true;
837
- this.textTail = (this.textTail + next).slice(-4);
838
- this.emitEvent({ state: 'delta', text: next });
839
- }
840
- scheduleProcessClose() {
841
- const proc = this.process;
842
- if (!proc || this.forceCloseTimer)
843
- return;
844
- this.forceCloseTimer = setTimeout(() => {
845
- this.forceCloseTimer = null;
846
- if (this.process !== proc)
847
- return;
848
- proc.kill('SIGTERM');
849
- setTimeout(() => {
850
- if (this.process === proc) {
851
- proc.kill('SIGKILL');
852
- }
853
- }, 1000);
854
- }, 250);
855
- }
856
- clearForceCloseTimer() {
857
- if (!this.forceCloseTimer)
858
- return;
859
- clearTimeout(this.forceCloseTimer);
860
- this.forceCloseTimer = null;
861
- }
862
- }
863
- registerAgent('codex', () => new CodexAdapter());
1
+ import{createInterface as u}from"node:readline";import{randomUUID as y}from"node:crypto";import{AgentAdapter as I,registerAgent as T}from"./adapter.js";import{resolveBuiltinCommand as h,spawnAgentCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as f}from"../agent-env.js";import{ensurePlatformInstructionsFile as S}from"./platform-instructions.js";import{CODEX_APP_SERVER_CLIENT_INFO as k,extractAppServerErrorMessage as E,formatCodexErrorMessage as w,formatTransientCodexStatus as C,isCodexCollabAgentItem as _,isCodexCollabAgentToolName as l,isCodexLegacyCollabAgentItem as A,isCodexUnsupportedEffortError as R,isMissingCodexRolloutError as P,isTransientCodexErrorMessage as q,looksLikeCodexInteractiveAuthPrompt as O,looksLikeFatalCodexStderr as $,makeThreadTitle as U,normalizeCodexModelId as g,normalizeCodexReasoningEffort as L,normalizeCodexStderr as v,normalizeTerminalText as b,safeStringify as x,stripGitDirectiveArtifacts as F}from"./codex-utils.js";import{isCodexUnsupportedEffortError as X,isMissingCodexRolloutError as Q,normalizeCodexModelId as Y,normalizeCodexReasoningEffort as Z}from"./codex-utils.js";function N(p,e){const t=e?.filter(s=>s.kind==="image").filter(s=>!/^https?:\/\//i.test(s.path)).map(s=>s.previewData?{path:s.path,data:s.previewData,mimeType:s.mimeType}:{path:s.path}).filter(s=>s.path);return{type:"text",text:p,text_elements:[],...t?.length?{local_images:t}:{}}}class M extends I{options;type="codex";process=null;workDir=null;seq=0;runId="";pendingUsage;agentSessionId=null;hasEmittedText=!1;textTail="";terminalState="open";forceCloseTimer=null;stderrBuf="";rpcSeq=1;pendingRequests=new Map;appServerReady=null;appServerStartupError=null;activeTurnId=null;namedThread=!1;deltaItemIds=new Set;lastTransientStatus="";constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{}}async start(e,t,s){this.workDir=t,this.seq=0,s&&(this.agentSessionId=s)}async send(e,t,s,r){this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess());const n=g(t),i=L(s);this.runId=y(),this.seq=0,this.resetRunState(),await this.ensureAppServer(n);const a=this.agentSessionId;if(!a)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:a}),this.namedThread||(await this.sendRpc("thread/name/set",{threadId:a,name:U(e)}).catch(()=>{}),this.namedThread=!0);const o=await this.startTurnWithRecovery(a,e,n,i,r);this.activeTurnId=o.turn?.id??null}async resume(e){await this.killProcess(),this.agentSessionId=e,this.namedThread=!0,this.resetTextState()}async setTitle(e,t,s){const r=e.trim(),n=t.replace(/\s+/g," ").trim();!r||!n||(this.agentSessionId=r,s?.trim()&&(this.workDir=s),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:r,name:n}),this.namedThread=!0)}async stop(){await this.interruptActiveTurn().catch(()=>{}),await this.killProcess()}async getStatus(){const e=this.agentSessionId,t=this.activeTurnId;if(!e||!this.process)return{active:!1,runId:t,runPhase:null,canStop:!1};let s=null;try{s=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const r=s?.type==="active"||!!t;return{active:r,runId:t,runPhase:r?this.mapThreadStatusToRunPhase(s):null,canStop:!!(r&&t&&this.process)}}spawnCodex(e){const t=h("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const s=m(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:f(this.extraEnv)});this.process=s,u({input:s.stdout}).on("line",i=>{if(i.trim())try{const a=JSON.parse(i);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:i})}});let n="";s.stderr?.on("data",i=>{n+=i.toString()}),s.on("close",i=>{this.process===s&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(i,n))}),s.on("error",i=>{this.process===s&&(this.process=null,i.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",i))})}spawnAppServer(){const e=h("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],s=this.options.modelInstructionsFile??S(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0);s&&t.push("-c",`model_instructions_file=${JSON.stringify(s)}`);const r=m(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:f({NO_COLOR:"1",...this.extraEnv})});this.process=r,this.stderrBuf="",this.appServerStartupError=null,u({input:r.stdout}).on("line",i=>{if(!i.trim())return;let a;try{a=JSON.parse(i)}catch{this.handleAppServerNonJsonStdout(i);return}this.handleAppServerMessage(a)}),r.stderr?.on("data",i=>{this.stderrBuf+=i.toString()}),r.on("close",i=>{if(this.process!==r)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.rejectAllPending(new Error(v(this.stderrBuf)||`codex app-server exited with code ${i}`)),this.terminalState==="open"&&a&&this.handleProcessClose(i,this.stderrBuf)}),r.on("error",i=>{this.process===r&&(this.process=null,this.rejectAllPending(i),i.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",i))})}async ensureAppServer(e){return this.appServerReady?this.appServerReady:(this.spawnAppServer(),this.appServerReady=this.initializeAppServer(e).catch(async t=>{throw this.appServerReady=null,await this.killProcess(),t}),this.appServerReady)}async initializeAppServer(e){const t=g(e);if(await this.sendRpc("initialize",{clientInfo:k,capabilities:{experimentalApi:!0}}),this.agentSessionId){const n=await this.sendRpc("thread/resume",{threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",sandbox:"danger-full-access",persistExtendedHistory:!0,...this.options.hidden?{threadSource:"subagent"}:{},...t?{model:t}:{}});this.agentSessionId=n.thread?.id??this.agentSessionId,this.namedThread=!!n.thread?.name;return}const s=await this.sendRpc("thread/start",{cwd:this.workDir,approvalPolicy:"never",sandbox:"danger-full-access",serviceName:"Shennian",threadSource:this.options.hidden?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...t?{model:t}:{}}),r=s.thread?.id;if(!r)throw new Error("codex app-server thread/start returned no thread id");this.agentSessionId=r,this.namedThread=!!s.thread?.name}async startTurnWithRecovery(e,t,s,r,n){try{return await this.startTurn(e,t,s,r,n)}catch(i){if(!P(i))throw i;return await this.killProcess(),await this.ensureAppServer(s),await this.startTurn(e,t,s,r,n)}}async startTurn(e,t,s,r,n){try{return await this.sendRpc("turn/start",{threadId:e,input:[N(t,n)],approvalPolicy:"never",sandboxPolicy:{type:"dangerFullAccess"},...s?{model:s}:{},...r?{effort:r}:{}})}catch(i){throw r&&R(i)?new Error(`Codex app-server does not accept reasoning effort "${r}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:i}):i}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;!e||!t||!this.process||(await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3),this.activeTurnId=null)}sendRpc(e,t,s=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const r=this.process;if(!r?.stdin)return Promise.reject(new Error("codex app-server is not running"));const n=this.rpcSeq++,i=JSON.stringify({id:n,method:e,params:t});return new Promise((a,o)=>{const d=setTimeout(()=>{this.pendingRequests.delete(n),o(new Error(`codex app-server request timed out: ${e}`))},s);this.pendingRequests.set(n,{resolve:a,reject:o,timer:d}),r.stdin.write(`${i}
2
+ `,c=>{c&&(clearTimeout(d),this.pendingRequests.delete(n),o(c))})})}handleAppServerMessage(e){if(e.id!=null){const s=this.pendingRequests.get(e.id);s&&(clearTimeout(s.timer),this.pendingRequests.delete(e.id),e.error?s.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):s.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;r&&(this.activeTurnId=r);break}case"thread/status/changed":{const s=typeof t.threadId=="string"?t.threadId:"";if(s&&this.agentSessionId&&s!==this.agentSessionId)break;const r=typeof t.status=="object"&&t.status!==null?t.status:null;r?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(r)});break}case"item/agentMessage/delta":{const s=typeof t.itemId=="string"?t.itemId:"";s&&this.deltaItemIds.add(s),typeof t.delta=="string"&&this.emitText(t.delta);break}case"item/completed":this.handleAppServerCompletedItem(t);break;case"thread/tokenUsage/updated":this.handleAppServerUsage(t);break;case"turn/completed":{const s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;(!this.activeTurnId||r===this.activeTurnId)&&(this.activeTurnId=null,this.emitFinalIfOpen());break}case"error":{const s=E(t);if(q(s)){this.emitTransientStatus(s);return}this.emitErrorIfOpen(s);break}}}mapThreadStatusToRunPhase(e){const t=e?.type==="active"&&Array.isArray(e.activeFlags)?e.activeFlags:[];return t.includes("waitingOnApproval")?"waiting_approval":t.includes("waitingOnUserInput")?"waiting_user_input":"thinking"}handleAppServerNonJsonStdout(e){const t=b(e);O(t)&&this.failAppServerStartup(new Error("Codex \u672A\u767B\u5F55\u3002\u8BF7\u5148\u5728\u8FD9\u53F0\u7535\u8111\u7684\u7EC8\u7AEF\u8FD0\u884C `codex`\uFF0C\u6309\u63D0\u793A\u5B8C\u6210 ChatGPT \u767B\u5F55\u6216\u914D\u7F6E API key\uFF0C\u7136\u540E\u518D\u56DE\u5230\u795E\u5FF5\u53D1\u9001\u6D88\u606F\u3002"))}failAppServerStartup(e){this.appServerStartupError||(this.appServerStartupError=e,this.rejectAllPending(e),this.process?.kill("SIGTERM"))}handleAppServerCompletedItem(e){const t=typeof e.item=="object"&&e.item!==null?e.item:null;if(!(!t||typeof t.type!="string")&&!_(t))switch(t.type){case"agentMessage":{const s=typeof t.id=="string"?t.id:"";if(s&&this.deltaItemIds.has(s))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const s=typeof t.command=="string"?t.command:"",r=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";s&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:s}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[r,t.exitCode!=null?`
3
+ (exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const s=typeof t.tool=="string"?t.tool:t.type;if(l(s))return;const r=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:s,args:r}),this.emitEvent({state:"tool-result",name:s,result:x(t.error??t.result??t.contentItems??"")});break}case"fileChange":{const r=(Array.isArray(t.changes)?t.changes:[]).map(n=>{if(typeof n!="object"||n===null)return"";const i=n,a=typeof i.path=="string"?i.path:typeof i.file=="string"?i.file:"",o=typeof i.kind=="string"?i.kind:typeof i.type=="string"?i.type:"change";return a?`${o}: ${a}`:""}).filter(Boolean).join(`
4
+ `);r&&this.emitText(r,"block");break}case"approvalRequest":case"approval_request":case"permissionRequest":case"permission_request":case"userInputRequired":case"user_input_required":{this.emitApprovalPending(t);break}}}handleAppServerUsage(e){const t=typeof e.tokenUsage=="object"&&e.tokenUsage!==null?e.tokenUsage:null,s=typeof t?.last=="object"&&t.last!==null?t.last:null,r=typeof t?.total=="object"&&t.total!==null?t.total:null,n=s??r;if(!n)return;const i=typeof n.inputTokens=="number"?n.inputTokens:void 0,a=typeof n.outputTokens=="number"?n.outputTokens:void 0;(i!=null||a!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:a??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!A(t))switch(t.type){case"agent_message":case"reasoning":{e.type==="item.completed"&&t.text&&this.emitText(t.text,"block");break}case"command_execution":{if(e.type==="item.started"&&t.command&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:t.command}}),e.type==="item.completed"){const s=[t.aggregated_output??"",t.exit_code!=null?`
5
+ (exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:s.join("").trim()||String(t.status??"")})}break}case"mcp_tool_call":{if(t.tool&&l(t.tool))return;if(e.type==="item.started"&&this.emitEvent({state:"tool-call",name:t.tool??"mcp_tool",args:{server:t.server,...t.arguments&&typeof t.arguments=="object"?t.arguments:{}}}),e.type==="item.completed"){const s=t.error&&typeof t.error=="object"?t.error.message:void 0;this.emitEvent({state:"tool-result",name:t.tool??"mcp_tool",result:s??x(t.result)})}break}case"file_change":{if(e.type==="item.completed"&&t.changes?.length){const s=t.changes.map(r=>`${r.kind}: ${r.path}`).join(`
6
+ `);this.emitText(s,"block")}break}case"web_search":{e.type==="item.completed"&&t.query&&this.emitText(`[web_search] ${t.query}`,"block");break}case"todo_list":{if(e.type==="item.completed"&&t.items?.length){const s=t.items.map(r=>`- [${r.completed?"x":" "}] ${r.text}`).join(`
7
+ `);this.emitText(s,"block")}break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{(e.type==="item.started"||e.type==="item.completed")&&this.emitApprovalPending(t);break}case"error":{e.type==="item.completed"&&t.message&&this.emitEvent({state:"error",message:t.message});break}default:break}}handleStreamEvent(e){const t=e.type??e.event;switch(t?.startsWith("item.")&&this.handleCodexItemLine(e),t){case"turn.completed":{const r=e.usage;if(r){const n=r.input_tokens??r.prompt_tokens,i=r.output_tokens??r.completion_tokens;(n!=null||i!=null)&&(this.pendingUsage={inputTokens:n??0,outputTokens:i??0})}this.emitFinalIfOpen();return}case"turn.failed":{const r=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(r);return}case"error":{const r=typeof e.message=="string"?e.message:"";if(r.includes("Reconnecting"))return;this.emitErrorIfOpen(r||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const r=e.thread_id??e.session_id??e.id;typeof r=="string"&&r&&(this.agentSessionId=r,this.emitEvent({state:"start",agentSessionId:this.agentSessionId}));return}case"turn.started":return}if(t?.startsWith("item."))return;switch(e.type??e.event){case"message":case"text":case"content":{const r=e.content??e.text;r&&this.emitText(r);break}case"function_call":case"tool_call":{if(this.isApprovalLikeName(e.name)){this.emitApprovalPending(e);break}if(e.name&&l(e.name))return;this.emitEvent({state:"tool-call",name:e.name,args:e.arguments??e.input});break}case"function_call_output":case"tool_result":{if(e.name&&l(e.name))return;this.emitEvent({state:"tool-result",name:e.name,result:e.output??e.result});break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{this.emitApprovalPending(e);break}case"error":break;case"done":case"completed":{e.usage&&(this.pendingUsage={inputTokens:e.usage.prompt_tokens??e.usage.input_tokens??0,outputTokens:e.usage.completion_tokens??e.usage.output_tokens??0}),this.emitFinalIfOpen();break}}}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}isApprovalLikeName(e){return typeof e!="string"?!1:/approval|permission|confirm|user[_-]?input|request[_-]?user/i.test(e)}emitApprovalPending(e){const t=typeof e.command=="string"?e.command:"",s=typeof e.title=="string"&&e.title.trim()?e.title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",r=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:t?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${t}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";this.emitEvent({state:"approval-pending",approval:{title:s,content:r,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"}})}async killProcess(){const e=this.process;if(this.appServerReady=null,this.activeTurnId=null,!e){this.rejectAllPending(new Error("codex app-server stopped"));return}this.process=null,this.clearForceCloseTimer(),e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}),this.rejectAllPending(new Error("codex app-server stopped"))}rejectAllPending(e){for(const[t,s]of this.pendingRequests.entries())clearTimeout(s.timer),s.reject(e),this.pendingRequests.delete(t)}handleProcessClose(e,t){const s=v(t);if(e!==0&&e!==null){const r=s||`codex exited with code ${e}`;this.emitErrorIfOpen(r)}else $(s)?this.emitErrorIfOpen(s):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent({state:"final",usage:this.pendingUsage,agentSessionId:this.agentSessionId??void 0}))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent({state:"error",message:w(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}emitTransientStatus(e){const t=C(e);!t||t===this.lastTransientStatus||this.terminalState!=="open"||(this.lastTransientStatus=t,this.emitEvent({state:"delta",text:`${t}
8
+ `,thinking:!0}))}resetTextState(){this.hasEmittedText=!1,this.textTail=""}emitText(e,t="delta"){if(!e)return;let s=F(e);s&&(t==="block"&&this.hasEmittedText&&!s.startsWith(`
9
+ `)&&(this.textTail.endsWith(`
10
+ `)?s=`
11
+ ${s}`:this.textTail.endsWith(`
12
+
13
+ `)||(s=`
14
+
15
+ ${s}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+s).slice(-4),this.emitEvent({state:"delta",text:s}))}scheduleProcessClose(){const e=this.process;!e||this.forceCloseTimer||(this.forceCloseTimer=setTimeout(()=>{this.forceCloseTimer=null,this.process===e&&(e.kill("SIGTERM"),setTimeout(()=>{this.process===e&&e.kill("SIGKILL")},1e3))},250))}clearForceCloseTimer(){this.forceCloseTimer&&(clearTimeout(this.forceCloseTimer),this.forceCloseTimer=null)}}T("codex",()=>new M);export{M as CodexAdapter,X as isCodexUnsupportedEffortError,Q as isMissingCodexRolloutError,Y as normalizeCodexModelId,Z as normalizeCodexReasoningEffort};