farming-code 2.2.7 → 2.2.11

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 (102) hide show
  1. package/README.md +110 -199
  2. package/README.zh_cn.md +106 -237
  3. package/THIRD_PARTY_NOTICES.md +11 -2
  4. package/backend/acp-checkpoint-store.js +198 -0
  5. package/backend/acp-runtime.js +306 -83
  6. package/backend/acp-session-state.js +202 -6
  7. package/backend/acp-transcript.js +112 -0
  8. package/backend/agent-activity.js +6 -157
  9. package/backend/agent-manager.js +1496 -737
  10. package/backend/agent-provider-session.js +17 -242
  11. package/backend/agent-runtime-binding.js +219 -0
  12. package/backend/agent-session-history.js +66 -1
  13. package/backend/auth.js +79 -6
  14. package/backend/codex-models.js +81 -84
  15. package/backend/codex-session-archive.js +45 -0
  16. package/backend/codex-terminal-profile.js +500 -0
  17. package/backend/codex-transcript-sanitizer.js +12 -0
  18. package/backend/codex-transcript.js +230 -8
  19. package/backend/config-manager.js +30 -0
  20. package/backend/control-api.js +192 -17
  21. package/backend/farming-net-pass.js +285 -0
  22. package/backend/farming-net-registry.js +112 -0
  23. package/backend/farming-net-server.js +298 -0
  24. package/backend/farming-session-store.js +5 -13
  25. package/backend/git-worktree-info.js +181 -0
  26. package/backend/local-session-engine.js +411 -186
  27. package/backend/main-page-session.js +5 -2
  28. package/backend/native-pty-controller-generation.js +106 -0
  29. package/backend/native-pty-host-client.js +275 -7
  30. package/backend/native-pty-host-identity.js +86 -0
  31. package/backend/native-pty-host.js +813 -114
  32. package/backend/native-session-engine.js +100 -28
  33. package/backend/packaged-node-pty.js +22 -2
  34. package/backend/provider-adapters.js +253 -0
  35. package/backend/provider-session-service.js +241 -0
  36. package/backend/runtime-observation.js +81 -0
  37. package/backend/server.js +294 -86
  38. package/backend/session-engine-bridge.js +21 -2
  39. package/backend/session-engine-router.js +1 -1
  40. package/backend/session-engine.js +1 -1
  41. package/backend/session-stream-protocol.js +185 -0
  42. package/backend/storage-layout.js +55 -0
  43. package/backend/terminal-attach-checkpoint.js +74 -0
  44. package/backend/terminal-exit-quiescence.js +39 -0
  45. package/backend/terminal-reducer-flow-control.js +97 -0
  46. package/backend/terminal-screen-state.js +11 -2
  47. package/backend/terminal-screen-worker-pool.js +59 -6
  48. package/backend/terminal-screen-worker-thread.js +97 -57
  49. package/backend/terminal-screen-worker.js +133 -51
  50. package/backend/terminal-state-serialization.js +127 -0
  51. package/backend/terminal-status.js +23 -4
  52. package/backend/usage-monitor.js +81 -4
  53. package/backend/workspace-directory.js +152 -0
  54. package/backend/workspace-file-router.js +182 -76
  55. package/backend/workspace-file-service.js +319 -4
  56. package/backend/workspace-root-registry.js +164 -0
  57. package/dist/assets/App-CHYpgECZ.js +208 -0
  58. package/dist/assets/{FileEditorMarkdownPreview-elKWc8Im.js → FileEditorMarkdownPreview--Cri80nQ.js} +92 -92
  59. package/dist/assets/FileEditorPane-DGJlEKdf.js +2 -0
  60. package/dist/assets/IconGlyphs-Cc14sepw.js +1 -0
  61. package/dist/assets/ProjectFilesSection-Cz1wG4NT.js +12 -0
  62. package/dist/assets/{ReviewPage-BaXu1ZdX.js → ReviewPage-LonRc6Fz.js} +1 -1
  63. package/dist/assets/code-dark-B1pAtO_P.css +1 -0
  64. package/dist/assets/file-icons-Bw2qd5iT.js +1 -0
  65. package/dist/assets/{index-B8SHldPE.js → index-miO2r9IZ.js} +3 -3
  66. package/dist/assets/main-DNe0jPw-.css +1 -0
  67. package/dist/assets/workspace-editor-model-BQol4qbA.js +1 -0
  68. package/dist/assets/workspace-editor-monaco-5gFbeDPF.js +1 -0
  69. package/dist/assets/workspace-editor-monaco-xPv8JzHH.js +4 -0
  70. package/dist/assets/workspace-view-state-DvYG_9PH.js +7 -0
  71. package/dist/assets/workspace-working-copy-D8-s_Sgh.js +1 -0
  72. package/dist/index.html +1 -1
  73. package/frontend/farming-net/app.css +625 -0
  74. package/frontend/farming-net/app.js +268 -0
  75. package/frontend/farming-net/index.html +86 -0
  76. package/frontend/reading-anchor.js +198 -0
  77. package/frontend/session-bridge.js +12 -3
  78. package/frontend/session-modal-bridge.js +5 -12
  79. package/frontend/skins/crt/app.js +1976 -793
  80. package/frontend/skins/crt/index.html +313 -23
  81. package/frontend/skins/crt/styles/billing.css +294 -223
  82. package/frontend/skins/crt/styles/monochrome-green.css +7 -2
  83. package/frontend/terminal-replay.js +372 -0
  84. package/package.json +10 -3
  85. package/shared/browser-protocol.d.ts +5 -0
  86. package/shared/browser-protocol.js +130 -0
  87. package/dist/assets/App-iA2BU9P6.js +0 -124
  88. package/dist/assets/FileEditorPane-RWiFD2cq.js +0 -5
  89. package/dist/assets/IconGlyphs-DfL0EBnj.js +0 -1
  90. package/dist/assets/ProjectFilesSection-Q4PDsWmM.js +0 -12
  91. package/dist/assets/code-dark-CDkOQAtK.css +0 -1
  92. package/dist/assets/file-icons-EFUGSSwf.js +0 -1
  93. package/dist/assets/main-DpYuBonn.css +0 -1
  94. package/dist/assets/qoder-C9LmmOSf.svg +0 -1
  95. package/dist/assets/qoder-Cf9gl0Y5.svg +0 -1
  96. package/dist/assets/qoder-gHCinseV.svg +0 -1
  97. package/dist/assets/workspace-view-state-CTyDzk2D.js +0 -1
  98. package/dist/assets/zsh-CLpveKlF.svg +0 -1
  99. package/dist/assets/zsh-FxSpMPbz.svg +0 -1
  100. /package/dist/assets/{api-D1lyBYIQ.js → api-D8nyOEbz.js} +0 -0
  101. /package/dist/assets/{core-ZlAPicox.js → core-D0LFJkDt.js} +0 -0
  102. /package/dist/assets/{useWorkspaceMenuKeyboard-CneKAZUJ.js → useWorkspaceMenuKeyboard-Brws6Ar9.js} +0 -0
