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.
- package/README.md +81 -105
- package/README.zh_cn.md +82 -106
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/backend/acp/packaged-codex-acp.js +13 -0
- package/backend/acp-runtime.js +192 -14
- package/backend/agent-manager.js +176 -510
- package/backend/agent-runtime-binding.js +9 -55
- package/backend/chat-runtime.js +28 -0
- package/backend/codex-transcript.js +2 -1
- package/backend/config-manager.js +22 -14
- package/backend/control-api.js +3 -1
- package/backend/farming-app-cli.js +13 -0
- package/backend/farming-session-store.js +2 -1
- package/backend/npm-update-helper.js +65 -7
- package/backend/provider-adapters.js +6 -2
- package/backend/server.js +89 -104
- package/backend/storage-layout.js +0 -16
- package/backend/update-service.js +94 -5
- package/dist/acp/LICENSE.codex-acp +190 -0
- package/dist/acp/codex-acp-1.1.4.js +30718 -0
- package/dist/assets/App-y6p2yRar.js +206 -0
- package/dist/assets/{FileEditorMarkdownPreview-UlVd0O4D.js → FileEditorMarkdownPreview-R0jwwLME.js} +4 -4
- package/dist/assets/FileEditorPane-DNy3LAqS.js +2 -0
- package/dist/assets/{IconGlyphs-AU22gPDY.js → IconGlyphs-BsUfqpKN.js} +1 -1
- package/dist/assets/ProjectFilesSection-BhDEUVHA.js +12 -0
- package/dist/assets/{ReviewPage-CeDKDyZh.js → ReviewPage-ChtByBe3.js} +1 -1
- package/dist/assets/{abnfDiagram-VRR7QNED-BgawzDQT.js → abnfDiagram-VRR7QNED-nnglrXFi.js} +1 -1
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-DUShwPg0.js → architectureDiagram-ZJ3FMSHR-BEXMaFfY.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-CeGZviXR.js → blockDiagram-677ZJIJ3-1mCezSBk.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-CLhYht6S.js → c4Diagram-LMCZKHZV-DhTiI6Uv.js} +1 -1
- package/dist/assets/channel-BVWNatUB.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-LFYpT-Cu.js → chunk-32BRIVSS-CCdsKd73.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BH6jDw9J.js → chunk-52WLFC77-Gncjk4LF.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-B_mIUlb7.js → chunk-C7G6YPKG-CiDVUP82.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-C63hVuAJ.js → chunk-EX3LRPZG-z6gXP2_N.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-CboX-WF_.js → chunk-FWX5IMBZ-DMZBccQa.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-BOgSd6Un.js → chunk-HOUHSVGY-D8tIeoPk.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-C0K-pgZt.js → chunk-ICXQ74PX-b6oQ2Q17.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-CVMfwN_7.js → chunk-MOJQB5TN-DAVH_p2a.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-DLT3pxyw.js → chunk-OGEWGWER-DxoiMCI-.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-Cd079yIQ.js → chunk-PUDLZKDR-NsOWh7HM.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-D0jHa6SO.js → chunk-Q4XR5HBZ-Di4H7L21.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-CVDtR75H.js → chunk-V7JOEXUC-DttpZg9Y.js} +1 -1
- package/dist/assets/{chunk-VAUOI2AC-B-W2_9Ot.js → chunk-VAUOI2AC-UHNr_yhX.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-FD3kWh32.js → chunk-VR4S4FIN-D-Fmtd4Z.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-C2bGaS5r.js → chunk-WYO6CB5R-CUxMtfK2.js} +4 -4
- package/dist/assets/{chunk-ZGVPDNZ5-ymgts0zo.js → chunk-ZGVPDNZ5-CoV2QEl0.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +1 -0
- package/dist/assets/code-dark-DKBGxoPX.css +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-CwpobXIl.js → cynefinDiagram-TSTJHNR4-0xPI92Lg.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-Dkhlfjna.js → dagre-VKFMJZFB-DfuMG4gU.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-hA8dicp6.js → diagram-FQU43EPY-CT00bLZ6.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-BTY6JTzO.js → diagram-G47NLZAW-Xbh7zXUx.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-xkaHHX9M.js → diagram-NH7WQ7WH-SRDVPfn7.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-gk_fg9UU.js → diagram-OA4YK3LP-CQRli4Np.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-BvC8xd6T.js → diagram-WEI45ONY-DMf17WQi.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CR7RaNat.js → ebnfDiagram-CCIWWBDH-DhpFkgJ5.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-_M-JFPbj.js → erDiagram-Q63AITRT-1IwxWmJo.js} +1 -1
- package/dist/assets/file-icons-DpbrD5ZP.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-Bl8P1yf_.js → ganttDiagram-NO4QXBWP-BNCdfyr1.js} +1 -1
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-DoHa5ysz.js → gitGraphDiagram-IHSO6WYX-pbyWZuiX.js} +1 -1
- package/dist/assets/{index-CYZKdoqi.js → index-iKBvPjWS.js} +2 -2
- package/dist/assets/{infoDiagram-FWYZ7A6U-DSFcSuUs.js → infoDiagram-FWYZ7A6U-BcTOgDbe.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-DdkqullI.js → ishikawaDiagram-FXEZZL3T-DCdvGKrn.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-B4hRnQJr.js → journeyDiagram-5HDEW3XC-BZ-1q_IT.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-tm2FZB2E.js → kanban-definition-HUTT4EX6-BIgejCjj.js} +1 -1
- package/dist/assets/{line-CmsjK4kn.js → line-CfptFMmX.js} +1 -1
- package/dist/assets/main-CdkLOXYl.css +1 -0
- package/dist/assets/{mermaid.core-C7UT_EEL.js → mermaid.core-gSpX30pV.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-DplfjGms.js → mindmap-definition-LN4V7U3C-BKBaI4LW.js} +1 -1
- package/dist/assets/{pegDiagram-2B236MQR-lu3SdkQs.js → pegDiagram-2B236MQR-sBvDY3VX.js} +1 -1
- package/dist/assets/{pieDiagram-ENE6RG2P-3fHbmDf9.js → pieDiagram-ENE6RG2P-DWlWcu08.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-a5qoWz-X.js → quadrantDiagram-ABIIQ3AL-DZutUd3J.js} +1 -1
- package/dist/assets/{railroadDiagram-RFXS5EU6-D2TOuXW9.js → railroadDiagram-RFXS5EU6-DQb3lXGM.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-LQBWpUvd.js → requirementDiagram-TGXJPOKE-DqFGXu2e.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-B1ohYoyK.js → sankeyDiagram-HTMAVEWB-d7ECahnO.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-C3vM-hMq.js → sequenceDiagram-DBY2YBRQ-DdsbKqct.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-BvFhOryI.js → stateDiagram-2N3HPSRC-P6iSrgwR.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-DKF_b-2s.js → swimlanes-5IMT3BWC-BGnZJx1M.js} +1 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-vAs11ux_.js → timeline-definition-FHXFAJF6-rA-r3aUR.js} +1 -1
- package/dist/assets/{vennDiagram-L72KCM5P-BybTX_9B.js → vennDiagram-L72KCM5P-DiVKUjA9.js} +1 -1
- package/dist/assets/{wardleyDiagram-EHGQE667-CicUmMBR.js → wardleyDiagram-EHGQE667-Cfxh7UOn.js} +1 -1
- package/dist/assets/workspace-editor-model-D0Im34S5.js +1 -0
- package/dist/assets/{workspace-editor-monaco-BICJESG0.js → workspace-editor-monaco-BIpqcWf1.js} +1 -1
- package/dist/assets/workspace-editor-monaco-DLlws5Ig.js +1 -0
- package/dist/assets/{workspace-view-state-DvYG_9PH.js → workspace-view-state-BpPINu84.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-BhWDyCX-.js → xychartDiagram-FW5EYKEG--hmryQeL.js} +1 -1
- package/dist/crt-mermaid-renderer.js +313 -313
- package/dist/index.html +1 -1
- package/frontend/session-bridge.js +24 -2
- package/frontend/skins/crt/app.js +194 -82
- package/frontend/skins/crt/index.html +2 -11
- package/frontend/skins/crt/styles/effects.css +2 -34
- package/package.json +23 -9
- package/shared/browser-protocol.js +3 -3
- package/backend/app-server-api.js +0 -917
- package/backend/codex-app-server-home.js +0 -39
- package/backend/codex-app-server-runtime.js +0 -901
- package/dist/assets/App-xASd_Y0F.js +0 -206
- package/dist/assets/FileEditorPane-Dxx4rKOg.js +0 -2
- package/dist/assets/ProjectFilesSection-DE7Nb9Jk.js +0 -12
- package/dist/assets/channel-Uw90AO0b.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DKW_dtku.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-C8Fi7-yv.js +0 -1
- package/dist/assets/code-dark-DPiuyeSp.css +0 -1
- package/dist/assets/code-mobile-3C2Rr-CN.css +0 -1
- package/dist/assets/file-icons-Bw2qd5iT.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DAiS5Y7G.js +0 -1
- package/dist/assets/main-Bqtb5kuz.css +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-WMrz4PNo.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-BGswC9oC.js +0 -8
- package/dist/assets/workspace-editor-model-BQol4qbA.js +0 -1
- package/dist/assets/workspace-editor-monaco-DRJ7IaXk.js +0 -1
- package/scripts/sync-ghostty-vendor.js +0 -66
- /package/dist/assets/{core-D0LFJkDt.js → core-B9MwRjDE.js} +0 -0
package/backend/acp-runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const EventEmitter = require('events');
|
|
2
|
+
const crypto = require('crypto');
|
|
2
3
|
const fs = require('fs');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const { spawn } = require('child_process');
|
|
@@ -9,6 +10,7 @@ const { AcpCheckpointStore } = require('./acp-checkpoint-store');
|
|
|
9
10
|
const { AcpSessionState } = require('./acp-session-state');
|
|
10
11
|
const { readCodexHistoryImageData } = require('./codex-transcript');
|
|
11
12
|
const { AcpClientFileSystem, AcpClientTerminalManager } = require('./acp/client-services');
|
|
13
|
+
const { PACKAGED_CODEX_ACP_ARG } = require('./acp/packaged-codex-acp');
|
|
12
14
|
const { permissionSecurityWarnings } = require('./acp/permission-security');
|
|
13
15
|
const { rejectPatch } = require('./acp/patch-decisions');
|
|
14
16
|
const { getProviderAdapter, listProviderAdapters } = require('./provider-adapters');
|
|
@@ -25,6 +27,17 @@ const DEFAULT_HISTORY_REPLAY_MIN_WAIT_MS = 350;
|
|
|
25
27
|
const DEFAULT_HISTORY_REPLAY_QUIET_MS = 150;
|
|
26
28
|
const DEFAULT_HISTORY_REPLAY_MAX_WAIT_MS = 5_000;
|
|
27
29
|
const CODEX_SET_SESSION_MODEL_METHOD = 'session/set_model';
|
|
30
|
+
const CODEX_STEER_METHOD = '_codex/session/steer';
|
|
31
|
+
const CODEX_ACP_PACKAGE = '@agentclientprotocol/codex-acp';
|
|
32
|
+
const CODEX_ACP_VERSION = '1.1.4';
|
|
33
|
+
const CODEX_ACP_SHA256 = '39cbae01e336c2ca185d624358e03280d1f6fef6d73bbe42dd9eb77e2b1efb32';
|
|
34
|
+
const CODEX_ACP_VENDOR_ENTRY = path.join(
|
|
35
|
+
__dirname,
|
|
36
|
+
'..',
|
|
37
|
+
'dist',
|
|
38
|
+
'acp',
|
|
39
|
+
`codex-acp-${CODEX_ACP_VERSION}.js`,
|
|
40
|
+
);
|
|
28
41
|
|
|
29
42
|
let sdkPromise;
|
|
30
43
|
const runtimeRequire = createRequire(__filename);
|
|
@@ -33,7 +46,24 @@ function loadAcpSdk() {
|
|
|
33
46
|
return sdkPromise;
|
|
34
47
|
}
|
|
35
48
|
|
|
49
|
+
function fileSha256(filePath) {
|
|
50
|
+
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function verifiedCodexAcpEntry(entry) {
|
|
54
|
+
const actualSha256 = fileSha256(entry);
|
|
55
|
+
if (actualSha256 !== CODEX_ACP_SHA256) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Codex ACP runtime failed integrity verification: expected ${CODEX_ACP_SHA256}, found ${actualSha256}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return entry;
|
|
61
|
+
}
|
|
62
|
+
|
|
36
63
|
function adapterEntry(packageName) {
|
|
64
|
+
if (packageName === CODEX_ACP_PACKAGE && fs.existsSync(CODEX_ACP_VENDOR_ENTRY)) {
|
|
65
|
+
return verifiedCodexAcpEntry(CODEX_ACP_VENDOR_ENTRY);
|
|
66
|
+
}
|
|
37
67
|
let sdkDirectory;
|
|
38
68
|
try {
|
|
39
69
|
sdkDirectory = path.dirname(runtimeRequire.resolve('@agentclientprotocol/sdk'));
|
|
@@ -42,11 +72,15 @@ function adapterEntry(packageName) {
|
|
|
42
72
|
}
|
|
43
73
|
const entry = path.resolve(sdkDirectory, '..', '..', packageName.split('/').pop(), 'dist', 'index.js');
|
|
44
74
|
if (!fs.existsSync(entry)) throw new Error(`ACP adapter is not installed: ${packageName}`);
|
|
75
|
+
if (packageName === CODEX_ACP_PACKAGE) return verifiedCodexAcpEntry(entry);
|
|
45
76
|
return entry;
|
|
46
77
|
}
|
|
47
78
|
|
|
48
|
-
function nodeAdapterLaunch(entry, env = process.env) {
|
|
79
|
+
function nodeAdapterLaunch(entry, env = process.env, packagedArg = '') {
|
|
49
80
|
const runtimeEnv = env && typeof env === 'object' ? env : process.env;
|
|
81
|
+
if (process.pkg && packagedArg) {
|
|
82
|
+
return { command: process.execPath, args: [packagedArg] };
|
|
83
|
+
}
|
|
50
84
|
const nodeBin = runtimeEnv.FARMING_NODE_BIN || process.execPath;
|
|
51
85
|
const ldPath = runtimeEnv.FARMING_NODE_LD || '';
|
|
52
86
|
const libraryPath = runtimeEnv.FARMING_NODE_LIBRARY_PATH || '';
|
|
@@ -69,6 +103,7 @@ function resolveAcpLaunch(provider, options = {}) {
|
|
|
69
103
|
const launch = nodeAdapterLaunch(
|
|
70
104
|
adapterEntry(adapter.acp.packageName),
|
|
71
105
|
options.runtimeEnv || process.env,
|
|
106
|
+
adapter.acp.packageName === CODEX_ACP_PACKAGE ? PACKAGED_CODEX_ACP_ARG : '',
|
|
72
107
|
);
|
|
73
108
|
return { ...launch, version: adapter.acp.version };
|
|
74
109
|
}
|
|
@@ -202,6 +237,59 @@ function clone(value) {
|
|
|
202
237
|
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
203
238
|
}
|
|
204
239
|
|
|
240
|
+
function acpSessionRequestOptions(options = {}, cwd = process.cwd()) {
|
|
241
|
+
const root = path.resolve(cwd || process.cwd());
|
|
242
|
+
const additionalDirectories = Array.isArray(options.additionalDirectories)
|
|
243
|
+
? [...new Set(options.additionalDirectories
|
|
244
|
+
.filter(directory => typeof directory === 'string' && directory.trim())
|
|
245
|
+
.map(directory => path.resolve(root, directory)))]
|
|
246
|
+
: [];
|
|
247
|
+
const mcpServers = Array.isArray(options.mcpServers)
|
|
248
|
+
? clone(options.mcpServers.filter(server => server && typeof server === 'object' && !Array.isArray(server)))
|
|
249
|
+
: [];
|
|
250
|
+
return { cwd: root, additionalDirectories, mcpServers };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function promptContentForCapabilities(content, capabilities = {}) {
|
|
254
|
+
const promptCapabilities = capabilities?.promptCapabilities || {};
|
|
255
|
+
return (Array.isArray(content) ? content : []).flatMap(block => {
|
|
256
|
+
if (!block || typeof block !== 'object') return [];
|
|
257
|
+
if (!['image', 'audio'].includes(block.type)) return [clone(block)];
|
|
258
|
+
if (promptCapabilities[block.type] === true) return [clone(block)];
|
|
259
|
+
|
|
260
|
+
const label = block.type === 'audio' ? 'audio' : 'image';
|
|
261
|
+
const attachmentPath = typeof block.path === 'string' && block.path.trim()
|
|
262
|
+
? path.resolve(block.path)
|
|
263
|
+
: '';
|
|
264
|
+
const name = attachmentPath ? path.basename(attachmentPath) : `attached ${label}`;
|
|
265
|
+
const detail = attachmentPath
|
|
266
|
+
? `${label[0].toUpperCase()}${label.slice(1)} path: ${attachmentPath}`
|
|
267
|
+
: `[The ACP Agent does not accept native ${label} content]`;
|
|
268
|
+
return [{ type: 'text', text: `Attached ${label}: ${name}\n\n${detail}` }];
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function supportsCodexSteer(capabilities = {}) {
|
|
273
|
+
const capability = capabilities?._meta?.codex?.steer;
|
|
274
|
+
return capability?.method === CODEX_STEER_METHOD
|
|
275
|
+
&& Number.isFinite(Number(capability.version))
|
|
276
|
+
&& Number(capability.version) >= 1;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isCodexSteerUnavailableError(error) {
|
|
280
|
+
const text = [
|
|
281
|
+
error?.message,
|
|
282
|
+
error?.data?.details,
|
|
283
|
+
error?.cause?.message,
|
|
284
|
+
error?.cause?.data?.details,
|
|
285
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
286
|
+
return text.includes('no active codex turn to steer')
|
|
287
|
+
|| text.includes('no active turn to steer')
|
|
288
|
+
|| /expected active turn id .* but found/.test(text)
|
|
289
|
+
|| text.includes('cannot steer a review turn')
|
|
290
|
+
|| text.includes('cannot steer a compact turn');
|
|
291
|
+
}
|
|
292
|
+
|
|
205
293
|
class AcpRuntime extends EventEmitter {
|
|
206
294
|
constructor(options = {}) {
|
|
207
295
|
super();
|
|
@@ -231,11 +319,14 @@ class AcpRuntime extends EventEmitter {
|
|
|
231
319
|
if (this.bindings.has(agentId)) throw new Error('ACP Agent is already registered');
|
|
232
320
|
const provider = String(options.provider || '').trim().toLowerCase();
|
|
233
321
|
const launch = this.resolveLaunch(provider, options);
|
|
322
|
+
const cwd = path.resolve(options.cwd || process.cwd());
|
|
323
|
+
const sessionRequestOptions = acpSessionRequestOptions(options, cwd);
|
|
234
324
|
const binding = {
|
|
235
325
|
agentId,
|
|
236
326
|
provider,
|
|
237
327
|
providerHomeId: String(options.providerHomeId || 'default'),
|
|
238
|
-
cwd
|
|
328
|
+
cwd,
|
|
329
|
+
sessionRequestOptions,
|
|
239
330
|
env: (getProviderAdapter(provider)?.prepareAcpEnvironment || (value => value.env || process.env))(options),
|
|
240
331
|
launch,
|
|
241
332
|
restartOptions: { ...options, agentId, provider },
|
|
@@ -257,6 +348,8 @@ class AcpRuntime extends EventEmitter {
|
|
|
257
348
|
subagentStates: new Map(),
|
|
258
349
|
interactionOrigins: new Map(),
|
|
259
350
|
promptActive: false,
|
|
351
|
+
supportsSteer: false,
|
|
352
|
+
steerTail: null,
|
|
260
353
|
historyReplayActive: false,
|
|
261
354
|
sessionState: null,
|
|
262
355
|
authTerminal: null,
|
|
@@ -308,12 +401,14 @@ class AcpRuntime extends EventEmitter {
|
|
|
308
401
|
if (binding.initializeResponse.protocolVersion !== sdk.PROTOCOL_VERSION) {
|
|
309
402
|
throw new Error(`ACP protocol version mismatch: Agent selected ${binding.initializeResponse.protocolVersion}, Farming supports ${sdk.PROTOCOL_VERSION}`);
|
|
310
403
|
}
|
|
404
|
+
binding.supportsSteer = binding.provider === 'codex'
|
|
405
|
+
&& supportsCodexSteer(binding.initializeResponse.agentCapabilities);
|
|
311
406
|
|
|
312
407
|
const requestedSessionId = String(options.sessionId || '').trim();
|
|
313
408
|
const revisionBase = Number.isFinite(Number(options.revisionBase))
|
|
314
409
|
? Math.max(0, Math.floor(Number(options.revisionBase)))
|
|
315
410
|
: 0;
|
|
316
|
-
const sessionRequest = { sessionId: requestedSessionId,
|
|
411
|
+
const sessionRequest = { sessionId: requestedSessionId, ...binding.sessionRequestOptions };
|
|
317
412
|
let sessionResponse;
|
|
318
413
|
let historyMode = 'new';
|
|
319
414
|
if (requestedSessionId) {
|
|
@@ -413,7 +508,7 @@ class AcpRuntime extends EventEmitter {
|
|
|
413
508
|
}
|
|
414
509
|
} else {
|
|
415
510
|
sessionResponse = await withTimeout(
|
|
416
|
-
connection.newSession(
|
|
511
|
+
connection.newSession(binding.sessionRequestOptions),
|
|
417
512
|
this.sessionSetupTimeoutMs,
|
|
418
513
|
'ACP session/new'
|
|
419
514
|
);
|
|
@@ -697,9 +792,11 @@ class AcpRuntime extends EventEmitter {
|
|
|
697
792
|
}
|
|
698
793
|
|
|
699
794
|
requestElicitation(binding, request) {
|
|
700
|
-
const
|
|
701
|
-
const
|
|
702
|
-
|
|
795
|
+
const hasSessionScope = typeof request?.sessionId === 'string' && request.sessionId.length > 0;
|
|
796
|
+
const requestSessionId = hasSessionScope ? String(request.sessionId) : '';
|
|
797
|
+
const isPrimarySession = hasSessionScope && requestSessionId === binding.sessionId;
|
|
798
|
+
const isRequestScoped = !hasSessionScope;
|
|
799
|
+
if (!isRequestScoped && !isPrimarySession && !binding.subagentStates.has(requestSessionId)) {
|
|
703
800
|
throw new Error('ACP elicitation does not match an active session');
|
|
704
801
|
}
|
|
705
802
|
if (!['form', 'url'].includes(String(request?.mode || ''))) {
|
|
@@ -715,7 +812,7 @@ class AcpRuntime extends EventEmitter {
|
|
|
715
812
|
...cloned,
|
|
716
813
|
...(protocolRequestId !== undefined ? { protocolRequestId } : {}),
|
|
717
814
|
requestId,
|
|
718
|
-
origin: isPrimarySession ? 'agent' : 'subagent',
|
|
815
|
+
origin: isRequestScoped ? 'request' : isPrimarySession ? 'agent' : 'subagent',
|
|
719
816
|
};
|
|
720
817
|
binding.interactionOrigins.set(requestId, binding.state);
|
|
721
818
|
binding.pendingElicitations.set(requestId, pending);
|
|
@@ -757,7 +854,11 @@ class AcpRuntime extends EventEmitter {
|
|
|
757
854
|
async prompt(agentId, prompt) {
|
|
758
855
|
const binding = this.requireBinding(agentId);
|
|
759
856
|
if (!['idle', 'error'].includes(binding.state)) throw new Error(`ACP Agent is not ready (${binding.state})`);
|
|
760
|
-
const
|
|
857
|
+
const rawContent = Array.isArray(prompt) ? prompt : [{ type: 'text', text: String(prompt || '') }];
|
|
858
|
+
const content = promptContentForCapabilities(
|
|
859
|
+
rawContent,
|
|
860
|
+
binding.initializeResponse?.agentCapabilities || {},
|
|
861
|
+
);
|
|
761
862
|
await this.markCheckpointDirty(binding);
|
|
762
863
|
binding.sessionState.beginPrompt(content);
|
|
763
864
|
binding.promptActive = true;
|
|
@@ -795,6 +896,54 @@ class AcpRuntime extends EventEmitter {
|
|
|
795
896
|
}
|
|
796
897
|
}
|
|
797
898
|
|
|
899
|
+
canSteer(agentId) {
|
|
900
|
+
const binding = this.bindings.get(agentId);
|
|
901
|
+
return binding?.provider === 'codex'
|
|
902
|
+
&& binding.supportsSteer === true
|
|
903
|
+
&& binding.promptActive === true
|
|
904
|
+
&& Boolean(binding.sessionId)
|
|
905
|
+
&& Boolean(binding.connection);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
async steer(agentId, prompt) {
|
|
909
|
+
const binding = this.requireBinding(agentId);
|
|
910
|
+
if (binding.provider !== 'codex' || binding.supportsSteer !== true) {
|
|
911
|
+
throw new Error(`${binding.provider} ACP Agent does not support steer`);
|
|
912
|
+
}
|
|
913
|
+
const rawContent = Array.isArray(prompt) ? prompt : [{ type: 'text', text: String(prompt || '') }];
|
|
914
|
+
const content = promptContentForCapabilities(
|
|
915
|
+
rawContent,
|
|
916
|
+
binding.initializeResponse?.agentCapabilities || {},
|
|
917
|
+
);
|
|
918
|
+
const clientMessageId = `farming-steer-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
919
|
+
const operation = async () => {
|
|
920
|
+
if (!binding.promptActive) throw new Error('No active Codex turn to steer');
|
|
921
|
+
await this.markCheckpointDirty(binding);
|
|
922
|
+
const response = await withTimeout(
|
|
923
|
+
binding.connection.request(CODEX_STEER_METHOD, {
|
|
924
|
+
sessionId: binding.sessionId,
|
|
925
|
+
prompt: content,
|
|
926
|
+
clientMessageId,
|
|
927
|
+
}),
|
|
928
|
+
this.requestTimeoutMs,
|
|
929
|
+
'Codex ACP steer',
|
|
930
|
+
);
|
|
931
|
+
return {
|
|
932
|
+
sessionId: binding.sessionId,
|
|
933
|
+
turnId: String(response?.turnId || ''),
|
|
934
|
+
clientMessageId,
|
|
935
|
+
};
|
|
936
|
+
};
|
|
937
|
+
const previous = binding.steerTail || Promise.resolve();
|
|
938
|
+
const pending = previous.catch(() => {}).then(operation);
|
|
939
|
+
binding.steerTail = pending;
|
|
940
|
+
try {
|
|
941
|
+
return await pending;
|
|
942
|
+
} finally {
|
|
943
|
+
if (binding.steerTail === pending) binding.steerTail = null;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
798
947
|
async cancel(agentId) {
|
|
799
948
|
const binding = this.requireBinding(agentId);
|
|
800
949
|
if (!binding.sessionId) return false;
|
|
@@ -871,10 +1020,18 @@ class AcpRuntime extends EventEmitter {
|
|
|
871
1020
|
if (!capabilities?.list) throw new Error(`${binding.provider} ACP Agent does not support session/list`);
|
|
872
1021
|
return withTimeout(binding.connection.listSessions({
|
|
873
1022
|
...(options.cwd ? { cwd: path.resolve(options.cwd) } : {}),
|
|
1023
|
+
...(Array.isArray(options.additionalDirectories)
|
|
1024
|
+
? { additionalDirectories: acpSessionRequestOptions(options, options.cwd || binding.cwd).additionalDirectories }
|
|
1025
|
+
: {}),
|
|
874
1026
|
...(options.cursor ? { cursor: String(options.cursor) } : {}),
|
|
875
1027
|
}), this.requestTimeoutMs, 'ACP session/list');
|
|
876
1028
|
}
|
|
877
1029
|
|
|
1030
|
+
getSessionRequestOptions(agentId) {
|
|
1031
|
+
const binding = this.requireBinding(agentId);
|
|
1032
|
+
return clone(binding.sessionRequestOptions);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
878
1035
|
async authenticate(agentId, methodId) {
|
|
879
1036
|
const binding = this.requireBinding(agentId);
|
|
880
1037
|
const method = binding.initializeResponse?.authMethods?.find(item => item.id === methodId);
|
|
@@ -896,6 +1053,20 @@ class AcpRuntime extends EventEmitter {
|
|
|
896
1053
|
return { authenticated: true, methodId };
|
|
897
1054
|
}
|
|
898
1055
|
|
|
1056
|
+
async logout(agentId) {
|
|
1057
|
+
const binding = this.requireBinding(agentId);
|
|
1058
|
+
if (binding.initializeResponse?.agentCapabilities?.auth?.logout == null) {
|
|
1059
|
+
throw new Error(`${binding.provider} ACP Agent does not support logout`);
|
|
1060
|
+
}
|
|
1061
|
+
await withTimeout(binding.connection.logout({}), this.requestTimeoutMs, 'ACP logout');
|
|
1062
|
+
binding.error = '';
|
|
1063
|
+
binding.stopReason = '';
|
|
1064
|
+
binding.updatedAt = new Date().toISOString();
|
|
1065
|
+
this.emitRuntime(binding);
|
|
1066
|
+
this.emitSession(binding);
|
|
1067
|
+
return { loggedOut: true };
|
|
1068
|
+
}
|
|
1069
|
+
|
|
899
1070
|
terminalAuthenticationLaunch(binding, method) {
|
|
900
1071
|
const legacy = method?._meta?.['terminal-auth'];
|
|
901
1072
|
if (legacy && typeof legacy === 'object' && legacy.command) {
|
|
@@ -994,13 +1165,13 @@ class AcpRuntime extends EventEmitter {
|
|
|
994
1165
|
const binding = this.requireBinding(agentId);
|
|
995
1166
|
const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
|
|
996
1167
|
if (!capabilities?.fork) throw new Error(`${binding.provider} ACP Agent does not support session/fork`);
|
|
1168
|
+
const sessionOptions = acpSessionRequestOptions({
|
|
1169
|
+
additionalDirectories: options.additionalDirectories ?? binding.sessionRequestOptions.additionalDirectories,
|
|
1170
|
+
mcpServers: options.mcpServers ?? binding.sessionRequestOptions.mcpServers,
|
|
1171
|
+
}, options.cwd || binding.cwd);
|
|
997
1172
|
return withTimeout(binding.connection.unstable_forkSession({
|
|
998
1173
|
sessionId: options.sessionId || binding.sessionId,
|
|
999
|
-
|
|
1000
|
-
additionalDirectories: Array.isArray(options.additionalDirectories)
|
|
1001
|
-
? options.additionalDirectories.map(directory => path.resolve(directory))
|
|
1002
|
-
: [],
|
|
1003
|
-
mcpServers: [],
|
|
1174
|
+
...sessionOptions,
|
|
1004
1175
|
}), this.sessionSetupTimeoutMs, 'ACP session/fork');
|
|
1005
1176
|
}
|
|
1006
1177
|
|
|
@@ -1186,6 +1357,7 @@ class AcpRuntime extends EventEmitter {
|
|
|
1186
1357
|
error: binding.error,
|
|
1187
1358
|
errorKind: binding.error ? acpErrorKind(binding.error) : '',
|
|
1188
1359
|
stopReason: binding.stopReason,
|
|
1360
|
+
supportsSteer: binding.supportsSteer === true,
|
|
1189
1361
|
protocolVersion: binding.initializeResponse?.protocolVersion || null,
|
|
1190
1362
|
agentInfo: binding.initializeResponse?.agentInfo || null,
|
|
1191
1363
|
capabilities: binding.initializeResponse?.agentCapabilities || {},
|
|
@@ -1376,6 +1548,7 @@ class AcpRuntime extends EventEmitter {
|
|
|
1376
1548
|
error: binding.error,
|
|
1377
1549
|
errorKind: binding.error ? acpErrorKind(binding.error) : '',
|
|
1378
1550
|
stopReason: binding.stopReason,
|
|
1551
|
+
supportsSteer: binding.supportsSteer === true,
|
|
1379
1552
|
pendingPermission: binding.pendingPermissions.values().next().value || null,
|
|
1380
1553
|
pendingPermissions: [...binding.pendingPermissions.values()],
|
|
1381
1554
|
pendingElicitation: binding.pendingElicitations.values().next().value || null,
|
|
@@ -1452,8 +1625,13 @@ class AcpRuntime extends EventEmitter {
|
|
|
1452
1625
|
module.exports = {
|
|
1453
1626
|
AcpRuntime,
|
|
1454
1627
|
ADAPTER_VERSIONS,
|
|
1628
|
+
acpSessionRequestOptions,
|
|
1455
1629
|
acpErrorKind,
|
|
1456
1630
|
autoPermissionResponse,
|
|
1457
1631
|
codexAcpEnvironment,
|
|
1632
|
+
promptContentForCapabilities,
|
|
1458
1633
|
resolveAcpLaunch,
|
|
1634
|
+
supportsCodexSteer,
|
|
1635
|
+
isCodexSteerUnavailableError,
|
|
1636
|
+
CODEX_STEER_METHOD,
|
|
1459
1637
|
};
|