farming-code 2.2.13 → 2.2.16

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 (119) hide show
  1. package/README.md +81 -105
  2. package/README.zh_cn.md +82 -106
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/backend/acp/packaged-codex-acp.js +13 -0
  5. package/backend/acp-runtime.js +192 -14
  6. package/backend/agent-manager.js +176 -510
  7. package/backend/agent-runtime-binding.js +9 -55
  8. package/backend/chat-runtime.js +28 -0
  9. package/backend/codex-transcript.js +2 -1
  10. package/backend/config-manager.js +22 -14
  11. package/backend/control-api.js +3 -1
  12. package/backend/farming-app-cli.js +13 -0
  13. package/backend/farming-session-store.js +2 -1
  14. package/backend/npm-update-helper.js +65 -7
  15. package/backend/provider-adapters.js +6 -2
  16. package/backend/server.js +89 -104
  17. package/backend/storage-layout.js +0 -16
  18. package/backend/update-service.js +94 -5
  19. package/dist/acp/LICENSE.codex-acp +190 -0
  20. package/dist/acp/codex-acp-1.1.4.js +30718 -0
  21. package/dist/assets/App-y6p2yRar.js +206 -0
  22. package/dist/assets/{FileEditorMarkdownPreview-UlVd0O4D.js → FileEditorMarkdownPreview-R0jwwLME.js} +4 -4
  23. package/dist/assets/FileEditorPane-DNy3LAqS.js +2 -0
  24. package/dist/assets/{IconGlyphs-AU22gPDY.js → IconGlyphs-BsUfqpKN.js} +1 -1
  25. package/dist/assets/ProjectFilesSection-BhDEUVHA.js +12 -0
  26. package/dist/assets/{ReviewPage-CeDKDyZh.js → ReviewPage-ChtByBe3.js} +1 -1
  27. package/dist/assets/{abnfDiagram-VRR7QNED-BgawzDQT.js → abnfDiagram-VRR7QNED-nnglrXFi.js} +1 -1
  28. package/dist/assets/{architectureDiagram-ZJ3FMSHR-DUShwPg0.js → architectureDiagram-ZJ3FMSHR-BEXMaFfY.js} +1 -1
  29. package/dist/assets/{blockDiagram-677ZJIJ3-CeGZviXR.js → blockDiagram-677ZJIJ3-1mCezSBk.js} +1 -1
  30. package/dist/assets/{c4Diagram-LMCZKHZV-CLhYht6S.js → c4Diagram-LMCZKHZV-DhTiI6Uv.js} +1 -1
  31. package/dist/assets/channel-BVWNatUB.js +1 -0
  32. package/dist/assets/{chunk-32BRIVSS-LFYpT-Cu.js → chunk-32BRIVSS-CCdsKd73.js} +1 -1
  33. package/dist/assets/{chunk-52WLFC77-BH6jDw9J.js → chunk-52WLFC77-Gncjk4LF.js} +1 -1
  34. package/dist/assets/{chunk-C7G6YPKG-B_mIUlb7.js → chunk-C7G6YPKG-CiDVUP82.js} +1 -1
  35. package/dist/assets/{chunk-EX3LRPZG-C63hVuAJ.js → chunk-EX3LRPZG-z6gXP2_N.js} +1 -1
  36. package/dist/assets/{chunk-FWX5IMBZ-CboX-WF_.js → chunk-FWX5IMBZ-DMZBccQa.js} +2 -2
  37. package/dist/assets/{chunk-HOUHSVGY-BOgSd6Un.js → chunk-HOUHSVGY-D8tIeoPk.js} +1 -1
  38. package/dist/assets/{chunk-ICXQ74PX-C0K-pgZt.js → chunk-ICXQ74PX-b6oQ2Q17.js} +1 -1
  39. package/dist/assets/{chunk-MOJQB5TN-CVMfwN_7.js → chunk-MOJQB5TN-DAVH_p2a.js} +1 -1
  40. package/dist/assets/{chunk-OGEWGWER-DLT3pxyw.js → chunk-OGEWGWER-DxoiMCI-.js} +1 -1
  41. package/dist/assets/{chunk-PUDLZKDR-Cd079yIQ.js → chunk-PUDLZKDR-NsOWh7HM.js} +1 -1
  42. package/dist/assets/{chunk-Q4XR5HBZ-D0jHa6SO.js → chunk-Q4XR5HBZ-Di4H7L21.js} +1 -1
  43. package/dist/assets/{chunk-V7JOEXUC-CVDtR75H.js → chunk-V7JOEXUC-DttpZg9Y.js} +1 -1
  44. package/dist/assets/{chunk-VAUOI2AC-B-W2_9Ot.js → chunk-VAUOI2AC-UHNr_yhX.js} +1 -1
  45. package/dist/assets/{chunk-VR4S4FIN-FD3kWh32.js → chunk-VR4S4FIN-D-Fmtd4Z.js} +1 -1
  46. package/dist/assets/{chunk-WYO6CB5R-C2bGaS5r.js → chunk-WYO6CB5R-CUxMtfK2.js} +4 -4
  47. package/dist/assets/{chunk-ZGVPDNZ5-ymgts0zo.js → chunk-ZGVPDNZ5-CoV2QEl0.js} +1 -1
  48. package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +1 -0
  49. package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +1 -0
  50. package/dist/assets/code-dark-DKBGxoPX.css +1 -0
  51. package/dist/assets/{cynefinDiagram-TSTJHNR4-CwpobXIl.js → cynefinDiagram-TSTJHNR4-0xPI92Lg.js} +1 -1
  52. package/dist/assets/{dagre-VKFMJZFB-Dkhlfjna.js → dagre-VKFMJZFB-DfuMG4gU.js} +1 -1
  53. package/dist/assets/{diagram-FQU43EPY-hA8dicp6.js → diagram-FQU43EPY-CT00bLZ6.js} +1 -1
  54. package/dist/assets/{diagram-G47NLZAW-BTY6JTzO.js → diagram-G47NLZAW-Xbh7zXUx.js} +1 -1
  55. package/dist/assets/{diagram-NH7WQ7WH-xkaHHX9M.js → diagram-NH7WQ7WH-SRDVPfn7.js} +1 -1
  56. package/dist/assets/{diagram-OA4YK3LP-gk_fg9UU.js → diagram-OA4YK3LP-CQRli4Np.js} +1 -1
  57. package/dist/assets/{diagram-WEI45ONY-BvC8xd6T.js → diagram-WEI45ONY-DMf17WQi.js} +1 -1
  58. package/dist/assets/{ebnfDiagram-CCIWWBDH-CR7RaNat.js → ebnfDiagram-CCIWWBDH-DhpFkgJ5.js} +1 -1
  59. package/dist/assets/{erDiagram-Q63AITRT-_M-JFPbj.js → erDiagram-Q63AITRT-1IwxWmJo.js} +1 -1
  60. package/dist/assets/file-icons-DpbrD5ZP.js +1 -0
  61. package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +1 -0
  62. package/dist/assets/{ganttDiagram-NO4QXBWP-Bl8P1yf_.js → ganttDiagram-NO4QXBWP-BNCdfyr1.js} +1 -1
  63. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DoHa5ysz.js → gitGraphDiagram-IHSO6WYX-pbyWZuiX.js} +1 -1
  64. package/dist/assets/{index-CYZKdoqi.js → index-iKBvPjWS.js} +2 -2
  65. package/dist/assets/{infoDiagram-FWYZ7A6U-DSFcSuUs.js → infoDiagram-FWYZ7A6U-BcTOgDbe.js} +1 -1
  66. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DdkqullI.js → ishikawaDiagram-FXEZZL3T-DCdvGKrn.js} +1 -1
  67. package/dist/assets/{journeyDiagram-5HDEW3XC-B4hRnQJr.js → journeyDiagram-5HDEW3XC-BZ-1q_IT.js} +1 -1
  68. package/dist/assets/{kanban-definition-HUTT4EX6-tm2FZB2E.js → kanban-definition-HUTT4EX6-BIgejCjj.js} +1 -1
  69. package/dist/assets/{line-CmsjK4kn.js → line-CfptFMmX.js} +1 -1
  70. package/dist/assets/main-CdkLOXYl.css +1 -0
  71. package/dist/assets/{mermaid.core-C7UT_EEL.js → mermaid.core-gSpX30pV.js} +3 -3
  72. package/dist/assets/{mindmap-definition-LN4V7U3C-DplfjGms.js → mindmap-definition-LN4V7U3C-BKBaI4LW.js} +1 -1
  73. package/dist/assets/{pegDiagram-2B236MQR-lu3SdkQs.js → pegDiagram-2B236MQR-sBvDY3VX.js} +1 -1
  74. package/dist/assets/{pieDiagram-ENE6RG2P-3fHbmDf9.js → pieDiagram-ENE6RG2P-DWlWcu08.js} +1 -1
  75. package/dist/assets/{quadrantDiagram-ABIIQ3AL-a5qoWz-X.js → quadrantDiagram-ABIIQ3AL-DZutUd3J.js} +1 -1
  76. package/dist/assets/{railroadDiagram-RFXS5EU6-D2TOuXW9.js → railroadDiagram-RFXS5EU6-DQb3lXGM.js} +1 -1
  77. package/dist/assets/{requirementDiagram-TGXJPOKE-LQBWpUvd.js → requirementDiagram-TGXJPOKE-DqFGXu2e.js} +1 -1
  78. package/dist/assets/{sankeyDiagram-HTMAVEWB-B1ohYoyK.js → sankeyDiagram-HTMAVEWB-d7ECahnO.js} +1 -1
  79. package/dist/assets/{sequenceDiagram-DBY2YBRQ-C3vM-hMq.js → sequenceDiagram-DBY2YBRQ-DdsbKqct.js} +1 -1
  80. package/dist/assets/{stateDiagram-2N3HPSRC-BvFhOryI.js → stateDiagram-2N3HPSRC-P6iSrgwR.js} +1 -1
  81. package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +1 -0
  82. package/dist/assets/{swimlanes-5IMT3BWC-DKF_b-2s.js → swimlanes-5IMT3BWC-BGnZJx1M.js} +1 -1
  83. package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +8 -0
  84. package/dist/assets/{timeline-definition-FHXFAJF6-vAs11ux_.js → timeline-definition-FHXFAJF6-rA-r3aUR.js} +1 -1
  85. package/dist/assets/{vennDiagram-L72KCM5P-BybTX_9B.js → vennDiagram-L72KCM5P-DiVKUjA9.js} +1 -1
  86. package/dist/assets/{wardleyDiagram-EHGQE667-CicUmMBR.js → wardleyDiagram-EHGQE667-Cfxh7UOn.js} +1 -1
  87. package/dist/assets/workspace-editor-model-D0Im34S5.js +1 -0
  88. package/dist/assets/{workspace-editor-monaco-BICJESG0.js → workspace-editor-monaco-BIpqcWf1.js} +1 -1
  89. package/dist/assets/workspace-editor-monaco-DLlws5Ig.js +1 -0
  90. package/dist/assets/{workspace-view-state-DvYG_9PH.js → workspace-view-state-BpPINu84.js} +1 -1
  91. package/dist/assets/{xychartDiagram-FW5EYKEG-BhWDyCX-.js → xychartDiagram-FW5EYKEG--hmryQeL.js} +1 -1
  92. package/dist/crt-mermaid-renderer.js +313 -313
  93. package/dist/index.html +1 -1
  94. package/frontend/session-bridge.js +24 -2
  95. package/frontend/skins/crt/app.js +194 -82
  96. package/frontend/skins/crt/index.html +2 -11
  97. package/frontend/skins/crt/styles/effects.css +2 -34
  98. package/package.json +23 -9
  99. package/shared/browser-protocol.js +3 -3
  100. package/backend/app-server-api.js +0 -917
  101. package/backend/codex-app-server-home.js +0 -39
  102. package/backend/codex-app-server-runtime.js +0 -901
  103. package/dist/assets/App-xASd_Y0F.js +0 -206
  104. package/dist/assets/FileEditorPane-Dxx4rKOg.js +0 -2
  105. package/dist/assets/ProjectFilesSection-DE7Nb9Jk.js +0 -12
  106. package/dist/assets/channel-Uw90AO0b.js +0 -1
  107. package/dist/assets/classDiagram-OUVF2IWQ-DKW_dtku.js +0 -1
  108. package/dist/assets/classDiagram-v2-EOCWNBFH-C8Fi7-yv.js +0 -1
  109. package/dist/assets/code-dark-DPiuyeSp.css +0 -1
  110. package/dist/assets/code-mobile-3C2Rr-CN.css +0 -1
  111. package/dist/assets/file-icons-Bw2qd5iT.js +0 -1
  112. package/dist/assets/flowDiagram-23GEKE2U-DAiS5Y7G.js +0 -1
  113. package/dist/assets/main-Bqtb5kuz.css +0 -1
  114. package/dist/assets/stateDiagram-v2-6OUMAXLB-WMrz4PNo.js +0 -1
  115. package/dist/assets/swimlanesDiagram-G3AALYLV-BGswC9oC.js +0 -8
  116. package/dist/assets/workspace-editor-model-BQol4qbA.js +0 -1
  117. package/dist/assets/workspace-editor-monaco-DRJ7IaXk.js +0 -1
  118. package/scripts/sync-ghostty-vendor.js +0 -66
  119. /package/dist/assets/{core-D0LFJkDt.js → core-B9MwRjDE.js} +0 -0