@@ -1,42 +1,10 @@
1
- const path = require('path');
2
1
  const { parseCommand } = require('./cli-agents');
3
- const {
4
- createProviderSessionId,
5
- createTemporaryProviderSessionId,
6
- isSafeProviderSessionId,
7
- } = require('./provider-session-id');
8
-
9
- const CODING_SESSION_PROVIDERS = new Set(['codex', 'claude', 'opencode', 'qoder']);
10
- const CODEX_VALUE_OPTIONS = new Set([
11
- '-a',
12
- '-c',
13
- '-C',
14
- '-m',
15
- '-s',
16
- '--ask-for-approval',
17
- '--cd',
18
- '--config',
19
- '--config-profile',
20
- '--model',
21
- '--profile',
22
- '--sandbox',
23
- ]);
24
-
25
- function normalizeProvider(provider) {
26
- const value = String(provider || '').trim().toLowerCase();
27
- return CODING_SESSION_PROVIDERS.has(value) ? value : '';
28
- }
29
-
30
- function providerForProgram(program) {
31
- const basename = path.basename(String(program || '').trim());
32
- if (basename === 'qodercli') return 'qoder';
33
- return normalizeProvider(basename);
34
- }
2
+ const { getProviderAdapter, providerForProgram } = require('./provider-adapters');
3
+ const { isSafeProviderSessionId } = require('./provider-session-id');
35
4
 