@@ -1,4 +1,4 @@
1
- const RUNTIME_KINDS = new Set(['terminal', 'acp', 'json', 'app-server']);
1
+ const RUNTIME_KINDS = new Set(['terminal', 'acp', 'json']);
2
2
 
3
3
  function terminalBinding() {
4
4
  return { kind: 'terminal' };
@@ -10,6 +10,7 @@ function acpBinding(source = {}) {
10
10
  state: source.state || source.acpState || '',
11
11
  error: source.error || source.acpError || '',
12
12
  stopReason: source.stopReason || source.acpStopReason || '',
13
+ supportsSteer: source.supportsSteer === true,
13
14
  pendingPermission: source.pendingPermission || source.acpPendingPermission || null,
14
15
  pendingPermissions: source.pendingPermissions || source.acpPendingPermissions || [],
15
16
  pendingElicitation: source.pendingElicitation || source.acpPendingElicitation || null,
@@ -30,38 +31,24 @@ function jsonBinding(source = {}) {
30
31
  };
31
32
  }
32
33
 
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
34
  function runtimeKind(agent) {
53
35
  if (RUNTIME_KINDS.has(agent?.runtimeBinding?.kind)) return agent.runtimeBinding.kind;
36
+ // App Server was an experimental Codex runtime. Persisted records migrate to
37
+ // ACP because codex-acp uses the same Codex thread id as its session id.
38
+ if (agent?.runtimeBinding?.kind === 'app-server' || agent?.codexRuntimeMode === 'app-server') return 'acp';
54
39
  if (agent?.agentRuntimeMode === 'acp') return 'acp';
55
40
  if (agent?.agentRuntimeMode === 'json') return 'json';
56
- return agent?.codexRuntimeMode === 'app-server' ? 'app-server' : 'terminal';
41
+ return 'terminal';
57
42
  }
58
43
 
59
44
  function bindingFromLegacy(agent) {
60
45
  if (RUNTIME_KINDS.has(agent?.runtimeBinding?.kind)) return agent.runtimeBinding;
46
+ if (agent?.runtimeBinding?.kind === 'app-server' || agent?.codexRuntimeMode === 'app-server') {
47
+ return acpBinding({ state: 'connecting' });
48
+ }
61
49
  switch (runtimeKind(agent)) {
62
50
  case 'acp': return acpBinding(agent);
63
51
  case 'json': return jsonBinding(agent);
64
- case 'app-server': return appServerBinding(agent);
65
52
  default: return terminalBinding();
66
53
  }
67
54
  }
@@ -70,7 +57,6 @@ function runtimeBindingFor(kind, source = {}) {
70
57
  switch (kind) {
71
58
  case 'acp': return acpBinding(source);
72
59
  case 'json': return jsonBinding(source);
73
- case 'app-server': return appServerBinding(source);
74
60
  default: return terminalBinding();
75
61
  }
76
62
  }
@@ -133,22 +119,6 @@ function publicRuntimeBinding(agent) {
133
119
  transcriptUpdatedAt: binding.transcriptUpdatedAt,
134
120
  };
135
121
  }
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
122
  return { ...binding };
153
123
  }
154
124
 
@@ -162,7 +132,6 @@ function runtimeState(agent) {
162
132
  function legacyRuntimeMetadata(agent) {
163
133
  const binding = bindingFromLegacy(agent);
164
134
  const metadata = {
165
- codexRuntimeMode: binding.kind === 'app-server' ? 'app-server' : 'cli',
166
135
  agentRuntimeMode: ['acp', 'json'].includes(binding.kind) ? binding.kind : 'terminal',
167
136
  };
168
137
  if (binding.kind === 'acp') {
@@ -188,21 +157,6 @@ function legacyRuntimeMetadata(agent) {
188
157
  jsonCliTranscriptUpdatedAt: binding.transcriptUpdatedAt,
189
158
  };
190
159
  }
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
160
  return metadata;
207
161
  }
208
162
 
@@ -0,0 +1,28 @@
1
+ // Chat is a product-level intent. ACP is the single structured Chat runtime for
2
+ // every supported coding agent, including Codex.
3
+ const CHAT_MODE = 'chat';
4
+
5
+ function chatRuntimeForProvider() {
6
+ return 'acp';
7
+ }
8
+
9
+ function isChatMode(mode) {
10
+ return mode === CHAT_MODE;
11
+ }
12
+
13
+ function chatCapabilitiesForProvider(provider) {
14
+ const runtime = chatRuntimeForProvider(provider);
15
+ return {
16
+ chatRuntime: runtime,
17
+ supportsChat: true,
18
+ // ACP does not currently define a turn-versioned steer operation.
19
+ supportsSteer: false,
20
+ };
21
+ }
22
+
23
+ module.exports = {
24
+ CHAT_MODE,
25
+ chatRuntimeForProvider,
26
+ chatCapabilitiesForProvider,
27
+ isChatMode,
28
+ };
@@ -12,7 +12,8 @@ const {
12
12
  } = require('./codex-transcript-sanitizer');
13
13
 
14
14
  // @deprecated `readCodexTranscript` below is the legacy JSONL file reader.
15
- // App Server uses the generic event-to-turn projection exported here instead.
15
+ // The generic event-to-turn projection remains for JSON compatibility feeds;
16
+ // live structured Chat is reduced from ACP session updates instead.
16
17
  const DEFAULT_MAX_READ_BYTES = 32 * 1024 * 1024;
17
18
  const DEFAULT_MAX_TURNS = 240;
18
19
  const USER_MESSAGE_BEGIN = '## My request for Codex:';
@@ -67,6 +67,7 @@ const MAX_SEARCH_TIMEOUT_MS = 180000;
67
67
  const DEFAULT_CRT_TERMINAL_FONT_SIZE = 15;
68
68
  const MIN_CRT_TERMINAL_FONT_SIZE = 10;
69
69
  const MAX_CRT_TERMINAL_FONT_SIZE = 20;
70
+ const MAX_INSTANCE_NAME_LENGTH = 80;
70
71
 
71
72
  const PERSISTED_SETTING_KEYS = new Set([
72
73
  'workspace',
@@ -75,6 +76,7 @@ const PERSISTED_SETTING_KEYS = new Set([
75
76
  'projectWorkspaces',
76
77
  'pinnedProjectWorkspaces',
77
78
  'projectNames',
79
+ 'instanceName',
78
80
  'theme',
79
81
  'appearance',
80
82
  'language',
@@ -88,7 +90,6 @@ const PERSISTED_SETTING_KEYS = new Set([
88
90
  'agentHomes',
89
91
  'updateUrl',
90
92
  'searchTimeoutMs',
91
- 'codexRuntimeMode',
92
93
  'codexApprovalMode',
93
94
  'codexModel',
94
95
  'codexReasoningEffort',
@@ -183,6 +184,19 @@ class ConfigManager {
183
184
  return this.farmingDir;
184
185
  }
185
186
 
187
+ normalizeInstanceName(value) {
188
+ if (typeof value !== 'string') return '';
189
+ return value
190
+ .replace(/[\x00-\x1f\x7f]/g, ' ')
191
+ .replace(/\s+/g, ' ')
192
+ .trim()
193
+ .slice(0, MAX_INSTANCE_NAME_LENGTH);
194
+ }
195
+
196
+ getInstanceName() {
197
+ return this.settings?.instanceName || this.normalizeInstanceName(os.hostname()) || 'Farming';
198
+ }
199
+
186
200
  normalizeWorkspaceHistory(history) {
187
201
  const entries = Array.isArray(history) ? history : [];
188
202
  const result = [];
@@ -290,6 +304,7 @@ class ConfigManager {
290
304
  projectWorkspaces: [],
291
305
  pinnedProjectWorkspaces: [],
292
306
  projectNames: {},
307
+ instanceName: '',
293
308
  theme: 'terminal',
294
309
  appearance: 'light',
295
310
  language: 'en',
@@ -306,7 +321,6 @@ class ConfigManager {
306
321
  agentHomes: cloneAgentHomes(DEFAULT_AGENT_HOMES),
307
322
  updateUrl: DEFAULT_UPDATE_URL,
308
323
  searchTimeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,
309
- codexRuntimeMode: 'cli',
310
324
  codexApprovalMode: 'approve',
311
325
  codexModel: 'gpt-5.5',
312
326
  codexReasoningEffort: 'xhigh',
@@ -326,6 +340,7 @@ class ConfigManager {
326
340
  projectWorkspaces: [],
327
341
  pinnedProjectWorkspaces: [],
328
342
  projectNames: {},
343
+ instanceName: '',
329
344
  theme: 'terminal',
330
345
  appearance: 'light',
331
346
  language: 'en',
@@ -342,7 +357,6 @@ class ConfigManager {
342
357
  agentHomes: cloneAgentHomes(DEFAULT_AGENT_HOMES),
343
358
  updateUrl: DEFAULT_UPDATE_URL,
344
359
  searchTimeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,
345
- codexRuntimeMode: 'cli',
346
360
  codexApprovalMode: 'approve',
347
361
  codexModel: 'gpt-5.5',
348
362
  codexReasoningEffort: 'xhigh',
@@ -376,13 +390,14 @@ class ConfigManager {
376
390
  this.settings.projectWorkspaces = this.normalizeProjectWorkspaces(this.settings.projectWorkspaces);
377
391
  this.settings.pinnedProjectWorkspaces = this.normalizeProjectWorkspaces(this.settings.pinnedProjectWorkspaces);
378
392
  this.settings.projectNames = this.normalizeProjectNames(this.settings.projectNames);
393
+ this.settings.instanceName = this.normalizeInstanceName(this.settings.instanceName);
379
394
  this.settings.agentHomes = this.normalizeAgentHomes(this.settings.agentHomes);
380
395
  if (this.settings.updateUrl === LEGACY_DEFAULT_UPDATE_URL || this.settings.updateUrl === API_DEFAULT_UPDATE_URL) {
381
396
  this.settings.updateUrl = DEFAULT_UPDATE_URL;
382
397
  }
383
398
  this.settings.updateUrl = this.normalizeUpdateUrl(this.settings.updateUrl);
384
399
  this.settings.searchTimeoutMs = this.normalizeSearchTimeoutMs(this.settings.searchTimeoutMs);
385
- this.settings.codexRuntimeMode = this.normalizeCodexRuntimeMode(this.settings.codexRuntimeMode);
400
+ delete this.settings.codexRuntimeMode;
386
401
  const legacyMainPageSessionKeys = this.normalizeMainPageSessionKeys(this.settings.mainPageSessionKeys);
387
402
  delete this.settings.mainPageSessionKeys;
388
403
  this.sessionStore.init({ legacyMainPageSessionKeys });
@@ -517,10 +532,6 @@ class ConfigManager {
517
532
  return ['ask', 'approve', 'full', 'custom'].includes(mode) ? mode : 'approve';
518
533
  }
519
534
 
520
- normalizeCodexRuntimeMode(mode) {
521
- return mode === 'app-server' ? 'app-server' : 'cli';
522
- }
523
-
524
535
  normalizeCodexModelPreset(preset) {
525
536
  if (preset === 'config') return preset;
526
537
  if (typeof preset !== 'string') return 'gpt-5.5:xhigh';
@@ -676,11 +687,6 @@ class ConfigManager {
676
687
  return this.getAgentLaunchProfile('codex').approvalMode;
677
688
  }
678
689
 
679
- getCodexRuntimeMode() {
680
- if (!this.settings) return 'cli';
681
- return this.normalizeCodexRuntimeMode(this.settings.codexRuntimeMode);
682
- }
683
-
684
690
  getCodexModelPreset() {
685
691
  if (!this.settings) return 'gpt-5.5:xhigh';
686
692
  return this.getAgentLaunchProfile('codex').modelPreset;
@@ -739,6 +745,7 @@ class ConfigManager {
739
745
  getSettings() {
740
746
  return {
741
747
  ...this.settings,
748
+ instanceName: this.getInstanceName(),
742
749
  workspace: this.farmingDir,
743
750
  mainPageSessionKeys: this.getMainPageSessionKeys(),
744
751
  taskHistory: this.getTaskHistory(),
@@ -819,10 +826,11 @@ class ConfigManager {
819
826
  this.settings.projectWorkspaces = this.normalizeProjectWorkspaces(this.settings.projectWorkspaces);
820
827
  this.settings.pinnedProjectWorkspaces = this.normalizeProjectWorkspaces(this.settings.pinnedProjectWorkspaces);
821
828
  this.settings.projectNames = this.normalizeProjectNames(this.settings.projectNames);
829
+ this.settings.instanceName = this.normalizeInstanceName(this.settings.instanceName);
822
830
  this.settings.agentHomes = this.normalizeAgentHomes(this.settings.agentHomes);
823
831
  this.settings.updateUrl = this.normalizeUpdateUrl(this.settings.updateUrl);
824
832
  this.settings.searchTimeoutMs = this.normalizeSearchTimeoutMs(this.settings.searchTimeoutMs);
825
- this.settings.codexRuntimeMode = this.normalizeCodexRuntimeMode(this.settings.codexRuntimeMode);
833
+ delete this.settings.codexRuntimeMode;
826
834
  delete this.settings.mainPageSessionKeys;
827
835
  delete this.settings.taskHistory;
828
836
  if (incomingMainPageSessionKeys !== undefined) {
@@ -219,9 +219,11 @@ function createControlRouter(agentManager, options = {}) {
219
219
  parentAgentId: typeof body.parentAgentId === 'string' ? body.parentAgentId : '',
220
220
  task,
221
221
  source: 'control-cli',
222
- agentRuntimeMode: ['json', 'acp'].includes(body.agentRuntimeMode) ? body.agentRuntimeMode : 'terminal',
222
+ agentRuntimeMode: ['json', 'acp', 'chat'].includes(body.agentRuntimeMode) ? body.agentRuntimeMode : 'terminal',
223
223
  acpHistoryMode: body.acpHistoryMode === 'resume' ? 'resume' : 'load',
224
224
  providerSessionTitle: typeof body.providerSessionTitle === 'string' ? body.providerSessionTitle : '',
225
+ ...(Array.isArray(body.additionalDirectories) ? { additionalDirectories: body.additionalDirectories } : {}),
226
+ ...(Array.isArray(body.mcpServers) ? { mcpServers: body.mcpServers } : {}),
225
227
  dangerouslySkipPermissions: body.dangerouslySkipPermissions === true,
226
228
  });
227
229
  });
@@ -7,6 +7,7 @@ const path = require('path');
7
7
  const { URLSearchParams } = require('url');
8
8
  const { execFileSync, spawn } = require('child_process');
9
9
  const { run: runControlCli } = require('./farming-cli');
10
+ const { PACKAGED_CODEX_ACP_ARG, runPackagedCodexAcp } = require('./acp/packaged-codex-acp');
10
11
  const storageLayout = require('./storage-layout');
11
12
 
12
13
  const SERVER_MODE_ARG = '--farming-server';
@@ -85,6 +86,13 @@ function appendNodeOption(existing, option) {
85
86
  function buildServerEnv(overrides = {}, baseEnv = process.env) {
86
87
  const env = { ...baseEnv, ...overrides };
87
88
  delete env.PKG_EXECPATH;
89
+ if (!process.pkg && !env.FARMING_MANAGED_PACKAGE_ROOT) {
90
+ try {
91
+ env.FARMING_MANAGED_PACKAGE_ROOT = fs.realpathSync(path.join(__dirname, '..'));
92
+ } catch {
93
+ // npm update verification will remain unavailable when the package root cannot be proven.
94
+ }
95
+ }
88
96
  const cliBinDir = process.pkg ? path.dirname(process.execPath) : path.join(__dirname, '..', 'bin');
89
97
  env.PORT = String(env.FARMING_PORT || env.PORT || DEFAULT_PORT);
90
98
  env.FARMING_BASE_PATH = env.FARMING_BASE_PATH || DEFAULT_BASE_PATH;
@@ -774,6 +782,11 @@ async function run(argv = process.argv.slice(2)) {
774
782
  return 0;
775
783
  }
776
784
 
785
+ if (argv[0] === PACKAGED_CODEX_ACP_ARG) {
786
+ runPackagedCodexAcp();
787
+ return 0;
788
+ }
789
+
777
790
  if (argv[0] === NATIVE_PTY_HOST_ARG || process.env.FARMING_RUN_NATIVE_PTY_HOST === '1') {
778
791
  runNativePtyHostInCurrentProcess();
779
792
  return 0;
@@ -25,8 +25,9 @@ function safeSessionFileName(id) {
25
25
  function atomicWriteJson(file, value) {
26
26
  fs.mkdirSync(path.dirname(file), { recursive: true });
27
27
  const tmpFile = `${file}.${process.pid}.${Date.now()}.tmp`;
28
- fs.writeFileSync(tmpFile, JSON.stringify(value, null, 2));
28
+ fs.writeFileSync(tmpFile, JSON.stringify(value, null, 2), { mode: 0o600 });
29
29
  fs.renameSync(tmpFile, file);
30
+ fs.chmodSync(file, 0o600);
30
31
  }
31
32
 
32
33
  function parseProviderSessionKey(key) {
@@ -64,12 +64,23 @@ function validatePayload(payload) {
64
64
  if (!/^[A-Za-z0-9@/._-]+$/.test(String(payload.packageName || ''))) throw new Error('Invalid npm package name');
65
65
  if (!/^[0-9A-Za-z.+-]+$/.test(String(payload.targetVersion || ''))) throw new Error('Invalid npm target version');
66
66
  if (!/^[0-9A-Za-z.+-]+$/.test(String(payload.previousVersion || ''))) throw new Error('Invalid npm previous version');
67
- for (const key of ['stateFile', 'logPath', 'cliPath', 'configDir']) {
67
+ for (const key of ['stateFile', 'logPath', 'cliPath', 'packageRoot', 'configDir']) {
68
68
  if (!path.isAbsolute(String(payload[key] || ''))) throw new Error(`Invalid npm update ${key}`);
69
69
  }
70
70
  if (payload.npmPrefix && !path.isAbsolute(String(payload.npmPrefix))) {
71
71
  throw new Error('Invalid npm update npmPrefix');
72
72
  }
73
+ if (payload.npmFallbackRegistryUrl) {
74
+ let registry;
75
+ try {
76
+ registry = new URL(String(payload.npmFallbackRegistryUrl));
77
+ } catch {
78
+ throw new Error('Invalid npm update registry');
79
+ }
80
+ if (!['http:', 'https:'].includes(registry.protocol)) {
81
+ throw new Error('Invalid npm update registry');
82
+ }
83
+ }
73
84
  return payload;
74
85
  }
75
86
 
@@ -109,15 +120,59 @@ function commandEnvironment() {
109
120
 
110
121
  async function installPackage(payload, version) {
111
122
  const packageSpec = `${payload.packageName}@${version}`;
112
- appendLog(payload.logPath, `Installing ${packageSpec}`);
123
+ return installPackageFromRegistry(payload, packageSpec);
124
+ }
125
+
126
+ function verifyInstalledVersion(payload, expectedVersion) {
127
+ const packageJsonPath = path.join(payload.packageRoot, 'package.json');
128
+ let metadata;
129
+ try {
130
+ metadata = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
131
+ } catch (error) {
132
+ throw new Error(`Installed ${payload.packageName} package metadata is unreadable: ${error.message || String(error)}`, { cause: error });
133
+ }
134
+ const actualVersion = String(metadata && metadata.version || '');
135
+ if (actualVersion !== expectedVersion) {
136
+ throw new Error(`Installed ${payload.packageName} version mismatch: expected ${expectedVersion}, found ${actualVersion || 'missing'}`);
137
+ }
138
+ }
139
+
140
+ function logSize(filePath) {
141
+ try {
142
+ return fs.statSync(filePath).size;
143
+ } catch {
144
+ return 0;
145
+ }
146
+ }
147
+
148
+ function logSince(filePath, offset) {
149
+ try {
150
+ return fs.readFileSync(filePath, 'utf8').slice(offset);
151
+ } catch {
152
+ return '';
153
+ }
154
+ }
155
+
156
+ async function installPackageFromRegistry(payload, packageSpec, registryUrl = '') {
157
+ appendLog(payload.logPath, `Installing ${packageSpec}${registryUrl ? ' from the update-status registry' : ''}`);
113
158
  const args = ['install', '--global'];
114
159
  if (payload.npmPrefix) args.push('--prefix', payload.npmPrefix);
160
+ if (registryUrl) args.push('--registry', registryUrl);
115
161
  args.push(packageSpec, '--no-audit', '--no-fund');
116
- await runCommand(payload.npmCommand || 'npm', args, {
117
- cwd: payload.configDir,
118
- env: commandEnvironment(),
119
- logPath: payload.logPath,
120
- });
162
+ const offset = logSize(payload.logPath);
163
+ try {
164
+ await runCommand(payload.npmCommand || 'npm', args, {
165
+ cwd: payload.configDir,
166
+ env: commandEnvironment(),
167
+ logPath: payload.logPath,
168
+ });
169
+ } catch (error) {
170
+ if (!registryUrl && payload.npmFallbackRegistryUrl && /(?:ETARGET|No matching version found)/.test(logSince(payload.logPath, offset))) {
171
+ appendLog(payload.logPath, `Configured npm registry has no ${packageSpec}; retrying from the update-status registry`);
172
+ return installPackageFromRegistry(payload, packageSpec, payload.npmFallbackRegistryUrl);
173
+ }
174
+ throw error;
175
+ }
121
176
  }
122
177
 
123
178
  async function startServer(payload, version = payload.targetVersion) {
@@ -134,6 +189,7 @@ async function runNpmUpdate(rawPayload) {
134
189
  try {
135
190
  writeJsonAtomic(payload.stateFile, stateFor(payload, 'installing'));
136
191
  await installPackage(payload, payload.targetVersion);
192
+ verifyInstalledVersion(payload, payload.targetVersion);
137
193
 
138
194
  writeJsonAtomic(payload.stateFile, stateFor(payload, 'restarting'));
139
195
  await stopProcess(Number(payload.serverPid));
@@ -151,6 +207,7 @@ async function runNpmUpdate(rawPayload) {
151
207
  try {
152
208
  writeJsonAtomic(payload.stateFile, stateFor(payload, 'rolling-back', { error: message }));
153
209
  await installPackage(payload, payload.previousVersion);
210
+ verifyInstalledVersion(payload, payload.previousVersion);
154
211
  await startServer(payload, payload.previousVersion);
155
212
  writeJsonAtomic(payload.stateFile, stateFor(payload, 'rolled-back', {
156
213
  version: payload.previousVersion,
@@ -196,5 +253,6 @@ module.exports = {
196
253
  runNpmUpdate,
197
254
  stopProcess,
198
255
  validatePayload,
256
+ verifyInstalledVersion,
199
257
  writeJsonAtomic,
200
258
  };
@@ -1,4 +1,5 @@
1
1
  const path = require('path');
2
+ const { chatCapabilitiesForProvider } = require('./chat-runtime');
2
3
  const {
3
4
  createProviderSessionId,
4
5
  createTemporaryProviderSessionId,
@@ -145,11 +146,11 @@ const PROVIDER_ADAPTERS = Object.freeze([
145
146
  interruptInput: '\x1b',
146
147
  freshAcpSessionSources: ['codex-temporary'],
147
148
  commands: ['codex'],
148
- supportedRuntimes: ['terminal', 'acp', 'json', 'app-server'],
149
+ supportedRuntimes: ['terminal', 'acp', 'json'],
149
150
  planSession: codexSessionPlan,
150
151
  acp: { packageName: '@agentclientprotocol/codex-acp', version: '1.1.4' },
151
152
  prepareAcpEnvironment: codexAcpEnvironment,
152
- capabilities: { runtimeSwitch: true, terminalProfile: true, goals: true },
153
+ capabilities: { runtimeSwitch: true, terminalProfile: true, goals: false },
153
154
  },
154
155
  {
155
156
  id: 'claude',
@@ -225,6 +226,9 @@ function providerCapabilities(provider) {
225
226
  runtimeSwitch: adapter?.capabilities?.runtimeSwitch === true,
226
227
  terminalProfile: adapter?.capabilities?.terminalProfile === true,
227
228
  goals: adapter?.capabilities?.goals === true,
229
+ ...(adapter
230
+ ? chatCapabilitiesForProvider(provider)
231
+ : { chatRuntime: '', supportsChat: false, supportsSteer: false }),
228
232
  };
229
233
  }
230
234