36
5
  function sessionFromExactResumeSource(source) {
37
- const match = String(source || '').match(/^(codex|claude|opencode|qoder)-history:(?:home:([A-Za-z0-9._-]+):)?([A-Za-z0-9._:-]+)$/);
38
- if (!match) return null;
39
- if (!isSafeProviderSessionId(match[3])) return null;
6
+ const match = String(source || '').match(/^([a-z0-9_-]+)-history:(?:home:([A-Za-z0-9._-]+):)?([A-Za-z0-9._:-]+)$/);
7
+ if (!match || !getProviderAdapter(match[1]) || !isSafeProviderSessionId(match[3])) return null;
40
8
  return {
41
9
  provider: match[1],
42
10
  providerHomeId: match[2] || 'default',
@@ -44,202 +12,30 @@ function sessionFromExactResumeSource(source) {
44
12
  };
45
13
  }
46
14
 
47
- function optionTakesValue(option, valueOptions) {
48
- if (!option || option.includes('=')) return false;
49
- return valueOptions.has(option);
50
- }
51
-
52
- function firstCodexSubcommand(args) {
53
- for (let index = 0; index < args.length; index += 1) {
54
- const arg = args[index];
55
- if (!arg) continue;
56
- if (arg.startsWith('-')) {
57
- if (optionTakesValue(arg, CODEX_VALUE_OPTIONS)) index += 1;
58
- continue;
59
- }
60
- return { value: arg, index };
61
- }
62
- return null;
63
- }
64
-
65
- function codexSessionIdAfterSubcommand(args, subcommandIndex) {
66
- const positional = [];
67
- for (let index = subcommandIndex + 1; index < args.length; index += 1) {
68
- const arg = args[index];
69
- if (!arg) continue;
70
- if (arg.startsWith('-')) {
71
- if (optionTakesValue(arg, CODEX_VALUE_OPTIONS)) index += 1;
72
- continue;
73
- }
74
- positional.push(arg);
75
- }
76
- const sessionId = positional.at(-1) || '';
77
- return isSafeProviderSessionId(sessionId) ? sessionId : '';
78
- }
79
-
80
- function argValue(args, names) {
81
- for (let index = 0; index < args.length; index += 1) {
82
- const arg = args[index];
83
- for (const name of names) {
84
- if (arg === name) return args[index + 1] || '';
85
- if (arg.startsWith(`${name}=`)) return arg.slice(name.length + 1);
86
- }
87
- }
88
- return '';
89
- }
90
-
91
- function hasArg(args, names) {
92
- return args.some(arg => names.includes(arg) || names.some(name => arg.startsWith(`${name}=`)));
93
- }
94
-
95
- function codexProviderSessionPlan(rawArgs) {
96
- const subcommand = firstCodexSubcommand(rawArgs);
97
- if (subcommand && subcommand.value === 'resume') {
98
- const sessionId = codexSessionIdAfterSubcommand(rawArgs, subcommand.index);
99
- if (sessionId) {
100
- return {
101
- id: sessionId,
102
- temporary: false,
103
- source: 'resume',
104
- };
105
- }
106
- }
107
-
108
- if (subcommand && subcommand.value === 'fork') {
109
- return {
110
- id: createTemporaryProviderSessionId(),
111
- temporary: true,
112
- source: 'codex-fork-temporary',
113
- forkedFromProviderSessionId: codexSessionIdAfterSubcommand(rawArgs, subcommand.index),
114
- };
115
- }
116
-
117
- if (subcommand && subcommand.value) {
118
- return null;
119
- }
120
-
121
- return {
122
- id: createTemporaryProviderSessionId(),
123
- temporary: true,
124
- source: 'codex-temporary',
125
- };
126
- }
127
-
128
- function claudeProviderSessionPlan(rawArgs, launchArgs) {
129
- const explicitSessionId = argValue(rawArgs, ['--session-id']);
130
- const resumeSessionId = argValue(rawArgs, ['--resume']);
131
- const isFork = hasArg(rawArgs, ['--fork-session']);
132
- const isContinue = hasArg(rawArgs, ['--continue', '-c']);
133
-
134
- if (explicitSessionId && isSafeProviderSessionId(explicitSessionId)) {
135
- return {
136
- id: explicitSessionId,
137
- temporary: false,
138
- source: 'launch-session-id',
139
- forkedFromProviderSessionId: isFork && isSafeProviderSessionId(resumeSessionId) ? resumeSessionId : '',
140
- };
141
- }
142
-
143
- if (resumeSessionId && isSafeProviderSessionId(resumeSessionId) && !isFork) {
144
- return {
145
- id: resumeSessionId,
146
- temporary: false,
147
- source: 'resume',
148
- };
149
- }
150
-
151
- if (isContinue) {
152
- return null;
153
- }
154
-
155
- const sessionId = createProviderSessionId();
15
+ function emptyPlan(args) {
156
16
  return {
157
- id: sessionId,
17
+ provider: '',
18
+ id: '',
158
19
  temporary: false,
159
- source: isFork ? 'claude-fork-session-id' : 'claude-session-id',
160
- forkedFromProviderSessionId: isFork && isSafeProviderSessionId(resumeSessionId) ? resumeSessionId : '',
161
- args: ['--session-id', sessionId, ...launchArgs],
162
- };
163
- }
164
-
165
- function qoderProviderSessionPlan(rawArgs, launchArgs) {
166
- const explicitSessionId = argValue(rawArgs, ['--session-id']);
167
- const resumeSessionId = argValue(rawArgs, ['--resume']);
168
- const isFork = hasArg(rawArgs, ['--fork-session']);
169
- const isContinue = hasArg(rawArgs, ['--continue']);
170
-
171
- if (explicitSessionId && isSafeProviderSessionId(explicitSessionId)) {
172
- return {
173
- id: explicitSessionId,
174
- temporary: false,
175
- source: 'launch-session-id',
176
- forkedFromProviderSessionId: isFork && isSafeProviderSessionId(resumeSessionId) ? resumeSessionId : '',
177
- };
178
- }
179
-
180
- if (resumeSessionId && isSafeProviderSessionId(resumeSessionId) && !isFork) {
181
- return {
182
- id: resumeSessionId,
183
- temporary: false,
184
- source: 'resume',
185
- };
186
- }
187
-
188
- if (isContinue) {
189
- return null;
190
- }
191
-
192
- const sessionId = createProviderSessionId();
193
- return {
194
- id: sessionId,
195
- temporary: false,
196
- source: isFork ? 'qoder-fork-session-id' : 'qoder-session-id',
197
- forkedFromProviderSessionId: isFork && isSafeProviderSessionId(resumeSessionId) ? resumeSessionId : '',
198
- args: ['--session-id', sessionId, ...launchArgs],
199
- };
200
- }
201
-
202
- function openCodeProviderSessionPlan(rawArgs) {
203
- const sessionId = argValue(rawArgs, ['--session', '-s']);
204
- if (!sessionId || !isSafeProviderSessionId(sessionId)) return null;
205
- if (hasArg(rawArgs, ['--fork'])) {
206
- return {
207
- id: createTemporaryProviderSessionId(),
208
- temporary: true,
209
- source: 'opencode-fork-temporary',
210
- forkedFromProviderSessionId: sessionId,
211
- };
212
- }
213
- return {
214
- id: sessionId,
215
- temporary: false,
216
- source: 'resume',
20
+ source: '',
21
+ forkedFromProviderSessionId: '',
22
+ args,
217
23
  };
218
24
  }
219
25
 
220
26
  function buildAgentProviderSessionPlan({ command, program, args, source } = {}) {
221
27
  const sourceSession = sessionFromExactResumeSource(source);
222
28
  const rawParts = parseCommand(command);
223
- const rawProgram = rawParts[0] || program || '';
224
- const provider = sourceSession ? sourceSession.provider : providerForProgram(rawProgram || program);
225
- if (!provider) {
226
- return {
227
- provider: '',
228
- id: '',
229
- key: '',
230
- temporary: false,
231
- source: '',
232
- forkedFromProviderSessionId: '',
233
- args: Array.isArray(args) ? args : [],
234
- };
235
- }
236
-
29
+ const provider = sourceSession?.provider || providerForProgram(rawParts[0] || program);
237
30
  const launchArgs = Array.isArray(args) ? args : [];
31
+ const adapter = getProviderAdapter(provider);
32
+ if (!adapter) return emptyPlan(launchArgs);
33
+
238
34
  if (sourceSession) {
239
35
  return {
240
36
  provider,
241
37
  id: sourceSession.sessionId,
242
- providerHomeId: sourceSession.providerHomeId || 'default',
38
+ providerHomeId: sourceSession.providerHomeId,
243
39
  temporary: false,
244
40
  source: 'resume-source',
245
41
  forkedFromProviderSessionId: '',
@@ -247,29 +43,8 @@ function buildAgentProviderSessionPlan({ command, program, args, source } = {})
247
43
  };
248
44
  }
249
45
 
250
- const rawArgs = rawParts.slice(1);
251
- let plan = null;
252
- if (provider === 'codex') {
253
- plan = codexProviderSessionPlan(rawArgs);
254
- } else if (provider === 'claude') {
255
- plan = claudeProviderSessionPlan(rawArgs, launchArgs);
256
- } else if (provider === 'qoder') {
257
- plan = qoderProviderSessionPlan(rawArgs, launchArgs);
258
- } else if (provider === 'opencode') {
259
- plan = openCodeProviderSessionPlan(rawArgs);
260
- }
261
-
262
- if (!plan || !plan.id) {
263
- return {
264
- provider: '',
265
- id: '',
266
- temporary: false,
267
- source: '',
268
- forkedFromProviderSessionId: '',
269
- args: launchArgs,
270
- };
271
- }
272
-
46
+ const plan = adapter.planSession(rawParts.slice(1), launchArgs);
47
+ if (!plan?.id) return emptyPlan(launchArgs);
273
48
  return {
274
49
  provider,
275
50
  id: plan.id,
@@ -0,0 +1,219 @@
1
+ const RUNTIME_KINDS = new Set(['terminal', 'acp', 'json', 'app-server']);
2
+
3
+ function terminalBinding() {
4
+ return { kind: 'terminal' };
5
+ }
6
+
7
+ function acpBinding(source = {}) {
8
+ return {
9
+ kind: 'acp',
10
+ state: source.state || source.acpState || '',
11
+ error: source.error || source.acpError || '',
12
+ stopReason: source.stopReason || source.acpStopReason || '',
13
+ pendingPermission: source.pendingPermission || source.acpPendingPermission || null,
14
+ pendingPermissions: source.pendingPermissions || source.acpPendingPermissions || [],
15
+ pendingElicitation: source.pendingElicitation || source.acpPendingElicitation || null,
16
+ pendingElicitations: source.pendingElicitations || source.acpPendingElicitations || [],
17
+ activeElicitations: source.activeElicitations || source.acpActiveElicitations || [],
18
+ sessionUpdatedAt: source.sessionUpdatedAt || source.acpSessionUpdatedAt || '',
19
+ sessionRevision: Number(source.sessionRevision ?? source.acpSessionRevision) || 0,
20
+ };
21
+ }
22
+
23
+ function jsonBinding(source = {}) {
24
+ return {
25
+ kind: 'json',
26
+ state: source.state || source.jsonCliState || '',
27
+ error: source.error || source.jsonCliError || '',
28
+ transcriptUpdatedAt: source.transcriptUpdatedAt || source.jsonCliTranscriptUpdatedAt || '',
29
+ events: source.events || source.jsonCliEvents || [],
30
+ };
31
+ }
32
+
33
+ function appServerBinding(source = {}) {
34
+ return {
35
+ kind: 'app-server',
36
+ state: source.state || source.codexAppServerState || '',
37
+ endpoint: source.endpoint || source.codexAppServerEndpoint || '',
38
+ threadId: source.threadId || source.codexAppServerThreadId || '',
39
+ turnId: source.turnId || source.codexAppServerTurnId || '',
40
+ error: source.error || source.codexAppServerError || '',
41
+ pendingRequestId: source.pendingRequestId || source.codexAppServerPendingRequestId || '',
42
+ pendingRequestMethod: source.pendingRequestMethod || source.codexAppServerPendingRequestMethod || '',
43
+ pendingRequest: source.pendingRequest || source.codexAppServerPendingRequest || null,
44
+ notice: source.notice || source.codexAppServerNotice || null,
45
+ goal: source.goal || source.codexAppServerGoal || null,
46
+ observerDeferred: source.observerDeferred === true || source.codexCliObserverDeferred === true,
47
+ homePath: source.homePath || source.codexAppServerHomePath || '',
48
+ transcriptUpdatedAt: source.transcriptUpdatedAt || source.codexAppServerTranscriptUpdatedAt || '',
49
+ };
50
+ }
51
+
52
+ function runtimeKind(agent) {
53
+ if (RUNTIME_KINDS.has(agent?.runtimeBinding?.kind)) return agent.runtimeBinding.kind;
54
+ if (agent?.agentRuntimeMode === 'acp') return 'acp';
55
+ if (agent?.agentRuntimeMode === 'json') return 'json';
56
+ return agent?.codexRuntimeMode === 'app-server' ? 'app-server' : 'terminal';
57
+ }
58
+
59
+ function bindingFromLegacy(agent) {
60
+ if (RUNTIME_KINDS.has(agent?.runtimeBinding?.kind)) return agent.runtimeBinding;
61
+ switch (runtimeKind(agent)) {
62
+ case 'acp': return acpBinding(agent);
63
+ case 'json': return jsonBinding(agent);
64
+ case 'app-server': return appServerBinding(agent);
65
+ default: return terminalBinding();
66
+ }
67
+ }
68
+
69
+ function runtimeBindingFor(kind, source = {}) {
70
+ switch (kind) {
71
+ case 'acp': return acpBinding(source);
72
+ case 'json': return jsonBinding(source);
73
+ case 'app-server': return appServerBinding(source);
74
+ default: return terminalBinding();
75
+ }
76
+ }
77
+
78
+ function runtimeBindingOf(agent, expectedKind) {
79
+ const binding = bindingFromLegacy(agent);
80
+ return !expectedKind || binding.kind === expectedKind ? binding : null;
81
+ }
82
+
83
+ function replaceRuntimeBinding(agent, kind, source = {}) {
84
+ const binding = runtimeBindingFor(kind, source);
85
+ agent.runtimeBinding = binding;
86
+ return binding;
87
+ }
88
+
89
+ const LEGACY_RUNTIME_FIELDS = [
90
+ 'acpState', 'acpError', 'acpStopReason', 'acpPendingPermission', 'acpPendingPermissions',
91
+ 'acpPendingElicitation', 'acpPendingElicitations', 'acpActiveElicitations',
92
+ 'acpSessionUpdatedAt', 'acpSessionRevision', 'jsonCliState', 'jsonCliError',
93
+ 'jsonCliTranscriptUpdatedAt', 'codexAppServerState', 'codexAppServerEndpoint',
94
+ 'codexAppServerThreadId', 'codexAppServerTurnId', 'codexAppServerError',
95
+ 'codexAppServerPendingRequestId', 'codexAppServerPendingRequestMethod',
96
+ 'codexAppServerPendingRequest', 'codexAppServerNotice', 'codexAppServerGoal',
97
+ 'codexCliObserverDeferred', 'codexAppServerHomePath', 'codexAppServerTranscriptUpdatedAt',
98
+ ];
99
+
100
+ function installRuntimeBinding(agent) {
101
+ if (!agent || typeof agent !== 'object') return agent;
102
+ const jsonResumeEvents = Array.isArray(agent.runtimeBinding?.events)
103
+ ? agent.runtimeBinding.events
104
+ : (Array.isArray(agent.jsonCliEvents) ? agent.jsonCliEvents : []);
105
+ const binding = bindingFromLegacy(agent);
106
+ agent.runtimeBinding = binding;
107
+ agent.runtimeResumeState = {
108
+ ...(agent.runtimeResumeState || {}),
109
+ jsonEvents: jsonResumeEvents,
110
+ };
111
+ for (const name of ['agentRuntimeMode', 'codexRuntimeMode', 'jsonCliEvents', ...LEGACY_RUNTIME_FIELDS]) {
112
+ delete agent[name];
113
+ }
114
+ return agent;
115
+ }
116
+
117
+ class RuntimeAgentMap extends Map {
118
+ set(key, agent) {
119
+ return super.set(key, installRuntimeBinding(agent));
120
+ }
121
+ }
122
+
123
+ function publicRuntimeBinding(agent) {
124
+ const binding = RUNTIME_KINDS.has(agent?.runtimeBinding?.kind)
125
+ ? agent.runtimeBinding
126
+ : bindingFromLegacy(agent);
127
+ if (binding.kind === 'terminal') return terminalBinding();
128
+ if (binding.kind === 'json') {
129
+ return {
130
+ kind: 'json',
131
+ state: binding.state,
132
+ error: binding.error,
133
+ transcriptUpdatedAt: binding.transcriptUpdatedAt,
134
+ };
135
+ }
136
+ if (binding.kind === 'app-server') {
137
+ return {
138
+ kind: 'app-server',
139
+ state: binding.state,
140
+ endpoint: binding.endpoint,
141
+ threadId: binding.threadId,
142
+ turnId: binding.turnId,
143
+ error: binding.error,
144
+ pendingRequestId: binding.pendingRequestId,
145
+ pendingRequestMethod: binding.pendingRequestMethod,
146
+ pendingRequest: binding.pendingRequest,
147
+ notice: binding.notice,
148
+ goal: binding.goal,
149
+ observerDeferred: binding.observerDeferred,
150
+ };
151
+ }
152
+ return { ...binding };
153
+ }
154
+
155
+ function runtimeState(agent) {
156
+ const binding = RUNTIME_KINDS.has(agent?.runtimeBinding?.kind)
157
+ ? agent.runtimeBinding
158
+ : bindingFromLegacy(agent);
159
+ return binding.kind === 'terminal' ? '' : binding.state || '';
160
+ }
161
+
162
+ function legacyRuntimeMetadata(agent) {
163
+ const binding = bindingFromLegacy(agent);
164
+ const metadata = {
165
+ codexRuntimeMode: binding.kind === 'app-server' ? 'app-server' : 'cli',
166
+ agentRuntimeMode: ['acp', 'json'].includes(binding.kind) ? binding.kind : 'terminal',
167
+ };
168
+ if (binding.kind === 'acp') {
169
+ return {
170
+ ...metadata,
171
+ acpState: binding.state,
172
+ acpError: binding.error,
173
+ acpStopReason: binding.stopReason,
174
+ acpPendingPermission: binding.pendingPermission,
175
+ acpPendingPermissions: binding.pendingPermissions,
176
+ acpPendingElicitation: binding.pendingElicitation,
177
+ acpPendingElicitations: binding.pendingElicitations,
178
+ acpActiveElicitations: binding.activeElicitations,
179
+ acpSessionUpdatedAt: binding.sessionUpdatedAt,
180
+ acpSessionRevision: binding.sessionRevision,
181
+ };
182
+ }
183
+ if (binding.kind === 'json') {
184
+ return {
185
+ ...metadata,
186
+ jsonCliState: binding.state,
187
+ jsonCliError: binding.error,
188
+ jsonCliTranscriptUpdatedAt: binding.transcriptUpdatedAt,
189
+ };
190
+ }
191
+ if (binding.kind === 'app-server') {
192
+ return {
193
+ ...metadata,
194
+ codexAppServerHomePath: binding.homePath,
195
+ codexAppServerState: binding.state,
196
+ codexAppServerEndpoint: binding.endpoint,
197
+ codexAppServerThreadId: binding.threadId,
198
+ codexAppServerTurnId: binding.turnId,
199
+ codexAppServerError: binding.error,
200
+ codexAppServerPendingRequestId: binding.pendingRequestId,
201
+ codexAppServerPendingRequestMethod: binding.pendingRequestMethod,
202
+ codexAppServerPendingRequest: binding.pendingRequest,
203
+ codexCliObserverDeferred: binding.observerDeferred,
204
+ };
205
+ }
206
+ return metadata;
207
+ }
208
+
209
+ module.exports = {
210
+ RuntimeAgentMap,
211
+ installRuntimeBinding,
212
+ legacyRuntimeMetadata,
213
+ publicRuntimeBinding,
214
+ replaceRuntimeBinding,
215
+ runtimeBindingFor,
216
+ runtimeBindingOf,
217
+ runtimeKind,
218
+ runtimeState,
219
+ };
@@ -25,6 +25,61 @@ function quoteCommandArg(value) {
25
25
  return `'${String(value).replace(/'/g, `'\\''`)}'`;
26
26
  }
27
27
 
28
+ function tomlStringAssignment(line, key) {
29
+ const match = String(line || '').match(new RegExp(`^\\s*${key}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*"|'[^']*')\\s*(?:#.*)?$`));
30
+ if (!match) return '';
31
+ const literal = match[1];
32
+ if (literal.startsWith('"')) {
33
+ try {
34
+ return JSON.parse(literal).trim();
35
+ } catch {
36
+ return '';
37
+ }
38
+ }
39
+ return literal.slice(1, -1).trim();
40
+ }
41
+
42
+ function codexProfileNameForSection(section) {
43
+ const match = String(section || '').trim().match(/^profiles\.(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_-]+))$/);
44
+ return match ? (match[1] || match[2] || match[3] || '') : '';
45
+ }
46
+
47
+ function resolveCodexResumeModelProvider(codexHome) {
48
+ const home = normalizePathValue(codexHome)
49
+ || normalizePathValue(process.env.CODEX_HOME)
50
+ || path.join(os.homedir(), '.codex');
51
+ let config = '';
52
+ try {
53
+ config = fs.readFileSync(path.join(home, 'config.toml'), 'utf8');
54
+ } catch {
55
+ return 'openai';
56
+ }
57
+
58
+ let section = '';
59
+ let activeProfile = '';
60
+ let topLevelProvider = '';
61
+ const profileProviders = new Map();
62
+ for (const line of config.split(/\r?\n/)) {
63
+ const sectionMatch = line.match(/^\s*\[([^\]]+)]\s*(?:#.*)?$/);
64
+ if (sectionMatch) {
65
+ section = sectionMatch[1].trim();
66
+ continue;
67
+ }
68
+ if (!section) {
69
+ activeProfile = tomlStringAssignment(line, 'profile') || activeProfile;
70
+ topLevelProvider = tomlStringAssignment(line, 'model_provider') || topLevelProvider;
71
+ continue;
72
+ }
73
+ const profileName = codexProfileNameForSection(section);
74
+ if (profileName) {
75
+ const provider = tomlStringAssignment(line, 'model_provider');
76
+ if (provider) profileProviders.set(profileName, provider);
77
+ }
78
+ }
79
+
80
+ return (activeProfile && profileProviders.get(activeProfile)) || topLevelProvider || 'openai';
81
+ }
82
+
28
83
  function normalizePathValue(value) {
29
84
  if (typeof value !== 'string') return '';
30
85
  const trimmed = value.trim();
@@ -752,8 +807,17 @@ function buildAgentSessionResumeCommand(provider, sessionId, options = {}) {
752
807
 
753
808
  if (normalizedProvider === 'codex') {
754
809
  const cwd = normalizePathValue(options.cwd);
810
+ const modelProvider = String(
811
+ options.modelProvider
812
+ || (Object.prototype.hasOwnProperty.call(options, 'providerHomePath')
813
+ ? resolveCodexResumeModelProvider(options.providerHomePath)
814
+ : '')
815
+ ).trim();
816
+ const providerArgs = modelProvider
817
+ ? ` -c ${quoteCommandArg(`model_provider=${JSON.stringify(modelProvider)}`)}`
818
+ : '';
755
819
  const cwdArgs = cwd ? ` -C ${quoteCommandArg(cwd)}` : '';
756
- return `codex ${options.fork === true ? 'fork' : 'resume'}${cwdArgs} ${normalizedSessionId}`;
820
+ return `codex ${options.fork === true ? 'fork' : 'resume'}${providerArgs}${cwdArgs} ${normalizedSessionId}`;
757
821
  }
758
822
 
759
823
  if (normalizedProvider === 'claude') {
@@ -884,5 +948,6 @@ module.exports = {
884
948
  listQoderSessions,
885
949
  normalizeProvider,
886
950
  paginateAgentSessions,
951
+ resolveCodexResumeModelProvider,
887
952
  searchAgentSessions,
888
953
  };